Service Mesh和Istio介绍

李成凯

Service Mesh的演进过程

微服务

在过去几年中,微服务一直是热议的话题。在微服务之前,项目以单机运行的方式居多。项目中包含了很多为业务逻辑,导致项目臃肿且可拓展性不高,让广大程序员极为头疼。而微服务的出现大大的提高了项目的可拓展性、可升级性、易维护性、资源隔离性等等,使得产品的研发效率有了质的提高。同时让产品的持续集成和发布变的更为便捷。

容器技术

同样微服务带来了一些严重问题,例如产品业务逻辑更加复杂,对于运维管理多达几千个应用提出了挑战。针对这些问题,逐渐演化出了容器技术,例如当前最流行的docker等等。我们可以为微服务单独创建容器,并以容器的方式进行部署,不仅能够在一台物理机上实现了资源隔离,而且还要比启动虚拟机的方式速度更快、成本更低。

容器编排

对于如何对众多容器进行管理,提高容器的可用性,自动生成容器、扩容、缩容,负载均衡等等诸多问题,kurbernets提出了解决方案。kurbernetes是当下最流行的容器编排工具,能够实现容器的自动创建、扩容、缩容、负载均衡、服务发现、跨主机调度,拥有很高的拓展性,能够大大提高运维的工作效率,缩短问题处理时间,改变了基础设施格局。

Service Mesh

image

容器编排工具(kubernetes)主要倾向于容器管理和调度问题,对于应用之间的指标收集、分布式链路追踪、熔断、流量迁移、限速、日志收集没有很好的解决方案。针对这些问题,Netflix公司推出了一系列工具,例如ribbon、feign、zuul、hystrix、trace等等,组建了spring cloud生态。但这些工具并不能解决多语言的问题,这些工具仅仅针对java。同时多语言也给中间件带来了挑战。中间件不仅需要推出多个语言的SDK,而且中间件的升级也有诸多不易。那么能够给出一个无关语言的中间件解决方案吗?Service Mesh能够解决类似的问题,当下流行的service mesh解决方案主要为Envoy和Linkerd。他们能够作为sidecar运行在每个pod里面,形成抽象的应用网络。所有业务应用的流量通过service mesh网格进行流动。能够通过流量劫持的方式实现以下几种功能,能够真正的让业务应用和中间件进行解耦,能够实现中间件的快速升级,实现语言无关性。例如Envoy能够提供许多方便的功能:

  • 支持HTTP,HTTP/2和gRPC
  • 健康检查
  • 负载均衡
  • Metrics
  • 追踪
  • 访问日志
  • 熔断
  • 重试策略
  • 超时配置
  • 限速
  • 支持Prometheus
  • 流量迁移
  • 通过发现服务来动态调整配置(XDS)

Istio

Istio和Envoy的差别在于,Envoy仅实现了数据平面,而Istio基于Envoy实现数据平面,同时增加了控制平面,使得功能更加丰富,能够控制所有的sidecar,控制所有的流量,为开发人员带了集中式的控制体验,也支持插件化的自定义配置。

Service Mesh缺点

从上文中我们可以看到service mesh能够带来诸多便利之处,但也带来了一些问题需要值得重视。 1. 增加了应用请求的耗时。从上文可以知道,所有应用的请求都会经过Envoy进行代理转发,这种方式将会增加毫秒级别的网络耗时。
2. 增加了运维复杂度。运维人员必须熟练掌握Docker、Kurbernetes、Envoy、Isito等相关知识。
3. 在生产落地上缺乏可靠的实际经验,目前应用的公司不多。针对国内普遍使用的Dubbo协议支持力度不够。对于这个问题,Dubbo团队也在努力适配Service Mesh,在可预计的dubbo3.0版本会初步支持Service Mesh。

Istio架构

image

从上图看,Istio由Proxy、Mixer、Pilot、Galley、Citadel等组件组成。这里将重点讲解下Proxy、Mixer、Pilot这三类组件功能。
Proxy
image

