巧用 Nsenter 调取宿主机工具调试容器内程序

Posted by Mike on 2020-10-14

nsenter 是一个可以用来进入到目标程序所在 Namespace 中运行命令的工具,一般常用于在宿主机上调试容器中运行的程序。

nsenter 安装

nsenter 位于 util-linux 包中,一般常用的 Linux 发行版都已经默认安装。如果你的系统没有安装,可以使用以下命令进行安装:

1
$ yum install util-linux

nsenter 用途

一个比较典型的用途就是进入容器的网络命名空间。通常容器为了轻量级,大多都是不包含较为基础网络管理调试工具,比如:ip、ping、telnet、ss、tcpdump 等命令,给调试容器内网络带来相当大的困扰。

nsenter 命令可以很方便的进入指定容器的网络命名空间,使用宿主机的命令调试容器网络。

除此以外,nsenter 还可以进入 mnt、uts、ipc、pid、user 等命名空间,以及指定根目录和工作目录。

nsenter 用法

首先看下 nsenter 命令的语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ nsenter [options] [program [arguments]]

options:

-a, --all enter all namespaces of the target process by the default /proc/[pid]/ns/* namespace paths.
-m, --mount[=<file>]:进入 mount 命令空间。如果指定了 file,则进入 file 的命名空间
-u, --uts[=<file>]:进入 UTS 命名空间。如果指定了 file,则进入 file 的命名空间
-i, --ipc[=<file>]:进入 System V IPC 命名空间。如果指定了 file,则进入 file 的命名空间
-n, --net[=<file>]:进入 net 命名空间。如果指定了 file,则进入 file 的命名空间
-p, --pid[=<file>:进入 pid 命名空间。如果指定了 file,则进入 file 的命名空间
-U, --user[=<file>:进入 user 命名空间。如果指定了 file,则进入 file 的命名空间
-t, --target <pid> # 指定被进入命名空间的目标进程的 pid
-G, --setgid gid:设置运行程序的 GID
-S, --setuid uid:设置运行程序的 UID
-r, --root[=directory]:设置根目录
-w, --wd[=directory]:设置工作目录

如果没有给出 program,则默认执行 $SHELL。最常用的参数组合是:

1
2
3
# 有的版本不一定有 -a 这个参数
$ nsenter -a -t <pid> <command>
$ nsenter -m -u -i -n -p -t <pid> <command>

nsenter 使用实例

  1. 进入 docker 容器的 namespace 中运行指定程序

使用 nsenter 进入 docker 容器的 namespace 是非常简单的,通常你只需要以下两步:

1
2
3
4
# 获取相应的 Dokcer 容器的 PID
$ PID=$(docker inspect --format {{.State.Pid}} <container_name_or_ID>)
# 使用相应参数进入程序所在的不同 NameSpace
$ nsenter -m -u -i -n -p -t $PID <command>

下面我们来看几个实例:

  • 进入指定程序所在的所有命名空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ docker run --rm --name test -d busybox  sleep 10000
8115009baccc53a2a5f6dfff642e0d8ab1dfb95dde473d14fb9a06ce4e89364c

$ docker ps |grep busybox
8115009baccc busybox "sleep 10000" 9 seconds ago Up 8 seconds test

$ PID=$(docker inspect --format {{.State.Pid}} 8115009baccc)

$ nsenter -m -u -i -n -p -t $PID ps aux
PID USER TIME COMMAND
1 root 0:00 sleep 10000
7 root 0:00 ps aux

$ nsenter -m -u -i -n -p -t $PID hostname
8115009baccc
  • 进入指定程序所在的网络命名空间

运行一个 Nginx 容器,查看该容器的 Pid:

1
2
$ docker inspect -f {{.State.Pid}} nginx
5645

然后,使用 nsenter 命令进入该容器的网络命令空间:

1
2
3
4
5
6
7
8
9
10
$ nsenter -n -t5645
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
  1. 进入 Kubernetes 容器的 namespace 中运行指定程序

如果是在 Kubernetes 中,在得到容器 Pid 之前,你还需先获取容器的 ID,可以使用如下命令获取:

1
2
$ kubectl get pod nginx -n web -o yaml|grep containerID
- containerID: docker://cf0873782d587dbca6aa32f49605229da3748600a9926e85b36916141597ec85

或者更为精确地获取 containerID:

1
2
$ kubectl get pod nginx -n web -o template --template='{{range .status.containerStatuses}}{{.containerID}}{{end}}'
docker://cf0873782d587dbca6aa32f49605229da3748600a9926e85b36916141597ec85

其它的步骤就和前面进入 Docker 容器的命名空间类似了。

参考文档

  1. https://www.googgle.com
  2. https://staight.github.io/2019/09/23/nsenter命令简介/
  3. https://mozillazg.com/2020/04/nsenter-usage.html