Istio服务网格细节剖析

Envoy简介

什么是Envoy

  • envoy 是作为微服务服务架构中以独立进程方式实现高级网络功能的,轻量级的7层服务代理程序,通常以sidecar的方式运行在应用程序的周边,也可以作为网络的边缘代理来运行。
  • envoy 的特性 进程外体系结构 ,L3/L4过滤器体系结构,HTTP L7过滤器体系结构, 一流的HTTP/2支持, HTTP/3支持(目前为alpha),HTTP L7路由,gRPC支持,服务发现和动态配置,健康检查,高级负载平衡,前端/边缘代理支持, 一流的可观察性

服务网格细节剖析

宏观分析

执行的操作:

  • 使用istioctl为pod注入了sidecar
  • 创建了virtualservice和destinationrule

如何最终影响到了pod的访问行为?

宏观角度

nginx的配置中,可以提供类似如下的配置片段实现按照权重的转发:

img

因为nginx是代理层,可以转发请求,istio也实现了流量转发的效果,肯定也有代理层,并且识别了前面创建的虚拟服务中定义的规则。

1
$ istioctl kube-inject -f front-tomcat-dpl-v1.yaml

可以看到注入后yaml中增加了很多内容:

pod被istio注入后,被纳入到服务网格中,每个pod都会添加一个名为istio-proxy的容器(常说的sidecar容器),istio-proxy容器中有两个进程,一个是piolot-agent,一个是envoy

img

1
2
$ kubectl exec -ti front-tomcat-v1-8687f9f845-rmlxt -c istio-proxy bash
# ps aux

目前已知:

  • 在istio网格内,每个Pod都会被注入一个envoy代理
  • envoy充当nginx的角色,做为proxy代理,负责接管pod的入口和出口流量

目前,还需要搞清楚几个问题:

  • istio-init初始化容器作用是什么?
  • istio-proxy如何接管业务服务的出入口流量?

认识envoy

Envoy 是为云原生应用设计的代理。

可以和nginx做类比: https://fuckcloudnative.io/posts/migrating-from-nginx-to-envoy/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$ docker run -d --name envoy -v `pwd`/envoy.yaml:/etc/envoy/envoy.yaml -p 10000:10000 envoyproxy/envoy-alpine:v1.15.2

$ curl localhost:10000
envoy.yaml
admin:
access_log_path: /tmp/admin_access.log
address:
socket_address: { address: 127.0.0.1, port_value: 9901 }

static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 10000 }
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: some_service }
http_filters:
- name: envoy.router
clusters:
- name: some_service
connect_timeout: 2s
type: STATIC
lb_policy: ROUND_ROBIN
hosts: [{ socket_address: { address: 10.103.211.217, port_value: 9999 }}]

脑补一下网络代理程序的流程,比如作为一个代理,首先要能获取请求流量,通常是采用监听端口的方式实现;其次拿到请求数据后需要对其做微处理,例如附加 Header 或校验某个 Header 字段的内容等,这里针对来源数据的层次不同,可以分为 L3/L4/L7,然后将请求转发出去;转发这里又可以衍生出如果后端是一个集群,需要从中挑选一台机器,如何挑选又涉及到负载均衡等。

  • listener : Envoy 的监听地址。Envoy 会暴露一个或多个 Listener 来监听客户端的请求。
  • filter : 过滤器。在 Envoy 中指的是一些“可插拔”和可组合的逻辑处理层,是 Envoy 核心逻辑处理单元。
  • route_config : 路由规则配置。即将请求路由到后端的哪个集群。
  • cluster : 服务提供方集群。Envoy 通过服务发现定位集群成员并获取服务,具体路由到哪个集群成员由负载均衡策略决定。

envoy动态配置(xDS)

Envoy的启动配置文件分为两种方式:静态配置和动态配置。

  • 静态配置是将所有信息都放在配置文件中,启动的时候直接加载。
  • 动态配置需要提供一个Envoy的服务端,用于动态生成Envoy需要的服务发现接口,这里叫XDS,通过发现服务来动态的调整配置信息,Istio就是实现了v2的API。

Envoy 接收到请求后,会先走 FilterChain,通过各种 L3/L4/L7 Filter 对请求进行微处理,然后再路由到指定的集群,并通过负载均衡获取一个目标地址,最后再转发出去。

其中每一个环节可以静态配置,也可以动态服务发现,也就是所谓的 xDS。这里的 x 是一个代词,类似云计算里的 XaaS 可以指代 IaaS、PaaS、SaaS 等。

