一、背景
對於運行在后台的docker容器,我們經常需要做的事情是進入到容器中,docker為我們提供了docker exec 、docker attach 命令,並且還提供了nsenter工具,外部工具供我們使用。
docker attach存在的問題是:當多個窗口同時attach到同一個容器時,所有的窗口都會同步的顯示,假如其中的一個窗口發生阻塞時,其它的窗口也會阻塞,docker attach命令可以說是最不方便的進入后台docker容器的方法。
docker exec命令是在docker 1.3之后增加的一個比docker attach命令更加方便的命令。
和docker exec差不多方便的命令是nsenter工具。
nsenter 是一個可以用來進入到目標程序說在 namespace 中運行命令的工具,一般可以用於在容器外 debug 容器中運行的程序。
二、安裝
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安裝
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 不錯的文章