1. 宿主机上捕获
我们知道应用其实是运行在 Pod 内的 Container 里的,所以只要定位到 Container 被调度到了哪个 Node 上,在相应的 Node 里,对容器进行抓包即可。
0) 先决条件
需要有宿主机的访问权限
1) 定位 Pod 的 containerID 以及它所运行的宿主机 IP
在 Kubernetes 集群内执行下面这个指令,并从返回的结果中拿到两个信息
- 宿主机 IP
- container ID
获取格式:
kubectl get pod \ -n ${NAMESPACE} ${POD_NAME} \ -o json|jq '.status|{hostIP: .hostIP, container: [.containerStatuses[]|{name: .name, containerID: .containerID}]}'
2) 查找网络接口索引
通过 ssh 登陆到 Pod 所在的宿主机上,然后在容器内执行 cat /sys/class/net/eth0/iflink
,查找容器中的网卡与宿主机的 veth 网卡之间的对应关系
格式:
docker exec -it ${CONTAINER_ID} /bin/bash -c 'cat /sys/class/net/eth0/iflink'
3) 查找与 container 内部链接的宿主机网络接口
在宿主机上做 ip link
| grep "刚才获取的数字:" 操作
4) 在宿主机上指定网络接口抓包
格式:
tcpdump -i ${获取的外部网络接口} -w /root/tcpdump.pcap
最后,我们通过 tcpdump 指令,将报文保存到文件中
我们 ping 一下 pod 所在的 IP,将 tcpdump 导出的文件使用 wireshark 进行分析
我们可以看一下来源IP 为 172.25.244.192 这个 IP 是发出 ping 命令的主机使用 tunl0 设备传输过去的,使用 tunl0 模式是因为跨节点了
如果使用pod所在的宿主思考机192.168.1.12 ping pod 的 IP,则不会经过 tun0
思考
通常情况下,并不是所有的开发者都能触碰到宿主机的权限的,所以这种方式只适合集群运维人员,而且这种操作确实也过于繁琐了。
对于开发者来说,离我们最近的还是 Pod,下面我们来看下直接通过 Pod 是如何进行抓包的。
2. 在 Pod 内抓包
0) 先决条件
- 目标 Pod 内的容器里必须要有 tcpdump 工具
- 本地安装 wireshark
1) 执行以下命令对目标 Pod 抓包
格式:
kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- tcpdump -i eth0 -w - | wireshark -k -i -
思考
不难发现,这种方法比第一种在执行层面上少了很多步骤。
只要满足了 2 个前置条件,执行一条指令即可通过 wireshark 来解析报文,而且不需要宿主机的权限,但是它会存在一个问题:就是容器里必须要有 tcpdump 这个工具
一般来说你可能会想到两个解决办法:
- 将 tcpdump 打包到镜像里。但是这样做,它不仅会增加镜像包的大小,也会有一定的风险,另外你不可能要求三方的镜像也必须默认装上对吧?
- 有同学说,直接进入容器里做现场安装咯?但是,你有没有想过,很多客户部署的环境都是要求隔离外网的,甚至有些 base image 用的是 无发行版镜像
3. 通过 ephemeral containers 抓包
值得一提的是 Kubernetes 在 v1.16 [Alpha] 开始支持 Ephemeral Containers,它正好可以解决上面提的 2 个痛点,临时容器对于排除交互式故障非常有用,Kubernetes 在 v1.23 [beta] 已经默认开启该功能了。比如我使用 nicolaka/netshoot
netshoot介绍
netshoot是一款开源的容器网络诊断工具,本质上是一个容器,里面默认安装了一些常用网络诊断工具,如tcpdump、netstat等,被称为网络排障瑞士军刀容器。
众所周知,容器下网络空间是相互隔离的,与宿主机的网络空间也是隔离的,往往需要通过工具进入容器空间排查问题,但是业务容器不一定安装了常见的调试工具,netshoot在这样的场景提供了很大的便利。
- 提供了“瑞士军刀”式的开箱即用的工具箱,包括各类网络诊断工具。
- 通过与业务容器共享网络命名空间,可以不重启、“不侵入”业务容器,对容器网络进行调试排错。
- K8s环境下pod中的容器共享网络命名空间,netshoot可以以sidecard对业务容器进行排错。
netshoot镜像构成
netshoot代码比较少,核心代码就是这样一个dockerfile,可以看到里面安装了很多的工具,与另一款Linux系统瑞士军刀busybox不同,netshoot主要关注在网络工具。
Kubernetes使用过程
用法格式:
kubectl debug -i ${POD_NAME} --image=nicolaka/netshoot --target=${CONTAINER_NAME} -- tcpdump -i eth0 -w - | wireshark -k -i -
# 或者进入容器进行网络检测
kubectl debug -ti ${POD_NAME} --image=nicolaka/netshoot
但是,如果你的集群版本比较低,在 v1.23 版本以下,且集群管理员并没有开启 Ephemeral Containers 这个特性,咋办咧?
如果是在生产环境,你不可能让管理员升级集群版本,或者开启某一个特性来做 Debug 吧?
那么问题来了,有没有一种工具,它既可以做到不用触碰宿主机,又可以不必在目标 Pod 内的容器里提前安装 tcpdump 呢?
4. 通过 Ksniff 抓包
ksniff 是一个 kubectl 的插件,它利用 tcpdump 和 Wireshark 对 Kubernetes 集群中的任何 Pod 启动远程抓包。
0) 先决条件
安装 Krew
krew 是一个用来管理 kubectl 插件的工具,类似于 apt 或 yum,支持搜索、安装和管理 kubectl 插件
安装:
(
set -x; cd "$(mktemp -d)" &&
curl -fsSLO "https://storage.googleapis.com/krew/v0.2.1/krew.{tar.gz,yaml}" &&
tar zxvf krew.tar.gz &&
./krew-"$(uname | tr '[:upper:]' '[:lower:]')_amd64" install \
--manifest=krew.yaml --archive=krew.tar.gz
)
安装完成后,把 krew 的二进制文件加入环境变量 PATH 中:
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
最后,再执行 kubectl 命令确认安装成功:
$ kubectl plugin list
The following kubectl-compatible plugins are available:
/home/<user>/.krew/bin/kubectl-krew
kubectl krew update
一旦安装完 Krew 后,就可以通过以下指令完成 sniff 的安装
kubectl krew install sniff
1) 执行 kubectl sniff 命令抓包
格式:
kubectl sniff ${POD_NAME} -n ${NAMESPACE}
执行 sniff 命令后,本地会自动启动 Wireshark 进行抓包,如下图
kubectl sniff nginx-6799fc88d8-l5tv5 -n default
sniff日志
从运行的日志来看,sniff 是将本地的 static-tcpdump 文件上传到 Pod 的指定容器的 /tmp
目录下,然后在容器内,通过运行以下命令来达到抓包的目的
/tmp/static-tcpdump -i any -U -w -
另外需要说明的是,如果你是在服务器上执行,且服务器并没有安装 wireshark,你可以将报文输出到文件中,然后用本地的 wireshark 来解析报文。
kubectl sniff httpbin -n default -o httpbin.pcap
这里可以看出,网络是从k8s-master01通过tunl0访问过去的,因为网址我们输入的是master01的IP地址
发布者:LJH,转发请注明出处:https://www.ljh.cool/39722.html