istio支持thrift吗,如果没有,后面有没有计划支持

回复

Istioliupengfei 发起了问题 • 1 人关注 • 0 个回复 • 9 次浏览 • 3 小时前 • 来自相关话题

开发环境下,如何使用Kubernete DNS host?

交流问答小数 回复了问题 • 2 人关注 • 1 个回复 • 82 次浏览 • 18 小时前 • 来自相关话题

Ambassador和Istio:边缘代理和服务网格

Istio小数 发表了文章 • 0 个评论 • 25 次浏览 • 1 天前 • 来自相关话题

 作者:Richard Li

翻译:王斌


原文:Ambassador and Istio: Edge proxy and service mesh




Ambassador(https://www.getambassador.io)是一个 Kubernetes 原生的微服务 API 网关,它部署在网络边缘,将传入网络的流量路由到相应的内部服务(也被称为“南北”流量)。Istio 是微服务的服务网格,旨在将L7的可观察性、路由和弹性加入到从服务到服务的流量中(也被称为“东西”流量)。 Istio 和 Ambassador底层都使用了 Envoy。

Ambassador 和 Istio 可以一起部署在 Kubernetes 上。在这种部署方式下,来自集群外部的入站流量首先会经过 Ambassador,再由 Ambassador将流量路由到 Istio。Ambassador 主要处理认证、边缘路由、TLS终结,以及一些传统的边缘功能。

这种部署方式能让运维人员得到一个高性能、现代化的边缘服务(Ambassador)与最先进的服务网格(Istio)相结合的网络。虽然 Istio 本身就有一个基本入口控制器,但其功能非常有限,并且不支持身份验证以及 Ambassador所拥有的许多功能。







Ambassador和 Istio 协同工作

要让 Ambassador 与 Istio 协同工作其实很简单,我们以 Istio 的bookinfo示例来举个例子:





1. 首先,在 Kubernetes 上安装 Istio。

参考指南:https://istio.io/docs/setup/ku ... .html

2. 然后,安装 Bookinfo 示例应用。

参考指南:https://istio.io/docs/guides/bookinfo.html

3. 验证示例是否按预期正常工作。




Bookinfo 示例默认使用的是 Istio 的入口控制器,要使用 Ambassador,我们还需要进行以下操作:

1. 安装Ambassador。

参考指南:https://www.getambassador.io/u ... arted

2. 更新 bookinfo.yaml 清单以包含必要的Ambassador注解,操作如下:


apiVersion: v1
kind: Service
metadata:
 name: productpage
 labels:
   app: productpage
 annotations:
   getambassador.io/config: |
      ---
     apiVersion: ambassador/v0
     kind: Mapping
     name: productpage_mapping
     prefix: /productpage/
     rewrite: /productpage
     service: productpage:9080
spec:
 ports:
 - port: 9080
   name: http
 selector:
   app: productpage


输入kubectl delete ingress gateway命令,这将会删除bookinfo.yaml 清单中的入口控制器。此步骤为可选。




访问$AMBASSADOR_IP/productpage/,测试 Ambassador 是否已经起作用。可以通过 kubectl get services ambassador 命令来获取 Ambassador 的实际IP地址。







Sidecar注入

新版本的 Istio 支持 Kubernetes 初始化程序自动注入Istio Sidecar。有了Ambassador,你不再需要注入Istio Sidecar,因为 Ambassador 的 Envoy 实例将自动路由到相应的服务。如果你正在使用的是自动Sidecar注入方式,那么需要将 Istio 配置成不要自动为 Ambassador pods 注入Sidecar。具体操作方法可以参阅这份说明文档。

文档链接:https://istio.io/docs/setup/ku ... tions


点击阅读原文,即可查看文档



  查看全部
 作者:Richard Li

翻译:王斌


原文:Ambassador and Istio: Edge proxy and service mesh




Ambassador(https://www.getambassador.io)是一个 Kubernetes 原生的微服务 API 网关,它部署在网络边缘,将传入网络的流量路由到相应的内部服务(也被称为“南北”流量)。Istio 是微服务的服务网格,旨在将L7的可观察性、路由和弹性加入到从服务到服务的流量中(也被称为“东西”流量)。 Istio 和 Ambassador底层都使用了 Envoy。

Ambassador 和 Istio 可以一起部署在 Kubernetes 上。在这种部署方式下,来自集群外部的入站流量首先会经过 Ambassador,再由 Ambassador将流量路由到 Istio。Ambassador 主要处理认证、边缘路由、TLS终结,以及一些传统的边缘功能。

这种部署方式能让运维人员得到一个高性能、现代化的边缘服务(Ambassador)与最先进的服务网格(Istio)相结合的网络。虽然 Istio 本身就有一个基本入口控制器,但其功能非常有限,并且不支持身份验证以及 Ambassador所拥有的许多功能。







Ambassador和 Istio 协同工作

要让 Ambassador 与 Istio 协同工作其实很简单,我们以 Istio 的bookinfo示例来举个例子:





1. 首先,在 Kubernetes 上安装 Istio。

参考指南:https://istio.io/docs/setup/ku ... .html

2. 然后,安装 Bookinfo 示例应用。

参考指南:https://istio.io/docs/guides/bookinfo.html

3. 验证示例是否按预期正常工作。




Bookinfo 示例默认使用的是 Istio 的入口控制器,要使用 Ambassador,我们还需要进行以下操作:

1. 安装Ambassador。

参考指南:https://www.getambassador.io/u ... arted

2. 更新 bookinfo.yaml 清单以包含必要的Ambassador注解,操作如下:


apiVersion: v1
kind: Service
metadata:
 name: productpage
 labels:
   app: productpage
 annotations:
   getambassador.io/config: |
      ---
     apiVersion: ambassador/v0
     kind: Mapping
     name: productpage_mapping
     prefix: /productpage/
     rewrite: /productpage
     service: productpage:9080
spec:
 ports:
 - port: 9080
   name: http
 selector:
   app: productpage


输入kubectl delete ingress gateway命令,这将会删除bookinfo.yaml 清单中的入口控制器。此步骤为可选。




访问$AMBASSADOR_IP/productpage/,测试 Ambassador 是否已经起作用。可以通过 kubectl get services ambassador 命令来获取 Ambassador 的实际IP地址。







Sidecar注入

新版本的 Istio 支持 Kubernetes 初始化程序自动注入Istio Sidecar。有了Ambassador,你不再需要注入Istio Sidecar,因为 Ambassador 的 Envoy 实例将自动路由到相应的服务。如果你正在使用的是自动Sidecar注入方式,那么需要将 Istio 配置成不要自动为 Ambassador pods 注入Sidecar。具体操作方法可以参阅这份说明文档。

文档链接:https://istio.io/docs/setup/ku ... tions


点击阅读原文,即可查看文档



 

专题 | A Service Mesh for Kubernetes第3期:DogFood环境,Ingress和Edge路由

Linkerd小数 发表了文章 • 0 个评论 • 23 次浏览 • 1 天前 • 来自相关话题

 概述

在这篇文章中,我们将向您展示如何使用Linkerd实现的Service Mesh来处理Kubernetes上的入口流量,在Service Mesh中的每个实例上分发流量。我们还将通过一个完整的例子来介绍Linkerd的高级路由功能:如何将特定的请求路由到较新版本的应用实例上,比如用于内部测试的、预发布的应用版本。

这篇文章是关于使用Linkerd作为到Kubernetes网络流量的入口点。 从0.9.1开始,Linkerd直接支持Kubernetes的Ingress资源,这对于本文中的一些用例来说是一个可替代的,也是更简单的起点。

注意: 这是关于Linkerd、Kubernetes和service mesh的系列文章其中一篇,其余部分包括:

1.Top-line service metrics
2.Pods are great, until they’re not
3.Encrypting all the things
4.Continuous deployment via traffic shifting
5.Dogfood environments, ingress, and edge routing(本文)
6.Staging microservices without the tears
7.Distributed tracing made easy
8.Linkerd as an ingress controller
9.gRPC for fun and profit
10.The Service Mesh API
11.Egress
12.Retry budgets, deadline propagation, and failing gracefully
13.Autoscaling by top-line metrics

在本系列的前几个部分,我们向您展示了如何使用Linkerd来捕获top-line的服务指标,在服务中透明地添加TLS以及执行蓝绿发布。这些文章展示了如Kubernetes这样的环境中如何使用Linkerd作为Service Mesh的组件,为内部服务到服务调用中添加了一层弹性和性能的保障,在本篇文章中,我们将这个模型扩展到入口路由。

虽然这篇文章以Kubernetes为例,但我们不会使用Kubernetes内置的Ingress资源对象。虽然Ingress提供了一种便捷的基于宿主机和基本路径的路由方法,但在本文撰写时,Kubernetes Ingress的功能是相当有限的。在下面的例子中,我们将远远超出Ingress提供的范围。



1、发布Linkerd Service Mesh

从以前文章中基本的Linkerd Service Mesh配置开始,我们进行两个更改来支持Ingress:我们将修改Linkerd的配置用以添加一个额外的逻辑路由器,我们在Kubernetes Service资源对象中调整VIP在Linkerd中的范围。

在Linkerd的实例提供了一个新的逻辑路由器,用于处理入口流量并将其路由到相应的服务:
yaml routers: - protocol: http  label: ingress  dtab: |    /srv                    => /#/io.l5d.k8s/default/http ;    /domain/world/hello/www => /srv/hello ;    /domain/world/hello/api => /srv/api ;    /host                  => /$/io.buoyant.http.domainToPathPfx/domain ;    /svc                    => /host ;  interpreter:    kind: default    transformers:    - kind: io.l5d.k8s.daemonset      namespace: default      port: incoming      service: l5d  servers:  - port: 4142    ip: 0.0.0.0在这个配置中,我们使用Linkerd的路由语法,dtabs将请求从域名传递到服务——在这种情况下从api.hello.world传递到api服务,从www.hello.world到hello服务。为了简单起见,我们已经为每个域添加了一个规则,但是对于更复杂的设置,也可以轻松地生成映射规则。

我们已经将这个入口路由器添加到每个Linkerd实例中 - 以真正Service Mesh的方式,我们将在这些实例中完全分配入口流量,使得应用不存在单点故障。

我们还需要修改Kubernetes Service对象,以在端口80上用非入口VIP替换出口的VIP——这将允许我们直接将出口流量发送到Linkerd的Service Mesh中,主要是用于调试的目的,因为这个流量在到达Linkerd之前不会被审查(在下一步,我们将解决这个问题)。

对Kubernetes Service修改如下:
apiVersion: v1 kind: Service metadata:  name: l5d spec:  selector:    app: l5d  type: LoadBalancer  ports:  - name: ingress    port: 80    targetPort: 4142  - name: incoming    port: 4141  - name: admin    port: 9990
以上所有的修改都可以通过简单运行一个命令就能生效,细节请查看:
$ kubectl apply -f https://raw.githubusercontent. ... s.yml





2、部署服务

对于此例中的Service,我们将使用早先发布的博客使用到的例子hello and world configs,并且我们会添加两个新的service:一个api service,其用来调用hello和world,还有world service的新版本:world-v2,其将会返回一个“earth”而非“world”——我们扩大黑客团队已经向我们保证,他们的A/B测试显示这一变化将使参与度提高10倍。

下面的命令将会把hello world services部署在default命名空间下。这些应用依赖于Kubernetes downward API提供的节点名来发现Linkerd。为了检查你的集群是否支持节点名,你可以运行该测试任务:k
ubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/node-name-test.yml 
然后看它的日志信息:kubectl logs node-name-test如果你看到了IP,那就非常棒,接着使用如下命令部署hello world应用:$ kubectl apply -f https://raw.githubusercontent. ... d.yml $ kubectl apply -f https://raw.githubusercontent. ... i.yml $ kubectl apply -f https://raw.githubusercontent. ... 2.yml如果你看到的是“server can’t find…”这样的报错信息,请部署hello-world遗留版本,该版本依赖于hostIP而不是节点名:$ kubectl apply -f https://raw.githubusercontent. ... y.yml $ kubectl apply -f https://raw.githubusercontent. ... y.yml $ kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/world-v2.yml 
 
这时我们应该能够通过入口Kubernetes VIP发送流量来测试设备,在没有使用DNS的情况下,我们将在请求中手动设置一个Host Header:
$ INGRESS_LB=$(kubectl get svc l5d -o jsonpath="{.status.loadBalancer.ingress[0].*}") $ curl -s -H "Host: www.hello.world" $INGRESS_LB Hello (10.0.5.7) world (10.0.4.7)!! $ curl -s -H "Host: api.hello.world" $INGRESS_LB {"api_result":"api (10.0.3.6) Hello (10.0.5.4) world (10.0.1.5)!!"}
或者如果集群不支持外部负载均衡器,请使用hostIP:$ INGRESS_LB=$(kubectl get po -l app=l5d -o jsonpath="{.items[0].status.hostIP}"):$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}')成功了!
我们已经将Linkerd设置为入口控制器,并且我们已经使用它将不同域中收到的请求路由到不同的服务。正如您所见,生产流量正冲击world-v1服务——我们还没准备要将world-v2推出。



3、Nginx层

至此ingress已经可以使用了。但还不能用于生产环境中。因为我们的ingress路由器不能将header从请求中提取出来。这就意味着我们不能接收包含header的外部请求。例如,Linkerd允许设置15d-dtab头部以按请求应用路由规则,这对于新服务的临时分级是一个有用的功能,但这可能不适合于来自外部的请求。

例如,我们可以使用15d-dtab请求头覆盖路由逻辑来使用world-v2而不是world-v1来服务外部请求:$ curl -H "Host: www.hello.world" -H "l5d-dtab: /host/world => /srv/world-v2;" $INGRESS_LB Hello (10.100.4.3) earth (10.100.5.5)!!当earth作为响应,意味着这是world-v2服务的结果。

我们通过添加nginx来解决这个问题(或者其他问题如服务静态文件)。如果我们在代理请求到linkerd ingress路由之前配置nginx来提取输入的header,我们将会两全其美:有能力安全处理外部流量的ingress层并且Linkerd做动态的、基于服务的路由。

让我们添加nginx到集群中。使用this nginx conf来配置它。我们将在www.hello.world和api.hello.world虚拟服务器下使用proxy_pass命令来发送请求给Linkerd实例,并且我们将使用Headers More模块提供的more_clear_input_headers命令来提取linkerd's context headers。

对于linkerd 0.9.0,我们可以通过在ingress路由器服务器上设置clearContext:true来清除输入的15d-*请求头。然而,nginx有许多我们可以使用到的特性,所以使用nginx连接Linkerd很有价值。

我们已经发布了一个安装了Headers More模块的Docker镜像:buoyantio/nginx:1.11.5。
我们用该配置部署这个镜像:
$ kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/nginx.yml 
 
在等待外部IP出现后,我们可以通过点击nginx.conf中的简单测试端点来测试nginx是否启动:$ INGRESS_LB=$(kubectl get svc nginx -o jsonpath="{.status.loadBalancer.ingress[0].*}") $ curl $INGRESS_LB 200 OK
或者如果集群的外部负载均衡器不可用,使用
hostIP:$ INGRESS_LB=$(kubectl get po -l app=nginx -o jsonpath="{.items[0].status.hostIP}"):$(kubectl get svc nginx -o 'jsonpath={.spec.ports[0].nodePort}')
 
我们现在应用已经可以通过nginx发送流量给我们的服务了:$ curl -s -H "Host: www.hello.world" $INGRESS_LB Hello (10.0.5.7) world (10.0.4.7)!! $ curl -s -H "Host: api.hello.world" $INGRESS_LB {"api_result":"api (10.0.3.6) Hello (10.0.5.4) world (10.0.1.5)!!"}
 
最后,让我们尝试直接与world-v2服务通信:$ curl -H "Host: www.hello.world" -H "l5d-dtab: /host/world => /srv/world-v2;" $INGRESS_LB Hello (10.196.1.8) world (10.196.2.13)!!没有earth,Nginx正在净化外部流量。



4、简述Dogfood

好了,到了重点部分:让我们使用world-v1服务来配置dogfood的环境,不过这仅针对一些流量。

为简化问题,我们只关注设置了特殊cookiespecial_employee_cookie的流量。在实践中,你可能想要比这更复杂的情况如验证它,要求它来自公司网络IP范围等。

用nginx和Linkerd安装,完成这个相当简单。我们将会使用nginx来检查该cookie是否存在,为linkerd设置一个dtab覆盖头来调整其路由。有关的nginx配置如下:
if ($cookie_special_employee_cookie ~* "dogfood") {  set $xheader "/host/world => /srv/world-v2;"; } proxy_set_header 'l5d-dtab' $xheader;
如果你已经按以上步骤做了,被部署的nginx已经包含了这个配置。我们可以用如下来进行测试:
$ curl -H "Host: www.hello.world" --cookie "special_employee_cookie=dogfood" $INGRESS_LB Hello (10.196.1.8) earth (10.196.2.13)!!
系统已经工作了。当这个cookie被设置了,你将置于dogfood模式下。如果没有它,你将置于常规生产流量模式。最重要的是,dogfood模式可以包含在service stack到处出现的service的新版本,甚至很多层——只要服务代码转发Linkerd的上下文头,Linkerd服务网格将负责其余部分。



结束语

在本文中,我们看到了如何使用Linkerd给Kubernetes集群提供有力而又灵活的入口。我们已经演示了如何部署名义上的生产就绪设置,使用Linkerd进行服务路由。我们已经演示了如何使用Linkerd的一些高级路由功能来将流量服务拓扑从部署拓扑结构中分离出来,从而允许创建dogfood环境而不需要单独的集群或部署时间复杂性。 查看全部
 概述

在这篇文章中,我们将向您展示如何使用Linkerd实现的Service Mesh来处理Kubernetes上的入口流量,在Service Mesh中的每个实例上分发流量。我们还将通过一个完整的例子来介绍Linkerd的高级路由功能:如何将特定的请求路由到较新版本的应用实例上,比如用于内部测试的、预发布的应用版本。

这篇文章是关于使用Linkerd作为到Kubernetes网络流量的入口点。 从0.9.1开始,Linkerd直接支持Kubernetes的Ingress资源,这对于本文中的一些用例来说是一个可替代的,也是更简单的起点。

注意: 这是关于Linkerd、Kubernetes和service mesh的系列文章其中一篇,其余部分包括:

1.Top-line service metrics
2.Pods are great, until they’re not
3.Encrypting all the things
4.Continuous deployment via traffic shifting
5.Dogfood environments, ingress, and edge routing(本文)
6.Staging microservices without the tears
7.Distributed tracing made easy
8.Linkerd as an ingress controller
9.gRPC for fun and profit
10.The Service Mesh API
11.Egress
12.Retry budgets, deadline propagation, and failing gracefully
13.Autoscaling by top-line metrics

在本系列的前几个部分,我们向您展示了如何使用Linkerd来捕获top-line的服务指标,在服务中透明地添加TLS以及执行蓝绿发布。这些文章展示了如Kubernetes这样的环境中如何使用Linkerd作为Service Mesh的组件,为内部服务到服务调用中添加了一层弹性和性能的保障,在本篇文章中,我们将这个模型扩展到入口路由。

虽然这篇文章以Kubernetes为例,但我们不会使用Kubernetes内置的Ingress资源对象。虽然Ingress提供了一种便捷的基于宿主机和基本路径的路由方法,但在本文撰写时,Kubernetes Ingress的功能是相当有限的。在下面的例子中,我们将远远超出Ingress提供的范围。



1、发布Linkerd Service Mesh

从以前文章中基本的Linkerd Service Mesh配置开始,我们进行两个更改来支持Ingress:我们将修改Linkerd的配置用以添加一个额外的逻辑路由器,我们在Kubernetes Service资源对象中调整VIP在Linkerd中的范围。

在Linkerd的实例提供了一个新的逻辑路由器,用于处理入口流量并将其路由到相应的服务:
yaml routers: - protocol: http  label: ingress  dtab: |    /srv                    => /#/io.l5d.k8s/default/http ;    /domain/world/hello/www => /srv/hello ;    /domain/world/hello/api => /srv/api ;    /host                  => /$/io.buoyant.http.domainToPathPfx/domain ;    /svc                    => /host ;  interpreter:    kind: default    transformers:    - kind: io.l5d.k8s.daemonset      namespace: default      port: incoming      service: l5d  servers:  - port: 4142    ip: 0.0.0.0在这个配置中,我们使用Linkerd的路由语法,dtabs将请求从域名传递到服务——在这种情况下从api.hello.world传递到api服务,从www.hello.world到hello服务。为了简单起见,我们已经为每个域添加了一个规则,但是对于更复杂的设置,也可以轻松地生成映射规则。

我们已经将这个入口路由器添加到每个Linkerd实例中 - 以真正Service Mesh的方式,我们将在这些实例中完全分配入口流量,使得应用不存在单点故障。

我们还需要修改Kubernetes Service对象,以在端口80上用非入口VIP替换出口的VIP——这将允许我们直接将出口流量发送到Linkerd的Service Mesh中,主要是用于调试的目的,因为这个流量在到达Linkerd之前不会被审查(在下一步,我们将解决这个问题)。

对Kubernetes Service修改如下:
apiVersion: v1 kind: Service metadata:  name: l5d spec:  selector:    app: l5d  type: LoadBalancer  ports:  - name: ingress    port: 80    targetPort: 4142  - name: incoming    port: 4141  - name: admin    port: 9990
以上所有的修改都可以通过简单运行一个命令就能生效,细节请查看:
$ kubectl apply -f https://raw.githubusercontent. ... s.yml





2、部署服务

对于此例中的Service,我们将使用早先发布的博客使用到的例子hello and world configs,并且我们会添加两个新的service:一个api service,其用来调用hello和world,还有world service的新版本:world-v2,其将会返回一个“earth”而非“world”——我们扩大黑客团队已经向我们保证,他们的A/B测试显示这一变化将使参与度提高10倍。

下面的命令将会把hello world services部署在default命名空间下。这些应用依赖于Kubernetes downward API提供的节点名来发现Linkerd。为了检查你的集群是否支持节点名,你可以运行该测试任务:k
ubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/node-name-test.yml 
然后看它的日志信息:kubectl logs node-name-test如果你看到了IP,那就非常棒,接着使用如下命令部署hello world应用:$ kubectl apply -f https://raw.githubusercontent. ... d.yml $ kubectl apply -f https://raw.githubusercontent. ... i.yml $ kubectl apply -f https://raw.githubusercontent. ... 2.yml如果你看到的是“server can’t find…”这样的报错信息,请部署hello-world遗留版本,该版本依赖于hostIP而不是节点名:$ kubectl apply -f https://raw.githubusercontent. ... y.yml $ kubectl apply -f https://raw.githubusercontent. ... y.yml $ kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/world-v2.yml 
 
这时我们应该能够通过入口Kubernetes VIP发送流量来测试设备,在没有使用DNS的情况下,我们将在请求中手动设置一个Host Header:
$ INGRESS_LB=$(kubectl get svc l5d -o jsonpath="{.status.loadBalancer.ingress[0].*}") $ curl -s -H "Host: www.hello.world" $INGRESS_LB Hello (10.0.5.7) world (10.0.4.7)!! $ curl -s -H "Host: api.hello.world" $INGRESS_LB {"api_result":"api (10.0.3.6) Hello (10.0.5.4) world (10.0.1.5)!!"}
或者如果集群不支持外部负载均衡器,请使用hostIP:$ INGRESS_LB=$(kubectl get po -l app=l5d -o jsonpath="{.items[0].status.hostIP}"):$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}')成功了!
我们已经将Linkerd设置为入口控制器,并且我们已经使用它将不同域中收到的请求路由到不同的服务。正如您所见,生产流量正冲击world-v1服务——我们还没准备要将world-v2推出。



3、Nginx层

至此ingress已经可以使用了。但还不能用于生产环境中。因为我们的ingress路由器不能将header从请求中提取出来。这就意味着我们不能接收包含header的外部请求。例如,Linkerd允许设置15d-dtab头部以按请求应用路由规则,这对于新服务的临时分级是一个有用的功能,但这可能不适合于来自外部的请求。

例如,我们可以使用15d-dtab请求头覆盖路由逻辑来使用world-v2而不是world-v1来服务外部请求:$ curl -H "Host: www.hello.world" -H "l5d-dtab: /host/world => /srv/world-v2;" $INGRESS_LB Hello (10.100.4.3) earth (10.100.5.5)!!当earth作为响应,意味着这是world-v2服务的结果。

我们通过添加nginx来解决这个问题(或者其他问题如服务静态文件)。如果我们在代理请求到linkerd ingress路由之前配置nginx来提取输入的header,我们将会两全其美:有能力安全处理外部流量的ingress层并且Linkerd做动态的、基于服务的路由。

让我们添加nginx到集群中。使用this nginx conf来配置它。我们将在www.hello.world和api.hello.world虚拟服务器下使用proxy_pass命令来发送请求给Linkerd实例,并且我们将使用Headers More模块提供的more_clear_input_headers命令来提取linkerd's context headers。

对于linkerd 0.9.0,我们可以通过在ingress路由器服务器上设置clearContext:true来清除输入的15d-*请求头。然而,nginx有许多我们可以使用到的特性,所以使用nginx连接Linkerd很有价值。

我们已经发布了一个安装了Headers More模块的Docker镜像:buoyantio/nginx:1.11.5。
我们用该配置部署这个镜像:
$ kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/nginx.yml 
 
在等待外部IP出现后,我们可以通过点击nginx.conf中的简单测试端点来测试nginx是否启动:$ INGRESS_LB=$(kubectl get svc nginx -o jsonpath="{.status.loadBalancer.ingress[0].*}") $ curl $INGRESS_LB 200 OK
或者如果集群的外部负载均衡器不可用,使用
hostIP:$ INGRESS_LB=$(kubectl get po -l app=nginx -o jsonpath="{.items[0].status.hostIP}"):$(kubectl get svc nginx -o 'jsonpath={.spec.ports[0].nodePort}')
 
我们现在应用已经可以通过nginx发送流量给我们的服务了:$ curl -s -H "Host: www.hello.world" $INGRESS_LB Hello (10.0.5.7) world (10.0.4.7)!! $ curl -s -H "Host: api.hello.world" $INGRESS_LB {"api_result":"api (10.0.3.6) Hello (10.0.5.4) world (10.0.1.5)!!"}
 
最后,让我们尝试直接与world-v2服务通信:$ curl -H "Host: www.hello.world" -H "l5d-dtab: /host/world => /srv/world-v2;" $INGRESS_LB Hello (10.196.1.8) world (10.196.2.13)!!没有earth,Nginx正在净化外部流量。



4、简述Dogfood

好了,到了重点部分:让我们使用world-v1服务来配置dogfood的环境,不过这仅针对一些流量。

为简化问题,我们只关注设置了特殊cookiespecial_employee_cookie的流量。在实践中,你可能想要比这更复杂的情况如验证它,要求它来自公司网络IP范围等。

用nginx和Linkerd安装,完成这个相当简单。我们将会使用nginx来检查该cookie是否存在,为linkerd设置一个dtab覆盖头来调整其路由。有关的nginx配置如下:
if ($cookie_special_employee_cookie ~* "dogfood") {  set $xheader "/host/world => /srv/world-v2;"; } proxy_set_header 'l5d-dtab' $xheader;
如果你已经按以上步骤做了,被部署的nginx已经包含了这个配置。我们可以用如下来进行测试:
$ curl -H "Host: www.hello.world" --cookie "special_employee_cookie=dogfood" $INGRESS_LB Hello (10.196.1.8) earth (10.196.2.13)!!
系统已经工作了。当这个cookie被设置了,你将置于dogfood模式下。如果没有它,你将置于常规生产流量模式。最重要的是,dogfood模式可以包含在service stack到处出现的service的新版本,甚至很多层——只要服务代码转发Linkerd的上下文头,Linkerd服务网格将负责其余部分。



结束语

在本文中,我们看到了如何使用Linkerd给Kubernetes集群提供有力而又灵活的入口。我们已经演示了如何部署名义上的生产就绪设置,使用Linkerd进行服务路由。我们已经演示了如何使用Linkerd的一些高级路由功能来将流量服务拓扑从部署拓扑结构中分离出来,从而允许创建dogfood环境而不需要单独的集群或部署时间复杂性。

3种Ansible Roles分步指南,教你如何使用Weave Scope探索微服务通信和服务网格

Istio小数 发表了文章 • 0 个评论 • 78 次浏览 • 2018-01-16 19:00 • 来自相关话题

 
 作者:Roger CARHUATOCTO 

翻译:张晔

原文:Using Weave Scope to explore Microservices Communication and Service Mesh 

原文地址:https://holisticsecurity.io/20 ... erral



如果你正在使用ESB、消息代理、BPMS、SOA或微服务,就会注意到,你正在以不同的方式解决相同的独立应用程序问题,因为它们都是不同类型的分布式应用程序。这些问题是:

用户管理、认证和授权

日志记录、调试、监视和报警

集群、高可用性、负载均衡等


什么是服务网格

服务网格是另一种类型的分布式应用程序,在这其中服务、微服务或者APIs都是相互关联的。







基于容器化和编排平台的服务网格

一般来说,基于微服务和/或APIs的服务网格是由多个容器来部署的,这些容器是用Kubernetes 来编排的。在这种情况下,我们需要面对新的挑战,比如临时基础设施、零信任网络、网络分割或者采用新的方法来测试、监控、部署、操作等等。这就是我们所说的DevOps。



容器部署模式

与 EIP (企业集成模式)和软件设计模式一样,在容器化的平台上部署服务网格也有一定的模式。Google、Microsoft、Netflix 等建议使用一些模式来实现上述问题的解决方案。


例如,Google解释了三种很好的模式:
 
Sidecar模式Ambassador模式适配器模式

它们都支持基于容器的服务网格的构建。欲知详情,请阅读:

《基于容器的分布式系统的设计模式》by 布伦丹·伯恩斯&大卫·奥本海默

PDF下载:https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/45406.pdf 



什么是Istio

摘录自 Istio.io :

Istio是一个开放的平台,提供了一种统一的方式来连接、管理微服务,保证微服务安全。Istio支持管理微服务之间的交通流、执行访问策略和聚合遥测数据,这些都不需要对微服务代码进行更改。

Istio 给你:
自动负载平衡HTTP、gRPC WebSocket,TCP流量;
 
细粒度控制的交通行为与丰富的路由规则,重试,故障转移,故障注入;
 
可插入策略层和配置API支持访问控制,速度和配额限制;
 
自动度量、日志和跟踪所有流量在一个集群,包括集群入口和出口;


在集群中的服务之间,通过强身份断言,实现服务对服务的安全认证。







为什么使用Istio

    摘录自 Istio.io :

Istio解决了开发人员和运维人员在向分布式微服务架构过渡时所面临的诸多挑战。服务网格作为一个术语,通常用于描述组成此类应用程序的微服务网络和它们之间的交互。随着服务网格的规模和复杂性的增长,它会变得越来越难以理解和管理。它的需求包括发现、负载均衡、故障恢复、度量和监控,以及更复杂的操作需求,如A/B测试、canary发布、速率限制、访问控制和端到端验证。


Istio提供了一个完整的解决方案,通过行为洞察和对整个服务网格的操作控制,来满足微服务应用程序的不同需求。它在服务网络中统一提供了许多关键功能:
 
交通管理。控制服务之间的流量和API调用,使调用更加可靠,并使网络在面对不利条件时更加健壮。
 
可观测性。了解服务之间的依赖关系和它们之间的流量之间的关系,提供快速识别问题的能力。
 
政策执行。将组织策略应用于服务之间的交互,确保执行访问策略,并且在用户中公平地分配资源。策略更改不必通过更改应用程序代码来实现,而是通过配置网格。
 
标识和安全服务。在网格中提供可验证身份的服务,并提供保护服务流量的能力,因为它在不同程度的trustability网络中流动。


基于容器的服务网格与Istio

为了轻松得到一个极简 OpenShift 集群,我创建了3个 Ansible Roles--Weave Scope 、Istio 和 BookInfo 应用(基于容器的 API 和微服务) 来理解我们必须面对的挑战。3个 Ansible Roles是:

1. Minishift Ansible Role

    地址:https://github.com/chilcano/ansible-role-minishift

在虚拟机中通过 Minishift 获得 OpenShift 集群。


2. Weave Scope Ansible Role

   地址:https://github.com/chilcano/an ... scope

在 OpenShift 中部署本地运行的 Weave Scope 应用。


3. Istio Ansible Role

    地址:https://github.com/chilcano/ansible-role-istio

在本地运行的 OpenShift 中部署和配置 Istio,部署 BookInfo 应用,并注入 Sidecar 代理(Envoy Proxy)。


我还创建了一些示例,可以测试并快速尝试上述角色的环境。

Weave Scope 将在这里发挥重要作用,它能够对整个基于容器的服务网格进行监控、可视化、故障诊断和轻松管理。

一旦完成了分步指南,你将获得下一个:

