乐趣区

关于阿里云:全景剖析阿里云容器网络数据链路六ASM-Istio

本系列文章由余凯执笔创作,联结作者:阿里云容器服务 谢石 对本文亦有奉献

近几年,企业基础设施云原生化的趋势越来越显著,从最开始的 IaaS 化到当初的微服务化,客户的颗粒度精细化和可观测性的需要也更加强烈。容器网络为了满足客户更高性能和更高密度的需要,也始终在高速的倒退和演进中,这必然给客户对云原生网络的可观测性带来了极高的门槛和挑战。为了进步云原生网络的可观测性,同时便于客户和前后线同学减少对业务链路的可读性,ACK 产研和 AES 联结共建,合作开发了 ack net-exporter 和云原生网络数据面可观测性系列,帮忙客户和前后线同学理解云原生网络架构体系,简化对云原生网络的可观测性的门槛,优化客户运维和售后同学解决疑难问题的体验,进步云原生网络的链路的稳定性。

△ 服务网格示例

△ Istio 数据面示意图

Kubernetes 的横空呈现突破了底层服务器、底层网络等计算资源的界线,给业务的灵便部署、疾速复原、弹性伸缩、资源效率最大化带来了有限可能。然而业务场景的‘贪心’是有限的,随着微服务趋势大肆倒退,业务上对于同一个 service,不同版本和流量管制有着更精细化的颗粒度的需要,最好能实现 Pod 维度的流量管制,可观测性等等。这些在 Kubernetes 上是无奈实现的:

  1. 从流量角度,K8s 最小的管制维度是 service,其余比方金丝雀等公布,借助各种 ingress controller 或者其余组件实现,并且这些也无奈实现 Pod 之间的流量和连贯状态的可观测性。
  2. K8s 给服务微型化,小型化发明了条件,如果前后端服务存在调用关怀,他们如果应用共享通信库,则会在开发阶段就要求所有微服务应用雷同的逻辑语言和堆栈,这从某种程度上又大大限度微服务的独立化,无奈实现齐全的‘不闻不问’
  3. 将原来集成在同一个 ECS 上的服务拆分成不同的模块,这些模块之间调用波及跨 ECS 等,那么必然须要在代码开发阶段须要思考超时,重试,连贯失败等逻辑机制,而这些与微服务最外围的服务利用其实没有太大关系,然而开发工作往往消耗大量的经验在逻辑设计上。

那么,有没有方法实现上述和微服务的业务齐全隔离呢?Istio 的呈现给这个带来了绝对完满的解决方案,让利用这和开发者更加关注业务自身的开发迭代。Istio 利用了 K8s 的 Pod 概念,会依据使用者的配置,在每个被注入的 Pod 部署时,主动注入 istio-proxy 容器和 initial 容器。initial 容器的目标是通过批改 Pod 独自网络命名空间的 iptables 规定,让须要代理的流量进入到 istio-proxy 监听的端口,istio-proxy 监听出入 两个端口,依据网格配置,来实现对出入流量的代理实现和干涉。而被同一个 istio 注入的载体,都被视为同一个服务网格之内,他们之间的调用曾经脱离了 service 的层面,会命中相干的 istio cluster 配置的 endpoint,这样咱们就能够实现 Pod 维度的流量治理、观测性、安全性等配置。

本文是 [全景分析容器网络数据链路] 第六局部,次要介绍 ASM Istio 模式下,数据面链路的转发链路,一是通过理解不同场景下的数据面转发链路,从而探知客户在不同的场景下拜访后果体现的起因,帮忙客户进一步优化业务架构;另一方面,通过深刻理解转发链路,在遇到容器网络抖动时候,客户运维以及阿里云同学能够晓得在哪些链路点进行部署观测手动,从而进一步定界问题方向和起因。

系列一:全景分析阿里云容器网络数据链路(一):Flannel

系列二:全景分析阿里云容器网络数据链路(二):Terway ENI

系列三:全景分析阿里云容器网络数据链路(三):Terway ENIIP

系列四:全景分析阿里云容器网络数据链路(四):Terway IPVLAN+EBPF

系列五:全景分析阿里云容器网络数据链路(五):Terway ENI-Trunking

ASM Istio 流量代理

1.1 Pod 注入

ASM 默认提供了一个 Webhook 控制器,能够将 Sidecar 代理主动增加到可用的 Pod 中。通过上面的命令能够看到 ASM 注入的集群有个 istio-sidecar-injector-1-15- 3 的 mutatingwebhookconfiguration,查看 webhook 内容,能够看到其中一条就是有 istio-inject:enabled 标签的 namespace 里的 pod 创立时候会主动注入。

除了命名空间维度,还有 Pod 维度,其余注解形式等多种维度实现 K8s 集群是否被退出到 Istio 服务网格中。为了充分利用服务网格的所有个性,服务网格中 ACK 集群的利用 Pod 必须蕴含一个 Sidecar 代理。除了手动注入形式外,通常倡议启用主动注入的形式来简化部署,ASM 曾经实现了注入配置的可视化操作,具体请见 多种形式灵便开启主动注入 [ 1]

1.2 Pod 流量转发

