nsenter(namesapce enter)进入命名空间
nsenter
是 Linux 提供的一个命令行工具,可以让我们进入特定的命名空间(Namespace)。它适用于调试容器、Kubernetes Pod,或直接进入某个进程,使用宿主机系统的命令进行调试,排查(其原理是通过指定选项进入目标命名空间:如容器的 mount namespace,或选择保留宿主机的 mount namespace 以使用其文件系统)。解决容器缺少命令的问题。
nsenter
的原理
nsenter
是一个 Linux 命令行工具,全称是 “namespace enter”,用于进入一个或多个 Linux 命名空间(namespace),并在其中执行指定的命令。它是利用了 Linux 内核提供的命名空间隔离机制,直接操作进程的命名空间,而无需通过容器运行时(如 Docker 或 Podman)的间接方式。
Linux 命名空间简介
Linux 命名空间是容器技术的核心,提供了进程隔离的机制。常见的命名空间包括:
- PID Namespace:隔离进程 ID。
- Network Namespace:隔离网络栈(接口、路由表等)。
- Mount Namespace:隔离挂载点。
- UTS Namespace:隔离主机名和域名。
- IPC Namespace:隔离进程间通信。
- User Namespace:隔离用户和组 ID。
- Cgroup Namespace:隔离控制组视图。
每个进程都关联到一个或多个命名空间,信息存储在 /proc/<pid>/ns/
目录下。例如:
1 | ls -l /proc/1/ns/ |
会显示进程 1 的命名空间链接(如 net -> net:[4026531992]
)。
nsenter
的工作原理
nsenter
通过以下步骤实现进入指定命名空间:
识别目标命名空间:
nsenter
接受目标进程的 PID,通过读取/proc/<pid>/ns/
中的符号链接来确定目标命名空间。- 或者直接指定命名空间文件(如
/var/run/netns/<name>
)。
**调用系统调用
setns()
**:nsenter
使用 Linux 系统调用setns()
将当前进程的命名空间切换到目标命名空间。setns()
的定义在<sched.h>
中,原型为:其中,1
int setns(int fd, int nstype);
fd
是命名空间文件的文件描述符,nstype
指定命名空间类型(如CLONE_NEWNET
)。
执行命令:
- 切换命名空间后,
nsenter
调用execvp()
执行用户指定的命令,新命令继承当前的命名空间环境。
- 切换命名空间后,
核心优势
- 直接性:不像
docker exec
或kubectl exec
需要通过容器运行时,nsenter
直接操作内核接口。 - 灵活性:可以进入任意进程的命名空间,不限于容器。
nsenter
的演变
起源
nsenter
是util-linux
软件包的一部分,最早出现在 2011 年左右,当时 Linux 内核的命名空间功能(从 2.6.23 开始逐步引入)逐渐成熟。- 在此之前,用户通常通过手动操作(如
unshare
创建命名空间,或用ip netns
管理网络命名空间)实现类似功能,但缺乏统一工具。
发展历程
早期(2007-2011):
- Linux 内核陆续引入命名空间(如 Network Namespace 在 2.6.29,User Namespace 在 3.8)。
- 社区使用低级工具(如
unshare
和ip netns
)或编写脚本管理命名空间。
**2011-2013:
nsenter
加入util-linux
**:util-linux
项目(包含mount
、fdisk
等常用工具)增加了nsenter
,提供用户友好的接口。- 最初主要用于调试网络命名空间,配合
ip netns
使用。
容器化时代(2013 后):
- Docker 的流行(2013 年发布)推动了命名空间的使用,
nsenter
成为调试容器的利器。 - 例如,进入 Docker 容器的网络命名空间:
1
nsenter -t <pid> -n tcpdump
- 社区开始广泛推广
nsenter
作为容器化环境的低级工具。
- Docker 的流行(2013 年发布)推动了命名空间的使用,
现代应用(2015 至今):
- Kubernetes 和其他容器编排工具普及后,
nsenter
被用于调试 Pod。例如:1
nsenter -t $(pidof <process>) -n ip addr
- 随着工具生态完善(如
netshoot
镜像),nsenter
的使用场景从手动调试转向脚本化和自动化。
- Kubernetes 和其他容器编排工具普及后,
与其他工具的关系
- **
unshare
**:用于创建新命名空间,而nsenter
是进入已有命名空间。 - **
ip netns
**:专注于网络命名空间管理,依赖/var/run/netns
,功能比nsenter
窄。 - 容器运行时:如
docker exec
,是高层封装,依赖守护进程,而nsenter
是底层工具。
示例
假设一个 Pod 的主进程 PID 是 1234,想进入它的网络命名空间:
1 | nsenter -t 1234 -n bash |
进入后,运行 ip addr
会看到 Pod 的网络接口,而不是宿主机的。
使用
1. 安装方式
直接安装util-linux, 其中包含nsenter命令
sudo yum install -y util-linux
/usr/bin/nsenter
[root@centos8-1 ~]# nsenter -h
Usage:
nsenter [options] [<program> [<argument>...]]
Run a program with namespaces of other processes.
Options:
-a, --all enter all namespaces
-t, --target <pid> target process to get namespaces from
-m, --mount[=<file>] enter mount namespace
-u, --uts[=<file>] enter UTS namespace (hostname etc)
-i, --ipc[=<file>] enter System V IPC namespace
-n, --net[=<file>] enter network namespace
-p, --pid[=<file>] enter pid namespace
-C, --cgroup[=<file>] enter cgroup namespace
-U, --user[=<file>] enter user namespace
-S, --setuid <uid> set uid in entered namespace
-G, --setgid <gid> set gid in entered namespace
--preserve-credentials do not touch uids or gids
-r, --root[=<dir>] set the root directory
-w, --wd[=<dir>] set the working directory
-F, --no-fork do not fork before exec'ing <program>
-Z, --follow-context set SELinux context according to --target PID
-h, --help display this help
-V, --version display version
2. 操作使用
nsenter
基本用法:
nsenter [options] [program [arguments]]
主要参数选项
选项 | 作用 |
---|---|
--target <PID>, -t |
指定要进入的进程 ID |
--mount, -m |
进入目标进程的挂载命名空间(Mount Namespace),用于访问其文件系统 |
--uts, -u |
进入目标进程的 UTS 命名空间,可以修改主机名 |
--ipc, -i |
进入目标进程的 IPC 命名空间,共享 System V IPC 资源(如消息队列、信号量) |
--net, -n |
进入目标进程的网络命名空间,访问其网络栈 |
--pid, -p |
进入目标进程的 PID 命名空间,管理进程树 |
--cgroup |
进入目标进程的 Cgroup 命名空间,查看资源限制 |
--user, -U |
进入目标进程的用户命名空间,切换到其用户身份 |
--all, -a |
进入目标进程的所有命名空间 |
--setuid <UID> |
切换到指定的用户 ID |
--setgid <GID> |
切换到指定的用户组 ID |
--preserve-credentials |
保持当前用户的 UID/GID,而不切换到目标进程的用户身份 |
--no-fork |
不创建新进程,直接执行命令 |
--wd <dir> |
进入后切换到指定工作目录 |
--help |
显示帮助信息 |
#在进程命名空间执行ifconfig
nsenter -t container_pid -n ifconfig
-t 选项用来指定要进入的进程ID
-n 指定要进入的网络命名空间
nsenter -t container_pid -n -- bash
nsenter --target $PID --mount --uts --ipc --net --pid
or
nsenter -m -u -n -i -p -t <PID>
3.进入nginx容器的net命名空间(进程同理)
[root@localhost ~]# docker inspect -f {{.State.Pid}} nginx
3047
[root@localhost ~]# nsenter --target 3047 --net
[root@localhost ~]# ip a
[root@centos8-1 ~]# exit
logout
不分配tty,只在当前terminal有效,exit退出namespace
4.进入nginx pod的net、uts命名空间
1.查看pod
[root@localhost ~]# kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-deploy-7bf8c77b5b-x45dj 1/1 Running 0 4d
2.获取容器ID
[root@localhost ~]# CONTAINER_ID=$(kubectl get pod nginx-deploy-7bf8c77b5b-x45dj -o jsonpath='{.status.containerStatuses[0].containerID}' | cut -d '/' -f3)
3.获取对应容器的进程PID
[root@localhost ~]# PID=$(nerdctl inspect -f '{{.State.Pid}}' $CONTAINER_ID)
[root@localhost ~]# PID=$(docker inspect -f '{{.State.Pid}}' $CONTAINER_ID)
[root@localhost ~]# echo $PID
44125
4.通过进程进入namespace
[root@localhost ~]# nsenter --target $PID --net --uts -- bash
[root@nginx-deploy-7bf8c77b5b-x45dj ~]# netstat -naplt
5.抓取容器内数据包
[root@localhost ~]# nsenter --target $PID --net --uts tcpdump -i eth0