(操作步骤详见:https://github.com/chilcano/an ... urity)






































 希望这篇文章对你能有所帮助。 查看全部

微信图片_20180116164346.jpg

 
 作者:Roger CARHUATOCTO 

翻译:张晔

原文:Using Weave Scope to explore Microservices Communication and Service Mesh 

原文地址:https://holisticsecurity.io/20 ... erral



如果你正在使用ESB、消息代理、BPMS、SOA或微服务,就会注意到,你正在以不同的方式解决相同的独立应用程序问题,因为它们都是不同类型的分布式应用程序。这些问题是:

用户管理、认证和授权

日志记录、调试、监视和报警

集群、高可用性、负载均衡等


什么是服务网格

服务网格是另一种类型的分布式应用程序,在这其中服务、微服务或者APIs都是相互关联的。

1.png



基于容器化和编排平台的服务网格

一般来说,基于微服务和/或APIs的服务网格是由多个容器来部署的,这些容器是用Kubernetes 来编排的。在这种情况下,我们需要面对新的挑战,比如临时基础设施、零信任网络、网络分割或者采用新的方法来测试、监控、部署、操作等等。这就是我们所说的DevOps。



容器部署模式

与 EIP (企业集成模式)和软件设计模式一样,在容器化的平台上部署服务网格也有一定的模式。Google、Microsoft、Netflix 等建议使用一些模式来实现上述问题的解决方案。


例如,Google解释了三种很好的模式:
 
  • Sidecar模式
  • Ambassador模式
  • 适配器模式


它们都支持基于容器的服务网格的构建。欲知详情,请阅读:

《基于容器的分布式系统的设计模式》by 布伦丹·伯恩斯&大卫·奥本海默

PDF下载:https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/45406.pdf 



什么是Istio

摘录自 Istio.io :

Istio是一个开放的平台,提供了一种统一的方式来连接、管理微服务,保证微服务安全。Istio支持管理微服务之间的交通流、执行访问策略和聚合遥测数据,这些都不需要对微服务代码进行更改。

Istio 给你:
  • 自动负载平衡HTTP、gRPC WebSocket,TCP流量;

 
  • 细粒度控制的交通行为与丰富的路由规则,重试,故障转移,故障注入;

 
  • 可插入策略层和配置API支持访问控制,速度和配额限制;

 
  • 自动度量、日志和跟踪所有流量在一个集群,包括集群入口和出口;



在集群中的服务之间,通过强身份断言,实现服务对服务的安全认证。

2.png



为什么使用Istio

    摘录自 Istio.io :

Istio解决了开发人员和运维人员在向分布式微服务架构过渡时所面临的诸多挑战。服务网格作为一个术语,通常用于描述组成此类应用程序的微服务网络和它们之间的交互。随着服务网格的规模和复杂性的增长,它会变得越来越难以理解和管理。它的需求包括发现、负载均衡、故障恢复、度量和监控,以及更复杂的操作需求,如A/B测试、canary发布、速率限制、访问控制和端到端验证。


Istio提供了一个完整的解决方案,通过行为洞察和对整个服务网格的操作控制,来满足微服务应用程序的不同需求。它在服务网络中统一提供了许多关键功能:
 
  • 交通管理。控制服务之间的流量和API调用,使调用更加可靠,并使网络在面对不利条件时更加健壮。

 
  • 可观测性。了解服务之间的依赖关系和它们之间的流量之间的关系,提供快速识别问题的能力。

 
  • 政策执行。将组织策略应用于服务之间的交互,确保执行访问策略,并且在用户中公平地分配资源。策略更改不必通过更改应用程序代码来实现,而是通过配置网格。

 
  • 标识和安全服务。在网格中提供可验证身份的服务,并提供保护服务流量的能力,因为它在不同程度的trustability网络中流动。



基于容器的服务网格与Istio

为了轻松得到一个极简 OpenShift 集群,我创建了3个 Ansible Roles--Weave Scope 、Istio 和 BookInfo 应用(基于容器的 API 和微服务) 来理解我们必须面对的挑战。3个 Ansible Roles是:

1. Minishift Ansible Role

    地址:https://github.com/chilcano/ansible-role-minishift

在虚拟机中通过 Minishift 获得 OpenShift 集群。


2. Weave Scope Ansible Role

   地址:https://github.com/chilcano/an ... scope

在 OpenShift 中部署本地运行的 Weave Scope 应用。


3. Istio Ansible Role

    地址:https://github.com/chilcano/ansible-role-istio

在本地运行的 OpenShift 中部署和配置 Istio,部署 BookInfo 应用,并注入 Sidecar 代理(Envoy Proxy)。


我还创建了一些示例,可以测试并快速尝试上述角色的环境。

Weave Scope 将在这里发挥重要作用,它能够对整个基于容器的服务网格进行监控、可视化、故障诊断和轻松管理。

一旦完成了分步指南,你将获得下一个:

(操作步骤详见:https://github.com/chilcano/an ... urity



3.png


4.png


5.png


6.png


7.png


8.png


9.png


 希望这篇文章对你能有所帮助。

万字雄文讲透现代网络负载均衡和代理技术,终于弄懂负载均衡那点事

ServiceMesh小数 发表了文章 • 0 个评论 • 350 次浏览 • 2018-01-11 16:48 • 来自相关话题

作者:Matt Klein

译者:杨泽

原题:Introduction to modern network load balancing and proxying






关于现代网络负载均衡和代理(proxy)的介绍性资料很少,这件事引起了我的注意。为什么会这样呢?构建一个可靠的分布式系统时,负载均衡是一个非常核心的概念。但可惜的是,搜索发现关于负载均衡和服务代理的内容很少。维基百科文章只包含了一些概念,没有对该主题的详细介绍,尤其是与现代微服务架构相关的。


本文试图通过对现代网络负载均衡和代理的简单介绍,来弥补这部分信息的缺乏。为了保持文章的长度,尽量将复杂的话题浓缩成简单的概述。


1什么是网络负载均衡和代理?


维基百科这样定义负载均衡:

在计算中,负载均衡可以改善在多个计算机资源(如计算机,计算机集群,网络链接,中央处理单元或磁盘驱动器)上的工作负载分布。负载均衡旨在优化资源的使用,最大吞吐量,及最小响应时间,避免任何单一资源的过载。通过使用多个组件而不是单个组件,负载均衡通过冗余来提高可靠性和可用性。负载均衡通常涉及专用软件和硬件,例如多层交换机或域名系统服务器进程。

以上定义适用于计算的各个方面,而不仅仅是网络。操作系统使用负载均衡在物理处理器之间安排任务,诸如Kubernetes之类的容器编排者使用负载均衡来计划跨计算集群的任务,网络负载均衡通过调用可用后端来平衡网络任务。以下内容仅涵盖网络负载均衡。






图 1: 网络负载均衡描述


图1展示了网络负载均衡高层次的概览。一些客户端从后端请求资源。负载均衡器位于客户端和后端之间,在更高层次执行关键任务:

服务发现:在系统中哪些后端可用?地址是多少?(换句话说,负载均衡器如何与它们通信?)

健康检查:在系统中目前哪些后端是健康的,可以接受请求?

负载均衡:用什么样的算法把独立的个人请求发给健康的后端?


在分布式系统中正确使用负载均衡会带来如下好处:

命名抽象:客户端可以通过预定义的机制来寻址负载均衡器,而不是每个客户端都需要了解每个后端(服务发现),然后将名称解析的任务委托给负载均衡器。预定义的机制包括内置库和众所周知的DNS/IP/端口地址,后面会有详细的讨论。

容错:通过健康检查和各种算法技术,负载均衡器的路由方法可以绕过坏的或者超负载的后端。这也意味着运维人员可以随时优先修复后端错误。

低成本和高性能:分布式系统网络一致性比较差。系统可能会横跨不同的网络区域。在一个区域内,网络之间通常以相对小的带宽建立的。而在不同的区域之间,网络大带宽成为常态(带宽的大小是指通过NIC使用的带宽量占路由器之间可用带宽的百分比)。智能的负载均衡可以尽可能保持区域内的请求流量,从而提高性能(低延迟)并降低整体系统成本(减少区域间的带宽和光纤)。



负载均衡器VS代理

在谈论到网络负载均衡器,Load Balancer 和 Proxy 两个术语在行业内可以互换使用。在本文中也把两者视为一致的表达。(不过,不是所有的代理都是负载均衡器,但绝大多数代理都将负载均衡作为主要功能)。


一些人会争辩说,当负载均衡器作为嵌入式客户端库的一部分实现时,这种负载均衡器并不是真正的代理。这种区分其实添加了不必要的复杂性。本文会详细讨论负载均衡器的拓扑类型,嵌入式负载均衡器是代理的一个特例,应用通过嵌入式库进行代理,嵌入式库与应用进程外的负载均衡器整体抽象是一致的。



L4(连接/会话)负载均衡

业界讨论负载均衡时,解决方式往往分为两类:L4 和 L7。指的是OSI模型的第四层和第七层。OSI模型包括传统的第四层协议(如TCP和UDP),还包含各种不同OSI层的协议。换句话说,如果 L4 TCP负载均衡器也支持 TLS 终止,这不就是 L7负载均衡器么?






图2:TCP L4 终止负载平衡

图2展示了传统的L4 TCP负载均衡器。在这种情况下,客户端与负载均衡器建立TCP连接,负载均衡器终止连接(即直接响应SYN),选择一个后端,并与后端建立新的TCP连接(即发起新的SYN)。


本节的关键是L4负载均衡器通常只在 L4 TCP / UDP连接/会话上运行。因此,负载平衡器负责操作字节,并确保来自同一会话的字节转发到同一后端。L4负载平衡器并不知道通信中的任何应用细节。这些字节可能是 HTTP,Redis,MongoDB或任何其他应用协议。


L7(应用)负载平衡

 L4负载均衡简单,能看到广泛使用。人们为什么放弃L4负载均衡,来研究L7(应用)负载均衡?以下是L4具体的案例:


两个gRPC / HTTP2 客户端通过L4负载均衡器与后端建立连接。

L4负载均衡器为每个接入的TCP连接建立一个传出的TCP连接,这样就产生了两个传入和两个传出连接。

但是,客户端A每分钟发送 1个请求(RPM),而客户端B通每秒发送50个请求(RPS)。


在上述场景中,后端选择处理客户端B请求是选择客户端A请求负载的大约3000倍。这是一个很大的问题,打破了负载均衡的目的。需要注意的是这个问题发生在任何复用,保持存活状态的协议。(复用意味着通过单个L4连接多次发送应用程序请求,保持存活就是没有活动请求时不关闭连接)。出于效率的考虑,所有现代协议都在不断发展,既要复用也要存活(创建连接通常是昂贵的,特别是用TLS进行加密连接时),所以L4负载均衡器随着时间的推移,负载失衡的情况更加明显。这个问题在L7负载均衡器中得到解决。







图3:HTTP / 2 L7终端负载均衡

图3展示了L7 HTTP/2负载平衡器。客户端与负载均衡器只建立一个 HTTP/2 TCP连接,连接两个后端。当客户端向负载均衡器发送两个HTTP/2 流时,流1被发送到后端1,流2被发送到后端2。因此,即使差别很大的请求负载的多路复用客户端也将被有效地平衡到不同的后端。这就是L7负载平衡对于现代协议的重要性所在。(L7负载平衡由于能够检查应用流量,而产生大量好处,后面会更详述)。



L7负载均衡和OSI模型

正如L4负载平衡部分所述,使用OSI模型描述负载均衡功能是有问题的。原因在于L7本身包含多个离散的负载均衡抽象层。例如,对于HTTP流量,请考虑以下子层:


可选的传输层安全性(TLS)。网络界对于TLS 属于OSI模型哪一层有争论。为了方便,我们考虑将TLS放在 L7。

物理HTTP协议(HTTP / 1或HTTP / 2)。

逻辑HTTP协议(Header、Body 和 Trailer)。

消息传递协议(gRPC,REST等)。


一个复杂的L7负载均衡器要提供与上述每个子层有关的功能。另L7负载均衡器可能只有一小部分功能属于OSI模型中L7层。简而言之,L7负载平衡器的功能远比L4类型复杂得多。(HTTP、 Redis、Kafka、MongoDB等都是应用L7 协议的例子,它们受益于L7负载均衡)。



2负载均衡器功能


在本节中将总结负载均衡器提供的高级功能,并非所有负载均衡器都提供全部功能。


服务发现

服务发现是负载平衡器确定可用后端的过程,实现的方式有很多种,一些例子包括:


静态配置文件。

DNS。

Zookeeper,Etcd,Consul等。

Envoy的通用数据平面API。


健康检查

运行状况检查是负载均衡器确定后端是否可用来为流量提供服务的过程。健康检查一般分为两类:

主动式:负载平衡器将定期发送ping(例如,对/healthcheck端点的HTTP请求)到后端,并用它来衡量健康状况。

被动式:负载均衡器从主数据流中检测健康状况。例如,如果连续出现三个连接错误,则L4负载均衡器会认为后端是不健康的。如果一行中有三个HTTP 503响应代码,则L7负载均衡器会认为后端是不健康的。



负载均衡

负载均衡器必须实际平衡负载!当给定一组健康的后端时,选择哪个后端处理请求?负载平衡算法是一个活跃的研究领域,从简单的算法(如随机选择和循环算法)到考虑可变延迟和后端负载的更复杂的算法。考虑到其性能和简单性,最流行的负载均衡算法是二节点最小请求负载均衡(power of 2 least request load balancing)。 


粘滞会话(Sticky sessions)

在某些应用中,同一会话的请求到达相同的后端是很重要的。这与缓存,临时复杂状态等有关。会话的定义各不相同,可能包括HTTP Cookie,客户端连接的配置或其他属性。许多L7负载平衡器支持会话粘连。会话粘性本质上是脆弱的(后端保存的会话可能会失效),所以在设计系统的时候减少对它们的依赖。



TLS终止

关于TLS,它在边缘服务和服务间通信方面值得肯定。因此,许多L7负载均衡器会执行大量的TLS处理,包括终止,证书验证和保存,使用SNI的证书服务等。



可观察性

正如谈话中所强调的:“可观察性,可观察性,可观察性”。网络本质上是不可靠的,负载均衡器通常负责导出统计信息,跟踪信息和记录日志,帮助运营人员找出问题所在,从而解决问题。最先进的负载平衡器可观察性输出包括数字统计,分布式跟踪和可定制的日志记录。增强的可观察性不是免费的; 负载平衡器必须做很多附加的工作来完成它。至于,负面影响,相对带来的好处,几乎不值一提。


安全性和DoS防范

特别是在边缘部署拓扑(见下文)中,负载均衡器通常实施各种安全功能,包括速率限制,身份验证和DoS防范(例如,IP地址标记和识别,缓送Tarpitting等)。



配置和控制面板

负载平衡器需要配置。在大规模部署中,这是一项重要工作。一般来说,配置负载均衡器的系统被称为“控制面板”,并且在其实现中变化很大。
 
3负载均衡器拓扑的类型

前面介绍了负载平衡器的概要,L4和L7负载平衡器之间的区别以及负载均衡器特性,接下来将转向部署负载均衡器的各种分布式系统拓扑。(以下每种拓扑均适用于L4和L7负载均衡器)。



中间代理






 
图4:中间代理负载均衡拓扑

图4所示的中间代理拓扑是大多数读者最熟悉的负载均衡一种方式。这一类包括Cisco,Juniper,F5等硬件设备; 云软件解决方案,如亚马逊的ALB和NLB以及Google的 Cloud Load Balancer ; 以及HAProxy,NGINX和Envoy等纯软件自主托管解决方案。中间代理解决方案使用简单。通常,用户通过DNS连接到负载均衡器,不需要担心其他任何事情。中间代理解决方案的特点是代理(即使是集群)是单一的故障点,也是扩展的瓶颈。中间代理通常是一个让运维变得困难的黑匣子。当问题出现时,是在客户端还是物理网络?在中间代理还是后端?这很难说。



边缘代理






图5:边缘代理负载平衡拓扑

图5所示实际上只是中间代理拓扑的一种变体,负载均衡器通过Internet访问。在这种情况下,负载均衡器通常必须提供附加的“API网关”功能,如TLS终止,速率限制,身份验证和复杂的流量路由。边缘代理的优缺点与中间代理相同。需要注意的是,在面向Internet的大型分布式系统中部署专用边缘代理通常是不可避免的。客户端通常需要使用服务所有者无法控制的任意网络库(后面会讨论嵌入式客户端库或附属代理拓扑结构不能直接在客户端上运行)通过DNS访问系统。另外,由于安全原因,希望具有单个网关,通过该网关,所有面向互联网的流量进入系统。



嵌入式客户端库






图6:通过嵌入式客户端库进行负载平衡

为了避免中间代理拓扑中固有的单点故障和扩展问题,更复杂的基础设施已经开始将负载均衡器直接嵌入到服务库中,如图6所示。支持的功能库有很大的不同,但是这个类别中最知名和最丰富的功能包括Finagle,Eureka / Ribbon / Hystrix和gRPC(松散地基于称为Stubby的内部Google系统)。基于库的解决方案的主要功能是将负载均衡器的所有功能完全分配给每个客户端,从而消除之前描述的单点故障和扩展问题。基于客户端库的解决方案的主要内容是客户端库必须每种语言调用。分布式架构正变得越来越“多语言”(多语言)。在这样的环境下,用许多不同的语言来重新实现一个非常复杂的网络库的成本会变得过高。最后,在大型服务体系结构中部署库升级可能会非常痛苦,这使得很多不同版本的库将在生产中同时运行,

综上所述,上面提到的这些库对于那些能够限制编程语言扩散并克服库升级难题的公司来说是成功的。



Sidecar代理






图7:通过sidecar代理进行负载平衡

嵌入式客户端库负载平衡器拓扑的一个变种是图7中所示的辅助代理拓扑。近年来,这种拓扑已经被普遍推广为“服务网格”(Service Mesh)。Sidecar代理背后的思路是,以跳过一个不同的过程的代价来承担轻微的延迟,嵌入式库的所有好处都可以在没有任何编程语言锁定的情况下获得。在撰写本文时,最受欢迎的sidecar代理负载平衡器是Envoy,NGINX,HAProxy和Linkerd。



负载均衡器拓扑的总结和优缺点

中间代理拓扑是最简单的负载平衡拓扑。缺点是:单点故障,伸缩瓶颈和黑箱操作。


边缘代理拓扑与中间代理类似,但通常无法避免。


嵌入式客户端库拓扑提供了最好的性能和可扩展性,但是需要在每种语言中实现该库,并跨所有服务升级库。


Sidecar代理拓扑性能比嵌入式客户端库拓扑弱,但不受任何限制。

总体而言,sidecar代理拓扑(服务网格)正在逐渐取代所有其他的服务。流量进入服务网格之前,始终需要边缘代理拓扑。



4L4负载均衡的现状


L4负载均衡器还有用吗?

本文讨论了L7负载平衡器如何用于现代协议,后面还会讨论L7负载平衡器的特性。这是否意味着L4负载平衡器没用了?没有!尽管在我看来,L7负载均衡器最终将完全取代L4负载均衡器进行服务间通信,但是L4负载均衡器对边缘通信仍然很重要。因为几乎所有现代大型分布式架构都使用两层L4 / L7负载平衡架构处理互联网流量。在L7负载均衡器之前在边缘部署中部署专用L4负载均衡器的好处是:


L7负载均衡器执行的应用程序流量的分析,转换和路由选择要复杂得多,所以它们可以处理相对于经过优化的L4负载均衡器能够处理的相对较小的原始流量负载(以每秒数据包和每秒字节数衡量)。这一现实通常使得L4负载平衡器成为处理某些类型的DoS攻击(例如,SYN flood,通用包flood攻击等)的更好位置。


L7负载均衡器往往比L4负载平衡器更积极地开发,部署更频繁,并且具有更多的缺陷。有一个L4负载均衡器,可以在L7负载平衡器部署期间执行健康检查和排空,这比现代L4负载平衡器(通常使用BGP和ECMP)(以下更多)中使用的部署机制要容易得多。最后,因为L7负载平衡器功能的复杂性更容易出现错误,所以使用绕开故障和异常的L4负载平衡器产生更稳定的整体系统。


下面将介绍几种不同的中间/边缘代理L4负载均衡器的设计。这些设计不适用于客户端库和Sidecar拓扑。



TCP / UDP终端负载均衡器





图8:L4终端负载均衡器


仍在使用的第一类L4负载均衡器是图8所示的终端负载均衡器。这类负载均衡器,使用两个离散的TCP连接:一个在客户端和负载均衡器之间,另一个在负载均衡器和后端之间。


L4终端负载均衡器仍然有两个原因:

实现相对简单。

靠近客户端的连接终端(低延迟)对性能有很大的影响。具体来说,如果终端负载均衡器可以放置在使用有损网络(例如蜂窝)的客户端附近,那么在将数据转移到可靠的光纤传输到其最终位置之前,容易发生重传。换句话说,这种负载平衡器适用于存在点(POP)场景下的原始TCP连接。




TCP / UDP直通负载均衡器







图9:L4直通负载均衡器

第二种类型的L4负载均衡器是图9所示的直通负载均衡器。在这种类型的负载均衡器中,TCP连接不会被负载均衡器终止。而是在连接跟踪和网络地址转换(NAT)发生后,将每个连接的数据包转发到选定的后端。首先,我们来定义一下连接跟踪和NAT:


连接跟踪:跟踪所有活动的TCP连接状态的过程。这包括握手是否完成,是否收到FIN,连接空闲多久,连接选择了哪个后端等等。


NAT:NAT是使用连接跟踪数据在通过负载均衡器时更改数据包的IP /端口信息的过程。


使用连接跟踪和NAT,负载平衡器可以将大部分原始TCP流量从客户端传递到后端。例如,假设客户正在交谈,1.2.3.4:80而选定的后端位于10.0.0.2:9000。客户端TCP数据包将到达负载均衡器at 1.2.3.4:80。然后负载均衡器将交换数据包的目标IP和端口10.0.0.2:9000。它还会将数据包的源IP与负载均衡器的IP地址进行交换。因此,当后端在TCP连接上响应时,数据包将返回到负载平衡器,在那里进行连接跟踪并且NAT可以在反向再次发生。


为什么会使用这种负载均衡器来代替上一节中描述的终端负载均衡器呢?几个原因:


性能和资源使用:由于直通负载均衡器不是终止TCP连接,所以不需要缓冲任何TCP连接窗口。每个连接存储的状态量非常小,通常通过高效的散列表查找来访问。因此,直通负载均衡器通常可以处理比终端负载均衡器大得多的活动连接数和数据包数(PPS)。


允许后端执行拥塞控制:TCP拥塞控制是因特网上的端点节制发送数据的机制,从而不会压倒可用带宽和缓冲区。由于直通负载均衡器不会终止TCP连接,因此不参与拥塞控制。这个事实允许后端根据他们的应用使用情况使用不同的拥塞控制算法。它还可以更容易地进行拥塞控制变更的实验(例如,最近的BBR部署)。


形成直接服务器返回(DSR)和群集L4负载平衡的基准:更高级的L4负载平衡技术(如DSR和使用分布式一致性散列的群集(以下各节中讨论))需要直通负载平衡。


直接服务器返回(Direct Server Return,DSR)








图10:L4直接服务器返回(DSR)

直接服务器返回(DSR)负载平衡器如图10所示。DSR构建在上一节中介绍的直通负载均衡器上。DSR是一种优化,只有入口/请求数据包才能通过负载均衡器。出口/响应数据包在负载平衡器周围直接返回到客户端。执行DSR很有趣的主要原因是,在许多工作负载中,响应流量对请求流量(例如,典型的HTTP请求/响应模式)产生了不满。假设10%的流量是请求流量,90%的流量是响应流量,如果使用DSR的负载均衡器为1/10的容量可以满足系统的需求。由于历史上负载平衡器非常昂贵,因此这种优化可能会对系统成本和可靠性产生重大影响。DSR负载均衡器将直通负载均衡器的概念扩展如下:

负载平衡器通常仍然执行部分连接跟踪。由于响应数据包不会遍历负载均衡器,因此负载均衡器将不会意识到完整的TCP连接状态。但是,负载均衡器可以通过查看客户端数据包并使用各种类型的空闲超时来强烈推断状态。


负载均衡器通常使用通用路由封装(GRE)来封装从负载均衡器发送到后端的IP数据包,而不使用NAT 。因此,当后端收到封装的数据包时,可以对其进行解封装,并知道客户端的原始IP地址和TCP端口。这允许后端直接响应客户端,而无需响应数据包流经负载平衡器。


DSR负载平衡器的一个重要组成部分是后端参与负载均衡。后端需要有一个正确配置的GRE隧道,根据网络设置的低层细节,可能需要自己的连接跟踪,NAT等。

请注意,在直通负载均衡器和DSR负载均衡器设计中,可以通过负载均衡器和后端来设置连接跟踪,NAT,GRE等多种方式。



通过高可用性配对的容错








图11:通过HA对和连接跟踪的L4容错

到目前为止,我们一直在考虑L4负载平衡器的设计。直通和DSR负载平衡器都需要负载均衡器本身的一些连接跟踪和状态。如果负载平衡器死了怎么办?如果负载平衡器的单个实例死亡,则所有通过负载平衡器的连接将被切断。取决于应用程序,这可能会对应用程序性能产生重大影响。


历史上,L4负载均衡器是从典型供应商(Cisco,Juniper,F5等)购买的硬件设备。这些设备非常昂贵,处理大量的流量。为了避免单个负载均衡器发生故障切断所有连接,并导致大量应用程序中断,负载均衡器通常部署在高可用性配对中,如图11所示。典型的HA负载均衡器设置具有以下设计:


一对HA边缘路由器为虚拟IP(VIP)提供服务。这些边缘路由器使用边界网关协议(BGP)宣布虚拟IP 。主边缘路由器具有比备份更高的BGP权重,因此在稳定状态下,它服务所有流量。(BGP是一个非常复杂的协议; 就本文而言,考虑BGP是网络设备宣布可以从其他网络设备获取流量的机制,并且每个链路都可以具有优先考虑链路流量的权重)。


同样,主L4负载均衡器向边缘路由器宣布自己的BGP权重高于备份,因此在稳定状态下,它将服务所有流量。

主负载均衡器交叉连接到备份,并共享其所有连接跟踪状态。因此,如果主负载宕机,备份可以接管处理所有活动的连接。


两台边缘路由器和两台负载均衡器都是交叉连接的。这意味着如果其中一个边缘路由器或负载平衡器中的一个死亡,或者由于某种其他原因撤消BGP通知,备份可以接管所有通信。


上面的设置是目前具有高流量的互联网应用还在使用的方式。但是,上述方法有很大的缺点:

考虑到容量使用情况,虚拟 IP (VIP)必须在HA负载平衡器对之间正确分片。如果单个VIP超出单个HA对的容量,则需要将VIP分成多个VIP。


系统的资源使用情况很差。50%的容量在稳定状态下闲置。鉴于历史上硬件负载平衡器非常昂贵,这导致大量闲置资本。

现代分布式系统设计比主动/备份提供更高的容错能力。例如,最佳地,系统应该能够同时遭受多个故障并继续运行。如果主动和备用负载均衡器同时死亡,则HA负载均衡器对可能会发生完全故障。


来自供应商的专有大型硬件设备非常昂贵,并导致供应商锁定。通常希望用可商购计算服务器构建的可横向扩展的软件解决方案替代这些硬件设备。



通过具有分布式一致散列的集群进行容错和扩展






图12:L4容错和通过集群负载平衡器和一致性哈希的扩展

上一节介绍了通过HA对的L4负载平衡器容错以及该设计中固有的问题。从21世纪初到中期,大型互联网基础设施开始设计和部署新的大规模并行L4负载平衡系统,如图12所示。这些系统的目标是:


减轻上一节中描述的HA对设计的所有缺点。

从供应商的专有硬件负载平衡器转移到使用标准计算服务器和NIC构建的商品软件解决方案。

这个L4负载均衡器设计最好称为容错和扩展,通过集群和分布式一致的散列。它的工作原理如下:

N个边缘路由器以相同的BGP权重宣布所有Anycast VIP。等价多路径路由(ECMP)用于确保通常来自单个流的所有数据包到达相同的边缘路由器。流程通常是源IP /端口和目标IP /端口的四元组。(简而言之,ECMP是一种使用一致散列在一组相同加权的网络链路上分发数据包的方法)。尽管边缘路由器本身并不特别关心哪个分组到达哪里,但是通常优选的是,来自一个流的所有分组都经过相同的一组链路,以避免乱序分组,从而降低性能。

N个L4负载均衡器机器以与边缘路由器相同的BGP权重宣布所有的VIP。再次使用ECMP,边缘路由器将通常为流选择相同的负载平衡器机器。

每个L4负载均衡器机器通常会执行部分连接跟踪,然后使用一致的哈希来为流选择一个后端。GRE用于封装从负载均衡器发送到后端的数据包。

DSR然后被用来通过边缘路由器直接从后端发送数据包到客户端。

L4负载均衡器使用的实际一致性散列算法是一个活跃的研究领域。主要围绕均衡负载进行权衡,最小化延迟,最大限度地减少后端更改期间的中断, 并最大限度地减少内存开销。关于这个主题的完整讨论超出了本文的范围。


让我们看看上面的设计是如何缓解HA对方法的所有缺点的:

新的边缘路由器和负载平衡器机器可以根据需要添加。在添加新机器时,每层都使用一致的散列来尽可能减少受影响的流量。
 
系统的资源使用可以按照需要高速运行,同时保持足够的突发容限和容错。

边缘路由器和负载平衡器现在都可以使用商用硬件来构建,而传统硬件负载平衡器的成本只是其中的一小部分(下面会详细介绍)。


通常被问及这个设计的一个问题是“为什么边缘路由器不通过ECMP直接与后端对话?为什么我们需要负载平衡器?“其原因主要是减少DoS和后端操作的简化。如果没有负载平衡器,每个后端都必须参与BGP,并且执行滚动部署的时间将更加困难。


所有现代化的L4负载均衡系统都在朝着这种设计方向发展(或其他变体)。最著名的两个例子是Google的Maglev和Amazon 的网络负载均衡器(NLB)。目前还没有实现这种设计的OSS负载平衡器,但是,我知道有一家公司计划在2018年将其发布到OSS。我对这个版本感到非常兴奋,因为现代L4负载平衡器是一个至关重要的部分在网络空间中缺少OSS。


5L7负载平衡的现有技术状态

在过去的几年中,L7负载平衡器/代理的发展已经复苏。随着分布式系统中的微服务架构的不断推进,这方面也在不断进步。从根本上说,固有故障的网络在使用更频繁时变得更加难以有效地运行。而且,自动扩展,容器调度等等的出现意味着在静态文件中配置静态IP的日子早已不复存在。系统不仅要利用网络,还要变得更加动态,需要负载平衡器的新功能。本节将简要概述在现代L7负载平衡器的最新发展。


协议支持

现代L7负载平衡器为许多不同的协议增加了明确的支持。负载平衡器对于应用程序流量的知识越多,关于可观察性输出,高级负载平衡和路由等方面可以做的更复杂的事情。例如,在撰写本文时,Envoy明确支持L7协议解析和路由用于HTTP / 1,HTTP2,gRPC,Redis,MongoDB和DynamoDB。未来可能会有更多协议被添加,包括MySQL和Kafka。



动态配置

如上所述,分布式系统越来越具有动态性,需要并行投资来创建动态和反应式控制系统。Istio就是这样一个例子。


高级负载平衡

L7负载均衡器现在通常内置对高级负载平衡功能的支持,如超时,重试,速率限制,断路,屏蔽,缓冲,基于内容的路由等。



可观察性

如上面有关一般负载平衡器功能的部分所述,正在部署的日益动态的系统越来越难以调试。强大的协议特定的可观测性输出是现代L7负载平衡器最重要的特性。任何L7负载平衡解决方案现在都需要输出数字统计信息,分布式跟踪和可自定义日志记录。



可扩展性

现代L7负载平衡器的用户通常希望轻松扩展以添加自定义功能。这可以通过写入加载到负载均衡器的可插入过滤器来完成。许多负载平衡器也支持脚本,通常通过Lua。



容错

L7负载均衡器的容错性如何?一般来说,我们把L7负载平衡器视为可消耗和无状态的。使用商业软件就能够对L7负载平衡器轻松水平扩展。此外,L7负载平衡器执行的处理和状态跟踪比L4复杂得多。试图建立一个L7负载平衡器的HA配对在技术上是可能的,但非常繁杂。


总的来说,在L4和L7负载平衡领域,业界正在从HA配对转向通过一致的哈希聚合横向水平可扩展系统。


更多

L7负载平衡器正在以惊人的速度发展。有关Envoy提供的示例,请参阅Envoy的架构概述。


6全球负载均衡和集中控制平面







图13:全局负载平衡

负载平衡的未来将越来越多地将单个负载平衡器视为商品设备。我认为,真正的创新和商业机会都在控制层面。图13显示了一个全局负载均衡系统的例子。在这个例子中,几个不同的事情正在发生:

每个Sidecar代理正在与三个不同区域(A,B和C)的后端进行通信。

如图所示,90%的流量正被发送到区域C,而5%的流量正被发送到区域A和B。

Sidecar代理和后端均向全局负载均衡器报告周期性状态。这允许全局负载均衡器做出考虑延迟,成本,负载,当前故障等的决定。

全局负载均衡器使用当前路由信息周期性地配置每个边车代理。

全球负载均衡器越来越能够做复杂的事情。例如:

自动检测并绕过区域故障。

应用全局安全和路由策略。

使用机器学习和神经网络检测和减轻流量异常,包括DDoS攻击。

提供集中的用户界面和可视化,使工程师能够理解和操作整个分布式系统。

为了实现全局负载均衡,用作数据平面的负载平衡器必须具有复杂的动态配置能力。



7从硬件到软件的演变


本文只是简单地提到了硬件和软件,主要是在L4负载平衡器HA对的历史背景下。这方面的业界趋势又是怎样呢?

历史上的路由器和负载平衡器都是昂贵的专有硬件。

越来越多的专有L3 / L4网络设备正在被商用服务器硬件,商用网卡和基于IPVS,DPDK和fd.io等框架的专业软件解决方案取代。价格低于5K美元的现代数据中心机器可以使用Linux和自定义的基于DPDK的 user-space 应用,轻松饱和具有非常小数据包的80Gbps NIC。与此同时,廉价的路由器/交换机ASIC可以以惊人的总带宽和数据包速率进行ECMP路由选择,正在被打包成商品路由器。

像NGINX,HAProxy和Envoy这样先进的L7软件负载平衡器也在迅速地迭代和侵入之前像F5这样的供应商领域。因此,L7负载平衡器也在积极地向商品软件解决方案迈进。

与此同时,整个行业向主流云提供商推动的IaaS,CaaS和FaaS意味着越来越多的工程师需要了解物理网络是如何工作的


8结论和负载均衡的未来


这篇文章的主要内容是:

负载平衡器是现代分布式系统中的关键组件。

有两种负载均衡器:L4和L7。

L4和L7负载平衡器都与现代架构中相关。

L4负载均衡器正朝着横向可扩展的分布式一致哈希水平扩展解决方案迈进。

由于动态微服务体系结构的激增,L7负载平衡器最近发展迅速。

全局负载平衡和控制平面与数据平面之间的分离,是负载平衡的未来,存在大量商业机会。

业界正在积极向商用OSS硬件和软件提供网络解决方案。相信像F5这样的传统负载均衡厂商将首先被OSS软件和云供应商所取代。传统的路由器/交换机厂商,如Arista / Cumulus /等。短期会有好的发展,但最终也将被公有云供应商和自研物理网络所取代。

总的来说,现在是计算机网络的一个激动人心的时代!大多数系统开始向OSS和软件方向转变,迭代速度加快。分布式系统向“无服务器”计算发展,必须继续充满活力,底层网络和负载平衡系统的复杂性也将齐头并进。

原文:https://blog.envoyproxy.io/int ... 80236 查看全部
作者:Matt Klein

译者:杨泽

原题:Introduction to modern network load balancing and proxying

3_(4).png


关于现代网络负载均衡和代理(proxy)的介绍性资料很少,这件事引起了我的注意。为什么会这样呢?构建一个可靠的分布式系统时,负载均衡是一个非常核心的概念。但可惜的是,搜索发现关于负载均衡和服务代理的内容很少。维基百科文章只包含了一些概念,没有对该主题的详细介绍,尤其是与现代微服务架构相关的。


本文试图通过对现代网络负载均衡和代理的简单介绍,来弥补这部分信息的缺乏。为了保持文章的长度,尽量将复杂的话题浓缩成简单的概述。


1什么是网络负载均衡和代理?


维基百科这样定义负载均衡:

在计算中,负载均衡可以改善在多个计算机资源(如计算机,计算机集群,网络链接,中央处理单元或磁盘驱动器)上的工作负载分布。负载均衡旨在优化资源的使用,最大吞吐量,及最小响应时间,避免任何单一资源的过载。通过使用多个组件而不是单个组件,负载均衡通过冗余来提高可靠性和可用性。负载均衡通常涉及专用软件和硬件,例如多层交换机或域名系统服务器进程。

以上定义适用于计算的各个方面,而不仅仅是网络。操作系统使用负载均衡在物理处理器之间安排任务,诸如Kubernetes之类的容器编排者使用负载均衡来计划跨计算集群的任务,网络负载均衡通过调用可用后端来平衡网络任务。以下内容仅涵盖网络负载均衡。


1.png

图 1: 网络负载均衡描述


图1展示了网络负载均衡高层次的概览。一些客户端从后端请求资源。负载均衡器位于客户端和后端之间,在更高层次执行关键任务:

服务发现:在系统中哪些后端可用?地址是多少?(换句话说,负载均衡器如何与它们通信?)

健康检查:在系统中目前哪些后端是健康的,可以接受请求?

负载均衡:用什么样的算法把独立的个人请求发给健康的后端?


在分布式系统中正确使用负载均衡会带来如下好处:

命名抽象:客户端可以通过预定义的机制来寻址负载均衡器,而不是每个客户端都需要了解每个后端(服务发现),然后将名称解析的任务委托给负载均衡器。预定义的机制包括内置库和众所周知的DNS/IP/端口地址,后面会有详细的讨论。

容错:通过健康检查和各种算法技术,负载均衡器的路由方法可以绕过坏的或者超负载的后端。这也意味着运维人员可以随时优先修复后端错误。

低成本和高性能:分布式系统网络一致性比较差。系统可能会横跨不同的网络区域。在一个区域内,网络之间通常以相对小的带宽建立的。而在不同的区域之间,网络大带宽成为常态(带宽的大小是指通过NIC使用的带宽量占路由器之间可用带宽的百分比)。智能的负载均衡可以尽可能保持区域内的请求流量,从而提高性能(低延迟)并降低整体系统成本(减少区域间的带宽和光纤)。



负载均衡器VS代理

在谈论到网络负载均衡器,Load Balancer 和 Proxy 两个术语在行业内可以互换使用。在本文中也把两者视为一致的表达。(不过,不是所有的代理都是负载均衡器,但绝大多数代理都将负载均衡作为主要功能)。


一些人会争辩说,当负载均衡器作为嵌入式客户端库的一部分实现时,这种负载均衡器并不是真正的代理。这种区分其实添加了不必要的复杂性。本文会详细讨论负载均衡器的拓扑类型,嵌入式负载均衡器是代理的一个特例,应用通过嵌入式库进行代理,嵌入式库与应用进程外的负载均衡器整体抽象是一致的。



L4(连接/会话)负载均衡

业界讨论负载均衡时,解决方式往往分为两类:L4 和 L7。指的是OSI模型的第四层和第七层。OSI模型包括传统的第四层协议(如TCP和UDP),还包含各种不同OSI层的协议。换句话说,如果 L4 TCP负载均衡器也支持 TLS 终止,这不就是 L7负载均衡器么?


2.png

图2:TCP L4 终止负载平衡

图2展示了传统的L4 TCP负载均衡器。在这种情况下,客户端与负载均衡器建立TCP连接,负载均衡器终止连接(即直接响应SYN),选择一个后端,并与后端建立新的TCP连接(即发起新的SYN)。


本节的关键是L4负载均衡器通常只在 L4 TCP / UDP连接/会话上运行。因此,负载平衡器负责操作字节,并确保来自同一会话的字节转发到同一后端。L4负载平衡器并不知道通信中的任何应用细节。这些字节可能是 HTTP,Redis,MongoDB或任何其他应用协议。


L7(应用)负载平衡

 L4负载均衡简单,能看到广泛使用。人们为什么放弃L4负载均衡,来研究L7(应用)负载均衡?以下是L4具体的案例:


两个gRPC / HTTP2 客户端通过L4负载均衡器与后端建立连接。

L4负载均衡器为每个接入的TCP连接建立一个传出的TCP连接,这样就产生了两个传入和两个传出连接。

但是,客户端A每分钟发送 1个请求(RPM),而客户端B通每秒发送50个请求(RPS)。


在上述场景中,后端选择处理客户端B请求是选择客户端A请求负载的大约3000倍。这是一个很大的问题,打破了负载均衡的目的。需要注意的是这个问题发生在任何复用,保持存活状态的协议。(复用意味着通过单个L4连接多次发送应用程序请求,保持存活就是没有活动请求时不关闭连接)。出于效率的考虑,所有现代协议都在不断发展,既要复用也要存活(创建连接通常是昂贵的,特别是用TLS进行加密连接时),所以L4负载均衡器随着时间的推移,负载失衡的情况更加明显。这个问题在L7负载均衡器中得到解决。


3.png


图3:HTTP / 2 L7终端负载均衡

图3展示了L7 HTTP/2负载平衡器。客户端与负载均衡器只建立一个 HTTP/2 TCP连接,连接两个后端。当客户端向负载均衡器发送两个HTTP/2 流时,流1被发送到后端1,流2被发送到后端2。因此,即使差别很大的请求负载的多路复用客户端也将被有效地平衡到不同的后端。这就是L7负载平衡对于现代协议的重要性所在。(L7负载平衡由于能够检查应用流量,而产生大量好处,后面会更详述)。



L7负载均衡和OSI模型

正如L4负载平衡部分所述,使用OSI模型描述负载均衡功能是有问题的。原因在于L7本身包含多个离散的负载均衡抽象层。例如,对于HTTP流量,请考虑以下子层:


可选的传输层安全性(TLS)。网络界对于TLS 属于OSI模型哪一层有争论。为了方便,我们考虑将TLS放在 L7。

物理HTTP协议(HTTP / 1或HTTP / 2)。

逻辑HTTP协议(Header、Body 和 Trailer)。

消息传递协议(gRPC,REST等)。


一个复杂的L7负载均衡器要提供与上述每个子层有关的功能。另L7负载均衡器可能只有一小部分功能属于OSI模型中L7层。简而言之,L7负载平衡器的功能远比L4类型复杂得多。(HTTP、 Redis、Kafka、MongoDB等都是应用L7 协议的例子,它们受益于L7负载均衡)。



2负载均衡器功能


在本节中将总结负载均衡器提供的高级功能,并非所有负载均衡器都提供全部功能。


服务发现

服务发现是负载平衡器确定可用后端的过程,实现的方式有很多种,一些例子包括:


静态配置文件。

DNS。

Zookeeper,Etcd,Consul等。

Envoy的通用数据平面API。


健康检查

运行状况检查是负载均衡器确定后端是否可用来为流量提供服务的过程。健康检查一般分为两类:

主动式:负载平衡器将定期发送ping(例如,对/healthcheck端点的HTTP请求)到后端,并用它来衡量健康状况。

被动式:负载均衡器从主数据流中检测健康状况。例如,如果连续出现三个连接错误,则L4负载均衡器会认为后端是不健康的。如果一行中有三个HTTP 503响应代码,则L7负载均衡器会认为后端是不健康的。



负载均衡

负载均衡器必须实际平衡负载!当给定一组健康的后端时,选择哪个后端处理请求?负载平衡算法是一个活跃的研究领域,从简单的算法(如随机选择和循环算法)到考虑可变延迟和后端负载的更复杂的算法。考虑到其性能和简单性,最流行的负载均衡算法是二节点最小请求负载均衡(power of 2 least request load balancing)。 


粘滞会话(Sticky sessions)

在某些应用中,同一会话的请求到达相同的后端是很重要的。这与缓存,临时复杂状态等有关。会话的定义各不相同,可能包括HTTP Cookie,客户端连接的配置或其他属性。许多L7负载平衡器支持会话粘连。会话粘性本质上是脆弱的(后端保存的会话可能会失效),所以在设计系统的时候减少对它们的依赖。



TLS终止

关于TLS,它在边缘服务和服务间通信方面值得肯定。因此,许多L7负载均衡器会执行大量的TLS处理,包括终止,证书验证和保存,使用SNI的证书服务等。



可观察性

正如谈话中所强调的:“可观察性,可观察性,可观察性”。网络本质上是不可靠的,负载均衡器通常负责导出统计信息,跟踪信息和记录日志,帮助运营人员找出问题所在,从而解决问题。最先进的负载平衡器可观察性输出包括数字统计,分布式跟踪和可定制的日志记录。增强的可观察性不是免费的; 负载平衡器必须做很多附加的工作来完成它。至于,负面影响,相对带来的好处,几乎不值一提。


安全性和DoS防范

特别是在边缘部署拓扑(见下文)中,负载均衡器通常实施各种安全功能,包括速率限制,身份验证和DoS防范(例如,IP地址标记和识别,缓送Tarpitting等)。



配置和控制面板

负载平衡器需要配置。在大规模部署中,这是一项重要工作。一般来说,配置负载均衡器的系统被称为“控制面板”,并且在其实现中变化很大。
 
3负载均衡器拓扑的类型

前面介绍了负载平衡器的概要,L4和L7负载平衡器之间的区别以及负载均衡器特性,接下来将转向部署负载均衡器的各种分布式系统拓扑。(以下每种拓扑均适用于L4和L7负载均衡器)。



中间代理


4.png

 
图4:中间代理负载均衡拓扑

图4所示的中间代理拓扑是大多数读者最熟悉的负载均衡一种方式。这一类包括Cisco,Juniper,F5等硬件设备; 云软件解决方案,如亚马逊的ALB和NLB以及Google的 Cloud Load Balancer ; 以及HAProxy,NGINX和Envoy等纯软件自主托管解决方案。中间代理解决方案使用简单。通常,用户通过DNS连接到负载均衡器,不需要担心其他任何事情。中间代理解决方案的特点是代理(即使是集群)是单一的故障点,也是扩展的瓶颈。中间代理通常是一个让运维变得困难的黑匣子。当问题出现时,是在客户端还是物理网络?在中间代理还是后端?这很难说。



边缘代理

5.png


图5:边缘代理负载平衡拓扑

图5所示实际上只是中间代理拓扑的一种变体,负载均衡器通过Internet访问。在这种情况下,负载均衡器通常必须提供附加的“API网关”功能,如TLS终止,速率限制,身份验证和复杂的流量路由。边缘代理的优缺点与中间代理相同。需要注意的是,在面向Internet的大型分布式系统中部署专用边缘代理通常是不可避免的。客户端通常需要使用服务所有者无法控制的任意网络库(后面会讨论嵌入式客户端库或附属代理拓扑结构不能直接在客户端上运行)通过DNS访问系统。另外,由于安全原因,希望具有单个网关,通过该网关,所有面向互联网的流量进入系统。



嵌入式客户端库

6.png


图6:通过嵌入式客户端库进行负载平衡

为了避免中间代理拓扑中固有的单点故障和扩展问题,更复杂的基础设施已经开始将负载均衡器直接嵌入到服务库中,如图6所示。支持的功能库有很大的不同,但是这个类别中最知名和最丰富的功能包括Finagle,Eureka / Ribbon / Hystrix和gRPC(松散地基于称为Stubby的内部Google系统)。基于库的解决方案的主要功能是将负载均衡器的所有功能完全分配给每个客户端,从而消除之前描述的单点故障和扩展问题。基于客户端库的解决方案的主要内容是客户端库必须每种语言调用。分布式架构正变得越来越“多语言”(多语言)。在这样的环境下,用许多不同的语言来重新实现一个非常复杂的网络库的成本会变得过高。最后,在大型服务体系结构中部署库升级可能会非常痛苦,这使得很多不同版本的库将在生产中同时运行,

综上所述,上面提到的这些库对于那些能够限制编程语言扩散并克服库升级难题的公司来说是成功的。



Sidecar代理

7.png


图7:通过sidecar代理进行负载平衡

嵌入式客户端库负载平衡器拓扑的一个变种是图7中所示的辅助代理拓扑。近年来,这种拓扑已经被普遍推广为“服务网格”(Service Mesh)。Sidecar代理背后的思路是,以跳过一个不同的过程的代价来承担轻微的延迟,嵌入式库的所有好处都可以在没有任何编程语言锁定的情况下获得。在撰写本文时,最受欢迎的sidecar代理负载平衡器是Envoy,NGINX,HAProxy和Linkerd。



负载均衡器拓扑的总结和优缺点

中间代理拓扑是最简单的负载平衡拓扑。缺点是:单点故障,伸缩瓶颈和黑箱操作。


边缘代理拓扑与中间代理类似,但通常无法避免。


嵌入式客户端库拓扑提供了最好的性能和可扩展性,但是需要在每种语言中实现该库,并跨所有服务升级库。


Sidecar代理拓扑性能比嵌入式客户端库拓扑弱,但不受任何限制。

总体而言,sidecar代理拓扑(服务网格)正在逐渐取代所有其他的服务。流量进入服务网格之前,始终需要边缘代理拓扑。



4L4负载均衡的现状


L4负载均衡器还有用吗?

本文讨论了L7负载平衡器如何用于现代协议,后面还会讨论L7负载平衡器的特性。这是否意味着L4负载平衡器没用了?没有!尽管在我看来,L7负载均衡器最终将完全取代L4负载均衡器进行服务间通信,但是L4负载均衡器对边缘通信仍然很重要。因为几乎所有现代大型分布式架构都使用两层L4 / L7负载平衡架构处理互联网流量。在L7负载均衡器之前在边缘部署中部署专用L4负载均衡器的好处是:


L7负载均衡器执行的应用程序流量的分析,转换和路由选择要复杂得多,所以它们可以处理相对于经过优化的L4负载均衡器能够处理的相对较小的原始流量负载(以每秒数据包和每秒字节数衡量)。这一现实通常使得L4负载平衡器成为处理某些类型的DoS攻击(例如,SYN flood,通用包flood攻击等)的更好位置。


L7负载均衡器往往比L4负载平衡器更积极地开发,部署更频繁,并且具有更多的缺陷。有一个L4负载均衡器,可以在L7负载平衡器部署期间执行健康检查和排空,这比现代L4负载平衡器(通常使用BGP和ECMP)(以下更多)中使用的部署机制要容易得多。最后,因为L7负载平衡器功能的复杂性更容易出现错误,所以使用绕开故障和异常的L4负载平衡器产生更稳定的整体系统。


下面将介绍几种不同的中间/边缘代理L4负载均衡器的设计。这些设计不适用于客户端库和Sidecar拓扑。



TCP / UDP终端负载均衡器

8.png

图8:L4终端负载均衡器


仍在使用的第一类L4负载均衡器是图8所示的终端负载均衡器。这类负载均衡器,使用两个离散的TCP连接:一个在客户端和负载均衡器之间,另一个在负载均衡器和后端之间。


L4终端负载均衡器仍然有两个原因:

实现相对简单。

靠近客户端的连接终端(低延迟)对性能有很大的影响。具体来说,如果终端负载均衡器可以放置在使用有损网络(例如蜂窝)的客户端附近,那么在将数据转移到可靠的光纤传输到其最终位置之前,容易发生重传。换句话说,这种负载平衡器适用于存在点(POP)场景下的原始TCP连接。




TCP / UDP直通负载均衡器


9.png


图9:L4直通负载均衡器

第二种类型的L4负载均衡器是图9所示的直通负载均衡器。在这种类型的负载均衡器中,TCP连接不会被负载均衡器终止。而是在连接跟踪和网络地址转换(NAT)发生后,将每个连接的数据包转发到选定的后端。首先,我们来定义一下连接跟踪和NAT:


连接跟踪:跟踪所有活动的TCP连接状态的过程。这包括握手是否完成,是否收到FIN,连接空闲多久,连接选择了哪个后端等等。


NAT:NAT是使用连接跟踪数据在通过负载均衡器时更改数据包的IP /端口信息的过程。


使用连接跟踪和NAT,负载平衡器可以将大部分原始TCP流量从客户端传递到后端。例如,假设客户正在交谈,1.2.3.4:80而选定的后端位于10.0.0.2:9000。客户端TCP数据包将到达负载均衡器at 1.2.3.4:80。然后负载均衡器将交换数据包的目标IP和端口10.0.0.2:9000。它还会将数据包的源IP与负载均衡器的IP地址进行交换。因此,当后端在TCP连接上响应时,数据包将返回到负载平衡器,在那里进行连接跟踪并且NAT可以在反向再次发生。


为什么会使用这种负载均衡器来代替上一节中描述的终端负载均衡器呢?几个原因:


性能和资源使用:由于直通负载均衡器不是终止TCP连接,所以不需要缓冲任何TCP连接窗口。每个连接存储的状态量非常小,通常通过高效的散列表查找来访问。因此,直通负载均衡器通常可以处理比终端负载均衡器大得多的活动连接数和数据包数(PPS)。


允许后端执行拥塞控制:TCP拥塞控制是因特网上的端点节制发送数据的机制,从而不会压倒可用带宽和缓冲区。由于直通负载均衡器不会终止TCP连接,因此不参与拥塞控制。这个事实允许后端根据他们的应用使用情况使用不同的拥塞控制算法。它还可以更容易地进行拥塞控制变更的实验(例如,最近的BBR部署)。


形成直接服务器返回(DSR)和群集L4负载平衡的基准:更高级的L4负载平衡技术(如DSR和使用分布式一致性散列的群集(以下各节中讨论))需要直通负载平衡。


直接服务器返回(Direct Server Return,DSR)



10.png


图10:L4直接服务器返回(DSR)

直接服务器返回(DSR)负载平衡器如图10所示。DSR构建在上一节中介绍的直通负载均衡器上。DSR是一种优化,只有入口/请求数据包才能通过负载均衡器。出口/响应数据包在负载平衡器周围直接返回到客户端。执行DSR很有趣的主要原因是,在许多工作负载中,响应流量对请求流量(例如,典型的HTTP请求/响应模式)产生了不满。假设10%的流量是请求流量,90%的流量是响应流量,如果使用DSR的负载均衡器为1/10的容量可以满足系统的需求。由于历史上负载平衡器非常昂贵,因此这种优化可能会对系统成本和可靠性产生重大影响。DSR负载均衡器将直通负载均衡器的概念扩展如下:

负载平衡器通常仍然执行部分连接跟踪。由于响应数据包不会遍历负载均衡器,因此负载均衡器将不会意识到完整的TCP连接状态。但是,负载均衡器可以通过查看客户端数据包并使用各种类型的空闲超时来强烈推断状态。


负载均衡器通常使用通用路由封装(GRE)来封装从负载均衡器发送到后端的IP数据包,而不使用NAT 。因此,当后端收到封装的数据包时,可以对其进行解封装,并知道客户端的原始IP地址和TCP端口。这允许后端直接响应客户端,而无需响应数据包流经负载平衡器。


DSR负载平衡器的一个重要组成部分是后端参与负载均衡。后端需要有一个正确配置的GRE隧道,根据网络设置的低层细节,可能需要自己的连接跟踪,NAT等。

请注意,在直通负载均衡器和DSR负载均衡器设计中,可以通过负载均衡器和后端来设置连接跟踪,NAT,GRE等多种方式。



通过高可用性配对的容错



11.png


图11:通过HA对和连接跟踪的L4容错

到目前为止,我们一直在考虑L4负载平衡器的设计。直通和DSR负载平衡器都需要负载均衡器本身的一些连接跟踪和状态。如果负载平衡器死了怎么办?如果负载平衡器的单个实例死亡,则所有通过负载平衡器的连接将被切断。取决于应用程序,这可能会对应用程序性能产生重大影响。


历史上,L4负载均衡器是从典型供应商(Cisco,Juniper,F5等)购买的硬件设备。这些设备非常昂贵,处理大量的流量。为了避免单个负载均衡器发生故障切断所有连接,并导致大量应用程序中断,负载均衡器通常部署在高可用性配对中,如图11所示。典型的HA负载均衡器设置具有以下设计:


一对HA边缘路由器为虚拟IP(VIP)提供服务。这些边缘路由器使用边界网关协议(BGP)宣布虚拟IP 。主边缘路由器具有比备份更高的BGP权重,因此在稳定状态下,它服务所有流量。(BGP是一个非常复杂的协议; 就本文而言,考虑BGP是网络设备宣布可以从其他网络设备获取流量的机制,并且每个链路都可以具有优先考虑链路流量的权重)。


同样,主L4负载均衡器向边缘路由器宣布自己的BGP权重高于备份,因此在稳定状态下,它将服务所有流量。

主负载均衡器交叉连接到备份,并共享其所有连接跟踪状态。因此,如果主负载宕机,备份可以接管处理所有活动的连接。


两台边缘路由器和两台负载均衡器都是交叉连接的。这意味着如果其中一个边缘路由器或负载平衡器中的一个死亡,或者由于某种其他原因撤消BGP通知,备份可以接管所有通信。


上面的设置是目前具有高流量的互联网应用还在使用的方式。但是,上述方法有很大的缺点:

考虑到容量使用情况,虚拟 IP (VIP)必须在HA负载平衡器对之间正确分片。如果单个VIP超出单个HA对的容量,则需要将VIP分成多个VIP。


系统的资源使用情况很差。50%的容量在稳定状态下闲置。鉴于历史上硬件负载平衡器非常昂贵,这导致大量闲置资本。

现代分布式系统设计比主动/备份提供更高的容错能力。例如,最佳地,系统应该能够同时遭受多个故障并继续运行。如果主动和备用负载均衡器同时死亡,则HA负载均衡器对可能会发生完全故障。


来自供应商的专有大型硬件设备非常昂贵,并导致供应商锁定。通常希望用可商购计算服务器构建的可横向扩展的软件解决方案替代这些硬件设备。



通过具有分布式一致散列的集群进行容错和扩展


12.png

图12:L4容错和通过集群负载平衡器和一致性哈希的扩展

上一节介绍了通过HA对的L4负载平衡器容错以及该设计中固有的问题。从21世纪初到中期,大型互联网基础设施开始设计和部署新的大规模并行L4负载平衡系统,如图12所示。这些系统的目标是:


减轻上一节中描述的HA对设计的所有缺点。

从供应商的专有硬件负载平衡器转移到使用标准计算服务器和NIC构建的商品软件解决方案。

这个L4负载均衡器设计最好称为容错和扩展,通过集群和分布式一致的散列。它的工作原理如下:

N个边缘路由器以相同的BGP权重宣布所有Anycast VIP。等价多路径路由(ECMP)用于确保通常来自单个流的所有数据包到达相同的边缘路由器。流程通常是源IP /端口和目标IP /端口的四元组。(简而言之,ECMP是一种使用一致散列在一组相同加权的网络链路上分发数据包的方法)。尽管边缘路由器本身并不特别关心哪个分组到达哪里,但是通常优选的是,来自一个流的所有分组都经过相同的一组链路,以避免乱序分组,从而降低性能。

N个L4负载均衡器机器以与边缘路由器相同的BGP权重宣布所有的VIP。再次使用ECMP,边缘路由器将通常为流选择相同的负载平衡器机器。

每个L4负载均衡器机器通常会执行部分连接跟踪,然后使用一致的哈希来为流选择一个后端。GRE用于封装从负载均衡器发送到后端的数据包。

DSR然后被用来通过边缘路由器直接从后端发送数据包到客户端。

L4负载均衡器使用的实际一致性散列算法是一个活跃的研究领域。主要围绕均衡负载进行权衡,最小化延迟,最大限度地减少后端更改期间的中断, 并最大限度地减少内存开销。关于这个主题的完整讨论超出了本文的范围。


让我们看看上面的设计是如何缓解HA对方法的所有缺点的:

新的边缘路由器和负载平衡器机器可以根据需要添加。在添加新机器时,每层都使用一致的散列来尽可能减少受影响的流量。
 
系统的资源使用可以按照需要高速运行,同时保持足够的突发容限和容错。

边缘路由器和负载平衡器现在都可以使用商用硬件来构建,而传统硬件负载平衡器的成本只是其中的一小部分(下面会详细介绍)。


通常被问及这个设计的一个问题是“为什么边缘路由器不通过ECMP直接与后端对话?为什么我们需要负载平衡器?“其原因主要是减少DoS和后端操作的简化。如果没有负载平衡器,每个后端都必须参与BGP,并且执行滚动部署的时间将更加困难。


所有现代化的L4负载均衡系统都在朝着这种设计方向发展(或其他变体)。最著名的两个例子是Google的Maglev和Amazon 的网络负载均衡器(NLB)。目前还没有实现这种设计的OSS负载平衡器,但是,我知道有一家公司计划在2018年将其发布到OSS。我对这个版本感到非常兴奋,因为现代L4负载平衡器是一个至关重要的部分在网络空间中缺少OSS。


5L7负载平衡的现有技术状态

在过去的几年中,L7负载平衡器/代理的发展已经复苏。随着分布式系统中的微服务架构的不断推进,这方面也在不断进步。从根本上说,固有故障的网络在使用更频繁时变得更加难以有效地运行。而且,自动扩展,容器调度等等的出现意味着在静态文件中配置静态IP的日子早已不复存在。系统不仅要利用网络,还要变得更加动态,需要负载平衡器的新功能。本节将简要概述在现代L7负载平衡器的最新发展。


协议支持

现代L7负载平衡器为许多不同的协议增加了明确的支持。负载平衡器对于应用程序流量的知识越多,关于可观察性输出,高级负载平衡和路由等方面可以做的更复杂的事情。例如,在撰写本文时,Envoy明确支持L7协议解析和路由用于HTTP / 1,HTTP2,gRPC,Redis,MongoDB和DynamoDB。未来可能会有更多协议被添加,包括MySQL和Kafka。



动态配置

如上所述,分布式系统越来越具有动态性,需要并行投资来创建动态和反应式控制系统。Istio就是这样一个例子。


高级负载平衡

L7负载均衡器现在通常内置对高级负载平衡功能的支持,如超时,重试,速率限制,断路,屏蔽,缓冲,基于内容的路由等。



可观察性

如上面有关一般负载平衡器功能的部分所述,正在部署的日益动态的系统越来越难以调试。强大的协议特定的可观测性输出是现代L7负载平衡器最重要的特性。任何L7负载平衡解决方案现在都需要输出数字统计信息,分布式跟踪和可自定义日志记录。



可扩展性

现代L7负载平衡器的用户通常希望轻松扩展以添加自定义功能。这可以通过写入加载到负载均衡器的可插入过滤器来完成。许多负载平衡器也支持脚本,通常通过Lua。



容错

L7负载均衡器的容错性如何?一般来说,我们把L7负载平衡器视为可消耗和无状态的。使用商业软件就能够对L7负载平衡器轻松水平扩展。此外,L7负载平衡器执行的处理和状态跟踪比L4复杂得多。试图建立一个L7负载平衡器的HA配对在技术上是可能的,但非常繁杂。


总的来说,在L4和L7负载平衡领域,业界正在从HA配对转向通过一致的哈希聚合横向水平可扩展系统。


更多

L7负载平衡器正在以惊人的速度发展。有关Envoy提供的示例,请参阅Envoy的架构概述。


6全球负载均衡和集中控制平面


13.png


图13:全局负载平衡

负载平衡的未来将越来越多地将单个负载平衡器视为商品设备。我认为,真正的创新和商业机会都在控制层面。图13显示了一个全局负载均衡系统的例子。在这个例子中,几个不同的事情正在发生:

每个Sidecar代理正在与三个不同区域(A,B和C)的后端进行通信。

如图所示,90%的流量正被发送到区域C,而5%的流量正被发送到区域A和B。

Sidecar代理和后端均向全局负载均衡器报告周期性状态。这允许全局负载均衡器做出考虑延迟,成本,负载,当前故障等的决定。

全局负载均衡器使用当前路由信息周期性地配置每个边车代理。

全球负载均衡器越来越能够做复杂的事情。例如:

自动检测并绕过区域故障。

应用全局安全和路由策略。

使用机器学习和神经网络检测和减轻流量异常,包括DDoS攻击。

提供集中的用户界面和可视化,使工程师能够理解和操作整个分布式系统。

为了实现全局负载均衡,用作数据平面的负载平衡器必须具有复杂的动态配置能力。



7从硬件到软件的演变


本文只是简单地提到了硬件和软件,主要是在L4负载平衡器HA对的历史背景下。这方面的业界趋势又是怎样呢?

历史上的路由器和负载平衡器都是昂贵的专有硬件。

越来越多的专有L3 / L4网络设备正在被商用服务器硬件,商用网卡和基于IPVS,DPDK和fd.io等框架的专业软件解决方案取代。价格低于5K美元的现代数据中心机器可以使用Linux和自定义的基于DPDK的 user-space 应用,轻松饱和具有非常小数据包的80Gbps NIC。与此同时,廉价的路由器/交换机ASIC可以以惊人的总带宽和数据包速率进行ECMP路由选择,正在被打包成商品路由器。

像NGINX,HAProxy和Envoy这样先进的L7软件负载平衡器也在迅速地迭代和侵入之前像F5这样的供应商领域。因此,L7负载平衡器也在积极地向商品软件解决方案迈进。

与此同时,整个行业向主流云提供商推动的IaaS,CaaS和FaaS意味着越来越多的工程师需要了解物理网络是如何工作的


8结论和负载均衡的未来


这篇文章的主要内容是:

负载平衡器是现代分布式系统中的关键组件。

有两种负载均衡器:L4和L7。

L4和L7负载平衡器都与现代架构中相关。

L4负载均衡器正朝着横向可扩展的分布式一致哈希水平扩展解决方案迈进。

由于动态微服务体系结构的激增,L7负载平衡器最近发展迅速。

全局负载平衡和控制平面与数据平面之间的分离,是负载平衡的未来,存在大量商业机会。

业界正在积极向商用OSS硬件和软件提供网络解决方案。相信像F5这样的传统负载均衡厂商将首先被OSS软件和云供应商所取代。传统的路由器/交换机厂商,如Arista / Cumulus /等。短期会有好的发展,但最终也将被公有云供应商和自研物理网络所取代。

总的来说,现在是计算机网络的一个激动人心的时代!大多数系统开始向OSS和软件方向转变,迭代速度加快。分布式系统向“无服务器”计算发展,必须继续充满活力,底层网络和负载平衡系统的复杂性也将齐头并进。

原文:https://blog.envoyproxy.io/int ... 80236

使用 Istio治理微服务入门

Istio小数 发表了文章 • 5 个评论 • 665 次浏览 • 2018-01-09 15:17 • 来自相关话题

作者:Tony Bai
 





 
近两年微服务架构流行,主流互联网厂商内部都已经微服务化,初创企业虽然技术积淀不行,但也通过各种开源工具拥抱微服务。再加上容器技术赋能,Kubernetes又添了一把火,微服务架构已然成为当前软件架构设计的首选。


但微服务化易弄,服务治理难搞!
 
 
微服务的“痛点”

微服务化没有统一标准,多数是进行业务领域垂直切分,业务按一定的粒度划分职责,并形成清晰、职责单一的服务接口,这样每一块规划为一个微服务。微服务之间的通信方案相对成熟,开源领域选择较多的有RPC或RESTful API方案,比如:gRPC、apache thrift等。这些方案多偏重于数据如何打包、传输与解包,对服务治理的内容涉及甚少。


微服务治理是头疼的事,也是微服务架构中的痛点。治理这个词有多元含义,很难下达一个精确定义,这里可以像小学二年级学生那样列出治理的诸多近义词:管理、控制、规则、掌控、监督、支配、规定、统治等。对于微服务而言,治理体现在以下诸多方面:
服务注册与发现 身份验证与授权 服务的伸缩控制 反向代理与负载均衡 路由控制 流量切换 日志管理 性能度量、监控与调优 分布式跟踪 过载保护 服务降级 服务部署与版本升级策略支持 错误处理

… …

从微服务治理角度来说,微服务其实是一个“大系统”,要想将这个大系统全部落地,绝非易事,尤其是之前尚没有一种特别优雅的技术方案。多数方案(比如:dubbo、go-kit等。)都或多或少地对应用逻辑有一定的侵入性,让业务开发人员不能只focus到业务本身,还要关心那些“治理”逻辑。并且市面上内置了微服务治理逻辑的框架较少,且很多编程语言相关。这种情况下,大厂多选择自研或基于某个框架改造,小厂一般只能“东拼西凑”一些“半成品”凑合着使用,就这样微服务也走过了若干年。


Service Mesh横空出世,Istio带来“福音”

我不知道在没有TCP/IP协议的年代,主机和主机之间的应用通信时是否需要应用关心底层通信协议实现逻辑。但是和TCP/IP诞生的思想类似,在微服务使用多年后,人们发现需要独立地抽象出一层逻辑网络,专门用于“微服务通信与治理策略的落地”,让应用只关心业务,把服务治理的事情全部交由“这一层”去处理。





图:传统微服务之间的微服务治理逻辑的位置
 





图:微服务治理逻辑被独立出来之后的位置
 
由“Service Govern Logic”这一层组成的逻辑网络被定义为Service Mesh,每个微服务都包含一个Service Mesh的端点。


“Service Mesh”概念还非常年轻,这个词在国内被翻译为“服务网格”或“服务啮合层”,我们这里就用Service Mesh这个英文词。这里摘录一下 ServiceMesh 中文网社区上的一篇名为“年度盘点2017之Service Mesh:群雄逐鹿烽烟起”的文章中对Service Mesh概念的回顾:


在 2016 年年初,“Service Mesh”还只是 Buoyant 公司的内部词汇,而之后,它开始逐步走向社区:

2016 年 9 月 29 日在 SF Microservices 上,“Service Mesh”这个词汇第一次在公开场合被使用。这标志着“Service Mesh”这个词,从 Buoyant 公司走向社区。

2016 年 10 月,Alex Leong 开始在 Buoyant 公司的官方 Blog 中连载系列文章“A Service Mesh for Kubernetes”。随着“The Services must Mesh”口号的喊出,Buoyant 和 Linkerd 开始 Service Mesh 概念的布道。

2017 年 4 月 25 日,William Morgan 发布博文“What’s a service mesh? And why do I need one?”。正式给 Service Mesh 做了一个权威定义。

而Service Mesh真正引起大家关注要源于Istio项目的开源发布。为什么呢?个人觉得还是因为“爹好”!Istio项目由Google、IBM共同合作创建,lyft公司贡献了Envoy项目将作为Istio Service Mesh的data panel。Google、IBM的影响力让Service Mesh概念迅速传播,同时也让大家认识到了Istio项目在Service Mesh领域的重要性,于是纷纷选择积极支持并将自己的产品或项目与Istio项目集成。


Istio项目是Service Mesh概念的最新实现,旨在所有主流集群管理平台上提供Service Mesh层,初期以实现Kubernetes上的服务治理层为目标。它由控制平面和数据平面组成(是不是感觉和SDN的设计理念相似啊)。控制平面由Go语言实现,包括pilot、mixer、auth三个组件;数据平面功能暂由Envoy在pod中以Sidecar的部署形式提供。下面是官方的架构图:





图:Istio架构图(来自官网)
Sidecar中Envoy代理了pod中真正业务container的所有进出流量,并对这些流量按照控制平面设定的“治理逻辑”进行处理。而这一切对pod中的业务应用是透明的,开发人员可以专心于业务逻辑,而无需再关心微服务治理的逻辑。Istio代表的Service Mesh的设计理念被认为是下一代“微服务统一框架”,甚至有人认为是微服务框架演化的终点。

Istio于2017 年 5 月 24 日发布了0.1 release 版本,截至目前为止Istio的版本更新到v0.4.0,演进速度相当快,不过目前依然不要用于生产环境,至少要等到1.0版本发布吧。但对于Istio的早期接纳者而言,现在正是深入研究Istio的好时机。在本篇的接下来内容中,我们将带领大家感性的认识一下Istio,入个门儿。
 
 
Istio安装
istio目前支持最好的就是kubernetes了,因此我们的实验环境就定在kubernetes上。至于版本,Istio当前最新版本为0.4.0,这个版本据说要K8S 1.7.4及以上版本用起来才不会发生小毛病:)。我的K8S集群是v1.7.6版本的,恰好满足条件。下面是安装过程:(Node上的os是ubuntu 16.04)
# wget -c https://github.com/istio/istio ... ar.gz



解压后,进入istio-0.4.0目录,



# ls -F

bin/  install/  istio.VERSION  LICENSE  README.md  samples/



# cat istio.VERSION

# DO NOT EDIT THIS FILE MANUALLY instead use

# install/updateVersion.sh (see install/README.md)

export CA_HUB="docker.io/istio"

export CA_TAG="0.4.0"

export MIXER_HUB="docker.io/istio"

export MIXER_TAG="0.4.0"

export PILOT_HUB="docker.io/istio"

export PILOT_TAG="0.4.0"

export ISTIOCTL_URL="https://storage.googleapis.com ... ot%3B

export PROXY_TAG="0.4.0"

export ISTIO_NAMESPACE="istio-system"

export AUTH_DEBIAN_URL="https://storage.googleapis.com ... ot%3B

export PILOT_DEBIAN_URL="https://storage.googleapis.com ... ot%3B

export PROXY_DEBIAN_URL="https://storage.googleapis.com ... ot%3B

export FORTIO_HUB="docker.io/istio"

export FORTIO_TAG="0.4.2"



# cd install/kubernetes



我们先不用auth功能,因此使用istio.yaml这个文件进行istio组件安装:



# kubectl apply -f istio.yaml

namespace "istio-system" created

clusterrole "istio-pilot-istio-system" created

clusterrole "istio-initializer-istio-system" created

clusterrole "istio-mixer-istio-system" created

clusterrole "istio-ca-istio-system" created

clusterrole "istio-sidecar-istio-system" created

clusterrolebinding "istio-pilot-admin-role-binding-istio-system" created

clusterrolebinding "istio-initializer-admin-role-binding-istio-system" created

clusterrolebinding "istio-ca-role-binding-istio-system" created

clusterrolebinding "istio-ingress-admin-role-binding-istio-system" created

clusterrolebinding "istio-sidecar-role-binding-istio-system" created

clusterrolebinding "istio-mixer-admin-role-binding-istio-system" created

configmap "istio-mixer" created

service "istio-mixer" created

serviceaccount "istio-mixer-service-account" created

deployment "istio-mixer" created

customresourcedefinition "rules.config.istio.io" created

customresourcedefinition "attributemanifests.config.istio.io" created

... ...

customresourcedefinition "reportnothings.config.istio.io" created

attributemanifest "istioproxy" created

attributemanifest "kubernetes" created

stdio "handler" created

logentry "accesslog" created

rule "stdio" created

metric "requestcount" created

metric "requestduration" created

metric "requestsize" created

metric "responsesize" created

metric "tcpbytesent" created

metric "tcpbytereceived" created

prometheus "handler" created

rule "promhttp" created

rule "promtcp" created

kubernetesenv "handler" created

rule "kubeattrgenrulerule" created

kubernetes "attributes" created

configmap "istio" created

customresourcedefinition "destinationpolicies.config.istio.io" created

customresourcedefinition "egressrules.config.istio.io" created

customresourcedefinition "routerules.config.istio.io" created

service "istio-pilot" created

serviceaccount "istio-pilot-service-account" created

deployment "istio-pilot" created

service "istio-ingress" created

serviceaccount "istio-ingress-service-account" created

deployment "istio-ingress" created

serviceaccount "istio-ca-service-account" created

deployment "istio-ca" created
 
注:我还曾在K8S v1.7.3上安装过istio 0.3.0版本,但在创建组件时会报下面错误(这个错误可能会导致后续addon安装后工作不正常):
unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric
安装后,我们在istio-system这个namespace下会看到如下pod和service在运行(由于istio的各个组件的image size都不小,因此pod状态变为running需要一丢丢时间,耐心等待)
# kubectl get pods -n istio-system

NAME                             READY     STATUS    RESTARTS   AGE

istio-ca-1363003450-jskp5        1/1       Running   0          3d

istio-ingress-1005666339-c7776   1/1       Running   4          3d

istio-mixer-465004155-twhxq      3/3       Running   24         3d

istio-pilot-1861292947-6v37w     2/2       Running   18         3d



# kubectl get svc -n istio-system

NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)                                                   AGE