通过 describe 被注入的 Pod,能够发现 Pod 中除了设置好的业务 container,还多出两个容器:istio-proxy 和 init  container:istio-init。这两个容器的镜像是一样的,只是运行的命令的不一样,这样的益处是只须要拉取一份镜像,节俭了拉取镜像的工夫。

Init Container

Init container 利用的是 K8s 的个性,一种具备特权的非凡容器,在 Pod 内的利用容器启动之前运行。Init 容器能够包含一些利用镜像中不存在的实用工具和装置脚本。每个 Pod 中能够蕴含多个容器和多个 Init 容器。他与一般容器很像,然而有本人独特点:

  1. 多个 init 容器是串行运行的。也就是说多个 init 容器会依序运行,等上一个 init 容器运行结束完结后,才会开始运行下一个容器。
  2. 只有等到所有的 init 容器全副运行完结退出后,业务容器才开始启动,在这之前,pod 不会处于 ready。
  3. 如果 Pod 的 Init 容器失败,kubelet 依据 pod 设置的 restartPolicy 进行相应的 action。

既然当初理解了 Init container 的作用,那咱们来看一下 istio-init 在启动的过程中做了哪些事件,能够通过上面的命令:

kubectl  logs -n istio-inject productpage-v1-797d845774-dndmk -c istio-init

能够看到 istio-init 在启动过程中进行了一连串的 iptables 规定的生成和配置,比方出方向转发到 15001 端口;入方向转发到 15006 端口;拜访 15008 端口,间接 return 不进行流量劫持等等。那有什么方法能够自定义配置么?查看 pod 的信息能够看到相干配置的启动参数,也就通过相干规定实现了出入流量重定向到设置的端口。

 -p: 所有出方向的流量被 iptables 重定向到 15001 端口 

-z: 所有入方向的流量被 iptables 重定向到 15006 端口 

-u: 用于排除用户 ID 为 1337,能够视为 envoy 利用自身应用 UID 1337 

-m: 流量重定向模式,“REDIRECT”或“TPROXY”

-i:  重定向出方向的地址范畴,“*”示意重定向所有出站流量。

-x: 指将从重定向出方向中排除的 IP 地址范畴

-b: 重定向入站端口列表

-d: 重定向入站端口中排除的端口列表

咱们从 Pod 的视角去察看,将 Pod 视为一个整体,外面有 istio-proxy 容器和业务容器 APP container

入方向流量转发

依据上文的 iptables 规定,咱们能够演绎出被入方向代理转发的端口,比方 80 等,在 Pod 的网络命名空间 netfilter 模块通过流程是 Client -> RREROUTING -> ISTIO_INBOUND -> ISTIO_IN_REDIRECT -> INPUT -> Envoy 15006(Inbound)-> OUTPUT -> ISTIO_OUTPUT  -> POSTROUTING -> APP。这样就实现了入方向流量先被转发到 sidecar 容器后,在转发到业务容器的监听端口。其中在步骤 5 和 6 之间,流量会依照设置好的 istio 规定进行解决。

出方向流量转发

依据上文的 iptables 规定,咱们能够演绎出被入方向代理转发的端口,比方 80 等,在 Pod 的网络命名空间 netfilter 模块通过流程是 APP  > OUTPUT -> ISTIO_OUTPUT -> ISTIO_REDIRECT -> Envoy 15001(Outbound)-> OUTPUT -> ISTIO_OUTPUT  -> POSTROUTING -> DST。这样就实现了出方向流量先被转发到 sidecar 容器后,在转发到目标监听端口。其中在步骤 d 和 e 之间,流量会依照设置好的 istio 规定进行解决。

入方向流量免转发

对于入方向的某些端口或者自定义端口,咱们不须要它通过 sidecar 容器,iptables 规定会设置将符合条件的入方向流量防止转发到 15006 端口,间接转发到业务容器监听端口 RREROUTING -> ISTIO_INBOUND  -> INPUT -> APP。

出方向流量免转发

对于出方向的某些端口或者自定义端口,咱们不须要它通过 sidecar 容器,iptables 规定会设置将符合条件的入方向流量防止转发到 15001 端口,间接来到 Pod 的网络命名空间 APP -> OUTPUT -> ISTIO_OUTPUT -> POSTROUTING  -> DST。

Istio-proxy

能够看到 15001 和 15006 被 envoy 利用所监听,而 envoy 利用就是 istio-proxy 容器程序。Init 容器启动的时候依据所设置的参数中指定将出入站流量重定向到 Envoy 的模式为“REDIRECT”或者“TPROXY”。应用 REDIRECT 形式,一旦 Pod 注入了 Sidecar 代理之后,所有入站流量都是从 Envoy 重定向,Envoy 将流量发送到绑定了本地地址(127.0.0.1)的应用程序,所以利用看不到真正的原始 IP。在服务网格环境下如何放弃服务拜访时的客户端源 IP 呢?能够应用 TPROXY 模式,目前 ASM 曾经反对了 TPROXY 模式,具体详情请见:

https://help.aliyun.com/document_detail/464794.html

在 TPROXY 模式下,Pod 的网络命名空间的 iptables 会有 mangle 配置。

