headless服务和envoy的使用

打印 上一主题 下一主题

主题 864|帖子 864|积分 2592

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

https://blog.51cto.com/u_15067223/4207308
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摘要。
  1. $ nslookup myapp
  2. Server: 10.40.0.10
  3. Address: 10.40.0.10#53
  4. Non-authoritative answer:
  5. Name: myapp.namespace.svc.cluster.local Address: 10.36.224.5
  6. Name: myapp.namespace.svc.cluster.local Address: 10.38.187.17
  7. Name: myapp.namespace.svc.cluster.local Address: 10.38.1.8
复制代码
Envoy的STRICT_DNS服务发现的工作方式是,它维护DNS返回的所有A记录的IP地址,并且每隔几秒钟刷新一次IP组。
2. 创建Envoy镜像

Dockerfile
  1. FROM envoyproxy/envoy:latest
  2. COPY envoy.yaml /tmpl/envoy.yaml.tmpl
  3. COPY docker-entrypoint.sh /
  4. RUN chmod 500 /docker-entrypoint.sh
  5. RUN apt-get update && \
  6.     apt-get install gettext -y && \
  7.     rm -rf /var/lib/apt/list/* && \
  8.     rm -rf /var/cache/apk/*
  9. ENTRYPOINT ["/docker-entrypoint.sh"]
复制代码
docker-entrypoint.sh
  1. #!/bin/sh
  2. set -e
  3. echo "Generating envoy.yaml config file..."
  4. cat /tmpl/envoy.yaml.tmpl | envsubst \$ENVOY_LB_ALG,\$SERVICE_NAME,\$SERVICE_PORT > /etc/envoy.yaml
  5. echo "Starting Envoy..."
  6. /usr/local/bin/envoy -c /etc/envoy.yaml
复制代码
envoy.yaml
  1. static_resources:
  2.   listeners:
  3.   - address:
  4.       socket_address:
  5.         address: 0.0.0.0
  6.         port_value: ${SERVICE_PORT}
  7.     filter_chains:
  8.     - filters:
  9.       - name: envoy.filters.network.http_connection_manager
  10.         typed_config:
  11.           "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
  12.           codec_type: auto
  13.           stat_prefix: ingress_http
  14.           access_log:
  15.           - name: envoy.access_loggers.file
  16.             typed_config:
  17.               "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
  18.               path: "/dev/stdout"
  19.           route_config:
  20.             name: local_route
  21.             virtual_hosts:
  22.             - name: backend
  23.               domains:
  24.               - "*"
  25.               routes:
  26.               - match:
  27.                   prefix: "/"
  28.                   grpc: {}
  29.                 route:
  30.                   cluster: backend_grpc_service
  31.           http_filters:
  32.           - name: envoy.filters.http.router
  33.             typed_config: {}
  34.   clusters:
  35.   - name: backend_grpc_service
  36.     connect_timeout: 0.250s
  37.     type: strict_dns
  38.     lb_policy: round_robin
  39.     http2_protocol_options: {}
  40.     load_assignment:
  41.       cluster_name: backend_grpc_service
  42.       endpoints:
  43.       - lb_endpoints:
  44.         - endpoint:
  45.             address:
  46.               socket_address:
  47.                 address: ${SERVICE_NAME}
  48.                 port_value: ${SERVICE_PORT}
复制代码
3. 创建Envoy deployment

最后,我们必须为Envoy本身创建一个部署。
service.yaml
  1. {
  2.   "kind": "Service",
  3.   "apiVersion": "v1",
  4.   "metadata": {
  5.     "name": "myapp-envoy-service",
  6.     "labels": {
  7.       "app": "myapp-envoy-service"
  8.     }
  9.   },
  10.   "spec": {
  11.     "ports": [
  12.       {
  13.         "name": "grpc",
  14.         "protocol": "TCP",
  15.         "port": 82,
  16.         "targetPort": 82
  17.       }
  18.     ],
  19.     "selector": {
  20.       "app": "myapp-envoy"
  21.     },
  22.     "type": "ClusterIP",
  23.     "sessionAffinity": "None"
  24.   },
  25.   "status": {
  26.     "loadBalancer": {}
  27.   }
  28. }
复制代码
deployment.yaml
  1. {
  2.   "kind": "Deployment",
  3.   "apiVersion": "extensions/v1beta1",
  4.   "metadata": {
  5.     "name": "myapp-envoy",
  6.     "labels": {
  7.       "app": "myapp-envoy"
  8.     },
  9.     "annotations": {
  10.       "deployment.kubernetes.io/revision": "1"
  11.     }
  12.   },
  13.   "spec": {
  14.     "replicas": 1,
  15.     "selector": {
  16.       "matchLabels": {
  17.         "app": "myapp-envoy"
  18.       }
  19.     },
  20.     "template": {
  21.       "metadata": {
  22.         "creationTimestamp": null,
  23.         "labels": {
  24.           "app": "myapp-envoy"
  25.         }
  26.       },
  27.       "spec": {
  28.         "containers": [
  29.           {
  30.             "name": "myapp-envoy",
  31.             "image": "****/envoy:latest",
  32.             "ports": [
  33.               {
  34.                 "name": "http",
  35.                 "containerPort": 82,
  36.                 "protocol": "TCP"
  37.               },
  38.               {
  39.                 "name": "envoy-admin",
  40.                 "containerPort": 8881,
  41.                 "protocol": "TCP"
  42.               }
  43.             ],
  44.             "env": [
  45.               {
  46.                 "name": "ENVOY_LB_ALG",
  47.                 "value": "LEAST_REQUEST"
  48.               },
  49.               {
  50.                 "name": "SERVICE_NAME",
  51.                 "value": "myapp2-service"
  52.               },
  53.               {
  54.                 "name": "SERVICE_PORT",
  55.                 "value": "82"
  56.               }
  57.             ],
  58.             "resources": {},
  59.             "terminationMessagePath": "/dev/termination-log",
  60.             "terminationMessagePolicy": "File",
  61.             "imagePullPolicy": "IfNotPresent"
  62.           }
  63.         ],
  64.         "restartPolicy": "Always",
  65.         "terminationGracePeriodSeconds": 30,
  66.         "dnsPolicy": "ClusterFirst",
  67.         "securityContext": {}
  68.         ],
  69.         "schedulerName": "default-scheduler"
  70.       }
  71.     },
  72.     "strategy": {
  73.       "type": "RollingUpdate",
  74.       "rollingUpdate": {
  75.         "maxUnavailable": 1,
  76.         "maxSurge": 1
  77.       }
  78.     },
  79.     "revisionHistoryLimit": 2147483647,
  80.     "progressDeadlineSeconds": 2147483647
  81.   }
  82. }