istio-ingress   10.98.10.87      <pending>     80:31759/TCP,443:25804/TCP                         4d

istio-mixer     10.109.244.155   <none>        9091/TCP,15004/TCP,9093/TCP,9094/TCP,9102/TCP,9125/UDP,42422/TCP   4d

istio-pilot     10.105.80.55     <none>        15003/TCP,443/TCP                                              4d
 
istio安装成功!
 
 
服务治理策略验证
 
接下来我们来用几个例子验证一下Istio在服务治理方面的能力!(istio自带一些完整的例子,比如bookinfo,用于验证服务治理的能力,但这里先不打算用这些例子)

验证环境和拓扑我们先来看一下验证环境的示意图:





 
我们看到在Service Mesh中部署了两个service: server_a和service_b,前者调用后者完成某项业务,后者则调用外部服务完成业务逻辑。




service_a: 模拟pay服务,在收到client请求后,进行pay处理,并将处理结果通过service_b提供的msg notify服务下发给user。该服务的endpoint为/pay;

service_b: 模拟notify服务,在收到service_a请求后,将message转发给external service,完成notify逻辑。该服务的endpoint为/notify;

external service: 位于service mesh之外。

client:我们使用curl模拟。
 





 
我们先来部署service_a和service_b的v0.1版本:
以service_a的部署为例, service_a的deployment文件如下:
 