所以,envoy的架构大致的样子如下:

Downstream

下游(downstream)主机连接到 Envoy,发送请求并或获得响应。

Upstream

上游(upstream)主机获取来自 Envoy 的链接请求和响应。

监听器

  • 除了过滤器链之外,还有一种过滤器叫监听器过滤器(Listener filters),它会在过滤器链之前执行,用于操纵连接的元数据。这样做的目的是,无需更改 Envoy 的核心代码就可以方便地集成更多功能。
  • 每个监听器都可以配置多个过滤器链(Filter Chains),监听器会根据 filter_chain_match 中的匹配条件将流量转交到对应的过滤器链,其中每一个过滤器链都由一个或多个网络过滤器Network filters)组成。这些过滤器用于执行不同的代理任务,如速率限制,TLS 客户端认证,HTTP 连接管理,MongoDB 嗅探,原始 TCP 代理等。

envoy在微服务治理中的工作环境

可以在服务旁运行,以平台无关的方式提供必要的特性,所有到服务的流量都通过 Envoy 代理,这里 Envoy 扮演的就是 Sidecar 的角色。

sidecar

针对于k8s的pod来讲:

在istio中,envoy的位置:

很明显,istio中,envoy进行流量治理,更多的使用的是XDS进行配置更新,而我们知道,XDS需要有服务端来提供接口,istiod中的pilot组件则提供了xDS服务端接口的实现 。

工作原理

目前为止,我们可以知道大致的工作流程:

  • 用户端,通过创建服务治理的规则(VirtualService、DestinationRule等资源类型),存储到ETCD中
  • istio控制平面中的Pilot服务监听上述规则,转换成envoy可读的规则配置,通过xDS接口同步给各envoy
  • envoy通过xDS获取最新的配置后,动态reload,进而改变流量转发的策略

思考两个问题:

  • istio中envoy的动态配置到底长什么样子?
  • 在istio的网格内,front-tomcat访问到bill-service,流量的流向是怎么样的?

针对问题1:

每个envoy进程启动的时候,会在127.0.0.1启动监听15000端口

1
2
3
4
$ kubectl exec -ti front-tomcat-v1-8687f9f845-rmlxt -c istio-proxy -- bash
# netstat -nltp
# curl localhost:15000/help
# curl localhost:15000/config_dump

针对问题2:

1
2
$ kubectl exec -ti front-tomcat-v1-8687f9f845-rmlxt -c front-tomcat -- bash
# curl bill-service:9999

按照之前的认知,

现在为什么流量分配由5:5 变成了9:1?流量经过envoy了的处理

