微服务治理逻辑被独立出来之后的位置由“Service Govern Logic”这一层组成的逻辑网络被定义为Service Mesh每个微服务都包含一个service mesh的端点。“Service Mesh”概念还非常年轻这个词在国内被翻译为“服务网格”或“服务啮合层”我们这里就用Service Mesh这个英文词。这里摘录一下ServiceMesh中文社区上的一篇名为《年度盘点2017之Service Mesh群雄逐鹿烽烟起[1]》的文章中对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的部署形式提供。下面是官方的架构图图 3Istio架构图来自官网Sidecar中Envoy代理了Pod中真正业务Container的所有进出流量并对这些流量按照控制平面设定的“治理逻辑”进行处理。而这一切对Pod中的业务应用是透明的开发人员可以专心于业务逻辑而无需再关心微服务治理的逻辑。Istio代表的Service Mesh的设计理念被认为是下一代“微服务统一框架”甚至有人认为是微服务框架演化的终点。Istio于2017年5月24日发布了0.1 release版本截至目前为止Istio的版本更新到v 0.4.0演进速度相当快不过目前依然不要用于生产环境至少要等到1.0版本发布吧。但对于Istio的早期接纳者而言现在正是深入研究Istio的好时机。在本篇的接下来内容中我们将带领大家感性的认识一下Istio入个门儿。三、Istio安装Istio目前支持最好的就是Kubernetes了因此我们的实验环境就定在Kubernetes上。至于版本Istio当前最新版本为0.4.0这个版本据说要Kubernetes 1.7.4及以上版本用起来才不会发生小毛病:)。我的Kubernetes集群是v1.7.6版本的恰好满足条件。下面是安装过程Node上的OS是Ubuntu 16.04# wget -c https://github.com/istio/istio/releases/download/0.4.0/istio-0.4.0-linux.tar.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_HUBdocker.io/istio export CA_TAG0.4.0 export MIXER_HUBdocker.io/istio export MIXER_TAG0.4.0 export PILOT_HUBdocker.io/istio export PILOT_TAG0.4.0 export ISTIOCTL_URLhttps://storage.googleapis.com/istio-release/releases/0.4.0/istioctl export PROXY_TAG0.4.0 export ISTIO_NAMESPACEistio-system export AUTH_DEBIAN_URLhttps://storage.googleapis.com/istio-release/releases/0.4.0/deb export PILOT_DEBIAN_URLhttps://storage.googleapis.com/istio-release/releases/0.4.0/deb export PROXY_DEBIAN_URLhttps://storage.googleapis.com/istio-release/releases/0.4.0/deb export FORTIO_HUBdocker.io/istio export FORTIO_TAG0.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注我还曾在Kubernetes v1.7.3上安装过Istio 0.3.0版本但在创建组件时会报下面错误这个错误可能会导致后续addon安装后工作不正常unable to recognize istio.yaml: no matches for config.istio.io/, Kindmetric unable to recognize istio.yaml: no matches for config.istio.io/, Kindmetric unable to recognize istio.yaml: no matches for config.istio.io/, Kindmetric unable to recognize istio.yaml: no matches for config.istio.io/, Kindmetric unable to recognize istio.yaml: no matches for config.istio.io/, Kindmetric unable to recognize istio.yaml: no matches for config.istio.io/, Kindmetric安装后我们在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 4dIstio安装成功四、服务治理策略验证接下来我们来用几个例子验证一下Istio在服务治理方面的能力Istio自带一些完整的例子比如bookinfo用于验证服务治理的能力但这里先不打算用这些例子1、验证环境和拓扑我们先来看一下验证环境的示意图我们看到在Service Mesh中部署了两个service: servera和serviceb前者调用后者完成某项业务后者则调用外部服务完成业务逻辑。servicea模拟pay服务在收到client请求后进行pay处理并将处理结果通过serviceb提供的msg notify服务下发给user。该服务的endpoint为/payserviceb模拟notify服务在收到servicea请求后将message转发给external service完成notify逻辑。该服务的endpoint为/notifyexternal service位于Service Mesh之外client我们使用curl模拟。我们先来部署servicea和serviceb的v0.1版本以servicea的部署为例servicea的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容器。当然也可以配置为自动为每个Kubernetes启动的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 21 [1] 9423实验环境OK了。下面我们来验证一下业务是否是通的。2、Egress Rules按照之前我们的设定我们使用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; charsetutf-8]] 0xc420058d40 66 [] false false map[] 0xc4200eaf00 nil} service_a:v0.1 notify customer ok // service_b的日志 {GET /notify?msgservice_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?msgservice_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?msgservice_a:v0.1-pays-ok: EOF我们看到servicea和serviceb都返回了错误日志注意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.yamlCreated config egress-rule/default/enable-svcb-engress-rule at revision 30031258这时你再尝试curl svca我们可以看到msgd的日志中出现了下面的内容2018/01/02 23:58:16 {GET /send?msgservice_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?msgservice_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