//svca-v0.1.yaml

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

 name: svca

spec:

 replicas: 1

 template:

   metadata:

     labels:

       app: svca

       version: v0.1

   spec:

     containers:

     - name: svca

       image: docker.io/bigwhite/istio-demo-svca:v0.1

       imagePullPolicy: Always

---

apiVersion: v1

kind: Service

metadata:

 name: svca

 labels:

   app: svca

spec:

 ports:

 - port: 80

   targetPort: 8080

   protocol: TCP

 selector:

   app: svca
 
注意,我们部署service_a时不能直接使用kubectl apply -f svca-v0.1.yaml,而是要apply经过istioctl(需将Istio安装目录下的bin放入PATH)处理过的yaml,以注入sidecar容器。当然也可以配置为自动为每个k8s启动的pod注入sidecar,但我们这里没有使用自动注入。我们执行下面命令:
 
# kubectl apply -f <(istioctl kube-inject -f svca-v0.1.yaml)

deployment "svca" created

service "svca" created



# kubectl get pods

NAME                               READY     STATUS    RESTARTS   AGE

svca-1997590752-tpwjf              2/2       Running   0          2m
 
 
同样的方法,我们来创建svcb:v0.1:
 
# kubectl apply -f <(istioctl kube-inject -f svcb-v0.1.yaml)

