Go to list of users who liked
Share on X(Twitter)
Share on Facebook
More than 5 years have passed since last update.
Dockerでデバッグ対象のコンテナにツールを入れずにtcpdump/straceなどを使うワンライナー
はじめに
Dockerであんなコンテナやこんなコンテナを動かしてると、なんかうまく動かなくて、デバッグのためにtcpdumpとかstraceなどのツールが使いたくなることが稀によくあります。
そんな時、デバッグ対象のコンテナ内にツールを一時的にインストールしちゃうというのが、まぁ簡単で分かりやすいんですが、デバッグ対象のコンテナを汚すのはできれば避けたいところです。
Dockerのコンテナの分離というのは、結局のところLinuxのリソースの名前空間の分離であるので、逆に同じ名前空間を共有すれば、デバッグ用に立てた隣のコンテナから、デバッグ対象のコンテナのネットワークやプロセスの状態を観察することも可能です。
また、docker buildはDockerfileを標準入力から受け取ることもできるので、ワンライナーにしてデバッグ用のコンテナをシュッと呼び出せるようにしてみました。
TL;DR
結論だけ知りたい人はこちらをどうぞ。<target> のところはデバッグ対象のコンテナ名またはコンテナIDで読み替えて下さい。
tcpdump
echo 'FROM alpine\nRUN apk add --no-cache tcpdump' \ | docker build -t debug -f - . \ && docker run -it --rm --net container:<target> debug tcpdump -nn -X port 80strace
$ echo 'FROM alpine\nRUN apk add --no-cache strace' \| docker build -t debug -f - . \&& docker run -it --rm --pid container:<target> --cap-add sys_ptrace debug strace -fp 1ワンライナーでデバッグ用のツールをインストールして、デバッグ用のコンテナを立てる方法は、使いたいツールを変えればいろいろ応用が効くので、シェルのヒストリやスニペットに保存してシュッと呼び出すとよさそうです。
解説
環境
稼働確認した手元のDockerのバージョンは 19.03.0-rc2です。
$ docker versionClient: Docker Engine - Community Version: 19.03.0-rc2 API version: 1.40 Go version: go1.12.5 Git commit: f97efcc Built: Wed Jun 5 01:37:53 2019 OS/Arch: darwin/amd64 Experimental: falseServer: Docker Engine - Community Engine: Version: 19.03.0-rc2 API version: 1.40 (minimum version 1.12) Go version: go1.12.5 Git commit: f97efcc Built: Wed Jun 5 01:42:10 2019 OS/Arch: linux/amd64 Experimental: true containerd: Version: v1.2.6 GitCommit: 894b81a4b802e4eb2a91d1ce216b8817763c29fb runc: Version: 1.0.0-rc8 GitCommit: 425e105d5a03fabd737a126ad93d62a9eeede87f docker-init: Version: 0.18.0 GitCommit: fec3683事前準備
ここではデバッグ対象として、適当なnginxのコンテナをtargetという名前で起動しておきます。
$ docker run -it --rm -p 8080:80 --name target nginx:alpinetcpdumpを使ってみる
tcpdumpだけ入ったDockerfileを作って、debugというタグを付けてビルドします。
$ cat DockerfileFROM alpineRUN apk add --no-cache tcpdump$ docker build -t debug .Sending build context to Docker daemon 2.048kBStep 1/2 : FROM alpine ---> 3f53bb00af94Step 2/2 : RUN apk add --no-cache tcpdump ---> Using cache ---> a000cadec8f5Successfully built a000cadec8f5Successfully tagged debug:latestdocker run --network container:<name|id> で対象のコンテナのネットワークにアタッチできます。
デバッグ用のコンテナをtargetのコンテナのネットワークにアタッチしつつ、tcpdumpを実行します。
$ docker run -it --rm --network container:target debug tcpdump -nn -X port 80 tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes試しに手元からcurlしてみると
$ curl http://localhost:8080/tcpdumpでパケットキャプチャできてることが分かります。
15:01:16.352790 IP 172.17.0.1.58354 > 172.17.0.2.80: Flags [S], seq 178914825, win 29200, options [mss 1460,sackOK,TS val 13258980 ecr 0,nop,wscale 7], length 0 0x0000: 4500 003c 56ed 4000 4006 8ba9 ac11 0001 E..<V.@.@....... 0x0010: ac11 0002 e3f2 0050 0aaa 0609 0000 0000 .......P........ 0x0020: a002 7210 5854 0000 0204 05b4 0402 080a ..r.XT.......... 0x0030: 00ca 50e4 0000 0000 0103 0307 ..P.........15:01:16.352899 IP 172.17.0.2.80 > 172.17.0.1.58354: Flags [S.], seq 1290103227, ack 178914826, win 28960, options [mss 1460,sackOK,TS val 13258980 ecr 13258980,nop,wscale 7], length 0 0x0000: 4500 003c 0000 4000 4006 e296 ac11 0002 E..<..@.@....... 0x0010: ac11 0001 0050 e3f2 4ce5 69bb 0aaa 060a .....P..L.i..... 0x0020: a012 7120 5854 0000 0204 05b4 0402 080a ..q.XT.......... 0x0030: 00ca 50e4 00ca 50e4 0103 0307 ..P...P.....staceを使ってみる
同様にstraceをインストールしたDockerfileを用意してビルドします。
$ cat DockerfileFROM alpineRUN apk add --no-cache strace$ docker build -t debug .Sending build context to Docker daemon 2.048kBStep 1/2 : FROM alpine ---> 3f53bb00af94Step 2/2 : RUN apk add --no-cache strace ---> Using cache ---> b357653376b3Successfully built b357653376b3Successfully tagged debug:lateststraceの場合は、docker run --pid container:<name|id> で対象のコンテナのPID名前空間にアタッチします。
とりあえずpsを打ってみると、プロセスが見えてるのが分かります。
$ docker run -it --rm --pid container:target debug psPID USER TIME COMMAND 1 root 0:00 nginx: master process nginx -g daemon off; 6 101 0:00 nginx: worker process 7 101 0:00 nginx: worker process 8 101 0:00 nginx: worker process 9 101 0:00 nginx: worker process 16 root 0:00 psただstraceを起動してみると、権限エラーが出ます。
$ docker run -it --rm --pid container:target debug strace -fp 1strace: attach: ptrace(PTRACE_SEIZE, 1): Operation not permittedDockerはデフォルトではいくつかの特権操作は許可されておらず、 straceにはdocker run --cap-add sys_ptrace が明示的に許可が必要です。
$ docker run -it --rm --pid container:target --cap-add sys_ptrace debug strace -fp 1strace: Process 1 attachedrt_sigsuspend([], 8こんどはうまくstraceが起動できました。ためしにnginx -s reload でプロセスにシグナルを送ってみましょう。
$ docker exec -it target nginx -s reload2019/07/13 15:26:45 [notice] 87#87: signal process startedstraceでシステムコールトレースができてることが分かります。
strace: Process 1 attachedrt_sigsuspend([], 8) = ? ERESTARTNOHAND (To be restarted if no handler)--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=87, si_uid=0} ---clock_gettime(CLOCK_REALTIME, {tv_sec=1563031605, tv_nsec=559042300}) = 0rt_sigreturn({mask=[HUP INT QUIT USR1 USR2 ALRM TERM CHLD WINCH IO]}) = -1 EINTR (Interrupted system call)clock_gettime(CLOCK_REALTIME, {tv_sec=1563031605, tv_nsec=559133400}) = 0clock_gettime(CLOCK_REALTIME, {tv_sec=1563031605, tv_nsec=559208800}) = 0clock_gettime(CLOCK_REALTIME, {tv_sec=1563031605, tv_nsec=559260400}) = 0uname({sysname="Linux", nodename="976678564bed", ...}) = 0open("/etc/nginx/nginx.conf", O_RDONLY) = 3(参考) デバッグ用のイメージnicolaka/netshoot
毎回デバッグ用のツールをインストールするのがめんどくさければ、デバッグによく使うコマンドを全部入りしたnicolaka/netshootというDockerイメージがあります。
何が入ってるかはDockerfileこれです。
- apache2-utils
- bash
- bind-tools
- bird
- bridge-utils
- busybox-extras
- calicoctl
- conntrack-tools
- ctop
- curl
- dhcping
- drill
- ethtool
- file
- fping
- iftop
- iperf
- iproute2
- iptables
- iptraf-ng
- iputils
- ipvsadm
- libc6-compat
- liboping
- mtr
- net-snmp-tools
- netcat-openbsd
- netgen
- nftables
- ngrep
- nmap
- nmap-nping
- openssl
- py-crypto
- py2-virtualenv
- python2
- scapy
- socat
- strace
- tcpdump
- tcptraceroute
- util-linux
- vim
ちなみにREADMEに貼ってある、どのレイヤを調査するのにどのツールが必要かの画像の元ネタはこれのようです。まじすげーなこのサイト。
引用元:http://www.brendangregg.com/linuxperf.html
ワンライナーを組み立てる
まぁ仕事で使ってる環境に公式イメージじゃない野良のイメージを使うのも若干抵抗があるので、自分がよく使うものだけ適当に入れたイメージを作って使うのがよいとは思います。
ただ個人的にはイメージのメンテすらあまりしたくないので、オンデマンドでビルドして使う技も思いつきました。
docker buildはDockerfileを標準入力から受け取ることもできるので、 tcpdumpの例をワンライナーで書くとこんなかんじです。
$ echo 'FROM alpine\nRUN apk add --no-cache tcpdump' \ | docker build -t debug -f - . \ && docker run -it --rm --net container:target debug tcpdump -nn -X port 80Sending build context to Docker daemon 2.607kBStep 1/2 : FROM alpine ---> 3f53bb00af94Step 2/2 : RUN apk add --no-cache tcpdump ---> Using cache ---> a000cadec8f5Successfully built a000cadec8f5Successfully tagged debug:latesttcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytesstraceだとこんなかんじでしょうか。
$ echo 'FROM alpine\nRUN apk add --no-cache strace' \| docker build -t debug -f - . \&& docker run -it --rm --pid container:target --cap-add sys_ptrace debug strace -fp 1Sending build context to Docker daemon 2.607kBStep 1/2 : FROM alpine ---> 3f53bb00af94Step 2/2 : RUN apk add --no-cache strace ---> Using cache ---> b357653376b3Successfully built b357653376b3Successfully tagged debug:lateststrace: Process 1 attachedrt_sigsuspend([], 8おわりに
ワンライナーでデバッグ用のツールをインストールして、デバッグ用のコンテナを立てる方法は、使いたいツールを変えればいろいろ応用が効くので、シェルのヒストリやスニペットに保存してシュッと呼び出すとよさそうです。
これであんなコンテナやこんなコンテナをデバッグし放題ですねー。
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme

