• 使用envoy在k8s中作grpc的负载均衡


    1. 为我们的应用创建headless服务

    在Kubernetes中,有一种称为headless服务的特定服务,恰好与Envoy的STRICT_DNS服务发现模式一起使用时非常方便。

    Headless服务不会为底层Pod提供单个IP和负载平衡,而只是具有DNS配置,该配置为我们提供了一个A记录,其中包含与标签选择器匹配的所有Pod的Pod IP地址。我们希望在实现负载平衡并自己维护与上游Pod的连接的情况下使用此服务类型,这正是我们使用Envoy可以做到的。

    我们可以通过将.spec.clusterIP字段设置为“None”来创建headless服务。因此,假设我们的应用程序pod的标签app的值为myapp,我们可以使用以下yaml创建headless服务。

    现在,如果我们在Kubernetes集群中检查服务的DNS记录,我们将看到带有IP地址的单独的A记录。如果我们有3个Pod,则会看到与此类似的DNS摘要。

    $ nslookup myapp 
    Server: 10.40.0.10 
    Address: 10.40.0.10#53 
    
    Non-authoritative answer: 
    Name: myapp.namespace.svc.cluster.local Address: 10.36.224.5 
    Name: myapp.namespace.svc.cluster.local Address: 10.38.187.17 
    Name: myapp.namespace.svc.cluster.local Address: 10.38.1.8

    Envoy的STRICT_DNS服务发现的工作方式是,它维护DNS返回的所有A记录的IP地址,并且每隔几秒钟刷新一次IP组。

    2. 创建Envoy镜像

    Dockerfile

    FROM envoyproxy/envoy:latest
    
    COPY envoy.yaml /tmpl/envoy.yaml.tmpl
    COPY docker-entrypoint.sh /
    
    RUN chmod 500 /docker-entrypoint.sh
    
    RUN apt-get update && 
        apt-get install gettext -y && 
        rm -rf /var/lib/apt/list/* && 
        rm -rf /var/cache/apk/*
    
    ENTRYPOINT ["/docker-entrypoint.sh"]

    docker-entrypoint.sh

    #!/bin/sh
    set -e
    
    echo "Generating envoy.yaml config file..."
    cat /tmpl/envoy.yaml.tmpl | envsubst $ENVOY_LB_ALG,$SERVICE_NAME,$SERVICE_PORT > /etc/envoy.yaml
    
    echo "Starting Envoy..."
    /usr/local/bin/envoy -c /etc/envoy.yaml

    envoy.yaml

    static_resources:
      listeners:
      - address:
          socket_address:
            address: 0.0.0.0
            port_value: ${SERVICE_PORT}
        filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
              codec_type: auto
              stat_prefix: ingress_http
              access_log:
              - name: envoy.access_loggers.file
                typed_config:
                  "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
                  path: "/dev/stdout"
              route_config:
                name: local_route
                virtual_hosts:
                - name: backend
                  domains:
                  - "*"
                  routes:
                  - match:
                      prefix: "/" 
                      grpc: {}
                    route:
                      cluster: backend_grpc_service
              http_filters:
              - name: envoy.filters.http.router
                typed_config: {}
      clusters:
      - name: backend_grpc_service
        connect_timeout: 0.250s
        type: strict_dns
        lb_policy: round_robin
        http2_protocol_options: {}
        load_assignment:
          cluster_name: backend_grpc_service
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: ${SERVICE_NAME}
                    port_value: ${SERVICE_PORT}

    3. 创建Envoy deployment

    最后,我们必须为Envoy本身创建一个部署。

    service.yaml

    {
      "kind": "Service",
      "apiVersion": "v1",
      "metadata": {
        "name": "myapp-envoy-service",
        "labels": {
          "app": "myapp-envoy-service"
        }
      },
      "spec": {
        "ports": [
          {
            "name": "grpc",
            "protocol": "TCP",
            "port": 82,
            "targetPort": 82
          }
        ],
        "selector": {
          "app": "myapp-envoy"
        },
        "type": "ClusterIP",
        "sessionAffinity": "None"
      },
      "status": {
        "loadBalancer": {}
      }
    }

    deployment.yaml

    {
      "kind": "Deployment",
      "apiVersion": "extensions/v1beta1",
      "metadata": {
        "name": "myapp-envoy",
        "labels": {
          "app": "myapp-envoy"
        },
        "annotations": {
          "deployment.kubernetes.io/revision": "1"
        }
      },
      "spec": {
        "replicas": 1,
        "selector": {
          "matchLabels": {
            "app": "myapp-envoy"
          }
        },
        "template": {
          "metadata": {
            "creationTimestamp": null,
            "labels": {
              "app": "myapp-envoy"
            }
          },
          "spec": {
            "containers": [
              {
                "name": "myapp-envoy",
                "image": "****/envoy:latest",
                "ports": [
                  {
                    "name": "http",
                    "containerPort": 82,
                    "protocol": "TCP"
                  },
                  {
                    "name": "envoy-admin",
                    "containerPort": 8881,
                    "protocol": "TCP"
                  }
                ],
                "env": [
                  {
                    "name": "ENVOY_LB_ALG",
                    "value": "LEAST_REQUEST"
                  },
                  {
                    "name": "SERVICE_NAME",
                    "value": "myapp2-service"
                  },
                  {
                    "name": "SERVICE_PORT",
                    "value": "82"
                  }
                ],
                "resources": {},
                "terminationMessagePath": "/dev/termination-log",
                "terminationMessagePolicy": "File",
                "imagePullPolicy": "IfNotPresent"
              }
            ],
            "restartPolicy": "Always",
            "terminationGracePeriodSeconds": 30,
            "dnsPolicy": "ClusterFirst",
            "securityContext": {}
            ],
            "schedulerName": "default-scheduler"
          }
        },
        "strategy": {
          "type": "RollingUpdate",
          "rollingUpdate": {
            "maxUnavailable": 1,
            "maxSurge": 1
          }
        },
        "revisionHistoryLimit": 2147483647,
        "progressDeadlineSeconds": 2147483647
      }
    }

    仅当我们使Envoy Docker镜像可参数化时,才需要env变量。

    Apply此Yaml后,Envoy代理应该可以运行,并且您可以通过将请求发送到Envoy服务的主端口来访问基础服务。

    故障排除和监视

    在Envoy配置文件中,您可以看到admin:部分,用于配置Envoy的管理端点。可用于检查有关代理的各种诊断信息。

    如果您没有发布admin端口的服务,默认情况下为9901,您仍然可以通过端口转发到带有kubectl的容器来访问它。假设其中一个Envoy容器称为myapp-envoy-656c8d5fff-mwff8,那么您可以使用命令kubectl port-forward myapp-envoy-656c8d5fff-mwff8 9901开始端口转发。然后您可以访问http://localhost:9901上的页面。

    一些有用的端点:

    • /config_dump:打印代理的完整配置,这对于验证正确的配置是否最终在Pod上很有用。
    • /clusters:显示Envoy发现的所有上游节点,以及为每个上游节点处理的请求数。例如,这对于检查负载平衡算法是否正常工作很有用。

    进行监视的一种方法是使用Prometheus从代理pods获取统计信息。 Envoy对此提供了内置支持,Prometheus统计信息在管理端口上的/ stats/prometheus路由上发布。

    您可以从该存储库下载可视化这些指标的Grafana仪表板,这将为您提供以下图表。

    b-08.png

    关于负载均衡算法

    负载平衡算法会对集群的整体性能产生重大影响。对于需要均匀分配负载的服务(例如,当服务占用大量CPU并很容易超载时),使用最少请求算法可能是有益的。另一方面,最少请求的问题在于,如果某个节点由于某种原因开始发生故障,并且故障响应时间很快,那么负载均衡器会将不成比例的大部分请求发送给故障节点,循环负载均衡算法不会有问题。

    我使用dummy API进行了一些基准测试,并比较了轮询和最少请求LB算法。事实证明,最少的请求可以带来整体性能的显着提高。

    我使用不断增加的输入流量对API进行了约40分钟的基准测试。在整个基准测试中,我收集了以下指标:

    • 服务器执行的请求数("requests in flight")
    • 每台服务器平均正在执行的请求数
    • 请求速率(每5分钟增加一次)
    • 错误率(通常没有,但是当事情开始放慢时,这开始显示出超时)
    • 服务器上记录的响应时间百分位数(0.50、0.90和0.99)

    ROUND_ROBIN的统计数据看起来像这样:
    b-09.png

    这些是LEAST_REQUEST的结果:
    b-10.png

    您可以从结果中看到LEAST_REQUEST可以导致流量在节点之间的分配更加顺畅,从而在高负载下降低了平均响应时间。

    确切的改进取决于实际的API,因此,我绝对建议您也使用自己的服务进行基准测试,以便做出决定。

    总结

    我希望此介绍对在Kubernetes中使用Envoy有所帮助。顺便说一下,这不是在Kubernetes上实现最少请求负载平衡的唯一方法。可以执行相同操作的各种ingress控制器(其中一个是在Envoy之上构建的Ambassador)。

    参考:https://segmentfault.com/a/1190000021800561

  • 相关阅读:
    React: React的组件状态机制
    React: React的复合组件
    JavaScript:ES6的新特性
    React: 研究React的组件化
    React: 认识React
    CSS:CSS弹性盒子布局 Flexible Box
    iOS:应用程序扩展开发之Today扩展(Today Extesnsion)
    《逆向工程核心原理》
    《左手数据,右手图表》
    《设计模式之禅(第2版)》
  • 原文地址:https://www.cnblogs.com/zl1991/p/12938795.html
Copyright © 2020-2023  润新知