deployment "svcb" created

service "svcb" created
 
 
我们看到istio向每个pod中插入一个sidecar container,这个就是前面说的envoy,只不过container名字为istio-proxy。

接下来,我们把那个external service启动起来:
# nohup ./msgd > 1.log & 2>&1

[1] 9423
 
实验环境ok了。下面我们来验证一下业务是否是通的。
 

egress rule
按照之前我们的设定,我们使用curl去访问service_a服务的/pay端点,我们查看一下svca服务的ip和端口:
# kubectl get svc

NAME               CLUSTER-IP       EXTERNAL-IP   PORT(S)

svca               10.105.38.238    <none>        80/TCP                                         9h

svcb               10.105.119.194   <none>        80/TCP                                         9h
 
我们访问一下svca服务,svca的服务地址可以通过kubectl get svc查到:

# curl {svca_ip}/pay

查看svca和svcb的日志:
//service_a的日志:



service_a:v0.1 is serving the request...

service_a:v0.1 pays ok

&{500 Internal Server Error 500 HTTP/1.1 1 1 map[X-Content-Type-Options:[nosniff] Date:[Tue, 02 Jan 2018 15:41:50 GMT] Content-Length:[66] Content-Type:[text/plain; charset=utf-8]] 0xc420058d40 66  false false map 0xc4200eaf00 <nil>}

service_a:v0.1 notify customer ok



// service_b的日志:

&{GET /notify?msg=service_a:v0.1-pays-ok HTTP/1.1 1 1 map[User-Agent:[Go-http-client/1.1] Accept-Encoding:[gzip]] {} <nil> 0  false svcb map map <nil> map 127.0.0.1:58778 /notify?msg=service_a:v0.1-pays-ok <nil> <nil> <nil> 0xc4200fa3c0}

service_b:v0.1 is serving the request...

service_b:v0.1 send msg error: Get http://10.100.35.27:9997/send% ... s-ok: EOF
我们看到service_a和service_b都返回了错误日志(注意:go http get方法对于non-2xx response不会返回错误,我们只是看到了response中的500状态码才意识到错误的存在)。其中源头在service_b,原因是其连不上那个external service!那么为什么连不上external service呢?这是由于缺省情况下,启用了Istio的服务是无法访问外部URL的,这是因为Pod中的iptables把所有外发传输都转向到了Sidecar代理,而这一代理只处理集群内的访问目标。因此位于service mesh内的服务svcb无法访问外部的服务(msgd),我们需要显式的添加egressrule规则:

我们创建一个允许svcb访问外部特定服务的EgressRule:
//rules/enable-svcb-engress-rule.yaml



apiVersion: config.istio.io/v1alpha2

kind: EgressRule

metadata:

 name: enable-svcb-engress-rule

spec:

 destination:

   service: 10.100.35.27

 ports:

   - port: 9997

     protocol: http
 
使规则生效:

# istioctl create -f enable-svcb-engress-rule.yaml

Created config egress-rule/default/enable-svcb-engress-rule at revision 30031258


这时你再尝试curl svca,我们可以看到msgd的日志中出现了下面的内容:
 

2018/01/02 23:58:16 &{GET /send?msg=service_a:v0.1-pays-ok HTTP/1.1 1 1 map[X-Ot-Span-Context:[2157e7ffb8105330;2157e7ffb8105330;0000000000000000] Content-Length:[0] User-Agent:[Go-http-client/1.1] X-Forwarded-Proto:[http] X-Request-Id:[13c3af6e-2f52-993d-905f-aa6aa4b57e2d] X-Envoy-Decorator-Operation:[default-route] X-B3-Spanid:[2157e7ffb8105330] X-B3-Sampled:[1] Accept-Encoding:[gzip] X-B3-Traceid:[2157e7ffb8105330] X-Istio-Attributes:[Ch8KCXNvdXJjZS5pcBISMhAAAAAAAAAAAAAA//8KLgAMCjoKCnNvdXJjZS51aWQSLBIqa3ViZXJuZXRlczovL3N2Y2ItMjAwODk3Mzc2OS1ncTBsaC5kZWZhdWx0]] {} <nil> 0 false 10.100.35.27:9997 map map <nil> map 10.100.35.28:38188 /send?msg=service_a:v0.1-pays-ok <nil> <nil> <nil> 0xc4200584c0} 2018/01/02 23:58:16 Msgd is serving the request... 2018/01/02 23:58:16 Msgd recv msg ok, msg= service_a:v0.1-pays-ok
 
 
说明Svcb到外部服务的通信被打通了!

迁移流量到新版本svcb:v0.2我们经常有这样的需求,当svcb运行一段时间后,svcb添加了新feature,版本要升级到v0.2了,这时我们会部署svcb:v0.2,并将流量逐步切到v0.2上。

我们先来部署一下svcb:v0.2:
// svcb-v0.2.yaml

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

 name: svcb-v0.2

spec:

 replicas: 1

 template:

   metadata:

     labels:

       app: svcb

       version: v0.2

   spec:

     containers:

     - name: svcb

       image: docker.io/bigwhite/istio-demo-svcb:v0.2

       imagePullPolicy: Always
我们可以看到,服务名不变,但版本的label变成了v0.2,我们来执行这次部署:
 
# kubectl apply -f <(istioctl kube-inject -f svcb-v0.2.yaml)

deployment "svcb-v0.2" created



# kubectl get pods

NAME                               READY     STATUS    RESTARTS   AGE

svca-1997590752-pq9zg              2/2       Running   0          9h

svcb-2008973769-gq0lh              2/2       Running   0          9h

svcb-v0.2-3233505404-0g55w         2/2       Running   0          1m
 
svcb服务下又增加了一个endpoint:
# kubectl describe svc/svcb



.... ...

Selector:        app=svcb

Type:            ClusterIP

IP:            10.105.119.194

Port:            <unset>    80/TCP

Endpoints:        10.40.0.28:8080,10.46.0.12:8080

... ...
 
此时,如果按照K8S的调度方式,v0.1和v0.2版本的两个svcb pod应该1:1均衡地承载流量。为了方便查看流量分布,我们将每个版本的svcb的pod副本数量都扩展为2个(replicas: 2),这样service mesh中一共会有4个 svcb endpoints。

通过curl访问svca注入流量后,我们发现流量都集中在一个svcb:v0.2的pod上,并且长时间没有变化。我们通过下面的route rule规则来尝试将流量在svcb:v0.1和svcb:v0.2之间1:1均衡:
 
// route-rules-svcb-v0.2-50.yaml

apiVersion: config.istio.io/v1alpha2

kind: RouteRule

metadata:

 name: route-rules-svcb

spec:

 destination:

   name: svcb

 precedence: 1

 route:

 - labels:

     version: v0.1

   weight: 50

 - labels:

     version: v0.2

   weight: 50



# istioctl create -f route-rules-svcb-v0.2-50.yaml

Created config route-rule/default/route-rules-svcb at revision 30080638
 
 
按照istio文档中的说法,这个规则的生效需要一些时间。之后我们注入流量,发现流量切换到svcb:v0.1的一个pod上去了,并且很长一段时间不曾变化,未均衡到svcb:v0.2上去。

我们更新一下route rule,将流量全部切到svcb:v0.2上去:
 

//route-rules-svcb-v0.2-100.yaml apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata:  name: route-rules-svcb spec:  destination:    name: svcb  precedence: 1  route:  - labels:      version: v0.2    weight: 100 # istioctl replace -f route-rules-svcb-v0.2-100.yaml Updated config route-rule/default/route-rules-svcb to revision 30082944
 
我们用Istio的replace命令更新了规则:route-rules-svcb。更新后,再次注入流量,这回流量重新集中在svcb:v0.2的一个pod上了,再过一段时间另外一个svcb:v0.2的pod上才有了一些流量。但svcb:v0.1上不再有流量,这个切换是成功的。


在K8S的service的负载均衡中,K8S就利用了iptables的概率转发(random –probability 0.5),因此这种流量均衡并非是精确的,只有在长时间大量流量经过后,才能看到流量的分布与设定的权重是相似的,可能Istio也是如此,这里仅是入门,就不深入挖掘了。


当然Istio在路由规则设施方面的“能耐”远不止上面例子中所展示的那样,如果要悉数列出,那本文的长度可是要爆掉了。有兴趣的朋友可以去翻看官方文档。
 
插件安装
Istio的强大微服务治理能力还体现在其集成了grafana、prometheus、servicegraph、zipkin等addons,应用程序无需做任何改动,就可以具有数据收集、度量与可视化的监控能力、服务的分布式跟踪能力等。我们可以在Istio的安装包中找到这些addons的安装文件,我们来逐一试试。

prometheus&grafana
我们先来安装一下prometheus 和 grafana插件(位于istio-0.4.0/install/kubernetes/addon下面):
# kubectl apply -f prometheus.yaml

configmap "prometheus" created

service "prometheus" created

deployment "prometheus" created



# kubectl apply -f grafana.yaml

service "grafana" created

deployment "grafana" created



# kubectl get pods -n istio-system

NAME                             READY     STATUS    RESTARTS   AGE

grafana-3617079618-zpglx         1/1       Running   0          5m

prometheus-168775884-ppfxr       1/1       Running   0          5m

... ...



# kubectl get svc -n istio-system

NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)            AGE

grafana         10.105.21.25     <none>        3000/TCP                     16m

prometheus      10.103.160.37    <none>        9090/TCP                16m
 
浏览器中输入prometheus的服务地址http://10.103.160.37:9090,访问prometheus:





点击菜单项:status -> targets,查看各个target的状态是否正常:





如果像上图所示那样,各个target都是up状态,那就说明Istio运行时ok的。否则请参考istio troubleshooting中的内容对Istio逐一进行排查,尤其是istio-mesh这个Target在istio-0.3.0+kubernetes 1.7.3的环境中就是Down的状态。

浏览器输入grafana的服务地址:http://10.105.21.25:3000/,打开grafana面板:





 
切换到Istio Dashboard,并向istio service mesh注入流量,我们会看到仪表盘变化如下:





 





 

servicegraph
servicegraph插件是用来查看服务调用关系的,我们来创建一下该组件:
# kubectl apply -f servicegraph.yaml

deployment "servicegraph" created

service "servicegraph" created



# kubectl get svc -n istio-system

NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)                 AGE

servicegraph    10.108.245.21    <none>        8088/TCP                     52s

... ...
 
 
创建成功后,向service mesh网络注入流量,然后访问servicegraph:http://{servicegraph_ip}:8088/dotviz,在我的环境里,我看到的图示如下:





 
调用关系似乎有些乱,难道是我在程序使用的调用方法不够标准?:(
 

zipkin
Istio集成了zipkin,利用zipkin我们可以做分布式服务调用的追踪。之前自己曾经搭建过基于jaeger和opentracing的分布式调用服务,十分繁琐。并且要想使用tracing,对应用代码的侵入必不可少。

我们安装一下zipkin addon:
# kubectl apply -f zipkin.yaml

deployment "zipkin" created

service "zipkin" created



# kubectl get svc -n istio-system

NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE

zipkin          10.105.7.219     <none>        9411/TCP                             1h
我们访问以下zikpin的UI,通过浏览器打开http://{zipkin_service_ip}:9411。
 





 
接下来,我们向service mesh注入一些流量,然后再zipkin首页的“服务名”下拉框中选择”svcb”,查找跟踪情况:





 
我们看到:在没有对svca, svcb做任何修改的情况下,我们依然可以在zipkin中找到svcb相关的调用。点击其中一个trace,可以查看细节:
 





 
当然如果你想做内容更为丰富的、更为强大的跟踪,可能需要在应用代码中做些配合,具体可以参见:istio的分布式跟踪。
 
 
小结
Istio项目诞生不到一年,目前离成熟还远。快速积极开发可能会导致istio的接口和实现机制都会发生很大的变化,因此本文不能保证内容将适用于后续所有istio的发布版本。

本文涉及到的源码在这里 https://github.com/bigwhite/ex ... -demo 可以下载到,demo service的镜像可以在我的docker hub: https://hub.docker.com/u/bigwhite 上pull。

© 2018, bigwhite. 版权所有.
 
原文链接 查看全部
作者:Tony Bai
 

is.jpg

 
近两年微服务架构流行,主流互联网厂商内部都已经微服务化,初创企业虽然技术积淀不行,但也通过各种开源工具拥抱微服务。再加上容器技术赋能,Kubernetes又添了一把火,微服务架构已然成为当前软件架构设计的首选。


但微服务化易弄,服务治理难搞!
 
 
微服务的“痛点”

微服务化没有统一标准,多数是进行业务领域垂直切分,业务按一定的粒度划分职责,并形成清晰、职责单一的服务接口,这样每一块规划为一个微服务。微服务之间的通信方案相对成熟,开源领域选择较多的有RPC或RESTful API方案,比如:gRPC、apache thrift等。这些方案多偏重于数据如何打包、传输与解包,对服务治理的内容涉及甚少。


微服务治理是头疼的事,也是微服务架构中的痛点。治理这个词有多元含义,很难下达一个精确定义,这里可以像小学二年级学生那样列出治理的诸多近义词:管理、控制、规则、掌控、监督、支配、规定、统治等。对于微服务而言,治理体现在以下诸多方面:
  • 服务注册与发现
  •  
  • 身份验证与授权
  •  
  • 服务的伸缩控制
  •  
  • 反向代理与负载均衡
  •  
  • 路由控制
  •  
  • 流量切换
  •  
  • 日志管理
  •  
  • 性能度量、监控与调优
  •  
  • 分布式跟踪
  •  
  • 过载保护
  •  
  • 服务降级
  •  
  • 服务部署与版本升级策略支持
  •  
  • 错误处理


… …

从微服务治理角度来说,微服务其实是一个“大系统”,要想将这个大系统全部落地,绝非易事,尤其是之前尚没有一种特别优雅的技术方案。多数方案(比如:dubbo、go-kit等。)都或多或少地对应用逻辑有一定的侵入性,让业务开发人员不能只focus到业务本身,还要关心那些“治理”逻辑。并且市面上内置了微服务治理逻辑的框架较少,且很多编程语言相关。这种情况下,大厂多选择自研或基于某个框架改造,小厂一般只能“东拼西凑”一些“半成品”凑合着使用,就这样微服务也走过了若干年。


Service Mesh横空出世,Istio带来“福音”

我不知道在没有TCP/IP协议的年代,主机和主机之间的应用通信时是否需要应用关心底层通信协议实现逻辑。但是和TCP/IP诞生的思想类似,在微服务使用多年后,人们发现需要独立地抽象出一层逻辑网络,专门用于“微服务通信与治理策略的落地”,让应用只关心业务,把服务治理的事情全部交由“这一层”去处理。

1.png

图:传统微服务之间的微服务治理逻辑的位置
 

2.jpg

图:微服务治理逻辑被独立出来之后的位置
 
由“Service Govern Logic”这一层组成的逻辑网络被定义为Service Mesh,每个微服务都包含一个Service Mesh的端点。


“Service Mesh”概念还非常年轻,这个词在国内被翻译为“服务网格”或“服务啮合层”,我们这里就用Service Mesh这个英文词。这里摘录一下 ServiceMesh 中文网社区上的一篇名为“年度盘点2017之Service Mesh:群雄逐鹿烽烟起”的文章中对Service Mesh概念的回顾:


在 2016 年年初,“Service Mesh”还只是 Buoyant 公司的内部词汇,而之后,它开始逐步走向社区:

2016 年 9 月 29 日在 SF Microservices 上,“Service Mesh”这个词汇第一次在公开场合被使用。这标志着“Service Mesh”这个词,从 Buoyant 公司走向社区。

2016 年 10 月,Alex Leong 开始在 Buoyant 公司的官方 Blog 中连载系列文章“A Service Mesh for Kubernetes”。随着“The Services must Mesh”口号的喊出,Buoyant 和 Linkerd 开始 Service Mesh 概念的布道。

2017 年 4 月 25 日,William Morgan 发布博文“What’s a service mesh? And why do I need one?”。正式给 Service Mesh 做了一个权威定义。

而Service Mesh真正引起大家关注要源于Istio项目的开源发布。为什么呢?个人觉得还是因为“爹好”!Istio项目由Google、IBM共同合作创建,lyft公司贡献了Envoy项目将作为Istio Service Mesh的data panel。Google、IBM的影响力让Service Mesh概念迅速传播,同时也让大家认识到了Istio项目在Service Mesh领域的重要性,于是纷纷选择积极支持并将自己的产品或项目与Istio项目集成。


Istio项目是Service Mesh概念的最新实现,旨在所有主流集群管理平台上提供Service Mesh层,初期以实现Kubernetes上的服务治理层为目标。它由控制平面和数据平面组成(是不是感觉和SDN的设计理念相似啊)。控制平面由Go语言实现,包括pilot、mixer、auth三个组件;数据平面功能暂由Envoy在pod中以Sidecar的部署形式提供。下面是官方的架构图:

3.jpg

图:Istio架构图(来自官网)
Sidecar中Envoy代理了pod中真正业务container的所有进出流量,并对这些流量按照控制平面设定的“治理逻辑”进行处理。而这一切对pod中的业务应用是透明的,开发人员可以专心于业务逻辑,而无需再关心微服务治理的逻辑。Istio代表的Service Mesh的设计理念被认为是下一代“微服务统一框架”,甚至有人认为是微服务框架演化的终点。

Istio于2017 年 5 月 24 日发布了0.1 release 版本,截至目前为止Istio的版本更新到v0.4.0,演进速度相当快,不过目前依然不要用于生产环境,至少要等到1.0版本发布吧。但对于Istio的早期接纳者而言,现在正是深入研究Istio的好时机。在本篇的接下来内容中,我们将带领大家感性的认识一下Istio,入个门儿。
 
 
Istio安装
istio目前支持最好的就是kubernetes了,因此我们的实验环境就定在kubernetes上。至于版本,Istio当前最新版本为0.4.0,这个版本据说要K8S 1.7.4及以上版本用起来才不会发生小毛病:)。我的K8S集群是v1.7.6版本的,恰好满足条件。下面是安装过程:(Node上的os是ubuntu 16.04)
# wget -c https://github.com/istio/istio ... ar.gz



解压后,进入istio-0.4.0目录,



# ls -F

bin/  install/  istio.VERSION  LICENSE  README.md  samples/



# cat istio.VERSION

# DO NOT EDIT THIS FILE MANUALLY instead use

# install/updateVersion.sh (see install/README.md)

export CA_HUB="docker.io/istio"

export CA_TAG="0.4.0"

export MIXER_HUB="docker.io/istio"

export MIXER_TAG="0.4.0"

export PILOT_HUB="docker.io/istio"

export PILOT_TAG="0.4.0"

export ISTIOCTL_URL="https://storage.googleapis.com ... ot%3B

export PROXY_TAG="0.4.0"

export ISTIO_NAMESPACE="istio-system"

export AUTH_DEBIAN_URL="https://storage.googleapis.com ... ot%3B

export PILOT_DEBIAN_URL="https://storage.googleapis.com ... ot%3B

export PROXY_DEBIAN_URL="https://storage.googleapis.com ... ot%3B

export FORTIO_HUB="docker.io/istio"

export FORTIO_TAG="0.4.2"



# cd install/kubernetes



我们先不用auth功能,因此使用istio.yaml这个文件进行istio组件安装:



# kubectl apply -f istio.yaml

namespace "istio-system" created

clusterrole "istio-pilot-istio-system" created

clusterrole "istio-initializer-istio-system" created

clusterrole "istio-mixer-istio-system" created

clusterrole "istio-ca-istio-system" created

clusterrole "istio-sidecar-istio-system" created

clusterrolebinding "istio-pilot-admin-role-binding-istio-system" created

clusterrolebinding "istio-initializer-admin-role-binding-istio-system" created

clusterrolebinding "istio-ca-role-binding-istio-system" created

clusterrolebinding "istio-ingress-admin-role-binding-istio-system" created

clusterrolebinding "istio-sidecar-role-binding-istio-system" created

clusterrolebinding "istio-mixer-admin-role-binding-istio-system" created

configmap "istio-mixer" created

service "istio-mixer" created

serviceaccount "istio-mixer-service-account" created

deployment "istio-mixer" created

customresourcedefinition "rules.config.istio.io" created

customresourcedefinition "attributemanifests.config.istio.io" created

... ...

customresourcedefinition "reportnothings.config.istio.io" created

attributemanifest "istioproxy" created

attributemanifest "kubernetes" created

stdio "handler" created

logentry "accesslog" created

rule "stdio" created

metric "requestcount" created

metric "requestduration" created

metric "requestsize" created

metric "responsesize" created

metric "tcpbytesent" created

metric "tcpbytereceived" created

prometheus "handler" created

rule "promhttp" created

rule "promtcp" created

kubernetesenv "handler" created

rule "kubeattrgenrulerule" created

kubernetes "attributes" created

configmap "istio" created

customresourcedefinition "destinationpolicies.config.istio.io" created

customresourcedefinition "egressrules.config.istio.io" created

customresourcedefinition "routerules.config.istio.io" created

service "istio-pilot" created

serviceaccount "istio-pilot-service-account" created

deployment "istio-pilot" created

service "istio-ingress" created

serviceaccount "istio-ingress-service-account" created

deployment "istio-ingress" created

serviceaccount "istio-ca-service-account" created

deployment "istio-ca" created
 
注:我还曾在K8S v1.7.3上安装过istio 0.3.0版本,但在创建组件时会报下面错误(这个错误可能会导致后续addon安装后工作不正常):
unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric

unable to recognize "istio.yaml": no matches for config.istio.io/, Kind=metric
安装后,我们在istio-system这个namespace下会看到如下pod和service在运行(由于istio的各个组件的image size都不小,因此pod状态变为running需要一丢丢时间,耐心等待)
# kubectl get pods -n istio-system

NAME                             READY     STATUS    RESTARTS   AGE

istio-ca-1363003450-jskp5        1/1       Running   0          3d

istio-ingress-1005666339-c7776   1/1       Running   4          3d

istio-mixer-465004155-twhxq      3/3       Running   24         3d

istio-pilot-1861292947-6v37w     2/2       Running   18         3d



# kubectl get svc -n istio-system

NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)                                                   AGE