ADS 聚合服务发现

咱们曾经晓得了服务网格会在每个注入的 Pod 内注入两个容器:istio-init 和 istio-proxy。一旦在网格管制面进行相干配置的批改,会通过 pilot 下发到每个 istio-proxy 容器去失效。而 istio 是通过 xDS 服务接口去实现相干配置的动静下发的,其中 xDS 蕴含了 LDS(Listener Discover Service)、CDS(Cluster Discover Service)、EDS(Endpoint Discovery Service)和 RDS(Route Discover Service)。个别状况下,在更新配置过程中应该先更新 Cluster-> 之后 CLuster 的 Endpoint 开始更新 -> 开始更新 Cluster 和 Endpoint 绝对应的 Listener -> Route 开始更新新配置的 Listener 信息 -> 最初删除不在应用 Cluster 和 Endpoint 以保障更新过程中流量无损。然而这些 xDS 接口是互相独立,所以在配置下发的时候,存在某些依赖关系的 DS 因配置失效前后关系造成了局部流量被抛弃,这在某些生产环境中是无奈承受的。

为了保证数据面配置的一致性,服务网格利用 gRPC 流来进行 ADS 聚合发现服务,通过一个 gRPC 流来保障各个 xDS 接口的调用程序,防止各个接口独立性造成数据配置的不匹配。详细信息能够参考:
https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_prot…

envoy-rev.json

能够看到 istio-proxy 启动了 pilot-agent 程序,pilot-agent 作为父过程启动了子过程 /usr/local/bin/envoy。其中 /etc/istio/proxy/envoy-rev.json 是 envoy 初始化的配置文件。

Node
蕴含了 istio-proxy 所在节点,以后 Pod,istio 版本、ACK 集群 ID、ASM 版本、必要端口等相干信息。

admin

istio-proxy 相干日志,治理端口等信息

dynamic_resources

ADS 相干配置信息,比方 api 协定,版本,超时工夫等

static_resources

蕴含了 prometheus_stats、agent、sds-grpc、xds-grpc 和 zipkin 五个 cluster 和一个在 15090 上监听的 listener,xds-grpc cluster 对应后面 dynamic_resources 中 ADS 配置。prometheus_stats cluster 和 15090 用于对外提供 prometheus 采集端口。zipkin cluster 是内部的 zipkin 服务器调用地址。

tracing

分布式链路跟踪,这里的 collector_cluster 是后面 static_resources 外面定义的 zipkin cluster。

拜访日志剖析

通过前文,咱们曾经晓得两个相互被注入的 pod 拜访,流量会被各自的 istio-proxy 所劫持并解决,那么只有剖析客户端和服务端的 istio-proxy 日志并进行加工,就能够对流量进行可观测性解读。咱们在这里还是以官网例子来举例。拜访 http://<gatewayIP>/productpage,productpage 利用会主动调用 details 服务,reviews 服务。咱们以 productpage 和 details 之间链路来进行举例剖析。

productpage-v1-797d845774-dndmk  IP 是 10.0.1.130,details 利用的 svc 的名称是 details,svc 地址是 192.168.1.125,svc 端口是 9080

申请发送方 productpage-v1-797d845774-dndmk 的 istio-proxy 日志

{"upstream_host":"10.0.1.127:9080","downstream_remote_address":"10.0.1.130:49586","downstream_local_address":"192.168.1.125:9080","duration":6,"upstream_cluster":"outbound|9080||details.istio-inject.svc.cluster.local","path":"/details/0","protocol":"HTTP/1.1","upstream_local_address":"10.0.1.130:50026","method":"GET","user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36","route_name":"default","request_id":"834147c2-435f-94a7-af11-8491df9ab4f8","start_time":"2023-01-31T14:23:20.603Z","upstream_transport_failure_reason":null,"upstream_service_time":"5","response_flags":"-","bytes_received":0,"authority_for":"details:9080","authority":"details:9080","requested_server_name":null,"istio_policy_status":null,"trace_id":"9712c9f3da936a8c927f227bfe536c16","response_code":200,"x_forwarded_for":null,"bytes_sent":178}

申请接受方 details-v1-6758dd9d8d-dtbdc 的 istio-proxy 日志

{"x_forwarded_for":null,"start_time":"2023-01-31T14:23:20.608Z","method":"GET","response_flags":"-","route_name":"default","istio_policy_status":null,"requested_server_name":"outbound_.9080_._.details.istio-inject.svc.cluster.local","bytes_received":0,"request_id":"834147c2-435f-94a7-af11-8491df9ab4f8","response_code":200,"upstream_host":"10.0.1.127:9080","trace_id":"9712c9f3da936a8c927f227bfe536c16","downstream_remote_address":"10.0.1.130:50026","protocol":"HTTP/1.1","bytes_sent":178,"upstream_transport_failure_reason":null,"downstream_local_address":"10.0.1.127:9080","upstream_local_address":"127.0.0.6:46225","authority":"details:9080","authority_for":"details:9080","upstream_service_time":"0","upstream_cluster":"inbound|9080||","duration":1,"path":"/details/0","user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"}