Proxy组件作为sidecar与应用一起部署,能够对应用的出入请求进行处理。Proxy由Pilot-agent和Envoy两部分组成。
- Pilot-agent在启动时会去Pilot-server拉取静态资源和动态资源,生成Envoy启动时需要的静态资源,对Envoy进行启动,同时在Pod的生命周期内对Envoy进行管理和监控。其核心功能为从Pilot-server中获取动态配置资源,将配置信息转换成Envoy能够使用的配置规则。
- Envoy作为数据平面,承担了流量控制、负载均衡、熔断等一系列重要功能。Istio对流量的控制主要由Envoy实现。从下图来看,Envoy提供了一组获取动态资源的接口XDS,可以分别获取CDS(Cluster Discovery Service)、EDS(Endpoint Discovery Service)、SDS(Service Discovery Service)、RDS(Route Discovery Service)、LDS(Listener Discovery Service)。Envoy可以有多个listener,每个Listener可以有多个Filter组成Chains。Listener(监听器)能够监听下游客户端连接的网络请求,进行Filter匹配,让Filter完成对元数据的处理。同时listener可以通过LDS进行动态获取。Listener filter(监听过滤器)对于满足匹配要求的请求进行处理,例如监控指标收集、限速等等。Http Route Table:HTTP 的路由规则,例如请求的域名,Path 符合什么规则,转发给哪个 Cluster。Health checking:健康检查会与SDS服务发现配合使用。但是,即使使用其他服务发现方式,也有相应需要进行主动健康检查的情况。 image

Pilot
Pilot分为Pilot-agent和Pilot-server两部分,Pilot-agent位于数据平面,Pilot-server位于控制平面。Pilot-agent在Proxy部分有过说明就不再重复。Pilot-server对应图中的Discovery Service,主要功能是在Kubernetes的Controller中注册Event事件,对Service、Pod、Endpoint、Node的信息变更进行监听。如果这些内容发生了变更,Pilot-server将会通知所有Pilot-agent。Pilot-agent会去更新Envoy中的配置规则。
Mixer
image Istio提供灵活的模型来执行授权策略,并为网格中的服务收集遥测数据。Istio提供统一抽象,使得Istio可以与一组开放式基础设施后端进行交互,能够改变层与层之间的边界,减少系统复杂性,消除业务代码中的收集逻辑,是业务应用无感知,对中间件进行解耦。而Mixer就是负责提供策略控制和遥测收集的Istio组件。
根据上图所示,Proxy在请求转发时都会请求Mixer进行Check And Report。Check是为了检测请求是否满足先决条件检查,Report报告遥测请求。我们可以发现每次请求都要去调用Mixer,不仅增加了两次请求耗时,而且受限于中心化的Mixer,将带来很严重的性能问题。官方虽然在Document中描述Envoy sidecar具有本地缓存的功能,可以在缓存中执行相对大比例的前提条件检查,但是我们仍对这个设计保持怀疑的态度。在SOFA Mesh生产落地的文章中也提到这一方面的性能问题,在后面章节我们再讲述如何针对这一点进行改进。
Mixer是高度模块化和可拓展的组件,它的可拓展性是由插件模型来实现的。每个插件被称为Adapter。通过配置能够决定在运行时使用的确切的适配器套件,并且可以轻松扩展到新的或定制的基础设施后端。Istio默认实现了一些插件,参考:https://istio.io/zh/docs/reference/config/policy-and-telemetry/adapters/。 若默认插件不满足要求,也可以开发自定义插件,参考:https://github.com/istio/istio/wiki/Mixer-Compiled-In-Adapter-Dev-Guide

流量转发

image 经过以上分析,相信大家对Service Mesh和Istio是什么有了一定的了解。在此进一步总结,Service Mesh翻译成中文称作服务网格。服务网格的含义是将应用网格化,应用之间不进行相互调用而是通过作为sidecar的proxy之间进行相互请求。将应用之间的网络调用、限流、熔断、监控从业务应用中剥离出来,保证请求的可靠传输,使得应用之间传输无感知,让业务应用更加注重业务逻辑。相比于Spring Cloud生态,我们不再需要增加Euraka作为注册中心、Hystrix作为熔断、Trace作为分布式链路追踪、Zuul作为网关等一些列的依赖。Istio将这些功能抽离出来作为云原生的基础设施,让开发更为便捷。 Istio是实现Service Mesh的一种解决方案。
通过之前的描述,大家是不是在脑中产生了一个疑问:Istio是如何将应用的流量代理到Proxy里面?Istio通过iptables路由表配置实现了端口之间的流量转发,下面将详细分析下流量转发的全过程。 1. Istio利用Kurbenetes的Initializers机制向即将创建的Pod容器中插入Istio-init容器。Istio-init的作用是通过配置iptables将流量从业务端口转发到15001端口,而这个端口就是Proxy监听的端口。