istio-ingress   10.98.10.87      <pending>     80:31759/TCP,443:25804/TCP                         4d

istio-mixer     10.109.244.155   <none>        9091/TCP,15004/TCP,9093/TCP,9094/TCP,9102/TCP,9125/UDP,42422/TCP   4d

istio-pilot     10.105.80.55     <none>        15003/TCP,443/TCP                                              4d
 
istio安装成功!
 
 
服务治理策略验证
 
接下来我们来用几个例子验证一下Istio在服务治理方面的能力!(istio自带一些完整的例子,比如bookinfo,用于验证服务治理的能力,但这里先不打算用这些例子)

验证环境和拓扑我们先来看一下验证环境的示意图:

4.jpg

 
我们看到在Service Mesh中部署了两个service: server_a和service_b,前者调用后者完成某项业务,后者则调用外部服务完成业务逻辑。




service_a: 模拟pay服务,在收到client请求后,进行pay处理,并将处理结果通过service_b提供的msg notify服务下发给user。该服务的endpoint为/pay;

service_b: 模拟notify服务,在收到service_a请求后,将message转发给external service,完成notify逻辑。该服务的endpoint为/notify;

external service: 位于service mesh之外。

client:我们使用curl模拟。
 

5.jpg

 
我们先来部署service_a和service_b的v0.1版本:
以service_a的部署为例, service_a的deployment文件如下:
 
//svca-v0.1.yaml

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

 name: svca

spec:

 replicas: 1

 template:

   metadata:

     labels:

       app: svca

       version: v0.1

   spec:

     containers:

     - name: svca

       image: docker.io/bigwhite/istio-demo-svca:v0.1

       imagePullPolicy: Always

---

apiVersion: v1

kind: Service

metadata:

 name: svca

 labels:

   app: svca

spec:

 ports:

 - port: 80

   targetPort: 8080

   protocol: TCP

 selector:

   app: svca
 
注意,我们部署service_a时不能直接使用kubectl apply -f svca-v0.1.yaml,而是要apply经过istioctl(需将Istio安装目录下的bin放入PATH)处理过的yaml,以注入sidecar容器。当然也可以配置为自动为每个k8s启动的pod注入sidecar,但我们这里没有使用自动注入。我们执行下面命令:
 
# kubectl apply -f <(istioctl kube-inject -f svca-v0.1.yaml)

deployment "svca" created

service "svca" created



# kubectl get pods

NAME                               READY     STATUS    RESTARTS   AGE

svca-1997590752-tpwjf              2/2       Running   0          2m
 
 
同样的方法,我们来创建svcb:v0.1:
 
# kubectl apply -f <(istioctl kube-inject -f svcb-v0.1.yaml)

deployment "svcb" created

service "svcb" created
 
 
我们看到istio向每个pod中插入一个sidecar container,这个就是前面说的envoy,只不过container名字为istio-proxy。

接下来,我们把那个external service启动起来:
# nohup ./msgd > 1.log & 2>&1

[1] 9423
 
实验环境ok了。下面我们来验证一下业务是否是通的。
 

egress rule
按照之前我们的设定,我们使用curl去访问service_a服务的/pay端点,我们查看一下svca服务的ip和端口:
# kubectl get svc

NAME               CLUSTER-IP       EXTERNAL-IP   PORT(S)

svca               10.105.38.238    <none>        80/TCP                                         9h

svcb               10.105.119.194   <none>        80/TCP                                         9h
 
我们访问一下svca服务,svca的服务地址可以通过kubectl get svc查到:

# curl {svca_ip}/pay

查看svca和svcb的日志:
//service_a的日志:



service_a:v0.1 is serving the request...

service_a:v0.1 pays ok

&{500 Internal Server Error 500 HTTP/1.1 1 1 map[X-Content-Type-Options:[nosniff] Date:[Tue, 02 Jan 2018 15:41:50 GMT] Content-Length:[66] Content-Type:[text/plain; charset=utf-8]] 0xc420058d40 66  false false map 0xc4200eaf00 <nil>}

service_a:v0.1 notify customer ok



// service_b的日志:

&{GET /notify?msg=service_a:v0.1-pays-ok HTTP/1.1 1 1 map[User-Agent:[Go-http-client/1.1] Accept-Encoding:[gzip]] {} <nil> 0  false svcb map map <nil> map 127.0.0.1:58778 /notify?msg=service_a:v0.1-pays-ok <nil> <nil> <nil> 0xc4200fa3c0}

service_b:v0.1 is serving the request...

service_b:v0.1 send msg error: Get http://10.100.35.27:9997/send% ... s-ok: EOF
我们看到service_a和service_b都返回了错误日志(注意:go http get方法对于non-2xx response不会返回错误,我们只是看到了response中的500状态码才意识到错误的存在)。其中源头在service_b,原因是其连不上那个external service!那么为什么连不上external service呢?这是由于缺省情况下,启用了Istio的服务是无法访问外部URL的,这是因为Pod中的iptables把所有外发传输都转向到了Sidecar代理,而这一代理只处理集群内的访问目标。因此位于service mesh内的服务svcb无法访问外部的服务(msgd),我们需要显式的添加egressrule规则:

我们创建一个允许svcb访问外部特定服务的EgressRule:
//rules/enable-svcb-engress-rule.yaml



apiVersion: config.istio.io/v1alpha2

kind: EgressRule

metadata:

 name: enable-svcb-engress-rule

spec:

 destination:

   service: 10.100.35.27

 ports:

   - port: 9997

     protocol: http
 
使规则生效:

# istioctl create -f enable-svcb-engress-rule.yaml

Created config egress-rule/default/enable-svcb-engress-rule at revision 30031258


这时你再尝试curl svca,我们可以看到msgd的日志中出现了下面的内容:
 