日志内容解读

“upstream_host”:”10.0.1.127:9080″,————对于 outbound,此是上游某个 Endpoint 的地址和端口

downstream_remote_address”:”10.0.1.130:49586″,” ————对于 outbound,此为本 pod-ip:随机端口 1

downstream_local_address”:”192.168.1.125:9080″,”————对于 outbound,此为目标 svc-ip:svc-port

duration”:6,”  ———— 整个申请工夫,单位 ms

upstream_cluster”:”outbound|9080||details.istio-inject.svc.cluster.local”,———— cluster 信息

“path”:”/details/0″,

“protocol”:”HTTP/1.1″,

“upstream_local_address”:”10.0.1.130:50026″, ————对于 outbound,此为本 pod-ip:随机端口 2

“method”:”GET”,

“user_agent”:”Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36″,

“route_name”:”default”,———— 路由名称

“request_id”:”834147c2-435f-94a7-af11-8491df9ab4f8″,

“start_time”:”2023-01-31T14:23:20.603Z”,

“upstream_transport_failure_reason”:null,

“upstream_service_time”:”5″,———— 上游返回申请工夫,单位 ms

“response_flags”:”-“,———— 返回标记,对于连贯或返回的详细信息

“bytes_received”:0,

“authority_for”:”details:9080″,

“authority”:”details:9080″,

“requested_server_name”:null,

“istio_policy_status”:null,

“trace_id”:”9712c9f3da936a8c927f227bfe536c16″,————  此 ID 为惟一值,能够在上游 istio-proxy 对应

“response_code”:200,———— 返回状态码

“x_forwarded_for”:null

,”bytes_sent”:178

日志解读能够具体见官网连贯:
https://www.envoyproxy.io/docs/envoy/latest/configuration/obs…

UPSTREAM_HOST

上游主机的 host,示意从 envoy 收回的申请的目标端

通常来说,对于 outbound cluster,此值是「上游 pod-ip : pod-port」,而对于 inbound cluster,此值是「本 pod-ip : pod-port」

UPSTREAM_LOCAL_ADDRESS

上游连贯中,以后 envoy 的本地地址

通常来说,对于 outbound cluster,此值是「本 pod-ip : 随机端口 2」,而对于 inbound cluster,此值是「127.0.0.6: 随机端口 3」,此处的 127.0.0.6 对应了【1.2 Pod 流量转发 -Init Container】中的 iptables 会将来自 127.0.0.6 的流量免于 istio 代理,因为这个流量是从 sidecar 自身收回的

DONSTREAM_LOCAL_ADDRESS

上游连贯中,以后 envoy 的本地地址

通常来说,对于 outbound cluster,此值是「目标 service-ip : service-port」,而对于 inbound cluster,此值是「以后 pod-ip : pod-port,此处和上游的 upstream_host 应该绝对应。

DOWNSTREAM_REMOTE_ADDRESS

通常来说,对于 outbound cluster,此值是「以后 pod-ip : 随机端口」,而对于 inbound cluster,此值是「上游 pod-ip : 随机端口 2」,此处和上游的 upstream_local_address 绝对应

1.3 Envoy 配置简读(数据链路)

背景

还是用官网的示例,以 productpage 拜访 reviews 服务来举例。

通过 Kubernetes 集群资源,咱们可一看到 reviews 有三个版本别离为 v1,v2,v3,pod 数量各一个。SVC reviews 是 ClusterIP 模式,svc 端口是 9080,targetport 是 pod 的 9080 端口,v1,v2,v3 都被加到了 reviews SVC 的 endpointslice。在未被 istio 注入的状况下,集群内 productpage pod 拜访 reviews.istio-inject 服务,会被 netfilter 以 round-robin 的形式均匀转发到 v1,v2,v3 三个 pod 上,每个 pod 应该接受 1 / 3 的流量。

在传统的 K8s 集群中,是无奈通过 K8s 的 resource 管制不同版本的流量调配的。然而理论的生产环境,咱们是有这方面的需要的。比方 v1 版本是线上业务版本,承载了次要业务流量,v2 版本是开发结束预上线版本,实质上是不心愿影响线上流量的,可能须要引流线上流量的 5% 到预发版本进行一段时间的察看,来判断新版本是否有问题,之后再进一步扩充引流比例至 100% 之后,v1 版本才下线,从而实现业务角度的平滑迁徙。或者比方 v3 是测试版本,咱们心愿察看流量在网络稳定超时状况下,业务的自我容灾和复原状况的行为是否合乎预期,以前这种需要须要通过在业务代码中写好熔断代码,不同熔断环境都须要从新发版,那么像这种流量管制在 ASM Istio 就能够很容易的实现。

上面就是一个 ASM Istio 中的 vs 和 dr 的配置。

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  creationTimestamp: '2023-01-30T06:25:21Z'
  generation: 1
  name: reviews
  namespace: istio-inject
  resourceVersion: '651722274'
  uid: 63f715c9-b253-4fbb-8351-5313371df14e
