天天看點

調試 Docker 容器内部程序總結

調試 Docker 容器内部程式總結
首發于官方部落格: https://nebula-graph.com.cn/posts/debug-nebula-graph-processes-docker/

摘要:本文以 Nebula Graph 程序為例,講解如何不破壞原有容器的内容,也不用在其中安裝任何的工具包前提下,像在本地一樣來調試程序

需求

在開發或者測試過程中,我們經常會用到

vesoft-inc/nebula-docker-compose

這個 repo 下的部署方式,因為當初為了盡可能的壓縮每個

Nebula Graph

服務的 docker 鏡像的體積,是以開發過程中常用的一切工具都沒有安裝,甚至連編輯器 VIM 都沒有。

這給我們在容器内部定位問題帶來一定的難度,因為每次隻能去 install 一些工具包,才能開展接下來的工作,甚是費事。其實調試容器内部的程序還有另外一種方式,不需要破壞原有容器的内容,也不用在其中安裝任何的工具包就能像在本地一樣來調試。

這種技術在 k8s 環境下其實已經挺常用,就是 sidecar 模式。原理也比較樸素就是再起一個容器然後讓這個容器跟你要調試的容器共享相同的 pid/network 的 namespace。這樣原容器中的程序和網絡空間在調試容器中就能“一覽無餘”,而在調試容器中安裝了你想要的一切順手工具,接下來的舞台就是留于你發揮了。

示範

接下來我就示範一下如何操作:

我們先用上述的 docker-compose 方式在本地部署一套 Nebula Graph 叢集,教程見 repo 中的 README。部署好後的結果如下:

$ docker-compose up -d
Creating network "nebula-docker-compose_nebula-net" with the default driver
Creating nebula-docker-compose_metad1_1 ... done
Creating nebula-docker-compose_metad2_1 ... done
Creating nebula-docker-compose_metad0_1 ... done
Creating nebula-docker-compose_storaged2_1 ... done
Creating nebula-docker-compose_storaged1_1 ... done
Creating nebula-docker-compose_storaged0_1 ... done
Creating nebula-docker-compose_graphd_1    ... done
$ docker-compose ps
              Name                             Command                       State                                             Ports
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
nebula-docker-compose_graphd_1      ./bin/nebula-graphd --flag ...   Up (health: starting)   0.0.0.0:32907->13000/tcp, 0.0.0.0:32906->13002/tcp, 0.0.0.0:3699->3699/tcp
nebula-docker-compose_metad0_1      ./bin/nebula-metad --flagf ...   Up (health: starting)   0.0.0.0:32898->11000/tcp, 0.0.0.0:32896->11002/tcp, 45500/tcp, 45501/tcp
nebula-docker-compose_metad1_1      ./bin/nebula-metad --flagf ...   Up (health: starting)   0.0.0.0:32895->11000/tcp, 0.0.0.0:32894->11002/tcp, 45500/tcp, 45501/tcp
nebula-docker-compose_metad2_1      ./bin/nebula-metad --flagf ...   Up (health: starting)   0.0.0.0:32899->11000/tcp, 0.0.0.0:32897->11002/tcp, 45500/tcp, 45501/tcp
nebula-docker-compose_storaged0_1   ./bin/nebula-storaged --fl ...   Up (health: starting)   0.0.0.0:32901->12000/tcp, 0.0.0.0:32900->12002/tcp, 44500/tcp, 44501/tcp
nebula-docker-compose_storaged1_1   ./bin/nebula-storaged --fl ...   Up (health: starting)   0.0.0.0:32903->12000/tcp, 0.0.0.0:32902->12002/tcp, 44500/tcp, 44501/tcp
nebula-docker-compose_storaged2_1   ./bin/nebula-storaged --fl ...   Up (health: starting)   0.0.0.0:32905->12000/tcp, 0.0.0.0:32904->12002/tcp, 44500/tcp, 44501/tcp           

這時我們分兩個場景來示範,一個是程序空間,一個是網絡空間。首先我們要先有一個順手的調試鏡像,我們就不自己建構了,從 docker hub 中找個已經打包好的用作示範,後期覺得不夠用,我們可以維護一份 nebula-debug 的鏡像,安裝我們想要的所有調試工具,此處先借用社群内的方案

nicolaka/netshoot

。我們先把鏡像拉取到本地:

$ docker pull nicolaka/netshoot
$ docker images
REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
vesoft/nebula-graphd     nightly             c67fe54665b7        36 hours ago        282MB
vesoft/nebula-storaged   nightly             5c77dbcdc507        36 hours ago        288MB
vesoft/nebula-console    nightly             f3256c99eda1        36 hours ago        249MB
vesoft/nebula-metad      nightly             5a78d3e3008f        36 hours ago        288MB
nicolaka/netshoot        latest              6d7e8891c980        2 months ago        352MB           

我們先看看直接執行這個鏡像會是什麼樣:

$ docker run --rm -ti nicolaka/netshoot bash
bash-5.0# ps
PID   USER     TIME  COMMAND
    1 root      0:00 bash
    8 root      0:00 ps
bash-5.0#           

上面顯示這個容器看不到任何 Nebula Graph 服務程序的内容,那麼我們給其加點參數再看看:

$ docker run --rm -ti --pid container:nebula-docker-compose_metad0_1 --cap-add sys_admin nicolaka/netshoot bash
bash-5.0# ps
PID   USER     TIME  COMMAND
    1 root      0:03 ./bin/nebula-metad --flagfile=./etc/nebula-metad.conf --daemonize=false --meta_server_addrs=172.28.1.1:45500,172.28.1.2:45500,172.28.1.3:45500 --local_ip=172.28.1.1 --ws_ip=172.28.1.1 --port=45500 --data_path=/data/meta --log_dir=/logs --v=15 --minloglevel=0
  452 root      0:00 bash
  459 root      0:00 ps
bash-5.0# ls -al /proc/1/net/
total 0
dr-xr-xr-x    6 root     root             0 Sep 18 07:17 .
dr-xr-xr-x    9 root     root             0 Sep 18 06:55 ..
-r--r--r--    1 root     root             0 Sep 18 07:18 anycast6
-r--r--r--    1 root     root             0 Sep 18 07:18 arp
dr-xr-xr-x    2 root     root             0 Sep 18 07:18 bonding
-r--r--r--    1 root     root             0 Sep 18 07:18 dev
...
-r--r--r--    1 root     root             0 Sep 18 07:18 sockstat
-r--r--r--    1 root     root             0 Sep 18 07:18 sockstat6
-r--r--r--    1 root     root             0 Sep 18 07:18 softnet_stat
dr-xr-xr-x    2 root     root             0 Sep 18 07:18 stat
-r--r--r--    1 root     root             0 Sep 18 07:18 tcp
-r--r--r--    1 root     root             0 Sep 18 07:18 tcp6
-r--r--r--    1 root     root             0 Sep 18 07:18 udp
-r--r--r--    1 root     root             0 Sep 18 07:18 udp6
-r--r--r--    1 root     root             0 Sep 18 07:18 udplite
-r--r--r--    1 root     root             0 Sep 18 07:18 udplite6
-r--r--r--    1 root     root             0 Sep 18 07:18 unix
-r--r--r--    1 root     root             0 Sep 18 07:18 xfrm_stat           

這次有點不一樣了,我們看到 metad0 的程序了,并且其 pid 還是 1。看到這個程序再想對其做點啥就好辦了,比如能不能直接在 gdb 中 attach 它,由于手邊沒有帶 nebula binary 的對應 image,就留給大家私下探索吧。

我們已經看到 pid 空間通過指定

--pid container:<container_name|id>

 可以共享了,那麼我們接下來看看網絡的情況,畢竟有時候需要抓個包,執行如下的指令:

bash-5.0# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name           

啥也沒有,跟預想的有點不一樣,我們有 metad0 這個程序不可能一個連接配接都沒有。要想看到這個容器内的網絡空間還要再加點參數,像如下方式再啟動調試容器:

$ docker run --rm -ti --pid container:nebula-docker-compose_metad0_1 --network container:nebula-docker-compose_metad0_1 --cap-add sys_admin nicolaka/netshoot bash
bash-5.0# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 172.28.1.1:11000        0.0.0.0:*               LISTEN      -
tcp        0      0 172.28.1.1:11002        0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:45500           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:45501           0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.11:33249        0.0.0.0:*               LISTEN      -
udp        0      0 127.0.0.11:51929        0.0.0.0:*                           -           

這回就跟上面的輸出不一樣了,加了

--network container:nebula-docker-compose_metad0_1

 運作參數後,metad0 容器内的連接配接情況也能看到了,那麼想抓包調試就都可以了。

總結

通過運作另外一個容器,并讓其跟想要調試的容器共享 pid/network namespace 是我們能像本地調試的關鍵。社群裡甚至還有人基于上述想法開發了一些小工具進一步友善使用:

推薦閱讀