2018/01/02 23:58:16 &{GET /send?msg=service_a:v0.1-pays-ok HTTP/1.1 1 1 map[X-Ot-Span-Context:[2157e7ffb8105330;2157e7ffb8105330;0000000000000000] Content-Length:[0] User-Agent:[Go-http-client/1.1] X-Forwarded-Proto:[http] X-Request-Id:[13c3af6e-2f52-993d-905f-aa6aa4b57e2d] X-Envoy-Decorator-Operation:[default-route] X-B3-Spanid:[2157e7ffb8105330] X-B3-Sampled:[1] Accept-Encoding:[gzip] X-B3-Traceid:[2157e7ffb8105330] X-Istio-Attributes:[Ch8KCXNvdXJjZS5pcBISMhAAAAAAAAAAAAAA//8KLgAMCjoKCnNvdXJjZS51aWQSLBIqa3ViZXJuZXRlczovL3N2Y2ItMjAwODk3Mzc2OS1ncTBsaC5kZWZhdWx0]] {} <nil> 0 false 10.100.35.27:9997 map map <nil> map 10.100.35.28:38188 /send?msg=service_a:v0.1-pays-ok <nil> <nil> <nil> 0xc4200584c0} 2018/01/02 23:58:16 Msgd is serving the request... 2018/01/02 23:58:16 Msgd recv msg ok, msg= service_a:v0.1-pays-ok
 
 
说明Svcb到外部服务的通信被打通了!

迁移流量到新版本svcb:v0.2我们经常有这样的需求,当svcb运行一段时间后,svcb添加了新feature,版本要升级到v0.2了,这时我们会部署svcb:v0.2,并将流量逐步切到v0.2上。

我们先来部署一下svcb:v0.2:
// svcb-v0.2.yaml

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

 name: svcb-v0.2

spec:

 replicas: 1

 template:

   metadata:

     labels:

       app: svcb

       version: v0.2

   spec:

     containers:

     - name: svcb

       image: docker.io/bigwhite/istio-demo-svcb:v0.2

       imagePullPolicy: Always
我们可以看到,服务名不变,但版本的label变成了v0.2,我们来执行这次部署:
 
# kubectl apply -f <(istioctl kube-inject -f svcb-v0.2.yaml)

deployment "svcb-v0.2" created



# kubectl get pods

NAME                               READY     STATUS    RESTARTS   AGE

svca-1997590752-pq9zg              2/2       Running   0          9h

svcb-2008973769-gq0lh              2/2       Running   0          9h

svcb-v0.2-3233505404-0g55w         2/2       Running   0          1m
 
svcb服务下又增加了一个endpoint:
# kubectl describe svc/svcb



.... ...

Selector:        app=svcb

Type:            ClusterIP

IP:            10.105.119.194

Port:            <unset>    80/TCP

Endpoints:        10.40.0.28:8080,10.46.0.12:8080

... ...
 
此时,如果按照K8S的调度方式,v0.1和v0.2版本的两个svcb pod应该1:1均衡地承载流量。为了方便查看流量分布,我们将每个版本的svcb的pod副本数量都扩展为2个(replicas: 2),这样service mesh中一共会有4个 svcb endpoints。

通过curl访问svca注入流量后,我们发现流量都集中在一个svcb:v0.2的pod上,并且长时间没有变化。我们通过下面的route rule规则来尝试将流量在svcb:v0.1和svcb:v0.2之间1:1均衡:
 
// route-rules-svcb-v0.2-50.yaml

apiVersion: config.istio.io/v1alpha2

kind: RouteRule

metadata:

 name: route-rules-svcb

spec:

 destination:

   name: svcb

 precedence: 1

 route:

 - labels:

     version: v0.1

   weight: 50

 - labels:

     version: v0.2

   weight: 50



# istioctl create -f route-rules-svcb-v0.2-50.yaml

Created config route-rule/default/route-rules-svcb at revision 30080638
 
 
按照istio文档中的说法,这个规则的生效需要一些时间。之后我们注入流量,发现流量切换到svcb:v0.1的一个pod上去了,并且很长一段时间不曾变化,未均衡到svcb:v0.2上去。

我们更新一下route rule,将流量全部切到svcb:v0.2上去:
 

//route-rules-svcb-v0.2-100.yaml apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata:  name: route-rules-svcb spec:  destination:    name: svcb  precedence: 1  route:  - labels:      version: v0.2    weight: 100 # istioctl replace -f route-rules-svcb-v0.2-100.yaml Updated config route-rule/default/route-rules-svcb to revision 30082944
 
我们用Istio的replace命令更新了规则:route-rules-svcb。更新后,再次注入流量,这回流量重新集中在svcb:v0.2的一个pod上了,再过一段时间另外一个svcb:v0.2的pod上才有了一些流量。但svcb:v0.1上不再有流量,这个切换是成功的。


在K8S的service的负载均衡中,K8S就利用了iptables的概率转发(random –probability 0.5),因此这种流量均衡并非是精确的,只有在长时间大量流量经过后,才能看到流量的分布与设定的权重是相似的,可能Istio也是如此,这里仅是入门,就不深入挖掘了。


当然Istio在路由规则设施方面的“能耐”远不止上面例子中所展示的那样,如果要悉数列出,那本文的长度可是要爆掉了。有兴趣的朋友可以去翻看官方文档。
 
插件安装
Istio的强大微服务治理能力还体现在其集成了grafana、prometheus、servicegraph、zipkin等addons,应用程序无需做任何改动,就可以具有数据收集、度量与可视化的监控能力、服务的分布式跟踪能力等。我们可以在Istio的安装包中找到这些addons的安装文件,我们来逐一试试。

prometheus&grafana
我们先来安装一下prometheus 和 grafana插件(位于istio-0.4.0/install/kubernetes/addon下面):
# kubectl apply -f prometheus.yaml

configmap "prometheus" created

service "prometheus" created

deployment "prometheus" created



# kubectl apply -f grafana.yaml

service "grafana" created

deployment "grafana" created



# kubectl get pods -n istio-system

NAME                             READY     STATUS    RESTARTS   AGE

grafana-3617079618-zpglx         1/1       Running   0          5m

prometheus-168775884-ppfxr       1/1       Running   0          5m

... ...



# kubectl get svc -n istio-system

NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)            AGE

grafana         10.105.21.25     <none>        3000/TCP                     16m

prometheus      10.103.160.37    <none>        9090/TCP                16m
 
浏览器中输入prometheus的服务地址http://10.103.160.37:9090,访问prometheus:

6.jpg

点击菜单项:status -> targets,查看各个target的状态是否正常:

6.jpg

如果像上图所示那样,各个target都是up状态,那就说明Istio运行时ok的。否则请参考istio troubleshooting中的内容对Istio逐一进行排查,尤其是istio-mesh这个Target在istio-0.3.0+kubernetes 1.7.3的环境中就是Down的状态。

浏览器输入grafana的服务地址:http://10.105.21.25:3000/,打开grafana面板:

8.jpg

 
切换到Istio Dashboard,并向istio service mesh注入流量,我们会看到仪表盘变化如下:

9.jpg

 

10.jpg

 

servicegraph
servicegraph插件是用来查看服务调用关系的,我们来创建一下该组件:
# kubectl apply -f servicegraph.yaml

deployment "servicegraph" created

service "servicegraph" created



# kubectl get svc -n istio-system

NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)                 AGE

servicegraph    10.108.245.21    <none>        8088/TCP                     52s

... ...
 
 
创建成功后,向service mesh网络注入流量,然后访问servicegraph:http://{servicegraph_ip}:8088/dotviz,在我的环境里,我看到的图示如下:

11.jpg

 
调用关系似乎有些乱,难道是我在程序使用的调用方法不够标准?:(
 

zipkin
Istio集成了zipkin,利用zipkin我们可以做分布式服务调用的追踪。之前自己曾经搭建过基于jaeger和opentracing的分布式调用服务,十分繁琐。并且要想使用tracing,对应用代码的侵入必不可少。

我们安装一下zipkin addon:
# kubectl apply -f zipkin.yaml

deployment "zipkin" created

service "zipkin" created



# kubectl get svc -n istio-system

NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE

zipkin          10.105.7.219     <none>        9411/TCP                             1h
我们访问以下zikpin的UI,通过浏览器打开http://{zipkin_service_ip}:9411。
 

12.jpg

 
接下来,我们向service mesh注入一些流量,然后再zipkin首页的“服务名”下拉框中选择”svcb”,查找跟踪情况:

13.jpg

 
我们看到:在没有对svca, svcb做任何修改的情况下,我们依然可以在zipkin中找到svcb相关的调用。点击其中一个trace,可以查看细节:
 

14.jpg

 
当然如果你想做内容更为丰富的、更为强大的跟踪,可能需要在应用代码中做些配合,具体可以参见:istio的分布式跟踪。
 
 
小结
Istio项目诞生不到一年,目前离成熟还远。快速积极开发可能会导致istio的接口和实现机制都会发生很大的变化,因此本文不能保证内容将适用于后续所有istio的发布版本。

本文涉及到的源码在这里 https://github.com/bigwhite/ex ... -demo 可以下载到,demo service的镜像可以在我的docker hub: https://hub.docker.com/u/bigwhite 上pull。

© 2018, bigwhite. 版权所有.
 
原文链接

聊聊ServiceMesh 数据面板 Envoy

Envoy小数 发表了文章 • 0 个评论 • 443 次浏览 • 2018-01-04 20:46 • 来自相关话题

 见过linkerd,聊过istio,今天聊聊不常提起的envoy,点击链接即可阅读。 查看全部

1507861978(1).png

 见过linkerd,聊过istio,今天聊聊不常提起的envoy,点击链接即可阅读。

专题 | A Service Mesh for Kubernetes 第二期:K8S+Linkerd

Linkerd小数 发表了文章 • 0 个评论 • 218 次浏览 • 2018-01-04 20:45 • 来自相关话题

 专题 | A Service Mesh for Kubernetes 第二期:K8S+Linkerd 查看全部

专题 | Kubernetes+Linkerd 第一期

Linkerd小数 发表了文章 • 0 个评论 • 304 次浏览 • 2018-01-03 16:58 • 来自相关话题

作者:ALEX LEONG


翻译:平凡

转自:容器时代(CaaSOne)
 
Service Mesh,一个为云平台而设计的应用,云平台的本地应用该如何使用它?本文中,我们将讲述在Kubernetes中如何应用Linkerd作为Service Mesh,如何捕获和报告顶层服务指标如成功率、请求量和延迟,而不需要更改应用的代码。


注意:这是关于Linkerd、Kubernetes和Service Mesh的系列文章其中一篇,其余部分包括:
Top-line service metrics (本文)Pods are great, until they’re notEncrypting all the thingsContinuous deployment via traffic shiftingDogfood environments, ingress, and edge routingStaging microservices without the tearsDistributed tracing made easyLinkerd as an ingress controllergRPC for fun and profitThe Service Mesh APIEgressRetry budgets, deadline propagation, and failing gracefullyAutoscaling by top-line metrics



使用service mesh的必要性

关于Linkerd最常见的一个疑问就是,到底什么是Service Mesh?当Kubernetes已经提供了如Service资源对象、负载均衡这样的基本功能后,为什么Service Mesh要作为云本地应用程序的关键组件。


简单来说,Service Mesh这一层管理着应用之间的通信(或者部分应用之间的通信如微服务)。传统应用中,这个逻辑直接构建到应用程序本身中:重试和超时,监控/可见性,追踪,服务发现等等。这些都被硬编码到每个应用程序中。

然而,随着应用程序架构被越来越多的分割成服务,将通信逻辑从应用中移出到底层基础设施中变得越来越重要。就像是应用程序不应该写它自己的TCP栈,他们也不应该管理自己的负载均衡逻辑,或他们自己的服务发现管理程序,以及他们自己的重试和超时逻辑。

像Linkerd这样的Service Mesh为大规模多服务应用程序提供关键的部件:

基本的适应性功能:重试预算,deadline,熔断器模式

顶层的服务指标:成功率,请求量,延迟

延迟和失败容忍度:感应失败和延迟的负载均衡,它可以绕过缓慢的或者损坏的服务实例

分发追踪:如Zipkin和OpenTracing

服务发现:找到目标实例

协议升级:在TLS中包装跨网络通信,或将HTTP/1.1转换为HTTP/2.0

路由:不同版本的服务之间的路由请求,集群之间的故障转移等



在这部分中,我们仅重点关注可见性:Service Mesh是如何自动捕获并报告如成功率等顶层服务指标的,我们将展示一个Kubernetes的小例子以此来引导你。
 
在Kubernetes中使用Linkerd作为Service的检测器
 
在请求层进行操作的一个优势是Service Mesh可以访问成功和失败的协议级语义。举个例子,如果你正在运行一个HTTP服务,Linkerd可以理解200 、400、500响应的含义,从而可以自动计算如成功率这样的指标。

让我们通过一个小例子讲解如何在Kubernetes中安装Linkerd,从而自动的捕获顶层服务成功率而不需要更改应用程序。
 

安装Linkerd
使用Kubernetes配置文件安装Linkerd。它会将Linkerd作为DaemonSet安装,且运行在Kubernetes的default命名空间:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/linkerd.yml 
 
你可以通过查看Linkerd的管理页面确认是否安装成功:
INGRESS_LB=$(kubectl get svc l5d -o jsonpath="{.status.loadBalancer.ingress[0].*}") open http://$INGRESS_LB:9990 # on OS X
 
如果集群不支持外部负载均衡,使用hostIP:
HOST_IP=$(kubectl get po -l app=l5d -o jsonpath="{.items[0].status.hostIP}") open http://$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[2].nodePort}') # on OS X








安装样例应用程序
在default命名空间下安装两个Service,“hello”和“world”。这些应用程序依赖于Kubernetes downward API提供的nodeName来发现Linkerd。为了检测你的集群是否支持nodeName,你可以运行:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/node-name-test.yml 
 
然后看它的日志:kubectl logs node-name-test
 
如果你看到了一个IP就说明支持。接下来继续部署hello world应用程序如下:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world.yml 
 
如果你看到报错“server can’t find…”,就部署旧版本hello-world,它依赖于hostIP而非nodeName:kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world-legacy.yml 
 
这两个Service——“hello”和“world”——功能在一起使得高度可扩展,“hello world”微服务(“hello”Service调用“world”Service完成这个请求)。

你可以通过给Linkerd的外部IP发送请求来查看此操作:
http_proxy=$INGRESS_LB:4140 curl -s http://hello或者直接使用hostIP:http_proxy=$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}') curl -s http://hello 
 
你应该可以看到字符串“Hello world”。
 

安装Linkerd-viz
最后,通过安装Linkerd-viz,让我们可以看看Service正在做什么。Linkerd-viz是一个附加包,它包括一个简单的Prometheus和Grafana设置,并可以自动发现Linkerd。
下面的命令会将Linkerd-viz安装到default命名空间下:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-viz/master/k8s/linkerd-viz.yml 
 
访问Linkerd-viz的外部IP查看dashboard:
VIZ_INGRESS_LB=$(kubectl get svc linkerd-viz -o jsonpath="{.status.loadBalancer.ingress[0].*}") open http://$VIZ_INGRESS_LB # on OS X
 
如果集群不支持外部负载均衡,使用hostIP:
VIZ_HOST_IP=$(kubectl get po -l name=linkerd-viz -o jsonpath="{.items[0].status.hostIP}") open http://$VIZ_HOST_IP:$(kubectl get svc linkerd-viz -o 'jsonpath={.spec.ports[0].nodePort}') # on OS X
 
你应该在dashboard上看到包括Service与实例的选择器,所有图表都反映了这些Service和实例的选择器。
 






Linkerd-viz的dashboard包括三部分:

顶层:集群范围的成功率与请求量

Service指标:每个已部署应用的指标,包括成功率、请求量和延迟

单个实例指标:集群上每一个node的成功率、请求量和延迟

用三个简单的命令我们就可以将Linkerd安装在集群上,安装应用,并且使用Linkerd了解应用程序Service的健康状况。当然,Linkerd提供的不仅仅是可见性,还提供了延迟感应负载均衡,自动重试和断路,分发追踪等功能。
 
 
 
第二部分:导言
 
在第一部分里我们提到了Linkerd是使用DaemonSet而非Sidecar来安装的。在第二部分中,我们将解释我们为什么(怎么样)这么做。

注意:这是关于Linkerd、Kubernetes和Service ,esh的系列文章其中一篇,其余部分包括:

Top-line service metrics

Pods are great, until they’re not(本文)

Encrypting all the things

Continuous deployment via traffic shifting

Dogfood environments, ingress, and edge routing

Staging microservices without the tears

Distributed tracing made easy

Linkerd as an ingress controller

gRPC for fun and profit

The Service Mesh API

Egress

Retry budgets, deadline propagation, and failing gracefully

Autoscaling by top-line metrics
 

Linkerd以DaemonSet方式运行


作为Service Mesh,Linkerd设计为与应用程序代码一起运行。它管理和监控service的内部通信,包括服务发现、重试、负载均衡与协议升级。

初次听闻,都会觉得这非常适合在Kubernetes中以Sidecar的方式部署。毕竟,Kubernetes的定义特征之一就是它的pod模型。作为Sidecar部署理论上简单,有清晰的失败语义,我们花了大量时间用于针对该用例的Linkerd优化。

然而,Sidecar模型有其缺陷:部署一个pod就要用掉部署一个pod的资源。如果你的Service较轻量并且跑了许多的应用实例,这样使用Sidecar来部署就会代价极高。

我们可以通过每一个host而非每一个pod部署一个Linkerd来减少资源的开销。它对每个主机进行扩展而消耗资源,这通常是比pod数量要显著更慢的增长的指标。并且很幸运,Kubernetes为此目的提供了DaemonSets。

但美中不足的是,对于Linkerd来说,按每个host部署比仅仅使用DaemonSets要复杂一些。我们如何解决Service Mesh这个问题?请继续阅读下文。
 

Kubernetes的Service 
MeshService Mesh的定义特征之一是它将应用通信与传输通信分离开的能力。例如,如果Service A与B使用HTTP协议通信,Service Mesh也许会通过电缆将之转换为HTTPS,而且应用程序并不知晓。Service Mesh也可以做连接池、权限控制,或其它的传输层特性,并且是以对应用程序透明的方式。

为了完美的做到这些,Linkerd必须作为本地实例的代理处于请求的发送端与接收端。例如HTTP升级为HTTPS,Linkerd一定要在传输层协议的开始与结束。在DaemonSet的世界,一个通过Linkerd的请求路线看起来如下图所示:





 
正如你所见,一个从Host 1中Pod A发起的目标为Host 2中Pod B的请求必须通过Pod A的本地Linkerd实例,然后到达Host 2的Linkerd实例,最后到达Pod J。这个路径引入了Linkerd必须解决的三个问题:


应用如何定位它的本地Linkerd

Linkerd如何将传出请求路由到目标Linkerd

Linkerd如何将传入请求路由到目标应用

接下来就讲述我们如何解决这三个问题的技术细节。
 
 

应用程序如何定位它的本地Linkerd
因为DaemonSet使用Kubernetes的host端口,我们就知道Linkerd运行于host IP的一个固定端口。因此,为了发送一个请求给运行于同一宿主机的Linkerd进程,我们需要确定这个主机的IP地址。

在Kubernetes 1.4及其以后的版本,此信息可通过底层API直接获取到。以下是来自hello-world.yaml的摘要,显示了如何将节点名称传递到应用程序中:
env: - name: NODE_NAME  valueFrom:    fieldRef:      fieldPath: spec.nodeName - name: http_proxy  value: $(NODE_NAME):4140 args: - "-addr=:7777" - "-text=Hello" - "-target=world"
 
(请注意,此例设置http_proxy环境变量以引导通过本地Linkerd实例的所有HTTP调用。然而此方法仅适用于大多数的HTTP应用,非HTTP应用需要做一些不同的工作)

Kubernetes 1.4之前的版本这些信息仍旧可用,但是方式要间接一些。

以下是hello-world-legacy.yml的一部分,显示如何将主机IP传递到应用程序中。
env: - name: POD_NAME  valueFrom:    fieldRef:      fieldPath: metadata.name - name: NS  valueFrom:    fieldRef:      fieldPath: metadata.namespace command: - "/bin/sh" - "-c" - "http_proxy=`hostIP.sh`:4140 helloworld -addr=:7777 -text=Hello -target=world"
 
请注意,hostIP.sh脚本需要将pod名和命名空间以环境变量的方式注入pod中。



Linkerd如何将传出请求路由到目标Linkerd
在我们的Service Mesh部署中,传出请求不应当直接发送给目标应用,而应当发送给运行于应用程序所在主机的Linkerd。为了做到这点,我们可以利用Linkerd 0.8.0引入的被称为transformer的新特性,它可以对Linker路由到的目标地址进行任意的后期处理。在这种情况下,我们可以使用DaemonSet transformer自动的让运行于目标主机的DaemonSet的地址取代目标地址。例如,这个Linkerd传出路由配置发送所有的请求到运行于相同主机的作为目标应用的Linkerd的输入端口。
 
routers: - protocol: http  label: outgoing  interpreter:    kind: default    transformers:    - kind: io.l5d.k8s.daemonset      namespace: default      port: incoming      service: l5d  ...

Linkerd如何将输入请求路由到目标应用
当一个请求最终到达目标pod的Linkerd实例,它一定得正确的路由到该pod。为此,我们使用localnode transformer将路由限制在运行于本地主机的pod。Linkerd配置示例如下:
routers: - protocol: http  label: incoming  interpreter:    kind: default    transformers:    - kind: io.l5d.k8s.localnode  ...


将Linkerd作为Kubernetes DaemonSet部署使得两全其美——它让我们完成service mesh的全部目标(如透明的TLS、协议升级、延迟感知负载均衡等等),同时按每个host扩缩Linkerd实例而非每个pod。
 
  查看全部
作者:ALEX LEONG


翻译:平凡

转自:容器时代(CaaSOne)
 
Service Mesh,一个为云平台而设计的应用,云平台的本地应用该如何使用它?本文中,我们将讲述在Kubernetes中如何应用Linkerd作为Service Mesh,如何捕获和报告顶层服务指标如成功率、请求量和延迟,而不需要更改应用的代码。


注意:这是关于Linkerd、Kubernetes和Service Mesh的系列文章其中一篇,其余部分包括:
  1. Top-line service metrics (本文)
  2. Pods are great, until they’re not
  3. Encrypting all the things
  4. Continuous deployment via traffic shifting
  5. Dogfood environments, ingress, and edge routing
  6. Staging microservices without the tears
  7. Distributed tracing made easy
  8. Linkerd as an ingress controller
  9. gRPC for fun and profit
  10. The Service Mesh API
  11. Egress
  12. Retry budgets, deadline propagation, and failing gracefully
  13. Autoscaling by top-line metrics




使用service mesh的必要性

关于Linkerd最常见的一个疑问就是,到底什么是Service Mesh?当Kubernetes已经提供了如Service资源对象、负载均衡这样的基本功能后,为什么Service Mesh要作为云本地应用程序的关键组件。


简单来说,Service Mesh这一层管理着应用之间的通信(或者部分应用之间的通信如微服务)。传统应用中,这个逻辑直接构建到应用程序本身中:重试和超时,监控/可见性,追踪,服务发现等等。这些都被硬编码到每个应用程序中。

然而,随着应用程序架构被越来越多的分割成服务,将通信逻辑从应用中移出到底层基础设施中变得越来越重要。就像是应用程序不应该写它自己的TCP栈,他们也不应该管理自己的负载均衡逻辑,或他们自己的服务发现管理程序,以及他们自己的重试和超时逻辑。

像Linkerd这样的Service Mesh为大规模多服务应用程序提供关键的部件:

基本的适应性功能:重试预算,deadline,熔断器模式

顶层的服务指标:成功率,请求量,延迟

延迟和失败容忍度:感应失败和延迟的负载均衡,它可以绕过缓慢的或者损坏的服务实例

分发追踪:如Zipkin和OpenTracing

服务发现:找到目标实例

协议升级:在TLS中包装跨网络通信,或将HTTP/1.1转换为HTTP/2.0

路由:不同版本的服务之间的路由请求,集群之间的故障转移等



在这部分中,我们仅重点关注可见性:Service Mesh是如何自动捕获并报告如成功率等顶层服务指标的,我们将展示一个Kubernetes的小例子以此来引导你。
 
在Kubernetes中使用Linkerd作为Service的检测器
 
在请求层进行操作的一个优势是Service Mesh可以访问成功和失败的协议级语义。举个例子,如果你正在运行一个HTTP服务,Linkerd可以理解200 、400、500响应的含义,从而可以自动计算如成功率这样的指标。

让我们通过一个小例子讲解如何在Kubernetes中安装Linkerd,从而自动的捕获顶层服务成功率而不需要更改应用程序。
 

安装Linkerd
使用Kubernetes配置文件安装Linkerd。它会将Linkerd作为DaemonSet安装,且运行在Kubernetes的default命名空间:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/linkerd.yml 
 
你可以通过查看Linkerd的管理页面确认是否安装成功:
INGRESS_LB=$(kubectl get svc l5d -o jsonpath="{.status.loadBalancer.ingress[0].*}") open http://$INGRESS_LB:9990 # on OS X
 
如果集群不支持外部负载均衡,使用hostIP:
HOST_IP=$(kubectl get po -l app=l5d -o jsonpath="{.items[0].status.hostIP}") open http://$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[2].nodePort}') # on OS X


1.png



安装样例应用程序
在default命名空间下安装两个Service,“hello”和“world”。这些应用程序依赖于Kubernetes downward API提供的nodeName来发现Linkerd。为了检测你的集群是否支持nodeName,你可以运行:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/node-name-test.yml 
 
然后看它的日志:kubectl logs node-name-test
 
如果你看到了一个IP就说明支持。接下来继续部署hello world应用程序如下:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world.yml 
 
如果你看到报错“server can’t find…”,就部署旧版本hello-world,它依赖于hostIP而非nodeName:kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-examples/master/k8s-daemonset/k8s/hello-world-legacy.yml 
 
这两个Service——“hello”和“world”——功能在一起使得高度可扩展,“hello world”微服务(“hello”Service调用“world”Service完成这个请求)。

你可以通过给Linkerd的外部IP发送请求来查看此操作:
http_proxy=$INGRESS_LB:4140 curl -s http://hello或者直接使用hostIP:http_proxy=$HOST_IP:$(kubectl get svc l5d -o 'jsonpath={.spec.ports[0].nodePort}') curl -s http://hello 
 
你应该可以看到字符串“Hello world”。
 

安装Linkerd-viz
最后,通过安装Linkerd-viz,让我们可以看看Service正在做什么。Linkerd-viz是一个附加包,它包括一个简单的Prometheus和Grafana设置,并可以自动发现Linkerd。
下面的命令会将Linkerd-viz安装到default命名空间下:
kubectl apply -f https://raw.githubusercontent.com/linkerd/linkerd-viz/master/k8s/linkerd-viz.yml 
 
访问Linkerd-viz的外部IP查看dashboard:
VIZ_INGRESS_LB=$(kubectl get svc linkerd-viz -o jsonpath="{.status.loadBalancer.ingress[0].*}") open http://$VIZ_INGRESS_LB # on OS X
 
如果集群不支持外部负载均衡,使用hostIP:
VIZ_HOST_IP=$(kubectl get po -l name=linkerd-viz -o jsonpath="{.items[0].status.hostIP}") open http://$VIZ_HOST_IP:$(kubectl get svc linkerd-viz -o 'jsonpath={.spec.ports[0].nodePort}') # on OS X
 
你应该在dashboard上看到包括Service与实例的选择器,所有图表都反映了这些Service和实例的选择器。
 

2.png


Linkerd-viz的dashboard包括三部分:

顶层:集群范围的成功率与请求量

Service指标:每个已部署应用的指标,包括成功率、请求量和延迟

单个实例指标:集群上每一个node的成功率、请求量和延迟

用三个简单的命令我们就可以将Linkerd安装在集群上,安装应用,并且使用Linkerd了解应用程序Service的健康状况。当然,Linkerd提供的不仅仅是可见性,还提供了延迟感应负载均衡,自动重试和断路,分发追踪等功能。
 
 
 
第二部分:导言
 
在第一部分里我们提到了Linkerd是使用DaemonSet而非Sidecar来安装的。在第二部分中,我们将解释我们为什么(怎么样)这么做。

注意:这是关于Linkerd、Kubernetes和Service ,esh的系列文章其中一篇,其余部分包括:

Top-line service metrics

Pods are great, until they’re not(本文)

Encrypting all the things

Continuous deployment via traffic shifting

Dogfood environments, ingress, and edge routing

Staging microservices without the tears

Distributed tracing made easy

Linkerd as an ingress controller

gRPC for fun and profit

The Service Mesh API

Egress

Retry budgets, deadline propagation, and failing gracefully

Autoscaling by top-line metrics
 

Linkerd以DaemonSet方式运行


作为Service Mesh,Linkerd设计为与应用程序代码一起运行。它管理和监控service的内部通信,包括服务发现、重试、负载均衡与协议升级。

初次听闻,都会觉得这非常适合在Kubernetes中以Sidecar的方式部署。毕竟,Kubernetes的定义特征之一就是它的pod模型。作为Sidecar部署理论上简单,有清晰的失败语义,我们花了大量时间用于针对该用例的Linkerd优化。

然而,Sidecar模型有其缺陷:部署一个pod就要用掉部署一个pod的资源。如果你的Service较轻量并且跑了许多的应用实例,这样使用Sidecar来部署就会代价极高。

我们可以通过每一个host而非每一个pod部署一个Linkerd来减少资源的开销。它对每个主机进行扩展而消耗资源,这通常是比pod数量要显著更慢的增长的指标。并且很幸运,Kubernetes为此目的提供了DaemonSets。

但美中不足的是,对于Linkerd来说,按每个host部署比仅仅使用DaemonSets要复杂一些。我们如何解决Service Mesh这个问题?请继续阅读下文。
 

Kubernetes的Service 
MeshService Mesh的定义特征之一是它将应用通信与传输通信分离开的能力。例如,如果Service A与B使用HTTP协议通信,Service Mesh也许会通过电缆将之转换为HTTPS,而且应用程序并不知晓。Service Mesh也可以做连接池、权限控制,或其它的传输层特性,并且是以对应用程序透明的方式。

为了完美的做到这些,Linkerd必须作为本地实例的代理处于请求的发送端与接收端。例如HTTP升级为HTTPS,Linkerd一定要在传输层协议的开始与结束。在DaemonSet的世界,一个通过Linkerd的请求路线看起来如下图所示:

31.png

 
正如你所见,一个从Host 1中Pod A发起的目标为Host 2中Pod B的请求必须通过Pod A的本地Linkerd实例,然后到达Host 2的Linkerd实例,最后到达Pod J。这个路径引入了Linkerd必须解决的三个问题:


应用如何定位它的本地Linkerd

Linkerd如何将传出请求路由到目标Linkerd

Linkerd如何将传入请求路由到目标应用

接下来就讲述我们如何解决这三个问题的技术细节。
 
 

应用程序如何定位它的本地Linkerd
因为DaemonSet使用Kubernetes的host端口,我们就知道Linkerd运行于host IP的一个固定端口。因此,为了发送一个请求给运行于同一宿主机的Linkerd进程,我们需要确定这个主机的IP地址。

在Kubernetes 1.4及其以后的版本,此信息可通过底层API直接获取到。以下是来自hello-world.yaml的摘要,显示了如何将节点名称传递到应用程序中:
env: - name: NODE_NAME  valueFrom:    fieldRef:      fieldPath: spec.nodeName - name: http_proxy  value: $(NODE_NAME):4140 args: - "-addr=:7777" - "-text=Hello" - "-target=world"
 
(请注意,此例设置http_proxy环境变量以引导通过本地Linkerd实例的所有HTTP调用。然而此方法仅适用于大多数的HTTP应用,非HTTP应用需要做一些不同的工作)

Kubernetes 1.4之前的版本这些信息仍旧可用,但是方式要间接一些。

以下是hello-world-legacy.yml的一部分,显示如何将主机IP传递到应用程序中。
env: - name: POD_NAME  valueFrom:    fieldRef:      fieldPath: metadata.name - name: NS  valueFrom:    fieldRef:      fieldPath: metadata.namespace command: - "/bin/sh" - "-c" - "http_proxy=`hostIP.sh`:4140 helloworld -addr=:7777 -text=Hello -target=world"
 
请注意,hostIP.sh脚本需要将pod名和命名空间以环境变量的方式注入pod中。



Linkerd如何将传出请求路由到目标Linkerd
在我们的Service Mesh部署中,传出请求不应当直接发送给目标应用,而应当发送给运行于应用程序所在主机的Linkerd。为了做到这点,我们可以利用Linkerd 0.8.0引入的被称为transformer的新特性,它可以对Linker路由到的目标地址进行任意的后期处理。在这种情况下,我们可以使用DaemonSet transformer自动的让运行于目标主机的DaemonSet的地址取代目标地址。例如,这个Linkerd传出路由配置发送所有的请求到运行于相同主机的作为目标应用的Linkerd的输入端口。
 
routers: - protocol: http  label: outgoing  interpreter:    kind: default    transformers:    - kind: io.l5d.k8s.daemonset      namespace: default      port: incoming      service: l5d  ...

Linkerd如何将输入请求路由到目标应用
当一个请求最终到达目标pod的Linkerd实例,它一定得正确的路由到该pod。为此,我们使用localnode transformer将路由限制在运行于本地主机的pod。Linkerd配置示例如下:
routers: - protocol: http  label: incoming  interpreter:    kind: default    transformers:    - kind: io.l5d.k8s.localnode  ...


将Linkerd作为Kubernetes DaemonSet部署使得两全其美——它让我们完成service mesh的全部目标(如透明的TLS、协议升级、延迟感知负载均衡等等),同时按每个host扩缩Linkerd实例而非每个pod。