nsenter 和 名称空间

一、背景

对于运行在后台的docker容器,我们经常需要做的事情是进入到容器中,docker为我们提供了docker exec 、docker attach 命令。

nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令。它位于util-linux包中。

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

只能通过 docker inspect ContainerID 命令获取到容器IP,以及无法测试和其他网络的连通性。这时就可以使用nsenter命令仅进入该容器的网络命名空间,使用宿主机的命令调试容器网络。

此外,nsenter也可以进入 mnt, uts, ipc, pid, user 命令空间,以及指定根目录和工作目录。

二、安装

1、源码安装 nsenter

$ cd /tmp;
$ curl https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz | tar -zxf-; cd util-linux-2.24;
$ ./configure  --without-ncurses
$ make nsenter && sudo cp nsenter /usr/local/bin 

2、yum安装

util-linux 是一个开放源码的软件包,是一个对任何 Linux 系统的基本工具套件。含有一些标准 Unix 工具,如 login。

util-linux 软件包包含许多工具。其中比较重要的是加载、卸载、格式化、分区和管理硬盘驱动器,打开 tty 端口和得到内核消息。

sudo yum install -y util-linux

三、nsenter使用

1、查看帮助

$ nsenter --help

用法:
 nsenter [options] <program> [<argument>...]
Run a program with namespaces of other processes.

选项:
 -t, --target <pid>     要获取名字空间的目标进程
 -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
 -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          执行 <程序> 前不 fork
 -Z, --follow-context   set SELinux context according to --target PID

 -h, --help     显示此帮助并退出
 -V, --version  输出版本信息并退出

更多信息请参阅 nsenter(1)。
$ nsenter  -V
nsenter,来自 util-linux 2.23.2

在使用nsenter命令之前需要获取到docker容器的进程,然后再使用nsenter工具进去到docker容器中
每一个容器都有.State.Pid,所以这个命令除了容器的id需要我们根据docker ps -a去查找,其他的全部为固定的格式

$ docker inspect -f {{.State.Pid}} 容器名或者容器id  

如下:
$ docker inspect -f {{.State.Pid}}  7b7af641a02d
20560
$ docker inspect -f {{.State.Pid}}  consul_client
20560 

输入该命令便进入到容器中

$ nsenter --target 上面查到的进程id --mount --uts --ipc --net --pid  

解释nsenter指令中进程id之后的参数的含义: 
* –mount参数是进去到mount namespace中  (文件系统)
* –uts参数是进入到uts namespace中  (主机名与域名)
* –ipc参数是进入到System V IPC namaspace中  (信号量、消息队列和共享内容)
* –net参数是进入到network namespace中   (网络设备、网络栈、端口)
* –pid参数是进入到pid namespace中    (进程编号)
* –user参数是进入到user namespace中 (用户和用户组)

docker隔离应用应用涉及到的六大名称空间

1、pid (进程ID)

不同用户的进程就是通过 pid 命名空间隔离开的,且不同命名空间中可以有相同 pid。所有的 LXC 进程在 Docker 中的父进程为 Docker 进程,每个 LXC 进程具有不同的命名空间。同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。

2、net (网络)

有了 pid 命名空间,每个命名空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net 命名空间实现的, 每个 net 命名空间有独立的 网络设备,IP 地址,路由表,/proc/net 目录。这样每个容器的网络就能隔离开来。Docker 默认采用 veth 的方式,将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起。

3、ipc (进程间通信)

容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、消息队列和共享内存等。然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互,因此需要在 IPC 资源申请时加入命名空间信息,每个 IPC 资源有一个唯一的 32 位 id。

4、mnt (挂载文件系统)

类似 chroot,将一个进程放到一个特定的目录执行。mnt 命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个命名空间中的容器在 /proc/mounts 的信息只包含所在命名空间的 mount point。

5、UTS (主机名/域名)

UTS("UNIX Time-sharing System") 命名空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程。

6、user (用户)

每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。

查看docker的连接

由于使用DOCKER的时候,ESTABLISHED连接不会出现在netstat中,在运行中的docker容器中列出打开的套接字的方法 ,查找docker的进程号 :

$ docker inspect -f '{{.State.Pid}}' <containerid>   
$ docker inspect -f '{{.State.Pid}}' 1746bf8c10aa  
1829 

查看连接: 

$ sudo nsenter -t <pid> -n netstat | grep ESTABLISHED  

示例:
$ nsenter -t 1829 -n netstat |grep ESTABLISHED  
tcp        0      0 localhost:60353         localhost:epmd          ESTABLISHED
tcp        0      0 localhost:epmd          localhost:60353         ESTABLISHED
tcp        0      0 localhost.localdo:15672 192.168.56.1:59679      ESTABLISHED
tcp6       0      0 172.17.0.2:amqp         192.168.56.1:59898      ESTABLISHED
tcp6      21      0 172.17.0.2:amqp         192.168.56.1:59571      ESTABLISHED

结合 docker 使用用于在某个容器的 namespace 中运行指定程序的常用命令是:

PID=$(docker inspect --format {{.State.Pid}} <container_name_or_ID>)

nsenter -m -u -i -n -p -t $PID <command>

例子:

$ 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
8115009bacc

创建docker-enter并置于$PATH下(将shell脚本放到环境变量中,任意路径可执行)

脚本放置:

cd ~
#创建bin文件夹,也可以按自己喜好在指定位置下创建目录(目录名也可以自定义)
mkdir bin

vim ~/.bashrc
#移到最后一行,添加以下命令,其中:表示路径分隔符,~/bin就是我们刚才创建的文件路径
export PATH=$PATH:~/bin

source ~/.bashrc

在~/bin 目录下创建自己的脚本

chmod +x  xxx.sh

脚本:vim docker-enter

#!/bin/bash

if [ -e $(dirname "$0")/nsenter ]; then
  # with boot2docker, nsenter is not in the PATH but it is in the same folder
  NSENTER=$(dirname "$0")/nsenter
else
  NSENTER=nsenter
fi

if [ -z "$1" ]; then
  echo "Usage: `basename "$0"` CONTAINER [COMMAND [ARG]...]"
  echo ""
  echo "Enters the Docker CONTAINER and executes the specified COMMAND."
  echo "If COMMAND is not specified, runs an interactive shell in CONTAINER."
else
  PID=$(docker inspect --format "{{.State.Pid}}" "$1")
  if [ -z "$PID" ]; then
    exit 1
  fi
  shift

  OPTS="--target $PID --mount --uts --ipc --net --pid /bin/bash"

  if [ -z "$1" ]; then
    # No command given.
    # Use su to clear all host environment variables except for TERM,
    # initialize the environment variables HOME, SHELL, USER, LOGNAME, PATH,
    # and start a login shell.
#"$NSENTER" $OPTS su - root
"$NSENTER" $OPTS
  else
    # Use env to clear all host environment variables.
    "$NSENTER" $OPTS env --ignore-environment -- "$@"
  fi
fi

常见问题

$ sudo nsenter -t 31340 -n netstat | grep ESTABLISHED
nsenter: failed to execute netstat: 没有那个文件或目录

 

 

https://www.cnblogs.com/rwxwsblog/p/5430902.html    不错的文章

 

 

posted @ 2020-12-09 21:32  凡人半睁眼  阅读(1967)  评论(0编辑  收藏  举报