1. 执行 kubectl get po -n default,可以获取namespace为default下的pod信息  
NAME                             READY   STATUS      RESTARTS   AGE  
curl-6bf6db5c4f-rgqzp            1/1     Running     1          25d  
details-v1-6f4c4dfb85-pzznh      2/2     Running     0          14d  
hello-build-pod-d3a958           0/1     Completed   0          10d  
productpage-v1-bb79f5cc5-fn76j   2/2     Running     0          14d  
ratings-v1-69757fc969-z89wc      2/2     Running     0          14d  
reviews-v1-6f9c998689-gfc4d      2/2     Running     0          14d  
reviews-v2-9c869845c-hwvwv       2/2     Running     0          14d  
reviews-v3-74b8d986c6-l2wx9      2/2     Running     0          14d  
test-git-branch-pod-3ba297       0/1     Completed   0          10d

2. 执行 kubectl describe pod details-v1-6f4c4dfb85-pzznh,获取details中的详细信息。  
Init Containers:  
  istio-init:
    Container ID:  docker://7c8b7773e0eb0cc6e3263328bf480eec2a472775e1da92b58ac645b41df2aa58
    Image:         docker.io/istio/proxy_init:1.2.2
    Image ID:      docker-pullable://istio/proxy_init@sha256:d291e6d584c86ed8a62d0936943082790b71dcb4fcc18dd4a6e2d31ca908b522
    Port:          <none>
    Host Port:     <none>
    Args:
      -p
      15001
      -u
      1337
      -m
      REDIRECT
      -i
      *
      -x

      -b
      9080
      -d
      15020
·········
Containers:  
  details:
    Container ID:   docker://ee4f83063bf6ad42eb95557d6bc9250cb137b4ba8787db75a3e1629c3db21a20
    Image:          istio/examples-bookinfo-details-v1:1.12.0
    Image ID:       docker-pullable://istio/examples-bookinfo-details-v1@sha256:7db14e9d9805d148fc52f99cff34de3db788788fe558081991b2d09044ff6e8f
    Port:           9080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Tue, 23 Jul 2019 10:49:43 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from bookinfo-details-token-j6vd6 (ro)
  istio-proxy:
    Container ID:  docker://22991a4818d05083f1704fad7f545699ec1b7267e7b9e5d48e6fc31186c5a01a
    Image:         docker.io/istio/proxyv2:1.2.2
    Image ID:      docker-pullable://istio/proxyv2@sha256:236527816ff67f8492d7286775e09c28e207aee2f6f3c3d9258cd2248af4afa5
    Port:          15090/TCP
    Host Port:     0/TCP

从上发现details中额外注入两个容器,分别为Istio-init和Istio-proxy。在看到Istio-init的Args参数,参数含义为通过Iptables将9080端口重定向到15001端口,排除1337用户的流量。由此可见,Istio通过Istio-init进行Iptables配置,将流量从业务端口重定向到代理端口,而Istio-proxy监听代理端口,实现数据平面处理。

