#1. 宿主机上捕获
我们知道应用其实是运行在 Pod 内的 Container 里的,所以只要定位到 Container 被调度到了哪个 Node 上,在相应的 Node 里,对容器进行抓包即可。
0) 先决条件
需要有宿主机的访问权限
1) 定位 Pod 的 containerID 以及它所运行的宿主机 IP
在 Kubernetes 集群内执行下面这个指令,并从返回的结果中拿到两个信息
- 宿主机 IP =
172.18.0.xxx
- container ID =
78e91175699f.....
# 注: 需要替换 namespace 和 pod name
➜ kubectl get pod \
-n ${NAMESPACE}${POD_NAME} \
-o json|jq '.status|{hostIP: .hostIP, container: [.containerStatuses[]|{name: .name, containerID: .containerID}]}'
{
"hostIP": "172.18.0.xxx",
"container": [
{
"name": "linkerd-proxy",
"containerID": "docker://78e91175699f8cc0a3b0ff87da97407c19c7a86706a5b74e2d86f4428a4de75a"
},
{
"name": "nginx",
"containerID": "docker://7a6f7eabc2d5112437d30ee8ec1aa7ef963e97c3d09c3bc63613a70d106d7d01"
}
]
}
2) 查找网络接口索引
通过 ssh 登陆到 Pod 所在的宿主机上,然后在容器内执行 cat /sys/class/net/eth0/iflink
,查找容器中的网卡与宿主机的 veth 网卡之间的对应关系
# 注: 需要替换 container id
docker exec -it ${CONTAINER_ID} /bin/bash -c 'cat /sys/class/net/eth0/iflink'
10227
3) 查找网络接口信息
在宿主机上做 ip link
操作
ip link |grep 10227
10227: calicf227ed888a@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP mode DEFAULT group default
4) 在宿主机上抓包
最后,我们通过 tcpdump 指令,将报文保存到文件中
tcpdump -i calicf227ed888a@if4 -w /root/tcpdump.pcap
#2. 在 Pod 内抓包
0) 先决条件
- 目标 Pod 内的容器里必须要有 tcpdump 工具
- 本地安装 wireshark
1) 执行以下命令对目标 Pod 抓包
# 注: -c ${CONTAINER_NAME} 是可选择的。如果 Pod 中仅包含一个容器,就可以忽略它
kubectl exec${POD_NAME} -c ${CONTAINER_NAME} -- tcpdump -i eth0 -w - | wireshark -k -i -
#3. 通过 ephemeral containers 抓包
值得一提的是 Kubernetes 在 v1.16 [Alpha] 开始支持 Ephemeral Containers[2],它正好可以解决上面提的 2 个痛点,临时容器对于排除交互式故障非常有用,Kubernetes 在 v1.23 [beta] 已经默认开启该功能了。
比如我使用 nicolaka/netshoot[3] 镜像用来调试,用法如下
# 注: 需要替换 pod name 和 container name
kubectl debug -i ${POD_NAME} \
--image=nicolaka/netshoot \
--target=${CONTAINER_NAME} -- tcpdump -i eth0 -w - | wireshark -k -i -
#4. 通过 Ksniff 抓包
ksniff 是一个 kubectl 的插件,它利用 tcpdump 和 Wireshark 对 Kubernetes 集群中的任何 Pod 启动远程抓包。
0) 先决条件
安装 Krew
(
set -x; cd"$(mktemp -d)" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/krew.tar.gz" &&
tar zxvf krew.tar.gz &&
KREW=./krew-"$(uname | tr '[:upper:]' '[:lower:]')_$(uname -m | sed -e 's/x86_64/amd64/' -e 's/arm.*$/arm/')" &&
"$KREW" install krew
)
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
安装 sniff 插件
一旦安装完 Krew 后,就可以通过以下指令完成 sniff 的安装
kubectl krew install sniff
1) 执行 kubectl sniff 命令抓包
# 注:需要替换 pod name 和 namespace
kubectl sniff ${POD_NAME} -n ${NAMESPACE}
执行 sniff 命令后,本地会自动启动 Wireshark 进行抓包,如下图
以下是 sniff 运行的日志,我只提取了日志的关键部分
➜ kubectl sniff httpbin -n default
time="2022-02-20T19:56:13+08:00" level=info msg="using tcpdump path at: '/Users/linqiong/.krew/store/sniff/v1.6.1/static-tcpdump'"
time="2022-02-20T19:56:13+08:00" level=info msg="selected container: 'httpbin'"
time="2022-02-20T19:56:13+08:00" level=info msg="uploading file: '/Users/linqiong/.krew/store/sniff/v1.6.1/static-tcpdump' to '/tmp/static-tcpdump' on container: 'httpbin'"
....
time="2022-02-20T19:56:14+08:00" level=info msg="tcpdump uploaded successfully"
time="2022-02-20T19:56:14+08:00" level=info msg="spawning wireshark!"
time="2022-02-20T19:56:14+08:00" level=info msg="start sniffing on remote container"
time="2022-02-20T19:56:14+08:00" level=info msg="executing command: '[/tmp/static-tcpdump -i any -U -w - ]' on container: 'httpbin', pod: 'httpbin', namespace: 'default'"
从运行的日志来看,sniff 是将本地的 static-tcpdump 文件上传到 Pod 的指定容器的 /tmp
目录下,然后在容器内,通过运行以下命令来达到抓包的目的
/tmp/static-tcpdump -i any -U -w -
另外需要说明的是,如果你是在服务器上执行,且服务器并没有安装 wireshark,你可以将报文输出到文件中,然后用本地的 wireshark 来解析报文。
kubectl sniff httpbin -n default -o httpbin.pcap
文章来源:https://www.cnaaa.net,转载请注明出处:https://www.cnaaa.net/archives/9551