spec:
  hosts:
    - reviews.istio-inject.svc.cluster.local
  http:
    - name: route
      route:
        - destination:
            host: reviews
            subset: v1
          weight: 10
        - destination:
            host: reviews
            subset: v2
          weight: 40
        - destination:
            host: reviews
            subset: v3
          weight: 50

其中在 reviews vs 的定义了集群内拜访 reviews.istio-inject.svc.cluster.local 是的 http 协定的规定。其中指明了 v1 版本权重 10%,v2 版本权重 40%,v3 版本权重 50%

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  creationTimestamp: '2023-01-30T06:28:46Z'
  generation: 2
  name: reviews
  namespace: istio-inject
  resourceVersion: '654863578'
  uid: fdbdfcea-1fcd-453e-96fb-ce41c91ded9b
spec:
  host: reviews
  subsets:
    - labels:
        version: v1
      name: v1
    - labels:
        version: v2
      name: v2
    - labels:
        version: v3
      name: v3
  trafficPolicy:
    connectionPool:
      http:
        http2MaxRequests: 1000
        maxRequestsPerConnection: 10
      tcp:
        maxConnections: 100
    outlierDetection:
      baseEjectionTime: 15m
      consecutive5xxErrors: 7
      interval: 5m

reviews dr 的定义了集群内 reviews 的几个版本,并定义了相干流量策略。其中 http2MaxRequests 表明 http 最大的申请数。maxRequestsPerConnection 表明每个连贯最大的申请数。tcp 最大连接数是 100。在熔断配置中,每隔 5min 中检测一次,间断 7 次 5xx,把后端移除 endpoint 15min。

通过前文咱们晓得 pilot 通过 xDS 接口将服务网格的配置下发到每个被注入的 pod 中的 istio-proxy 中。那么对于每个 pod 中的 istio-proxy,咱们是否有方法去查看相干的加载的配置信息呢?istio-proxy 通过 15000 端口对外裸露治理端口,咱们能够通过如图所示的命令获取到相干的配置信息。

其中能够通过 curl 127.0.0.1:15000/config_dump 能够获取到残缺的配置信息,因为此配置信息超过 1 万多行,咱们就不在这里做全副的展现,感兴趣的同学能够自行钻研下,下文会针对此 config_dump 信息中的 cluster,Listener,endpoint,route 等要害信息做个相干展现和简要阐明,同时也和前文的 xDS 做个响应。

kubectl exec -n istio-inject productpage-v1-797d845774-dndmk -c istio-proxy  -it -- curl 127.0.0.1:15000/config_dump

Bootstrap

Envoy 的初始化配置,与前文中的 envoy-rev0.json 是统一的。其中 drainDuration —— 热重启期间 Envoy 敞开连贯的工夫(以秒为单位),默认是 45s。

parentShutdownDuration ——  热重启期间,在齐全敞开父过程之前,等到的最大工夫,默认 60s。此数值应该大于 drainDuration。

terminationDrainDuration —— 默认 5s。proxy 在敞开之前,容许连贯敞开工夫。通过前文,咱们晓得 pod 流量先过 istio 再被转发到业务容器。当利用发版时候,如果保障现有连贯优雅敞开,保障 istio-proxy 容器在业务容器齐全敞开现有连贯后,再退出是发版更新时候须要思考的问题,此值是实现业务平滑更新须要思考的。

static_resources

config_dump 中动态资源,是来自 envoy-rev0.json,外面蕴含了 prometheus_stats、agent、sds-grpc、xds-grpc 和 zipkin 等配置

dynamic_resources

动静资源,是通过 xDS 接口下发到每个 istio-proxy 容器失效的 ASM Istio 的配置。也是上述 reviews dr,vs 配置后通过 ASM 管控侧下发的。咱们接下来关注动静资源配置

Listeners

Envoy 采纳的 listener 来承受达到 Envoy 的流量申请。Listener 和 IP Sock、Unix Domain Socket 绑定,也能够不绑定到具体的端口,而是接管从其余 listener 转发来的流量。ASM Istio 就是利用了 Envoy listener 的这一个性来实现转发给不同的服务申请交给不同的 Listeners 来解决。

还是以 productpage 拜访 reviews 来举例,productpage 拜访的是 reviews 的 9080 端口,依据上文咱们晓得 productpage container 拜访内部的 9080 端口会被先转发到 15001 端口,所以咱们先看下 15001 的端口 listeners。

VirtualOutbound

Envoy 在 15001 端口创立了 Listeners,所有被 iptables 转发的对外申请都会被转到 envoy 的 15001 端口。能够从配置看到,envoy 承受到了流量后,并不会做相干的业务动作,而是依据 “use_original_dst”: true, 这个配置,根据申请的目标端口转发到相应的 listeners 进行解决。

那么必定有个疑难了,如果申请的目标端口并没有配置相干的 listeners 设置,流量该如何进行解决呢?这个取决于 outboundTrafficPolicy 的配置,详情请见:
https://istio.io/latest/docs/reference/config/istio.mesh.v1al…

Outbound

