Movatterモバイル変換


[0]ホーム

URL:


LoginSignup
203

Go to list of users who liked

154

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Dockerでデバッグ対象のコンテナにツールを入れずにtcpdump/straceなどを使うワンライナー

Last updated atPosted at 2019-07-13

はじめに

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 80

strace

$ 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:alpine

tcpdumpを使ってみる

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:latest

docker 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:latest

straceの場合は、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 permitted

Dockerはデフォルトではいくつかの特権操作は許可されておらず、 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 started

straceでシステムコールトレースができてることが分かります。

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

image.png

ワンライナーを組み立てる

まぁ仕事で使ってる環境に公式イメージじゃない野良のイメージを使うのも若干抵抗があるので、自分がよく使うものだけ適当に入れたイメージを作って使うのがよいとは思います。

ただ個人的にはイメージのメンテすらあまりしたくないので、オンデマンドでビルドして使う技も思いつきました。
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 bytes

straceだとこんなかんじでしょうか。

$ 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

おわりに

ワンライナーでデバッグ用のツールをインストールして、デバッグ用のコンテナを立てる方法は、使いたいツールを変えればいろいろ応用が効くので、シェルのヒストリやスニペットに保存してシュッと呼び出すとよさそうです。

これであんなコンテナやこんなコンテナをデバッグし放題ですねー。

203

Go to list of users who liked

154
0

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
203

Go to list of users who liked

154

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?


[8]ページ先頭

©2009-2025 Movatter.jp