AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub

nsenter —— 进入 Linux namespace 的"传送门"

一句话定义

nsenter 让你"进入"某个进程的 Linux namespace(网络 / 文件系统 / PID / IPC ...),仿佛在那个容器内执行命令——但不需要容器本身有 shell / 工具。是 K8s pod 排错的核心利器。

典型场景

  • pod 没装 tcpdump / curl / netstat —— 节点上用 nsenter -t <PID> -n tcpdump 进 pod netns 抓包
  • pod 卡 Terminating、kubectl exec 都进不去 —— nsenter 仍然能进
  • distroless 镜像(没 shell)—— 用节点工具 + nsenter 调试
  • 看节点 root netns 的网络全景:nsenter -t 1 -n ip route

装

通常 Linux 自带(util-linux 包)。看:

nsenter --version

没装:

apt install -y util-linux       # 通常已装

基本语法

nsenter -t <PID> [namespace flags] <command>

-t = target PID。指定要进入哪个进程的 namespace。

namespace flag 速查

flag进入的 namespace含义
-nnet网络(最常用:网卡 / 路由 / iptables / socket)
-mmnt文件系统挂载点
-ppidPID(容器内看到的 PID 1)
-uutshostname / domainname
-iipcSystem V IPC
-Ccgroupcgroup 视图
-UuserUID / GID 映射
-Ttime时间 namespace(5.6+)
-aall进所有 namespace(最大限度模拟容器视角)

进 K8s pod 的 4 个常用入口

1. 进 pod 网络命名空间(最常用)

