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])。

b25b5994f6a9b6c42b1fc3cc5c21e1c3.png

nsenter 的工作原理

nsenter 通过以下步骤实现进入指定命名空间:

  1. 识别目标命名空间

    • nsenter 接受目标进程的 PID,通过读取 /proc/<pid>/ns/ 中的符号链接来确定目标命名空间。
    • 或者直接指定命名空间文件(如 /var/run/netns/<name>)。
  2. **调用系统调用 setns()**:

    • nsenter 使用 Linux 系统调用 setns() 将当前进程的命名空间切换到目标命名空间。
    • setns() 的定义在 <sched.h> 中,原型为:
      1
      int setns(int fd, int nstype);
      其中,fd 是命名空间文件的文件描述符,nstype 指定命名空间类型(如 CLONE_NEWNET)。
  3. 执行命令

    • 切换命名空间后,nsenter 调用 execvp() 执行用户指定的命令,新命令继承当前的命名空间环境。

核心优势

  • 直接性:不像 docker execkubectl exec 需要通过容器运行时,nsenter 直接操作内核接口。
  • 灵活性:可以进入任意进程的命名空间,不限于容器。

nsenter 的演变

起源

  • nsenterutil-linux 软件包的一部分,最早出现在 2011 年左右,当时 Linux 内核的命名空间功能(从 2.6.23 开始逐步引入)逐渐成熟。
  • 在此之前,用户通常通过手动操作(如 unshare 创建命名空间,或用 ip netns 管理网络命名空间)实现类似功能,但缺乏统一工具。

发展历程

  1. 早期(2007-2011)

    • Linux 内核陆续引入命名空间(如 Network Namespace 在 2.6.29,User Namespace 在 3.8)。
    • 社区使用低级工具(如 unshareip netns)或编写脚本管理命名空间。
  2. **2011-2013:nsenter 加入 util-linux**:

    • util-linux 项目(包含 mountfdisk 等常用工具)增加了 nsenter,提供用户友好的接口。
    • 最初主要用于调试网络命名空间,配合 ip netns 使用。
  3. 容器化时代(2013 后)

    • Docker 的流行(2013 年发布)推动了命名空间的使用,nsenter 成为调试容器的利器。
    • 例如,进入 Docker 容器的网络命名空间:
      1
      nsenter -t <pid> -n tcpdump
    • 社区开始广泛推广 nsenter 作为容器化环境的低级工具。
  4. 现代应用(2015 至今)

    • Kubernetes 和其他容器编排工具普及后,nsenter 被用于调试 Pod。例如:
      1
      nsenter -t $(pidof <process>) -n ip addr
    • 随着工具生态完善(如 netshoot 镜像),nsenter 的使用场景从手动调试转向脚本化和自动化。

与其他工具的关系

  • **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