1. 执行 kubectl exec details-v1-6f4c4dfb85-pzznh -c istio-proxy -- ps -ef  
UID        PID  PPID  C STIME TTY          TIME CMD  
istio-p+     1     0  0 Jul23 ?        00:09:39 /usr/local/bin/pilot-agent proxy sidecar --domain default.svc.cluster.local --configPath /etc/istio/proxy --binaryPath /usr/local/bin/envoy --serviceCluster details.default --drainDuration 45s --parentShutdownDuration 1m0s --discoveryAddress istio-pilot.istio-system:15010 --zipkinAddress zipkin.istio-system:9411 --dnsRefreshRate 300s --connectTimeout 10s --proxyAdminPort 15000 --concurrency 2 --controlPlaneAuthPolicy NONE --statusPort 15020 --applicationPorts 9080  
istio-p+    20     1  0 Jul23 ?        00:35:05 /usr/local/bin/envoy -c /etc/istio/proxy/envoy-rev0.json --restart-epoch 0 --drain-time-s 45 --parent-shutdown-time-s 60 --service-cluster details.default --service-node sidecar~10.244.3.58~details-v1-6f4c4dfb85-pzznh.default~default.svc.cluster.local --max-obj-name-len 189 --local-address-ip-version v4 --allow-unknown-fields -l warning --component-log-level misc:error --concurrency 2  
istio-p+    46     0  2 07:02 ?        00:00:00 ps -ef  

这个命令中我们可以知道,Istio-proxy中包含了两个进程:Pilot-agent和Envoy。

Iptables
image netfilter/iptables(简称为iptables)组成Linux平台下的包过滤防火墙,完成封包过滤、封包重定向和网络地址转换(NAT)等功能。Iptables 可以检测、修改、转发、重定向和丢弃IPv4数据包。从上图可以看出Iptables包含五种链Prerouting、Inpput、Forward、Output、Postrouting。其中NAT表包含Prerouting、Postrouting、Output三种。Istio的流量转发使用Prerouting、Postrouting进行流量转发到代理端口。
image 从上图可以看出入站请求链为:
INPUT -> PREROUTING -> ISTIOINBOUND -> ISTIOINREDIRECT
tcp端口9080的流量REDIRECT到15001端口
OUTPUT -> ISTIO
OUTPUT -> ISTIO_REDIRECT
将出站请求流量REDIRECT到15001端口

灰度发布

到这里想必大家对Istio有了大致了解,现根据Istio提供的官方示例bookinfo对灰度发布过程进行讲解。我当前使用的Istio版本是1.2.2,下载压缩包,打开istio-1.2.2/samples/bookinfo/platform/kube/bookinfo.yaml,可以看到bookinfo的kurbernets配置。这里就不进行详细描述,有兴趣的同学可以自行下载。打开istio-1.2.2/samples/bookinfo/networking/目录,可以看到bookinfo的DestinationRule、Gateway、VirtualService配置。原先 项目virtual-service文件

apiVersion: networking.istio.io/v1alpha3  
kind: VirtualService  
metadata:  
  name: reviews
spec:  
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v3
      weight: 100

更改配置为下面配置,将流量50%导入到v3版本。从而实现新版本的灰度发布。

apiVersion: networking.istio.io/v1alpha3  
kind: VirtualService  
metadata:  
  name: reviews
spec:  
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 50
    - destination:
        host: reviews
        subset: v3
      weight: 50
执行 kubectl apply -f virtual-service-reviews-50-v3.yaml 进行配置更新

总结

Service Mesh这个概念从16年开始提出,到现在已经有了蓬勃的发展。Istio和Conduit都是其中的佼佼者。但在生产落地的过程中存在一些问题,下面将针对这些问题进行一些探讨。
1. proxy处理每个请求时,需要向mixer进行check and report,可能带来很大的性能问题。
当前的解决方案是将mixer的部分功能下放到proxy中实现,例如通过envoy的filter方式实现。
2. proxy在收集指标的时候,通过http的方式进行上报,可能会对业务应用带来影响。
可以将指标或者日志打印到文件,然后通过filebeat进行日志采集。 或者通过异步和批量的方式进行上报,减少请求数。
3. Istio目前仅支持http1.1/2.0和gRPC协议,对于国内用户普遍采用的dubbo协议带来一定的麻烦。
目前dubbo团队已经计划在3.0版本支持Service Mesh。目前Sofa Mesh已经实现了对Dubbo RPC支持,以及对Istio的改进。感兴趣的同学可以深入了解一下。

参考文章

为什么说 Service Mesh 是微服务发展到今天的必然产物
Envoy 基本架构和配置解析
浅谈Service Mesh体系中的Envoy
Istio-策略与遥测
深度剖析Kubernetes动态准入控制之Initializers
iptables详解