复制代码
仅当我们使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上的页面。
一些有用的端点:
  1. ​​/config_dump​​:打印代理的完整配置,这对于验证正确的配置是否最终在Pod上很有用。
  2. ​​/clusters​​:显示Envoy发现的所有上游节点,以及为每个上游节点处理的请求数。例如,这对于检查负载平衡算法是否正常工作很有用。
复制代码
进行监视的一种方法是使用Prometheus从代理pods获取统计信息。 Envoy对此提供了内置支持,Prometheus统计信息在管理端口上的/ stats/prometheus路由上发布。
您可以从该存储库下载可视化这些指标的Grafana仪表板,这将为您提供以下图表。
关于负载均衡算法

负载平衡算法会对集群的整体性能产生重大影响。对于需要均匀分配负载的服务(例如,当服务占用大量CPU并很容易超载时),使用最少请求算法可能是有益的。另一方面,最少请求的问题在于,如果某个节点由于某种原因开始发生故障,并且故障响应时间很快,那么负载均衡器会将不成比例的大部分请求发送给故障节点,循环负载均衡算法不会有问题。
我使用​ ​dummy API​​进行了一些基准测试,并比较了轮询和最少请求LB算法。事实证明,最少的请求可以带来整体性能的显着提高。
我使用不断增加的输入流量对API进行了约40分钟的基准测试。在整个基准测试中,我收集了以下指标:
  1. 服务器执行的请求数("requests in flight")
  2. 每台服务器平均正在执行的请求数
  3. 请求速率(每5分钟增加一次)
  4. 错误率(通常没有,但是当事情开始放慢时,这开始显示出超时)
  5. 服务器上记录的响应时间百分位数(0.50、0.90和0.99)
复制代码
ROUND_ROBIN的统计数据看起来像这样:
这些是LEAST_REQUEST的结果:
您可以从结果中看到LEAST_REQUEST可以导致流量在节点之间的分配更加顺畅,从而在高负载下降低了平均响应时间。
确切的改进取决于实际的API,因此,我绝对建议您也使用自己的服务进行基准测试,以便做出决定。
总结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

自由的羽毛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表