前一篇《Service Mesh 微服务架构介绍》简单介绍了 Service Mesh 微服务架构较之像 SpringCloud 这类传统微服务架构所带来的优势,和 Service Mesh 架构的核心设计理念。

这一篇主要解决以下问题:

  • 在 Service Mesh 架构下,以 Istio 为例,它是怎样去实现微服务治理的?
  • 基于 Kubernetes + Istio 情况下,SpringBoot 开发的服务间如果使用 Http 协议、gRPC 协议通信?

1. Kubernetes 环境准备及 Istio 安装

要体验 Service Mesh 微服务架构,基本的前提是需要一个功能完整的 Kubernetes 环境,这里所使用的 Kubernetes 环境是基于四台 Ubuntu 物理机 和 Kubernetes v1.18.8 版本所搭建的 K8S 集群。

  1. $ kubectl get nodes
  2. NAME STATUS ROLES AGE VERSION
  3. k8s-master Ready master 237d v1.18.8
  4. server-1 Ready <none> 123d v1.18.8
  5. server-2 Ready <none> 237d v1.18.8
  6. server-3 Ready <none> 237d v1.18.8

另外 Istio 对 Kubernetes 的版本是有要求的,此时 istio 最新版为 1.9.4,这里选择版本:istio-1.8.4

