背景
在日常kubernetes的运维中,经常遇到pod的网络问题,如pod间网络不通,或者端口不通,更复杂的,需要在容器里面抓包分析才能定位。而kubertnets的场景,pod使用的镜像一般都是尽量精简,很多都是基于alpine基础镜像制作的,因而pod内没有ping,telnet,nc,curl命令,更别说tcpdump这种复杂的工具了。除了在容器或者镜像内直接安装这些工具这种最原始的法子,我们探讨下其他法子。
实现
kubectl debug插件方式
项目地址 kubect debug,https://github.com/aylei/kubectl-debug
kubectl-debug
是一个简单的 kubectl 插件,能够帮助你便捷地进行 Kubernetes 上的 Pod 排障诊断。背后做的事情很简单: 在运行中的 Pod 上额外起一个新容器,并将新容器加入到目标容器的 pid
, network
, user
以及 ipc
namespace 中,这时我们就可以在新容器中直接用 netstat
, tcpdump
这些熟悉的工具来解决问题了, 而旧容器可以保持最小化,不需要预装任何额外的排障工具。操作流程可以参见官方项目地址文档。
一条 kubectl debug命令背后是这样的
步骤分别是:
- 插件查询 ApiServer:demo-pod 是否存在,所在节点是什么
- ApiServer 返回 demo-pod 所在所在节点
- 插件请求在目标节点上创建 Debug Agent Pod
- Kubelet 创建 Debug Agent Pod
- 插件发现 Debug Agent 已经 Ready,发起 debug 请求(长连接)
- Debug Agent 收到 debug 请求,创建 Debug 容器并加入目标容器的各个 Namespace 中,创建完成后,与 Debug 容器的 tty 建立连接
接下来,客户端就可以开始通过 5,6 这两个连接开始 debug 操作。操作结束后,Debug Agent 清理 Debug 容器,插件清理 Debug Agent,一次 Debug 完成。
直接进入容器net ns方式
有2种进入pod 所在net ns的方式,前提都是需要登录到pod所在宿主机,且需要找出pod对应的容器ID或者名字。
ip netns方式
获取pod对应容器的ID或者name
pid="$(docker inspect -f '{{.State.Pid}}' <container_name | uuid>)" #替换为环境实际的容器名字或者uuid
创建容器对应netns
ip netns会到/var/run/netns目录下寻找network namespace,把容器进程中netns连接到这个目录中后,ip netns才会感知到
$ sudo mkdir -p /var/run/netns #docker默认不会创建这个链接,需要手动创建,这时候执行ip netns,就应当看到链接过来的network namespace $ sudo ln -sf /proc/$pid/ns/net "/var/run/netns/<container_name|uuid>"
执行ip netns <<container_name|uuid > bash,进入容器ns
ip netns exec <container_name|uuid> bash
执行telnet,tcpdump等命令,此时执行ip a或者ifconfig,只能看到容器本身的IP
nsenter方式
nsenter为util-linux里面的一个工具,除了进入容器net ns,还支持其他很多操作,可以查看官方文档。
pid="$(docker inspect -f '{{.State.Pid}}' <container_name | uuid>)"
nsenter -t $pid -n /bin/bash
tcpdump -i eth0 -nn #此时利用宿主机的tcpdump执行抓包操作,等于在容器内抓包
总结
- kubectl debug方式功能更强大,缺点是需要附加镜像,要在目标pod创建debug agent的容器,比较笨重,但是优点是能使用的工具更多,不需要ssh到pod所在节点,除了netstat,tcpdump工具,还能使用htop,iostat等其他高级工具,不仅能对网络进行debug,还能对IO等其他场景进行诊断,适用更复杂的debug场景。
- 直接进入容器net ns方式相对比较轻量,复用pod所在宿主机工具,但鱼和熊掌不可兼得,缺点是只能进行网络方面的debug,且需要ssh登录到pod所在节点操作。