生产容器基本都是精简镜像,里面没有 ss、tcpdump,甚至没 sh。想抓个包、看个端口,总不能往运行中的容器里装东西。nsenter 是这类场景的救星:从宿主机直接进入容器的命名空间执行命令,容器里缺工具也没关系——用的是宿主机的。
原理一句话
容器隔离的本质是 Linux namespace。nsenter 是对 setns 系统调用的封装:指定一个目标进程的 PID,把当前命令「塞进」它的某个命名空间里执行。容器里 1 号进程的所有命名空间就是这个容器的隔离边界,进去就等于站在容器内部。
基本用法
nsenter -t <PID> [命名空间选项] <要执行的命令>
常用命名空间选项:
| 选项 | 进入的空间 | 典型用途 |
|---|---|---|
-n | network | 抓包、看端口、看路由(用得最多) |
-m | mount | 看容器视角的文件系统挂载 |
-p | pid | 看容器内的进程列表 |
-i | ipc | 共享内存、消息队列 |
-u | uts | 主机名 |
-a | 全部 | 完全进入容器环境 |
也可以不通过 PID、直接指定命名空间文件,比如 Kubernetes 注册的:nsenter --net=/var/run/netns/cni-xxx ...
先拿到容器的 PID
# Docker
PID=$(docker inspect -f '{{.State.Pid}}' <容器名>)
# Kubernetes 节点上(找 Pod 沙箱的 PID)
crictl pods --name <pod名> # 拿到 pod 沙箱 ID
crictl inspect <沙箱ID> | grep pid # 里面的 info.pid
K8s 里一个 Pod 的所有容器共享网络命名空间,所以进 pause 沙箱的网络空间就等于进了整个 Pod 的网络。
实战场景
抓容器网卡的包(最经典,配合 tcpdump 笔记一起看):
nsenter -t $PID -n tcpdump -i eth0 -nn port 80
看容器里实际监听的端口(镜像里没 ss 时):
nsenter -t $PID -n ss -lnt
看容器视角的挂载和磁盘:
nsenter -t $PID -m -- df -h
nsenter -t $PID -m -- mount | grep nfs
验证容器内 DNS 解析:
nsenter -t $PID -n -- nslookup kubernetes.default
完全进入容器环境执行一系列命令(近似 docker exec,但不要求容器里有 shell):
nsenter -t $PID -a /bin/bash
几个坑
- 需要 root 或
CAP_SYS_ADMIN,普通用户直接跑会 permission denied - 目标进程退出,命名空间就没了:拿着 PID 正准备进,容器恰好重启,PID 失效,报
setns: cannot set namespace——重新取一次 PID 即可 -m里别乱动:在挂载命名空间里 umount 或挂东西影响的是容器本体,只读看看就好- 进网络命名空间后看到的
eth0是容器的网卡,和宿主机的eth0不是一回事,别抓错方向
小结
nsenter 把「容器里没工具」这个借口彻底消灭了:宿主机的 tcpdump、ss、ip、nslookup 全都是容器的排查工具。记住核心三板斧——docker inspect 拿 PID、nsenter -t $PID -n 进网络空间、完事退出不留痕。它比进容器装调试镜像优雅得多,也不会污染运行环境。