Kubernetes Pod 流量抓取的多种方式
描述:随着容器化技术的普及,越来越多的应用部署在容器中,有时在正式环境中我们可能需要对容器进行抓包,以了解其网络通信情况及其排除通信异常的原因。
此处,由于程序是在运行在 Kubernetes 的 Pod 容器中,而且承载业务的容器往往没有安装tcpdump
等相关数据包抓取命令,所以常常对Pod抓包而感到苦恼。
为了解决这个问题,我们可以通过以下几种方式针对Kubernetes
中的Pod容器进行抓包。
1.在 Pod 容器内抓包
描述:这是我们初学者最先想到的办法,当然也最简单的一种方式,在Pod容器中安装tcpdump
命令,然后直接使用tcpdump
命令进行抓包。
但是由于Pod容器中往往没有安装tcpdump
命令,通过安装tcpdump的方式增加了镜像大小,且在无外网连接的情况下也就无法在线安装。
# 1.安装 tcpdump
# Alpine 镜像
kubectl exec ${POD_NAME} -- apk add tcpdump
# CentOS 镜像
kubectl exec ${POD_NAME} -- yum install tcpdump -y
# Ubuntu 镜像
kubectl exec ${POD_NAME} -- dnf install tcpdump -y
# 2.抓包并使用 wireshark 进行分析
kubectl exec ${POD_NAME} -- tcpdump -i eth0 -w - | wireshark -k -i -
2.在节点主机上抓包
描述:这种方式是我们在实际生产环境中经常使用的,通过找到Pod容器所运行工作节点主机(Node)的,通过嗅探在Pod所在的节点上网络接口索引进行抓包,然后通过wireshark
工具进行分析。
- 1.定位 Pod 的 containerID 以及它所运行的宿主机 IP。
$ kubectl get --all-namespaces -o wide pods | grep ${POD_NAME}
# 或者
$ kubectl get pod -n ${NAMESPACE}${POD_NAME} -o json|jq '.status|{hostIP: .hostIP, container: [.containerStatuses[]|{name: .name, containerID: .containerID}]}'
{
"hostIP": "10.10.66.203",
"container": [
{
"name": "app",
"containerID": "docker://808605e67373b6d97dfb4bf67745e8cd0f5062978385707c91e42d1c37bfba57"
}
]
}
- 2.登录到运行此Pod容器的宿主机(
10.103.236.203
)查找容器中的网卡与宿主机的 veth 网卡之间的对应关系,及其网络接口信息。
$ PodName=Gateway-756894b7d7-2qvzj
$ kubectl exec -it ${PodName} -- /bin/sh -c "cat /sys/class/net/eth0/iflink"
# 16
$ ip link |grep 16
16: cali3002ec233ba@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
- 3.确定好容器中的网卡与宿主机的 veth 网卡
cali3002ec233ba@if6
关系后,就可以通过tcpdump
命令进行抓包了。
$ tcpdump -i ali3002ec233ba@if6 -w /tmp/Gateway-756894b7d7-2qvzj.pcap
3.使用nsenter命令抓包
通过nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令,使用它我们可以在Pod容器中直接使用tcpdump命令进行抓包。
- 1.找到Pod容器所在宿主机,及其Pid信息。
# 找到Pod调度的工作节点
$ podName=fzxt-backend-admin-64c89dd789-bvggq
$ kubectl get pods -A -o wide | grep ${PodName}
# prod fzxt-backend-admin-64c89dd789-bvggq 1/1 Running 0 28d 172.19.1.88 weiyigeek-214 <none> <none>
# 登录节点上执行如下命令:
# Containerd
$ crictl inspect `(crictl ps --name ${PodName} | cut -d " " -f 1 | grep -v CONTAINE)` | grep "pid"
"pid": 46241,
"pid": 1
"type": "pid"
# Docker
$ docker inspect 3002ec233ba |grep Pid
"Pid": 8105,
"PidMode": "",
"PidsLimit": null,
- 2.在承载此Pod容器的工作节点上,安装nsenter命令。
# 软件包管理工具安装
yum install util-linux
# 源码安装
# 下载
wget https://www.kernel.org/pub/linux/utils/util-linux/v2.40/util-linux-2.40.tar.gz
# 解压
tar zxvf util-linux-2.40.tar.gz && cd util-linux-2.40
# 编译
./configure --without-ncurses && make
cp nsenter /usr/bin/
- 3.使用nsenter命令进入Pod容器的命名空间,然后使用 tcpdump 进行抓包。
# 进入到pod命名空间
nsenter -t 8105 -n
# 使用 tcpdump 进行抓包
tcpdump -nnnvv -As 0 -i eth0 port 80 -w fzxt-backend-admin.pcap
# 参数解释:
-nnn:
第一个 n 表示不将网络地址转换为名称。这意味着 tcpdump 会显示 IP 地址而不是尝试将它们解析为主机名。
第二个 n 也是同样的作用,但有些版本的 tcpdump 可能不支持重复使用 -n 选项。在这种情况下,第二个 n 可能会被忽略。
第三个 n(如果支持)通常表示不将端口号转换为服务名称,即显示端口号的数字而不是服务名称。
-vv:
第一个 v 表示详细输出。这会提供更多的信息,比如数据包的头部信息。
第二个 v 表示更详细的输出。这会提供比单个 -v 更详细的信息。
-A:表示以 ASCII 格式打印每个数据包的内容,方便阅读文本数据。
-s 0:
-s 选项后跟一个数值,表示从每个捕获的数据包中截取的字节数。0 表示不截取,即捕获每个数据包的全部
4.使用ksniff插件抓包
描述:ksniff 是一个 kubectl 的插件,它利用 tcpdump 和 Wireshark 对 Kubernetes 集群中的任何 Pod 启动远程抓包(这是作者推荐的方式)
- 1.安装 krew 命令及 sniff 插件
# 安装 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
$ kubectl krew install sniff
Updated the local copy of plugin index.
Installing plugin: sniff
W0710 17:54:14.755671 84252 install.go:160] Skipping plugin "sniff", it is already installed
- 2.使用 sniff 插件抓包,实际上是映射本地的
/tmp/static-tcpdump
文件到Pod中运行。 通常情况下服务器节点没有安装 wireshark,你可以将报文输出到文件中,然后用本地Windows系统中的 wireshark 工具来解析报文。
kubectl sniff fzxt-backend-admin-64c89dd789-bvggq -n prod -o fzxt-backend-admin.pcap