envoy如何接管由front-tomcat容器发出的请求流量?(istio-init

回顾iptables:

istio-init容器作用

Istio 给应用 Pod 注入的配置主要包括:

  • Init 容器 istio-init

    Istio 在 pod 中注入的 Init 容器名为 istio-init,作用是为 pod 设置 iptables 端口转发。

    我们在上面 Istio 注入完成后的 YAML 文件中看到了该容器的启动命令是:

    1
    istio-iptables -p 15001 -z 15006 -u 1337 -m REDIRECT -i '*' -x "" -b '*' -d 15090,15021,15020

    Init 容器的启动入口是 istio-iptables 命令行,该命令行工具的用法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $ istio-iptables [flags]
    -p: 指定重定向所有 TCP 出站流量的 sidecar 端口(默认为 $ENVOY_PORT = 15001)
    -m: 指定入站连接重定向到 sidecar 的模式,“REDIRECT” 或 “TPROXY”(默认为 $ISTIO_INBOUND_INTERCEPTION_MODE)
    -b: 逗号分隔的入站端口列表,其流量将重定向到 Envoy(可选)。使用通配符 “*” 表示重定向所有端口。为空时表示禁用所有入站重定向(默认为 $ISTIO_INBOUND_PORTS
    -d: 指定要从重定向到 sidecar 中排除的入站端口列表(可选),以逗号格式分隔。使用通配符“*” 表示重定向所有入站流量(默认为 $ISTIO_LOCAL_EXCLUDE_PORTS
    -o:逗号分隔的出站端口列表,不包括重定向到 Envoy 的端口。
    -i: 指定重定向到 sidecar 的 IP 地址范围(可选),以逗号分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量。空列表将禁用所有出站重定向(默认为 $ISTIO_SERVICE_CIDR
    -x: 指定将从重定向中排除的 IP 地址范围,以逗号分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量(默认为 $ISTIO_SERVICE_EXCLUDE_CIDR)。
    -k:逗号分隔的虚拟接口列表,其入站流量(来自虚拟机的)将被视为出站流量。
    -g:指定不应用重定向的用户的 GID。(默认值与 -u param 相同)
    -u:指定不应用重定向的用户的 UID。通常情况下,这是代理容器的 UID(默认值是 1337,即 istio-proxy 的 UID)。
    -z: 所有进入 pod/VM 的 TCP 流量应被重定向到的端口(默认 $INBOUND_CAPTURE_PORT = 15006)。

    以上传入的参数都会重新组装成 iptables 规则,关于 Istio 中端口用途请参考 Istio 官方文档

    这条启动命令的作用是:

    • 将应用容器的所有入站流量都转发到 sidecar的 15006 端口(15090 端口(Envoy Prometheus telemetry)和 15020 端口(Ingress Gateway)除外,15021(sidecar健康检查)端口)
    • 将所有出站流量都重定向到 sidecar 代理(通过 15001 端口)
    • 上述规则对id为1337用户除外,因为1337是istio-proxy自身的流量

    该容器存在的意义就是让 sidecar 代理可以拦截pod所有的入站(inbound)流量以及出站(outbound)流量,这样就可以实现由sidecar容器来接管流量,进而实现流量管控。

init容器进行入站出站流量监控

因为 Init 容器初始化完毕后就会自动终止,因为我们无法登陆到容器中查看 iptables 信息,但是 Init 容器初始化结果会保留到应用容器和 sidecar 容器中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 查看front-tomcat服务的istio-proxy容器的id
$ docker ps |grep front-tomcat
6c0c6478ce2a 43e421a14aec "/bin/sh -c 'mkdir /…" 2 hours ago Up 2 hours k8s_front-tomcat_front-tomcat-v1-8687f9f845-rmlxt_default_0204ab7e-a083-4e71-9af6-dcc675ec7000_0

# 根据容器id获取front-tomcat容器在宿主机中的进程
$ docker inspect 6c0c6478ce2a|grep -i pid
"Pid": 97369,
"PidMode": "",
"PidsLimit": null,
# 进入该进程的网络命名空间
$ nsenter -n --target 97369
# 查看命名空间的iptables规则
$ iptables -t nat -vnL
# PREROUTING 链:用于目标地址转换(DNAT),将所有入站 TCP 流量跳转到 ISTIO_INBOUND 链上。
Chain PREROUTING (policy ACCEPT 3879 packets, 233K bytes)
pkts bytes target prot opt in out source destination
3884 233K ISTIO_INBOUND tcp -- * * 0.0.0.0/0 0.0.0.0/0

# INPUT 链:处理输入数据包,非 TCP 流量将继续 OUTPUT 链。
Chain INPUT (policy ACCEPT 3884 packets, 233K bytes)
pkts bytes target prot opt in out source destination

# OUTPUT 链:将所有出站数据包跳转到 ISTIO_OUTPUT 链上。
Chain OUTPUT (policy ACCEPT 46 packets, 3926 bytes)
pkts bytes target prot opt in out source destination
8 480 ISTIO_OUTPUT tcp -- * * 0.0.0.0/0 0.0.0.0/0
# POSTROUTING 链:所有数据包流出网卡时都要先进入POSTROUTING 链,内核根据数据包目的地判断是否需要转发出去,我们看到此处未做任何处理。
Chain POSTROUTING (policy ACCEPT 46 packets, 3926 bytes)
pkts bytes target prot opt in out source destination

# ISTIO_INBOUND 链:将所有入站流量重定向到 ISTIO_IN_REDIRECT 链上,目的地为 15090,15020,15021端口的流量除外,发送到以上两个端口的流量将返回 iptables 规则链的调用点,即 PREROUTING 链的后继 POSTROUTING。
Chain ISTIO_INBOUND (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:15008
0 0 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:15090
3879 233K RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:15021
0 0 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:15020
5 300 ISTIO_IN_REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0

# ISTIO_IN_REDIRECT 链:将所有入站流量跳转到本地的 15006 端口,至此成功的拦截了流量到sidecar中。
Chain ISTIO_IN_REDIRECT (3 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 redir ports 15006

# ISTIO_OUTPUT 链:选择需要重定向到 Envoy(即本地) 的出站流量,所有非 localhost 的流量全部转发到 ISTIO_REDIRECT。为了避免流量在该 Pod 中无限循环,所有到 istio-proxy 用户空间的流量都返回到它的调用点中的下一条规则,本例中即 OUTPUT 链,因为跳出 ISTIO_OUTPUT 规则之后就进入下一条链 POSTROUTING。如果目的地非 localhost 就跳转到 ISTIO_REDIRECT;如果流量是来自 istio-proxy 用户空间的,那么就跳出该链,返回它的调用链继续执行下一条规则(OUTPUT 的下一条规则,无需对流量进行处理);所有的非 istio-proxy 用户空间的目的地是 localhost 的流量就跳转到 ISTIO_REDIRECT。
Chain ISTIO_OUTPUT (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- * lo 127.0.0.6 0.0.0.0/0
0 0 ISTIO_IN_REDIRECT all -- * lo 0.0.0.0/0 !127.0.0.1 owner UID match 1337
0 0 RETURN all -- * lo 0.0.0.0/0 0.0.0.0/0 ! owner UID match 1337
8 480 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 owner UID match 1337
0 0 ISTIO_IN_REDIRECT all -- * lo 0.0.0.0/0 !127.0.0.1 owner GID match 1337
0 0 RETURN all -- * lo 0.0.0.0/0 0.0.0.0/0 ! owner GID match 1337
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 owner GID match 1337
0 0 RETURN all -- * * 0.0.0.0/0 127.0.0.1
0 0 ISTIO_REDIRECT all -- * * 0.0.0.0/0 0.0.0.0/0

# ISTIO_REDIRECT 链:将所有流量重定向到 Sidecar(即本地) 的 15001 端口。
Chain ISTIO_REDIRECT (1 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 redir ports 15001
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ kubectl exec -ti front-tomcat-v1-8687f9f845-rmlxt -c istio-proxy -- bash
istio-proxy@front-tomcat-v1-8687f9f845-rmlxt:/$ netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:8005 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:15021 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15021 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15001 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15001 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 127.0.0.1:15004 0.0.0.0:* LISTEN 1/pilot-agent
tcp 0 0 0.0.0.0:15006 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15006 0.0.0.0:* LISTEN 16/envoy
tcp6 0 0 :::15020 :::* LISTEN 1/pilot-agent

说明pod内的出站流量请求被监听在15001端口的envoy的进程接收到,进而就走到了envoy的Listener -> route -> cluster -> endpoint 转发流程。

问题就转变为:如何查看envoy的配置,跟踪转发的过程?

envoy流量转发跟踪分析

我们知道,envoy的配置非常复杂,直接在config_dump里去跟踪xDS的过程非常繁琐。因此istio提供了调试命令,方便查看envoy的流量处理流程。

1
$ istioctl proxy-config -h

比如,通过如下命令可以查看envoy的监听器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 查看15001的监听
$ istioctl proxy-config listener front-tomcat-v1-8687f9f845-rmlxt --port 15001 -ojson
# virtualOutbound的监听不做请求处理,useOriginalDst: true, 直接转到原始的请求对应的监听器中

# 查看访问端口是9999的监听器
$ istioctl proxy-config listener front-tomcat-v1-8687f9f845-rmlxt --port 9999 -ojson
...
{
"name": "0.0.0.0_9999",
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 9999
}
},
"filterChains": [
{
"filterChainMatch": {
"applicationProtocols": [
"http/1.0",
"http/1.1",
"h2c"
]
},
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "outbound_0.0.0.0_9999",
"rds": {
"configSource": {
"ads": {},
"resourceApiVersion": "V3"
},
"routeConfigName": "9999"
},
...

envoy收到请求后,会转给监听器进行处理请求,监听器先匹配address和port和socket都一致的Listener,如果没找到再找port一致,address==0.0.0.0的Listener

发现istio会为网格内的Service Port创建名为0.0.0.0_<Port>的虚拟监听器,本例中为0.0.0.0_9999

envoy的15001端口收到请求后,直接转到了0.0.0.0_9999,进而转到了"routeConfigName": "9999",即9999这个route中。

下面,看下route的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
$ istioctl pc route front-tomcat-v1-8687f9f845-rmlxt --name 9999
NOTE: This output only contains routes loaded via RDS.
NAME DOMAINS MATCH VIRTUAL SERVICE
9999 bill-service /* vs-bill-service.default

# 发现了前面创建的virtual service
$ istioctl pc route front-tomcat-v1-8687f9f845-rmlxt --name 9999 -ojson
[
{
"name": "9999",
"virtualHosts": [
{
"name": "allow_any",
"domains": [
"*"
],
"routes": [
{
"name": "allow_any",
"match": {
"prefix": "/"
},
"route": {
"cluster": "PassthroughCluster",
"timeout": "0s",
"maxGrpcTimeout": "0s"
}
}
],
"includeRequestAttemptCount": true
},
{
"name": "bill-service.default.svc.cluster.local:9999",
"domains": [
"bill-service.default.svc.cluster.local",
"bill-service.default.svc.cluster.local:9999",
"bill-service",
"bill-service:9999",
"bill-service.default.svc.cluster",
"bill-service.default.svc.cluster:9999",
"bill-service.default.svc",
"bill-service.default.svc:9999",
"bill-service.default",
"bill-service.default:9999",
"192.168.18.251",
"192.168.18.251:9999"
],
"routes": [
{
"name": "bill-service-route",
"match": {
"prefix": "/"
},
"route": {
"weightedClusters": {
"clusters": [
{
"name": "outbound|9999|v1|bill-service.default.svc.cluster.local",
"weight": 90
},
{
"name": "outbound|9999|v2|bill-service.default.svc.cluster.local",
"weight": 10
}
]
},
...

满足访问domains列表的会优先匹配到,我们访问的是192.168.18.251:9999,因此匹配bill-service.default.svc.cluster.local:9999这组虚拟hosts,进而使用到基于weight的集群配置。

我们看到,流量按照预期的配置进行了转发:

1
2
90% -> outbound|9999|v1|bill-service.default.svc.cluster.local
10% -> outbound|9999|v2|bill-service.default.svc.cluster.local

下面,看一下cluster的具体内容:

1
2
3
4
5
6
7
8
9
10
11
12
$ istioctl pc cluster front-tomcat-v1-8687f9f845-rmlxt --fqdn bill-service.default.svc.cluster.local -ojson
...
"name": "outbound|9999|v1|bill-service.default.svc.cluster.local",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {},
"resourceApiVersion": "V3"
},
"serviceName": "outbound|9999|v1|bill-service.default.svc.cluster.local"
},
...

我们发现,endpoint列表是通过eds获取的,因此,查看endpoint信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ istioctl pc endpoint front-tomcat-v1-8687f9f845-rmlxt  --cluster 'outbound|9999|v1|bill-service.default.svc.cluster.local' -ojson
[
{
"name": "outbound|9999|v1|bill-service.default.svc.cluster.local",
"addedViaApi": true,
"hostStatuses": [
{
"address": {
"socketAddress": {
"address": "172.7.100.3",
"portValue": 80
}
},
...

目前为止,经过envoy的规则,流量从front-tomcat的pod中知道要发往10.244.0.7:80 这个pod地址。前面提到过,envoy不止接管出站流量,入站流量同样会接管。

下面看下流量到达bill-service-v1的pod后的处理:

先回顾前面的iptables规则,除特殊情况以外,所有的出站流量被监听在15001端口的envoy进程拦截处理,同样的,分析bill-service-v1的iptables规则可以发现,监听在15006端口的envoy进程通过在PREROUTING链上添加规则,同样将进入pod的入站流量做了拦截。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# PREROUTING 链:用于目标地址转换(DNAT),将所有入站 TCP 流量跳转到 ISTIO_INBOUND 链上。
Chain PREROUTING (policy ACCEPT 148 packets, 8880 bytes)
pkts bytes target prot opt in out source destination
148 8880 ISTIO_INBOUND tcp -- * * 0.0.0.0/0 0.0.0.0/0

# INPUT 链:处理输入数据包,非 TCP 流量将继续 OUTPUT 链。
Chain INPUT (policy ACCEPT 148 packets, 8880 bytes)
pkts bytes target prot opt in out source destination

# ISTIO_INBOUND 链:将所有入站流量重定向到 ISTIO_IN_REDIRECT 链上,目的地为 15090,15020,15021端口的流量除外,发送到以上两个端口的流量将返回 iptables 规则链的调用点,即 PREROUTING 链的后继 POSTROUTING。
Chain ISTIO_INBOUND (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:15008
0 0 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:15090
143 8580 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:15021
5 300 RETURN tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:15020
0 0 ISTIO_IN_REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0

# ISTIO_IN_REDIRECT 链:将所有入站流量跳转到本地的 15006 端口,至此成功的拦截了流量到sidecar中。
Chain ISTIO_IN_REDIRECT (3 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 redir ports 15006

15006端口是一个名为 virtualInbound虚拟入站监听器,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ istioctl pc l bill-service-v1-59b4f4ccc7-qvhw6.default --port 15006 -ojson
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "inbound_0.0.0.0_80",
"routeConfig": {
"name": "inbound|80||",
"virtualHosts": [
{
"name": "inbound|http|9999",
"domains": [
"*"
],
"routes": [
{
"name": "default",
"match": {
"prefix": "/"
},
"route": {
"cluster": "inbound|80||",
"timeout": "0s",
"maxStreamDuration": {
"maxStreamDuration": "0s",
"grpcTimeoutHeaderMax": "0s"
}
},
"decorator": {
"operation": "bill-service.default.svc.cluster.local:9999/*"
}
}
]
}
],
"validateClusters": false

相比于VirtualOutboundvirtualInbound 不会再次转给别的虚拟监听器,而是直接由本监听器的filterChains处理,本例中我们可以发现本机目标地址为80的http请求,转发到了inbound|9999|http|bill-service.default.svc.cluster.local这个集群中。

查看该集群的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
$ istioctl pc cluster bill-service-v1-59b4f4ccc7-qvhw6.default -h
$ istioctl pc cluster bill-service-v1-59b4f4ccc7-qvhw6.default --fqdn "inbound|80||" -ojson
[
{
"name": "inbound|80||",
"type": "ORIGINAL_DST",
"connectTimeout": "10s",
"lbPolicy": "CLUSTER_PROVIDED",
"circuitBreakers": {
"thresholds": [
{
"maxConnections": 4294967295,
"maxPendingRequests": 4294967295,
"maxRequests": 4294967295,
"maxRetries": 4294967295,
"trackRemaining": true
}
]
},
"cleanupInterval": "60s",
"upstreamBindConfig": {
"sourceAddress": {
"address": "127.0.0.6",
"portValue": 0
}
},
"metadata": {
"filterMetadata": {
"istio": {
"services": [
{
"host": "bill-service.default.svc.cluster.local",
"name": "bill-service",
"namespace": "default"
}
]
}
}
}
}
]

istio小知识

同一个Pod,不同的表现

1
2
3
4
5
$ kubectl exec -ti front-tomcat-v1-8687f9f845-rmlxt -c front-tomcat bash
# curl bill-service:9999

$ kubectl exec -ti front-tomcat-v1-8687f9f845-rmlxt -c istio-proxy bash
# curl bill-service:9999

可以发现,在front-tomcat容器中的访问请求,是受到我们设置的 9:1的流量分配规则限制的,但是istio-proxy容器中的访问是不受限制的。

istio-proxy自身,发起的往192.168.18.251的请求,使用的用户是 uid=1337(istio-proxy),因此不会被istio-init初始化的防火墙规则拦截,可以直接走pod的网络进行通信。

集群内的Service都相应的创建了虚拟出站监听器

1
2
3
4
5
6
7
8
9
10
11
$ kubectl exec -ti front-tomcat-v1-8687f9f845-rmlxt -c front-tomcat bash
# curl sonarqube.jenkins:9000

$ istioctl pc l front-tomcat-v1-8687f9f845-rmlxt --port 9000
ADDRESS PORT MATCH DESTINATION
192.168.18.33 9000 App: HTTP Route: sonarqube.jenkins.svc.cluster.local:9000
192.168.18.33 9000 ALL Cluster: outbound|9000||sonarqube.jenkins.svc.cluster.local

$ istioctl pc r front-tomcat-v1-8687f9f845-rmlxt --name 'sonarqube.jenkins.svc.cluster.local:9000'

$ istioctl pc ep front-tomcat-v1-8687f9f845-rmlxt --cluster 'outbound|9000||sonarqube.jenkins.svc.cluster.local'

virtualOutBound 15001 –> virtial listener 192.168.18.33_9000 –> route sonarqube.jenkins.svc.cluster.local:9000 –> cluster outbound|9000||sonarqube.jenkins.svc.cluster.local –> 192.168.18.33:9000

istio服务网格内,流量请求完全绕过了kube-proxy组件

通过上述流程调试,我们可以得知,front-tomcat中访问bill-service:9999,流量是没有用到kube-proxy维护的宿主机中的iptables规则的。

img