outbound 监听命名是 0.0.0.0_9080,表明发向任何 IP 地址的 9080 端口都被此监听涵盖。”bind_to_port”: false 此值表明监听没有绑定到 tcp 端口,流量是有 virtualOutbound 转发而来的。那么首先咱们须要区别这个监听是为了入方向还是出方向呢?对于入方向,流量会通过 15006 端口的 virtualInbound 的 listeners,是不会进入 0.0.0.0_9080 的 listeners。

从配置上能够看到 filter 中并没有非凡的志敏筛选条件,阐明承受任何流量,其中 config_source 为 ads,表明这个是来自动静发现。

依据前文能够可看到 revirews,ratings,details 几个 service 都是 9080 端口,这些利用都被同一个服务网格注入,那么 productpage 拜访的目标地址的 9080,Envoy 如何刚晓得是哪个 service?是如何判断如果目标端口未 9080 的地址不是网格内,该如何解决呢?通过上图 ”route_config_name”: “9080” 能够看到存在一个‘9080’的路由规定,在这个路由规定中规定不同的申请目的地的不同的处理结果,下一大节咱们将探讨。

Route

前文咱们曾经晓得 productpage 利用拜访 reviews 的 9080 端口会被 listeners outbound 0.0.0.0_9080 路由规定到 9080 的路由。以下是‘9080’路由的全副信息。咱们能够看到一共有 5 个 virtual_hosts,别离是 allow_any、details、productpage、ratings、和 reviews。其中前面 4 个对应 4 个不同 outbound 的 cluster,allow_any 对应的是 PassthroughCluster,当出方向申请找到相应的 Cluster 规定时候,会采纳默认间接通过。

可能有小伙伴很奇怪 productpage 为什么不间接调用 ratings 服务,为什么 productpage envoy 配置会蕴含 ratings 的信息。这是因为 ASM  Istio 管制面是无奈感知到数据面各个服务之间是如何调用的,因而会将所有的配置信息都下发到被注入的 envoy 外面,这样保障了网格配置的一致性,然而随着网格服务配置的增多,每个 envoy 承受和本 envoy 不相干的配置信息就会变多,这样对 envoy 资源应用会有肯定影响,如果小伙伴有很好的 envoy 开发能力,并且对业务之间调用十分相熟,想去除掉本 pod 中 envoy 无关的规定,能够通过 sidecar 规定自定义配置对 egress 和 ingress 进行调整,详情请见:

https://istio.io/latest/docs/reference/config/networking/side…


    {
     "version_info": "2023-01-30T06:25:21Z/19",
     "route_config": {
      "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
      "name": "9080",
      "virtual_hosts": [
       {
        "name": "allow_any",
        "domains": ["*"],
        "routes": [
         {
          "match": {"prefix": "/"},
          "route": {
           "cluster": "PassthroughCluster",
           "timeout": "0s",
           "max_grpc_timeout": "0s"
          },
          "name": "allow_any"
         }
        ],
        "include_request_attempt_count": true
       },
       {
        "name": "details.istio-inject.svc.cluster.local:9080",
        "domains": [
         "details.istio-inject.svc.cluster.local",
         "details.istio-inject.svc.cluster.local:9080",
         "details",
         "details:9080",
         "details.istio-inject.svc",
         "details.istio-inject.svc:9080",
         "details.istio-inject",
         "details.istio-inject:9080",
         "192.168.1.125",
         "192.168.1.125:9080"
        ],
        "routes": [
         {
          "match": {"prefix": "/"},
          "route": {
           "cluster": "outbound|9080||details.istio-inject.svc.cluster.local",
           "timeout": "0s",
           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"}
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [503]
           },
           "max_stream_duration": {
            "max_stream_duration": "0s",
            "grpc_timeout_header_max": "0s"
           }
          },
          "decorator": {"operation": "details.istio-inject.svc.cluster.local:9080/*"},
          "name": "default"
         }
        ],
        "include_request_attempt_count": true
       },
       {
        "name": "istio-ingressgateway.istio-system.svc.cluster.local:9080",
        "domains": [
         "istio-ingressgateway.istio-system.svc.cluster.local",
         "istio-ingressgateway.istio-system.svc.cluster.local:9080",
         "istio-ingressgateway.istio-system",
         "istio-ingressgateway.istio-system:9080",
         "istio-ingressgateway.istio-system.svc",
         "istio-ingressgateway.istio-system.svc:9080",
         "192.168.1.250",
         "192.168.1.250:9080"
        ],
        "routes": [
         {
          "match": {"prefix": "/"},
          "route": {
           "cluster": "outbound|9080||istio-ingressgateway.istio-system.svc.cluster.local",
           "timeout": "0s",
           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"}
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [503]
           },
           "max_stream_duration": {
            "max_stream_duration": "0s",
            "grpc_timeout_header_max": "0s"
           }
          },
          "decorator": {
           "operation": "istio-ingressgateway.istio-system.svc.cluster.local:9080/*"
          "name": "default"
         }
        ],
        "include_request_attempt_count": true
       },
       },     
       {
        "name": "productpage.istio-inject.svc.cluster.local:9080",
        "domains": [
         "productpage.istio-inject.svc.cluster.local",
         "productpage.istio-inject.svc.cluster.local:9080",
         "productpage",
         "productpage:9080",
         "productpage.istio-inject.svc",
         "productpage.istio-inject.svc:9080",
         "productpage.istio-inject",
         "productpage.istio-inject:9080",
         "192.168.6.226",
         "192.168.6.226:9080"
        ],
        "routes": [
         {
          "match": {"prefix": "/"},
          "route": {
           "cluster": "outbound|9080||productpage.istio-inject.svc.cluster.local",
           "timeout": "0s",
           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"}
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [503]
           },
           "max_stream_duration": {
            "max_stream_duration": "0s",
            "grpc_timeout_header_max": "0s"
           }
          },
          "decorator": {"operation": "productpage.istio-inject.svc.cluster.local:9080/*"},
          "name": "default"
         }
        ],
        "include_request_attempt_count": true
       },
       {
        "name": "ratings.istio-inject.svc.cluster.local:9080",
        "domains": [
         "ratings.istio-inject.svc.cluster.local",
         "ratings.istio-inject.svc.cluster.local:9080",
         "ratings",
         "ratings:9080",
         "ratings.istio-inject.svc",
         "ratings.istio-inject.svc:9080",
         "ratings.istio-inject",
         "ratings.istio-inject:9080",
         "192.168.1.172",
         "192.168.1.172:9080"
        ],
        "routes": [
         {
          "match": {"prefix": "/"},
          "route": {
           "cluster": "outbound|9080||ratings.istio-inject.svc.cluster.local",
           "timeout": "0s",
           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"}
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [503]
           },
           "max_stream_duration": {
            "max_stream_duration": "0s",
            "grpc_timeout_header_max": "0s"
           }
          },
          "decorator": {"operation": "ratings.istio-inject.svc.cluster.local:9080/*"},
          "name": "default"
         }
        ],
        "include_request_attempt_count": true
       },
       {
        "name": "reviews.istio-inject.svc.cluster.local:9080",
        "domains": [
         "reviews.istio-inject.svc.cluster.local",
         "reviews.istio-inject.svc.cluster.local:9080",
         "reviews",
         "reviews:9080",
         "reviews.istio-inject.svc",
         "reviews.istio-inject.svc:9080",
         "reviews.istio-inject",
         "reviews.istio-inject:9080",
         "192.168.4.113",
         "192.168.4.113:9080"
        ],
        "routes": [
         {
          "match": {"prefix": "/"},
          "route": {
           "weighted_clusters": {
            "clusters": [
             {
              "name": "outbound|9080|v1|reviews.istio-inject.svc.cluster.local",
              "weight": 10
             },
             {
              "name": "outbound|9080|v2|reviews.istio-inject.svc.cluster.local",
              "weight": 40
             },
             {
              "name": "outbound|9080|v3|reviews.istio-inject.svc.cluster.local",
              "weight": 50
             }
            ],
            "total_weight": 100
           },
           "timeout": "0s",
           "retry_policy": {
            "retry_on": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
            "num_retries": 2,
            "retry_host_predicate": [
             {
              "name": "envoy.retry_host_predicates.previous_hosts",
              "typed_config": {"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"}
             }
            ],
            "host_selection_retry_max_attempts": "5",
            "retriable_status_codes": [503]
           },
           "max_stream_duration": {
            "max_stream_duration": "0s",
            "grpc_timeout_header_max": "0s"
           }
          },
          "metadata": {
           "filter_metadata": {
            "istio": {"config": "/apis/networking.istio.io/v1alpha3/namespaces/istio-inject/virtual-service/reviews"}
           }
          },
          "decorator": {"operation": "reviews:9080/*"},
          "name": "route"
         }
        ],
        "include_request_attempt_count": true
       }
      ],
      "validate_clusters": false
     },
     "last_updated": "2023-01-30T06:25:21.804Z"
    },

咱们还是以 productpage 调用 reviews 来举例,Envoy 会依据 HTTP header 中 domains 来匹配 VirtualHost 中 domain,所以能够看到 domains 中列举了 reviews 的集群内的长短域名,和 svc 的地址。match“/”会路由到三个 cluster “outbound|9080|v1|reviews.istio-inject.svc.cluster.local”、”outbound|9080|v2|reviews.istio-inject.svc.cluster.local” 和 ”outbound|9080|v3|reviews.istio-inject.svc.cluster.local”,权重别离为 10,40,50,名称是‘route’,看到这里是不是有点相熟?对的,这和前文 [1.3 Envoy 配置简读 - 背景]中 reviews 的 VS 中的设置,故咱们在 vs 中的相干配置信息,最终 envoy 会转为 route 的配置加载后失效。

通过后面咱们还能够看到默认超时是 0s,默认重试次数是 2 次,重试条件是 ”connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes”。

Cluster

outbound cluster

依据上一下大节,productpage 拜访 reviews,会被 productpage 中的 istio-proxy 匹配到‘9080’路由 -> 根据 vs 配置的相干信息,进入三个 cluster(“outbound|9080|v1|reviews.istio-inject.svc.cluster.local”、”outbound|9080|v2|reviews.istio-inject.svc.cluster.local” 和 ”outbound|9080|v3|reviews.istio-inject.svc.cluster.local”)中一个。这里咱们就以 ”outbound|9080|v1|reviews.istio-inject.svc.cluster.local”cluster 为例

