图片来源: Rowan Atkinson Goes to Battle in ‘Man vs. Bee’
本文摘自我的开源书《Istio & Envoy 内幕》 中的 Istio 端口 与 组件。如图片不清,可回到原文。
最近,需要在 mTLS 的 Istio 1.12 环境下定位一些 “神奇” 的网络问题。经历过一些 tcpdump 抓取 Istio 流量的折腾,被迫又复习了一次 Istio 的网络魔术,固记录于本文。
前言
无论你承认与否,我们习惯是缘用旧方法或工具去解决新问题。就算已经到达 Cloud Natvie, Service Mesh 时代,人们也会默认继续用 tcpdump 去定位网络问题。这有问题吗?或者有,或者没有,我没答案。
tcpdump 的历史
https://en.wikipedia.org/wiki...
tcpdump was originally written in 1988 by Van Jacobson, Sally Floyd, Vern Paxson and Steven McCanne who were, at the time, working in the Lawrence Berkeley Laboratory Network Research Group. By the late 1990s there were numerous versions of tcpdump distributed as part of various operating systems, and numerous patches that were not well coordinated. Michael Richardson (mcr)&action=edit&redlink=1) and Bill Fenner created www.tcpdump.org in 1999.
对读者的假设
假设读者已经了解过 Istio。想看看其 POD 内的流量魔术,以在排坑时有一定套路和理论依据。
互动图片
本文的正常打开方法是,点击 “用 Draw.io 打开” 后,进入互动图片状态。本文的大部分内容是放在图中了。看图比文字更重要。
Istio 的网络魔术预览
原始的我是个喜欢说话直接了当的人(虽然社会已经迫我学习和爱上绕圈子)。这里先直接贴上我的研究结果图,后面会分析。
图:Istio端口与组件
tcpdump 抓包方法
这里先给个结论。为面再讲原理和绕圈子。
Sidecar 抓包
图:Istio端口与组件
中包含了一些在 Istio 中时行 tcpdump 的说明。在此不再赘述。要补充一下是的 tcpdump 的抓包点。因为,这个点影响了,tcpdump 的过滤条件与输出。主要是这个点与 iptables 的 redirect 规则生效的前后问题。
tcpdump capture pinpoint:
tcpdump will see inbound traffic before iptables, but will see
outbound traffic only after the firewall has processed it.As a matter of fact, tcpdump is the first software found after the wire (and the NIC, if you will) on the way IN, and the last one on the way OUT.
- Wire -> NIC -> tcpdump -> netfilter/iptables
- iptables -> tcpdump -> NIC -> Wire
######## sidecar ########
sudo nsenter -t $PID_OF_SIDECAR_ENVOY -n -u
export ETH0_IP=$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
export LOCAL_IP=$(ip addr show lo | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
# inbound mTLS
sudo tcpdump -i eth0 -n -vvvv "(dst port 8080 and dst $ETH0_IP) or (src port 8080 and src $ETH0_IP)"
# inbound 明文
sudo tcpdump -i lo -n -vvvv -A "(dst port 8080 and dst $ETH0_IP) or (src port 8080 and src $ETH0_IP)"
# outbound 明文
sudo tcpdump -i lo -n -vvvv -A "((dst port 15001 and dst 127.0.0.1) or (dst portrange 20000-65535 and dst $ETH0_IP))"
# outbound mTLS
sudo tcpdump -i eth0 -n -vvvv -A "((src portrange 20000-65535 and src $ETH0_IP) or (dst portrange 20000-65535 and dst $ETH0_IP))"
有一点比较麻烦的是,outbound 明文
抓包,出向 ip packet 抓到的是 redirect 后的 127.0.0.1,入向 ip packet 抓到的是未 redirect 前的 ip 地址。如果你用 Wireshark 等工具分析。是无法 Follow TCP Stream 的。
Istio Gateway 抓包
一般,Istio Gateway 的 upsteam (Cluster 内部),与 downsteam(Cluster 外部)会在不同的 subnet,所以,可以用 CIDR 去区分。
首先,看看 kubernetes cluster 的 pod 的 cidr 范围:
ps -ef | grep cidr
root 48587 20177 0 Dec08 ? 00:21:25 kube-controller-manager ... --cluster-cidr=192.168.0.0/12 ...--service-cluster-ip-range=10.96.0.0/12 ...
这时,如果尝试直接使用上面的参数会出错:
$ sudo tcpdump -i br0 -vvvv -A net 192.168.0.0/12
tcpdump: non-network bits set in "192.168.0.0/12"
发现,tcpdump 对 cidr 的格式要求比较严格,要求用首个可用 ip 段。使用 https://cidr.xyz/ 分析出 192.168.0.0/12
的首个可用 ip 为 192.160.0.1
,固:
sudo tcpdump -i br0 -vvvv -A net 192.160.0.0/12
Istio 的网络魔术
监听端口
可以用以下方式查看监听的端口:
$ nsenter -n -t $PID_OF_ENVOY
$ ss -ln
u_str LISTEN etc/istio/proxy/SDS 34782 * 0 users:(("pilot-agent",pid=3406,fd=13))
u_str LISTEN etc/istio/proxy/XDS 34783 * 0 users:(("pilot-agent",pid=3406,fd=16))
u_str ESTAB etc/istio/proxy/XDS 1379729 * 1379728 users:(("pilot-agent",pid=3406,fd=8))
u_str ESTAB * 1379728 * 1379729 users:(("envoy",pid=3555,fd=37))
u_str ESTAB etc/istio/proxy/SDS 45274 * 46319 users:(("pilot-agent",pid=3406,fd=15))
u_str ESTAB * 46319 * 45274 users:(("envoy",pid=3555,fd=19))
tcp LISTEN 0.0.0.0:15021 0.0.0.0:* users:(("envoy",pid=3555,fd=40),("envoy",pid=3555,fd=34),("envoy",pid=3555,fd=22))
tcp LISTEN 0.0.0.0:15090 0.0.0.0:* users:(("envoy",pid=3555,fd=39),("envoy",pid=3555,fd=33),("envoy",pid=3555,fd=21))
tcp LISTEN 127.0.0.1:15000 0.0.0.0:* users:(("envoy",pid=3555,fd=18))
tcp LISTEN 0.0.0.0:15001 0.0.0.0:* users:(("envoy",pid=3555,fd=41),("envoy",pid=3555,fd=35),("envoy",pid=3555,fd=31))
tcp LISTEN 127.0.0.1:15004 0.0.0.0:* users:(("pilot-agent",pid=3406,fd=17))
tcp LISTEN 0.0.0.0:15006 0.0.0.0:* users:(("envoy",pid=3555,fd=42),("envoy",pid=3555,fd=36),("envoy",pid=3555,fd=32))
tcp ESTAB 172.21.206.227:40560 10.108.217.90:15012 users:(("pilot-agent",pid=3406,fd=19))
tcp ESTAB 172.21.206.227:43240 10.108.217.90:15012 users:(("pilot-agent",pid=3406,fd=14))
tcp LISTEN *:15020 *:* users:(("pilot-agent",pid=3406,fd=12))
tcp ESTAB 127.0.0.1:35256 127.0.0.1:15020 users:(("envoy",pid=3555,fd=43))
tcp ESTAB 127.0.0.1:35238 127.0.0.1:15020 users:(("envoy",pid=3555,fd=20))
tcp ESTAB [::ffff:127.0.0.1]:15020 [::ffff:127.0.0.1]:35238 users:(("pilot-agent",pid=3406,fd=6))
tcp ESTAB [::ffff:127.0.0.1]:15020 [::ffff:127.0.0.1]:35256 users:(("pilot-agent",pid=3406,fd=18))
iptables
$ iptables-save
# Generated by iptables-save v1.8.7 on Fri Dec 16 16:18:31 2022
*nat
:PREROUTING ACCEPT [104490:5433496]
:INPUT ACCEPT [105123:5471476]
:OUTPUT ACCEPT [24745:1633905]
:POSTROUTING ACCEPT [42627:2706825]
:ISTIO_INBOUND - [0:0]
:ISTIO_IN_REDIRECT - [0:0]
:ISTIO_OUTPUT - [0:0]
:ISTIO_REDIRECT - [0:0]
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --uid-owner 201507 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -m owner --uid-owner 201507 -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --gid-owner 201507 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
COMMIT
这个 iptable 的原理网上说了很多,这里不再说明了。
连接
图:Istio端口与组件
中包含了一些在 Istio 中时行 pod 内部的 TCP 连接的说明(见其中的 ss
命令输出)。在此不再赘述。
神奇的 127.0.0.6
出于非常多的原因,inbound 流量在由 Envoy 转到 app 时,Envoy 建立的 TCP 连接 bind 的 ip 地址是一个ip addr
也查不到的,在 lo
接口上的 127.0.0.6
。如果你好奇,可以看:
Why the bind magic
127.0.0.6
?
一点感想
如果用传统 Linux 网络运维的方法,去解决 Istio 复杂实现下的网络问题,非常不直观和容易。在 Service Mesh 应用的同时,可观察性的工具和方法应该作出相应的变化。不然大规模使用后,问题的解决将花费相当大的代价。
其实,在可观察性方面,我们已经有所谓的 distributed tracing(全链路跟踪)
。为何不能 全链路跟踪 TCP 连接的状态变化和事件源?如,是什么组件发出的 TCP RST/FIN/SYN。连接的参与方是什么?影响是什么?
而这些事件源的采集上,可以利用 eBPF 等技术去捕获连接的重要事件。业界已经有一些类似的开源实现。只是大家的重视程度未上去。
公司也好,业界也好,技术的演进大都是由解决问题来推动的,而很少是真正 “设计” 出来的。而关键的,可能是去发现变化和主动适应甚至利用变化。