# 找 pod 容器的 PID
$ PID=$(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')

# 用节点的工具 + pod 的网络视角
$ nsenter -t $PID -n ip addr               # pod 视角看网卡
$ nsenter -t $PID -n ip route              # pod 视角看路由
$ nsenter -t $PID -n ss -lntp               # pod 视角看监听
$ nsenter -t $PID -n tcpdump -i any -nn     # pod 视角抓包
$ nsenter -t $PID -n curl http://...        # pod 视角访问

这是关键操作

pod 里没工具时——nsenter 是你的救命稻草。distroless 镜像 / scratch 镜像里啥都没有,但节点上 tcpdump / curl 都装着。用 nsenter -t PID -n <tool> 用节点工具 + pod 视角。

2. 进 pod 文件系统视角

$ nsenter -t $PID -m ls /                    # pod 视角看根目录
$ nsenter -t $PID -m cat /etc/resolv.conf    # pod 的 DNS 配置
$ nsenter -t $PID -m -- bash                  # 进 pod 的 mount + 启动 bash

注意:bash 是节点的 /bin/bash——pod 的 mount ns 里可能没 bash(distroless)。

要"真正像在 pod 里"——加 -m -n -p -u:

$ nsenter -t $PID -m -n -p -u -- /bin/sh
# 现在 PID 视角、mount 视角、网络视角都是 pod 的
# bash 仍来自节点(但跑在 pod 视角下)

3. 一气全进(最像 kubectl exec)

$ nsenter -t $PID -a /bin/sh

-a = 进所有 namespace。等价于完整模拟 kubectl exec 的体验,但不需要容器有 shell。

-a 需要 --mount 提前指定

新版 nsenter -a 要求显式指定目标的 mount ns(因为 mount ns 决定可执行文件在哪)。如果 nsenter -t $PID -a 报错 mount namespace not provided,明确加:

$ nsenter --target $PID --mount --uts --ipc --net --pid -- /bin/sh

或者用 --mount=/proc/$PID/ns/mnt 这种全路径形式。

4. 进 node root netns(看节点全局视角)

$ nsenter -t 1 -n ip route                   # PID 1 是 init / systemd
$ nsenter -t 1 -n iptables -L -n -v

K8s 节点 PID 1 在 root netns 里——这是看节点级网络配置的方法(如果你登进了某个 pod 的 shell 看不到节点全貌)。


找 pod 容器 PID 的 N 种方法

nsenter -t $PID 需要先拿到 PID。

方法 1:crictl + jq(推荐)

$ PID=$(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')
$ echo $PID
12345

如果一个 pod 有多容器,--name 匹配的可能不只一个。更精确:

$ crictl ps --name my-pod
CONTAINER     IMAGE        NAME           POD ID    POD
abc12345      nginx        nginx          xyz...    my-pod
def67890      sidecar      sidecar        xyz...    my-pod

# 选具体一个 container 的 PID
$ PID=$(crictl inspect abc12345 | jq '.info.pid')

方法 2:从 K8s pod metadata 找

# kubectl 拿到 containerID
$ CID=$(kubectl get pod my-pod -o jsonpath='{.status.containerStatuses[0].containerID}' | sed 's|.*//||')

# 在节点上
$ PID=$(crictl inspect $CID | jq '.info.pid')

方法 3:ps + grep(简易但易错)

$ ps -ef | grep my-pod-image
# 看进程

不可靠——多个 pod 用同一镜像就分不清。

方法 4:bash function 封装

写进 .bashrc:

podpid() {
  local pod=$1
  crictl inspect $(crictl ps -q --name "$pod" | head -1) | jq -r '.info.pid'
}

podexec() {
  local pod=$1
  shift
  nsenter -t $(podpid "$pod") -n "$@"
}

# 用法
$ podpid my-pod
12345
$ podexec my-pod ip addr
$ podexec my-pod tcpdump -i any -nn

实战场景

场景 1:distroless pod 抓包

# pod 是 distroless(没 shell / curl / tcpdump)
$ kubectl exec my-pod -- /bin/sh
# OCI runtime exec failed: exec failed: unable to start container process: ...

# 用 nsenter 救
$ ssh m4
$ PID=$(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')
$ nsenter -t $PID -n tcpdump -i any -nn -c 50

# 看到 pod 网络流量

场景 2:测试 pod 内的 DNS

$ PID=$(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')

# 看 pod 用的 DNS
$ nsenter -t $PID -m cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

# pod 视角解析 K8s 内部域名
$ nsenter -t $PID -n dig kubernetes.default

# pod 视角解析外网(看 CoreDNS forward 是否工作)
$ nsenter -t $PID -n dig github.com

注意:/etc/resolv.conf 是 mount ns 隔离的,所以要 -m。但实际上 dig 命令读 /etc/resolv.conf 时 —— 如果你只 -n 不 -m,会读节点的 resolv.conf,结果错。

排查 DNS 一定要 -n -m 一起:

$ nsenter -t $PID -n -m dig kubernetes.default

场景 3:pod 卡 Terminating

$ kubectl get pod my-pod
NAME      READY   STATUS        AGE
my-pod    1/1     Terminating   5m

$ kubectl exec my-pod -- ps    # 也进不去
# 节点上看
$ PID=$(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')
$ nsenter -t $PID -p ps -ef         # 看 pod 内进程
$ nsenter -t $PID -n ss -t              # 看活跃 socket

通常发现某个进程卡在 close、或者还有未完成的 syscall。

场景 4:进 Cilium / 网络 pod 看 eBPF / iptables

$ kubectl get pod -n kube-system -l k8s-app=cilium -o wide
NAME            READY   STATUS    NODE
cilium-abc12    1/1     Running   m1

$ ssh m1
$ PID=$(crictl inspect $(crictl ps -q --name cilium-agent) | jq '.info.pid')
$ nsenter -t $PID -m -- cilium-dbg endpoint list      # cilium 自带工具
$ nsenter -t $PID -n ip route                         # cilium pod 的网络视角

场景 5:用 cgroup 视角看资源占用

$ nsenter -t $PID -C cat /sys/fs/cgroup/memory/memory.usage_in_bytes
# pod 视角的当前内存使用

通常用 kubectl top 更方便,但深入调试时这个直接。


节点上"模拟 kubectl exec"完整脚本

#!/bin/bash
# kexec - 通过 nsenter 在节点上进入 pod
#
# 用法: ./kexec <pod-name> [container-name] [command...]

POD_NAME=$1
shift

CTR=$(crictl ps -q --name "$POD_NAME" | head -1)
if [ -z "$CTR" ]; then
    echo "Pod not found: $POD_NAME"
    exit 1
fi

PID=$(crictl inspect "$CTR" | jq '.info.pid')

# 默认进 net + mount + pid + uts
nsenter -t "$PID" -n -m -p -u -- ${@:-/bin/sh}

放到 /usr/local/bin/kexec,节点上:

$ kexec my-pod ip addr
$ kexec my-pod tcpdump -i any -nn
$ kexec my-pod /bin/bash       # 如果 pod 有 bash

进阶:用 nsenter 直接读 /proc/PID/ns/

nsenter 本质是个 wrapper、调 setns() 系统调用。namespace 在 /proc/PID/ns/ 下:

$ ls -l /proc/12345/ns/
lrwxrwxrwx ... net -> 'net:[4026532152]'
lrwxrwxrwx ... mnt -> 'mnt:[4026532149]'
lrwxrwxrwx ... pid -> 'pid:[4026532151]'
...

数字(4026532152)是 namespace 的 inode。两个 PID 的同名 ns 数字一样 = 同一个 namespace。

看 pod 的多容器共享 net ns:

$ kubectl get pod my-pod -o jsonpath='{.status.containerStatuses[*].containerID}' | tr ' ' '\n'
containerd://abc123
containerd://def456

$ for cid in abc123 def456; do
    pid=$(crictl inspect $cid | jq '.info.pid')
    ns=$(readlink /proc/$pid/ns/net)
    echo "container=$cid pid=$pid net=$ns"
  done
container=abc123 pid=12345 net=net:[4026532152]
container=def456 pid=23456 net=net:[4026532152]
                                          ^^^^^^^^^
                                          相同 -> 同 pod,共享 net ns

K8s pod 设计:pod 内所有容器共享同一个 net ns + ipc ns。


反面教材 / 踩坑

❌ 坑 1:忘了对应的 namespace flag,读错配置

$ nsenter -t $PID -n dig kubernetes.default
# /etc/resolv.conf 读的是**节点的**、不是 pod 的
# 结果错

/etc/resolv.conf 在 mount ns 里。要 -n -m 一起:

$ nsenter -t $PID -n -m dig kubernetes.default

经验:网络相关排错除非很确定,一般 -n -m 都加上。

❌ 坑 2:用 PID 1(init)当成"任意容器"

$ nsenter -t 1 -n ip route
# 节点 root netns 的路由表(不是任何 pod 的)

节点 PID 1 是 systemd / init —— 节点视角。pod 的 PID 在 /proc/N/ns/net 下数字和节点 PID 1 的 net ns 不同。

如果想"看节点视角",PID 1 OK。想"看 pod 视角",PID 是 pod 容器 PID。

❌ 坑 3:进了 pod ns 但 cwd 还是节点的

$ nsenter -t $PID -n -m -- bash
[bash]$ pwd
/some/host/path             # 节点的 cwd!

nsenter 不改 working directory。要"像在 pod 里":

$ nsenter -t $PID -n -m --wd / -- bash

--wd / 设 cwd 到 /。

❌ 坑 4:用 /bin/sh 但 pod 里没有

$ nsenter -t $PID -m -- /bin/sh
# distroless pod: /bin/sh: No such file or directory

-m 之后命令是从 pod 的 mount ns 里找的——distroless 没 sh。两个办法:

  1. 不加 -m:用节点的 sh
$ nsenter -t $PID -n -p -- /bin/sh        # ← 不加 -m,sh 来自节点

但这样进的 sh 看不到 pod 的文件系统(因为 mount ns 还是节点的)。

  1. 在 pod ns 里执行节点的 sh(复杂、不推荐)

通常不加 -m 用节点 sh + 用绝对路径访问 /proc/PID/root/ 看 pod 文件:

$ nsenter -t $PID -n -- /bin/bash         # bash 来自节点
[bash]$ ls /proc/$PID/root/               # 节点视角看 pod 的文件系统

❌ 坑 5:pod 容器已死、PID 不存在

$ nsenter -t 12345 -n ip route
# Couldn't open ns file: /proc/12345/ns/net: No such file or directory

pod 重启了、PID 变了。重新拿:

$ PID=$(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')

养成"每次都重新拿 PID"的习惯,不要保存。

❌ 坑 6:在 K8s pod 里跑 nsenter

$ kubectl exec test-pod -- nsenter -t 1 -n ip route
# nsenter: failed to execute setns: Operation not permitted

K8s pod 默认没 CAP_SYS_ADMIN,setns() 调用被拒。

要让 pod 能用 nsenter(特权操作、慎用):

securityContext:
  privileged: true
# 或
securityContext:
  capabilities:
    add: ["SYS_ADMIN", "SYS_PTRACE"]
hostPID: true                    # 看到所有 PID

通常在节点上跑 nsenter 而不是 pod 内。

❌ 坑 7:用 root 之外的用户

$ nsenter -t $PID -n ip route
nsenter: cannot open /proc/12345/ns/net: Permission denied

非 root 没权限读别人的 namespace。sudo nsenter ... 或者用 root。


nsenter vs kubectl exec vs kubectl debug

工具优点缺点
kubectl exec简单 / 不需登节点要 pod 有 shell
kubectl debug (1.23+)在 pod 旁起 sidecar / 修改 image / 提权创建临时资源、复杂
nsenter (节点上)不依赖 pod 镜像 / 灵活到极致要登节点 / 要 root

kubectl debug 最现代:

$ kubectl debug my-pod -it --image=nicolaka/netshoot
# 起一个含网络工具的 sidecar、共享 my-pod 的网络命名空间

但很多生产场景 kubectl debug 也不够:node 上排错、pod 卡 Terminating、CNI 故障 —— 都得 nsenter。


关联命令

  • crictl —— 找 pod 容器 PID
  • tcpdump —— 经 nsenter 进 pod 抓包
  • ip —— pod 视角看路由 / 网卡
  • ss —— pod 视角看 socket
  • dig —— pod 视角解析 DNS(记得 -n -m)
  • kubectl —— kubectl debug 是 nsenter 的 K8s 包装
  • unshare —— nsenter 的"反向":创建新 namespace
在 GitHub 上编辑此页