outbound|9080|v1|reviews.istio-inject.svc.cluster.local cluster 配置中能够看到,其类型为 EDS,即示意该 Cluster 的 endpoint 来自于动静发现,动静发现中 eds_config 则表明是由 ads 下发的。同样能够看到与前文 [1.3 Envoy 配置简读 - 背景] 中 reviews 的 dr 中的设置相熟的配置,比方 connectionpool,outlierDetection 等这些相干配置信息,最终 envoy 会转为 cluster 的配置加载后失效。

接下来咱们略微探讨下其余几个非凡的 cluster。

BlackHoleCluster

Envoy 对于找不到后端解决申请的会默认抛弃,所以会有对立的一个 blackholecluster,没有任何指明的后端 svc,任何无匹配后端的流量会被转发到这个 cluster。

PassthroughCluster

和 BlackHoleCluter 正好相同,发向 PassthroughCluster 的申请会被间接发送到器申请连贯中的原始目地的,type 类型是 ”type”: “ORIGINAL_DST”,表明其发到原始的目标地址:端口

Inbound Cluster

inbound Cluster 是为了 pod 上的入方向的申请,对于 reviews 来说,其对应的 Inbound Cluster 只有一个 inbound|9080。

Endpoint

从 endpoint 文件内容能够看到,reviews cluster“outbound|9080|v1|reviews.istio-inject.svc.cluster.local”只有 1 个 endpoint 地址,是 reviews-v1-74fb8fdbd8-qwsjq 的 pod ip 10.0.3.29。

至此,咱们大略梳理结束服务网格内两个服务之间拜访,istio-proxy 日志解读和配置对应关系。

Tips

前文的 config_dump 文件太长,解读起来其实比拟费劲,服务网格提供了 asmctl 工具能够很不便的去解读 listeners,route,cluster,endpoint 等,详细信息请见:
https://help.aliyun.com/document_detail/313187.html

[root@shycmain ~]# asmctl --asmconfig asmconfig pc  listeners  productpage-v1-797d845774-dndmk.istio-inject --port 9080
ADDRESS PORT MATCH                        DESTINATION
0.0.0.0 9080 Trans: raw_buffer; App: HTTP Route: 9080
0.0.0.0 9080 ALL                          PassthroughCluster

[root@shycmain ~]# asmctl --asmconfig asmconfig pc  routes  productpage-v1-797d845774-dndmk.istio-inject  --name 9080
NOTE: This output only contains routes loaded via RDS.
NAME     DOMAINS                               MATCH     VIRTUAL SERVICE
9080     details                               /*
9080     istio-ingressgateway.istio-system     /*
9080     productpage                           /*
9080     ratings                               /*
9080     reviews                               /*        reviews.istio-inject

[root@shycmain ~]# asmctl --asmconfig asmconfig pc  cluster  productpage-v1-797d845774-dndmk.istio-inject --fqdn reviews.istio-inject.svc.cluster.local
SERVICE FQDN                               PORT     SUBSET     DIRECTION     TYPE     DESTINATION RULE
reviews.istio-inject.svc.cluster.local     9080     -          outbound      EDS      reviews.istio-inject
reviews.istio-inject.svc.cluster.local     9080     v1         outbound      EDS      reviews.istio-inject
reviews.istio-inject.svc.cluster.local     9080     v2         outbound      EDS      reviews.istio-inject
reviews.istio-inject.svc.cluster.local     9080     v3         outbound      EDS      reviews.istio-inject

[root@shycmain ~]# asmctl --asmconfig asmconfig pc  endpoint  productpage-v1-797d845774-dndmk.istio-inject  --cluster "outbound|9080|v1|reviews.istio-inject.svc.cluster.local"
ENDPOINT           STATUS      OUTLIER CHECK     CLUSTER
10.0.3.29:9080     HEALTHY     OK                outbound|9080|v1|reviews.istio-inject.svc.cluster.local

企业拥抱容器化总结

本篇文章次要聚焦在 ASM Istio 服务网格模式下,被注入 pod 的数据面流量转发链路状况。istio 灵便注入实现了在 Pod 维度对流量的定制化配置和观测性,带来了业务链路角度实现的更多种的可能。在服务网格中配置 gateway,virtualservice,destinationrule 等规定在通过 xDS 下发到 envoy 后,会通过 listeners,route、cluster、endpoint 等一个环节一个环节最终体现在流量转发规定上。那么在使用 ASM 遇到不合乎预期状况时,这些环节都是须要思考的方向。ASM 除了流量治理,还有平安,鉴权,可观测方面的便捷使用,这些方面的配置,最终也会体现在相干的网格服务资源配置上,感兴趣的小伙伴能够参考ASM 官网文档 [ 2]

参考链接

https://istio.io/latest/docs/reference/config/networking/side…

https://www.envoyproxy.io/docs/envoy/latest/configuration/obs…

[1] 多种形式灵便开启主动注入

https://help.aliyun.com/document_detail/186136.html

[2] 官网文档

https://help.aliyun.com/product/147365.html

退出移动版