具体步骤如下:(官方参考文档

1.1 下载 Istio 发布包

由于官方提供的下载脚本执行速度比较慢,可以直接在GitHub找到相应的 Istio 发布版本后通过 wget 命令将其下载至主机指定目录(能正常连接 Kubernetes 集群):

  1. $ wget https://github.com/istio/istio/releases/download/1.8.4/istio-1.8.4-linux-amd64.tar.gz

下载成功后,解压安装包:

  1. $ tar -zxvf istio-1.8.4-linux-amd64.tar.gz

进入解压安装包目录:

  1. $ cd istio-1.8.4

1.2 将 istioctl 客户端添加到系统可执行路径

在具体安装 Istio 时需要使用 istioctl 命令,因此需要先将该命令加入系统可执行路径,命令如下:

  1. $ export PATH=$PWD/bin:$PATH

1.3 执行安装 istio 命令

这里使用 istioctl 命令执行安装命令,具体如下:

  1. $ istioctl install --set profile=demo

这里“—set profile=demo”表示安装一个 istio 测试环境!成功安装后的信息输出如下:

  1. Detected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/v1.8/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details.
  2. This will install the Istio 1.8.4 demo profile with ["Istio core" "Istiod" "Ingress gateways" "Egress gateways"] components into the cluster. Proceed? (y/N) Y
  3. Istio core installed
  4. Istiod installed
  5. Ingress gateways installed
  6. Egress gateways installed
  7. Installation complete

如果安装成功,则可以通过 kubectl 命令查看 Istio 相关组件是否已经安装在 Kubernetes 环境之中,命令如下:

  1. $ kubectl get svc -n istio-system
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. istio-egressgateway ClusterIP 10.101.134.226 <none> 80/TCP,443/TCP,15443/TCP 8m12s
  4. istio-ingressgateway LoadBalancer 10.96.167.106 <pending> 15021:31076/TCP,80:31032/TCP,443:31438/TCP,31400:32751/TCP,15443:31411/TCP 8m11s
  5. istiod ClusterIP 10.102.112.111 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP

此时可以看到 Istio 的核心组件 istiod,以及入口网关 Ingress Gateway、出口网关 Egress Gateway 已经成功以Service 资源的方式运行在了 Kubernetes 集群之中!

1.4 (重要!!!)Kubernetes 在默认命名空间下开启自动注入 Envoy Sidecar

这是一个关键的步骤,如果我们的微服务应用未来是默认部署在 Kubernetes 的 default 命名空间,那么在安装 Istio 时需要开启该空间的 Sidecar 自动注入功能。这是我们前面提到每启动一个微服务应用,Kubernetes 就会默认在相同的 Pod 中自动启动一个代理进程的关键设置!

具体命令如下:

  1. $ kubectl label namespace default istio-injection=enabled
  2. namespace/default labeled

1.5 Istio 可观测性部署

Kiali 是一个基于服务网格的 Istio 管理控制台,它提供了一些数据仪表盘和可观测能力,同时也可以让我们去操作网格的配置。使用如下方式快速部署一个用于演示的 Kiali,命令如下:

  1. $ kubectl apply -f samples/addons
  2. serviceaccount/grafana created
  3. configmap/grafana created
  4. service/grafana created
  5. deployment.apps/grafana created
  6. configmap/istio-grafana-dashboards created
  7. configmap/istio-services-grafana-dashboards created
  8. deployment.apps/jaeger created
  9. service/tracing created
  10. service/zipkin created
  11. service/jaeger-collector created
  12. Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition
  13. customresourcedefinition.apiextensions.k8s.io/monitoringdashboards.monitoring.kiali.io created
  14. serviceaccount/kiali created
  15. configmap/kiali created
  16. clusterrole.rbac.authorization.k8s.io/kiali-viewer created
  17. clusterrole.rbac.authorization.k8s.io/kiali created
  18. clusterrolebinding.rbac.authorization.k8s.io/kiali created
  19. role.rbac.authorization.k8s.io/kiali-controlplane created
  20. rolebinding.rbac.authorization.k8s.io/kiali-controlplane created
  21. service/kiali created
  22. deployment.apps/kiali created
  23. serviceaccount/prometheus created
  24. configmap/prometheus created
  25. clusterrole.rbac.authorization.k8s.io/prometheus created
  26. clusterrolebinding.rbac.authorization.k8s.io/prometheus created
  27. service/prometheus created
  28. deployment.apps/prometheus created
  29. ....

其中具体会安装部署 Promethues、Grafana、Zipkin 等指标及链路采集服务!因为安装的组件比较多,也比较耗费资源,如果集群资源不是很充足,可能会出现启动比较慢的情况。

如果正常部署成功,可以查看 Pod 状态,命令如下:

  1. $ kubectl get pod -n istio-system -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. grafana-94f5bf75b-4mkcn 1/1 Running 1 30h 10.32.0.11 kubernetes <none> <none>
  4. istio-egressgateway-7f79bc776-w6rqn 1/1 Running 3 30h 10.32.0.3 kubernetes <none> <none>
  5. istio-ingressgateway-74ccb8977c-gnhbb 1/1 Running 2 30h 10.32.0.8 kubernetes <none> <none>
  6. istiod-5d4dbbb8fc-lhgsj 1/1 Running 2 30h 10.32.0.5 kubernetes <none> <none>
  7. jaeger-5c7675974-4ch8v 1/1 Running 3 30h 10.32.0.13 kubernetes <none> <none>
  8. kiali-667b888c56-8xm6r 1/1 Running 3 30h 10.32.0.6 kubernetes <none> <none>
  9. prometheus-7d76687994-bhsmj 2/2 Running 7 30h 10.32.0.14 kubernetes <none> <none>

由于前面安装 Istio 时,我们并没有在 istio-system 空间开启自动注入 Sidecar(其 label istio-injection=disabled),这里为了在 Kubernetes 集群之外正常访问 Kiali、Prometheus、Granfana、Tracing 的控制面板(它们共同组成了 Service Mesh 的可观测体系),可以通过 nodePort 的方式对外暴露端口。

Kiali 的 NodePort 访问操作方式:
将部署的 Kiali 的 Service 文件导出到主机的某个目录,例如:

  1. $ kubectl get svc -n istio-system kiali -o yaml > kiali-nodeport.yaml

之后编辑导出的文件,删除 metadata 下的 annotation、resourceVersion、selfFlink、uid 等信息;并修改下 spec下的 type 类型值,将 ClusterIP 修改为 NodePort,并指定 nodePort 端口信息;同时删除 status 状态字段即可。具体如下:

  1. spec:
  2. ...
  3. ports:
  4. - name: http
  5. nodePort: 31001
  6. ...
  7. type: NodePort

编辑完成后执行执行命令:

  1. $ kubectl apply -f kiali-nodeport.yaml

之后查看服务端口,命令如下:

  1. $ kubectl get svc -n istio-system kiali
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kiali NodePort 10.100.214.196 <none> 20001:31001/TCP,9090:30995/TCP 41h

此时通过 Kubernetes 的集群外部 IP+31001 端口就能访问 Kiali 控制面板了,效果如下图所示:
基于 Kubernetes Istio 治理 Java 微服务 - 图1
与 Kiali 的操作方式类似,我们也可以通过获取修改部署的 Promethues、Granfana、Tracing、Zipkin 等服务的发布文件,通过设置 NodePort 端口,从而在 Kubernetes 集群外部进行可观测界面的访问!例如:

  1. #Prometheus
  2. $ kubectl get svc -n istio-system prometheus -o yaml > prometheus-nodeport.yaml
  3. $ kubectl apply -f prometheus-nodeport.yaml
  4. #Granfana
  5. $ kubectl get svc -n istio-system grafana -o yaml > grafana-nodeport.yaml
  6. $ kubectl apply -f grafana-nodeport.yaml
  7. #Jaeger(分布式链路)
  8. $ kubectl get svc -n istio-system tracing -o yaml > tracing-nodeport.yaml
  9. $ kubectl apply -f tracing-nodeport.yaml
  10. ...

2. 开发及部署 SpringBoot 微服务

经过前面的步骤,我们就已经完成了基于 Istio 的 Service Mesh 微服务架构的基础设施环境的构建。

如果类比之前基于 Spring Cloud 框架的微服务开发体验,那么在 Istio 体系下应该如何进行微服务应用的开发呢?

2.1 应用架构

接下来我们通过一个实际的应用示例来演示,如何开发基于 Istio 的 Service Mesh 微服务应用,服务链路如下:
基于 Kubernetes Istio 治理 Java 微服务 - 图2
如上所示链路,具体说明如下:

  • 为了完整演示在 Service Mesh 架构下微服务的研发过程,这里我们定义3个微服务,其中 micro-api 服务是面向外部客户端接入的 Api 服务提供 Http 协议访问;
  • 而 micro-api 与 micro-order 之间则基于微服务的注册发现机制进行内部服务调用,具体采用 Http 协议;
  • 而 micro-order 与 micro-pay 之间也基于微服务注册发现机制进行内部微服务调用,为了演示更多的研发场景,这两个微服务之间的通信我们采用 Grpc 协议。

2.2 代码结构

规划好了微服务应用架构,接下来就可以具体开发了!具体的服务代码层面的构建,这里并不需要做任何微服务框架的引入,你只需要通过 Spring Boot 构建几个基本的 Spring Boot 应用即可,不需要引入任何服务治理相关的组件,只是一个简单且单纯的 Spring Boot 应用,不需要连接注册中心,也不需要引入什么 OpenFeign、Hystrix、Sentinel 之类的组件。

具体的代码结构如下图所示:
基于 Kubernetes Istio 治理 Java 微服务 - 图3
可以看到应用的入口类中已经没有服务发现之类的注解!

接下来我们讲重点:

首先,在之前基于 Spring Cloud 的微服务调用中,如果通过 Http 协议进行服务调用,一般我们是通过引入 OpenFeign 来实行,服务方提供一个 FeignClient 接口定义,调用方代码直接引入即可,而具体的运行逻辑,则是 OpenFeign 中集成的 Ribbon 组件会从注册中心获取目标服务地址列表,然后进行负载均衡调用。

但在 Service Mesh 架构下负载均衡及服务发现的逻辑已经由 Istio 中的 Sidecar 帮我们干了,所以在这里就不能还像以前一样引入 OpenFeign 了!那么怎么办呢?为了延续之前的编程风格及服务通信代码的简易性,这里我们需要自己定制一个类似于 OpenFeign 的框架,可以基于 OpenFeign 的源码进行改造,但是要去掉其中关于服务负载均衡、熔断限流等服务治理相关的逻辑,让它变成一个只是简单进行 Http 服务调用的框架。

目前市面上并没有这样一个官方的适配框架,所以一些落地 Service Mesh 架构的公司为了兼容 Spring Cloud 微服务体系的迁移,也是自己单独改造和封装的,这里我从 github 上找了一个个人改造的代码并进行了适配修改,测试是可以的!其具备的能力说明如下:

  • 支持在 istio 服务网格体系下,完成服务间的快速调用(体验和原先 Spring Cloud Feign 类似);
  • 支持多环境配置,例如本地环境微服务的调用地址可配置为本地,其他环境默认为 Kubernetes 集群中的服务;
  • 支持链路追踪,默认透传如下 Header,可以自动支持 jaeger、zipkin 链路追踪等,如下:
    1. "x-request-id", "x-b3-traceid", "x-b3-spanid", "x-b3-sampled", "x-b3-flags", "x-b3-parentspanid","x-ot-span-context", "x-datadog-trace-id", "x-datadog-parent-id", "x-datadog-sampled", "end-user", "user-agent"`

最后的实际编程风格是这样的:

  1. @FakeClient(name = "micro-order")
  2. @RequestMapping("/order")
  3. public interface OrderServiceClient {
  4. /**
  5. * 订单创建
  6. */
  7. @PostMapping("/create")
  8. ResponseResult<CreateOrderBO> create(@RequestBody CreateOrderDTO createOrderDTO);
  9. }

这里是 micro-order 微服务给 micro-api 所提供的接口调用代码,micro-api 服务引入调用即可,从编程风格上与之前 Spring Cloud 微服务的开发方式十分类似。只不过到这里为止,你并没有能看到任何与服务注册发现相关的逻辑!

其次,服务治理的核心逻辑都是由 Istio 及 Sidecar 代理完成的,完成应用开发后,只需要编写 Kubernetes 部署文件将服务部署进安装了 Istio 环境的 Kubernetes 集群即可,而将编写的 Java 服务部署到 Kubernetes 集群的流程,涉及 “Docker 镜像打包->镜像仓库发布-> Kubernetes 部署拉取镜像” 这一套 CI/CD 操作流程。

2.3 应用的 K8S yaml 文件

接下来重点演示 micro-api 及 micro-order 的 Kubernetes 发布文件,看看它们有什么特别之处:

  • micro-order 服务 Kubernetes 发布文件(micro-order.yaml): ```yaml apiVersion: v1 kind: Service metadata: name: micro-order labels: app: micro-order service: micro-order spec: type: ClusterIP ports:
    • name: http port: 80 targetPort: 9091 selector: app: micro-order

apiVersion: apps/v1 kind: Deployment metadata: name: micro-order-v1 labels: app: micro-order version: v1 spec: replicas: 2 selector: matchLabels: app: micro-order version: v1 template: metadata: labels: app: micro-order version: v1 spec: containers:

  1. - name: micro-order
  2. image: 10.211.55.2:8080/micro-service/micro-order:1.0-SNAPSHOT
  3. imagePullPolicy: Always
  4. tty: true
  5. ports:
  6. - name: http
  7. protocol: TCP
  8. containerPort: 19091
  1. 如上所示,这是 micro-order 服务的 Kubernetes 部署文件,就是正常定义了该应用的 Service 资源及 Deployment 编排资源;为了后面演示服务的负载均衡调用,这里我特地将该应用部署成了2个副本!
  2. - 接下来继续看看调用方 micro-api 服务的 Kubernetes 发布文件(micro-api.yaml):
  3. ```yaml
  4. apiVersion: v1
  5. kind: Service
  6. metadata:
  7. name: micro-api
  8. spec:
  9. type: ClusterIP
  10. ports:
  11. - name: http
  12. port: 19090
  13. targetPort: 9090
  14. selector:
  15. app: micro-api
  16. ---
  17. apiVersion: apps/v1
  18. kind: Deployment
  19. metadata:
  20. name: micro-api
  21. spec:
  22. replicas: 1
  23. selector:
  24. matchLabels:
  25. app: micro-api
  26. template:
  27. metadata:
  28. labels:
  29. app: micro-api
  30. spec:
  31. containers:
  32. - name: micro-api
  33. image: 10.211.55.2:8080/micro-service/micro-api:1.0-SNAPSHOT
  34. imagePullPolicy: Always
  35. tty: true
  36. ports:
  37. - name: http
  38. protocol: TCP
  39. containerPort: 19090

与 micro-order 一样也只是定义了该应用的 Kubernetes 正常发布资源,到这里也并没有体现出 micro-api 是怎么调用 micro-order 服务的!接下来我们通过这个文件将服务发布至 Kubernetes 集群中(注意,是开启了 Sidecar 自动注入的默认命名空间)!

部署成功后,查看 Pods 信息,具体如下:

  1. $ kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. micro-api-6455654996-57t4z 2/2 Running 4 28h
  4. micro-order-v1-84ddc57444-dng2k 2/2 Running 3 23h
  5. micro-order-v1-84ddc57444-zpmjl 2/2 Running 4 28h

如上所示,可以看到一个 micro-api Pod 两个 micro-order Pod 都已经正常运行起来了!但不知你发现没有,每个 Pod 中 READY 字段显示的都是 2/2,这意味着每个 Pod 中都启动了两个容器,一个是微服务应用本身,另外一个就是自动注入启动的 Sidecar 代理进程。为了更深理解这个逻辑,我们可以通过命令查看下 Pod 的描述信息:

  1. $ kubectl describe pod micro-api-6455654996-57t4z
  2. Name: micro-api-6455654996-57t4z
  3. ...
  4. IP: 10.32.0.10
  5. IPs:
  6. IP: 10.32.0.10
  7. Controlled By: ReplicaSet/micro-api-6455654996
  8. Init Containers:
  9. istio-init:
  10. Container ID: docker://eb0298bc8456f5f1336dfe2e8baab6035fccce898955469353da445aceab15cb
  11. Image: docker.io/istio/proxyv2:1.8.4
  12. Image ID: docker-pullable://istio/proxyv2@sha256:6a4ac67c1a74f95d3b307a77ad87e3abb4fcd64ddffe707f99a4458f39d9ce85
  13. ....
  14. Containers:
  15. micro-api:
  16. Container ID: docker://ebb45c5fa826f78c354877fc0a4c07d6b2fae4c6304e15729268b1cc6a69abca
  17. Image: 10.211.55.2:8080/micro-service/micro-api:1.0-SNAPSHOT
  18. Image ID: docker-pullable://10.211.55.2:8080/micro-service/micro-api@sha256:f303016a604f30b99df738cbb61f89ffc166ba96d59785172c7b769c1c75a18d
  19. 此处省略……
  20. istio-proxy:
  21. Container ID: docker://bba9dc648b9e1a058e9c14b0635e0872079ed3fe7d55e34ac90ae03c5e5f3a66
  22. Image: docker.io/istio/proxyv2:1.8.4
  23. Image ID: docker-pullable://istio/proxyv2@sha256:6a4ac67c1a74f95d3b307a77ad87e3abb4fcd64ddffe707f99a4458f39d9ce85
  24. 此处省略……

可以看到在开启了 Sidecar 自动注入的命名空间中,每启动一个 Pod,Istio 都会将 Sidecar 代理以初始化容器(Init Containers)的方式,自动启动一个对应地 istio-proxy 代理进程(Envoy),到这里你应该真实感到到 Sidecar 到底是一个什么样的存在了吧!

3. 部署 Istio 微服务网关

前面的步骤中我们已经完成了微服务应用的开发,并且也已经将其部署到了 Kubernetes 集群,Sidecar 代理也正常启动了,那么怎么访问呢?

一般来说如果要访问 Kubernetes 集群中的 Service,可以通过 NodePort 端口映射及 Ingress 的方式向 Kubernetes 集群外暴露访问端口。但在 Istio 中采用了一种新的模型—— Istio Gateway 来代替 Kubernetes 中的 Ingress 资源类型。在 Istio 微服务体系中,所有外部流量的访问都应该通过 Gateway 进来,并由 Gateway 转发到对应的内部微服务!

而基于统一的控制面配置,Istio 也可以集中管理 Gateway 网关的流量访问规则,实现对外部流量访问的整体管控!在前面部署 Istio 时,“istio-ingressgateway”入口流量网关已经作为 Istio 体系的一部分运行在 Kubernetes 集群中了,如下:

  1. $ kubectl get svc -n istio-system|grep istio-ingressgateway
  2. istio-ingressgateway LoadBalancer 10.100.69.24 <pending> 15021:31158/TCP,80:32277/TCP,443:30508/TCP,31400:30905/TCP,15443:30595/TCP 46h

接下来我们需要设置通过该网关访问 micro-api 微服务的逻辑,编写网关部署文件(micro-gateway.yaml):

  1. apiVersion: networking.istio.io/v1alpha3
  2. kind: Gateway
  3. metadata:
  4. name: micro-gateway
  5. spec:
  6. selector:
  7. istio: ingressgateway
  8. servers:
  9. - port:
  10. number: 80
  11. name: http
  12. protocol: HTTP
  13. hosts:
  14. - "*"
  15. ---
  16. apiVersion: networking.istio.io/v1alpha3
  17. kind: VirtualService
  18. metadata:
  19. name: micro-gateway
  20. spec:
  21. hosts:
  22. - "*"
  23. gateways:
  24. - micro-gateway
  25. http:
  26. - match:
  27. - uri:
  28. exact: /api/order/create
  29. route:
  30. - destination:
  31. host: micro-api
  32. port:
  33. number: 19090

如上所示,该部署文件中定义了路由匹配规则,凡是访问 /api/order/create 地址的请求都会被转发到 micro-api 服务的19090 端口!

配置完上述网关路由转发规则后,我们尝试通过访问 istio-ingressgateway 来到达访问微服务接口的效果,具体链路是:“外部调用 ->i stio-ingressgateway- > micro-api -> micro-order”。

但是对于 istio-ingressgateway 的访问,由于也是 Kubernetes 内部 Pod,所以暂时先配置一个 NodePort 端口映射,具体可以通过以下命令进行操作:

  1. export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
  2. export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
  3. export INGRESS_HOST=127.0.0.1
  4. export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT

以上分别设置了 istio-ingressgateway 的 http/https 的 NodePort 访问端口,设置完成后具体查看 nodePort 端口映射情况:

  1. $ kubectl get svc -n istio-system|grep istio-ingressgateway
  2. istio-ingressgateway LoadBalancer 10.100.69.24 <pending> 15021:31158/TCP,80:32277/TCP,443:30508/TCP,31400:30905/TCP,15443:30595/TCP 46h

可以看到通过 http 的 32277 以及 https 的 30508 端口可以访问 istio-ingressgateway。具体访问 url 是: http://{Kubernetes 集群 IP}:32277/接口url。具体访问效果如下:
基于 Kubernetes Istio 治理 Java 微服务 - 图4
从调用效果上可以看到,基于 Istio 的 Service Mesh 微服务体系已经运行成功!而从编程体验上看,你似乎已经快感觉不出微服务的存在了!反正稀里糊涂的服务就调通了,服务发现怎么做到的?负载均衡怎么做到的?这些问题在不需要你关心的同时,可能也引起了你的疑惑!接下来我们通过调用日志简单感知下调用链路所经过的逻辑!

4. 链路调用日志原理分析

通过 Postman 调用返回结果后,我们分别看下链路所经过的服务日志!先看看 istio-ingressgateway 的容器日志,具体如下:

  1. $ kubectl logs istio-ingressgateway-74ccb8977c-gnhbb -n istio-system
  2. ...
  3. 2021-03-18T08:02:30.863243Z info xdsproxy Envoy ADS stream established
  4. 2021-03-18T08:02:30.865335Z info xdsproxy connecting to upstream XDS server: istiod.istio-system.svc:15012
  5. [2021-03-18T08:14:00.224Z] "POST /api/order/create HTTP/1.1" 200 - "-" 66 75 7551 6144 "10.32.0.1" "PostmanRuntime/7.26.8" "8e8bad1d-5dd9-954b-b218-15f8c9595a24" "10.211.55.12:32277" "10.32.0.10:9090" outbound|19090||micro-api.default.svc.cluster.local 10.32.0.8:57460 10.32.0.8:8080 10.32.0.1:33229 - -
  6. [2021-03-18T08:14:32.465Z] "POST /api/order/create HTTP/1.1" 200 - "-" 66 75 3608 3599 "10.32.0.1" "PostmanRuntime/7.26.8" "ccf56049-88e8-9170-a1f5-93affbf6e098" "10.211.55.12:32277" "10.32.0.10:9090" outbound|19090||micro-api.default.svc.cluster.local 10.32.0.8:57460 10.32.0.8:8080 10.32.0.1:33229 - -
  7. [2021-03-18T08:16:37.242Z] "POST /api/order/create HTTP/1.1" 200 - "-" 66 75 68 67 "10.32.0.1" "PostmanRuntime/7.26.8" "98ecbd52-91a0-97c6-9ce6-d8f6094560e0" "10.211.55.12:32277" "10.32.0.10:9090" outbound|19090||micro-api.default.svc.cluster.local 10.32.0.8:57460 10.32.0.8:8080 10.32.0.1:33229 - -

如上所示,从 istio-ingressgateway 的网关日志中,可以看到 /api/order/create 接口的访问情况,确实是被转发到了 micro-api 所在的 Pod IP,符合前面配置的网关路由规则。

接下来我们查看 micro-api 的 istio-proxy 代理的日志:

  1. $ kubectl logs micro-api-6455654996-57t4z istio-proxy
  2. ...
  3. [2021-03-18T08:41:10.750Z] "POST /order/create HTTP/1.1" 200 - "-" 49 75 19 18 "-" "PostmanRuntime/7.26.8" "886390ea-e881-9c45-b859-1e0fc4733680" "micro-order" "10.32.0.7:9091" outbound|80||micro-order.default.svc.cluster.local 10.32.0.10:54552 10.99.132.246:80 10.32.0.10:39452 - default
  4. [2021-03-18T08:41:10.695Z] "POST /api/order/create HTTP/1.1" 200 - "-" 66 75 104 103 "10.32.0.1" "PostmanRuntime/7.26.8" "886390ea-e881-9c45-b859-1e0fc4733680" "10.211.55.12:32277" "127.0.0.1:9090" inbound|9090|| 127.0.0.1:52782 10.32.0.10:9090 10.32.0.1:0 outbound_.19090_._.micro-api.default.svc.cluster.local default
  5. ...
  6. [2021-03-18T08:47:22.215Z] "POST /order/create HTTP/1.1" 200 - "-" 49 75 78 70 "-" "PostmanRuntime/7.26.8" "9bbd3a3c-86c4-943f-999a-bc9a1dc02c35" "micro-order" "10.32.0.9:9091" outbound|80||micro-order.default.svc.cluster.local 10.32.0.10:54326 10.99.132.246:80 10.32.0.10:44338 - default
  7. [2021-03-18T08:47:22.173Z] "POST /api/order/create HTTP/1.1" 200 - "-" 66 75 134 129 "10.32.0.1" "PostmanRuntime/7.26.8" "9bbd3a3c-86c4-943f-999a-bc9a1dc02c35" "10.211.55.12:32277" "127.0.0.1:9090" inbound|9090|| 127.0.0.1:57672 10.32.0.10:9090 10.32.0.1:0 outbound_.19090_._.micro-api.default.svc.cluster.local default

这里我们访问了两次接口情况,可以看到 micro-api 的 Sidecar 代理以负载均衡的方式,分别调用了 micro-order 服务的两个不同实例(打下划线 IP)!

而访问 micro-order 的 istio-proxy 代理日志:

  1. # kubectl logs micro-order-v1-84ddc57444-dng2k istio-proxy
  2. ...
  3. 2021-03-18T08:33:06.146178Z info xdsproxy Envoy ADS stream established
  4. 2021-03-18T08:33:06.146458Z info xdsproxy connecting to upstream XDS server: istiod.istio-system.svc:15012
  5. [2021-03-18T08:34:59.055Z] "POST /order/create HTTP/1.1" 200 - "-" 49 75 8621 6923 "-" "PostmanRuntime/7.26.8" "b1685670-9e54-9970-a915-5c5dd18debc8" "micro-order" "127.0.0.1:9091" inbound|9091|| 127.0.0.1:36420 10.32.0.7:9091 10.32.0.10:54552 outbound_.80_._.micro-order.default.svc.cluster.local default
  6. [2021-03-18T08:41:10.751Z] "POST /order/create HTTP/1.1" 200 - "-" 49 75 17 16 "-" "PostmanRuntime/7.26.8" "886390ea-e881-9c45-b859-1e0fc4733680" "micro-order" "127.0.0.1:9091" inbound|9091|| 127.0.0.1:41398 10.32.0.7:9091 10.32.0.10:54552 outbound_.80_._.micro-order.default.svc.cluster.local default
  7. ....

可以看到,请求通过 micro-order 的 istio-proxy 代理被转到了具体的 micro-order 实例!

通过上面日志的分析,虽然很细节的原理可能还是有疑问,但至少可以得到一个结论,那就是在 Istio 的 Service Mesh 微服务架构中,服务的转发、路由逻辑的确都是由 Sidecar 代理来干的,而且从日志中可以看到 Envoy 代理时刻都在保持着同控制面服务 istiod 的连接,并随时通过 xDS 协议更新着服务治理规则!

来源: https://mp.weixin.qq.com/s/L1LoiI9NZqwZWsuCzEJN1A https://github.com/manongwudi/istio-micro-service-demo