LoginSignup
10
7

More than 5 years have passed since last update.

Docker containerのnetwork namespaceでdebugしたい

Posted at

コンテナからインターネットに抜けられないとか、ネットワーク設定にトラブルがありそうだという時にコンテナに入っていろいろコマンドを試したいが、scratchイメージでbashが入っていないとかログインできないことがあります。そういう時に、どうするかという話です。単的にいうと下記の3行ですが、タラタラと説明してみたいと思います。

pid=$(docker inspect $CONTAINER_ID --format '{{.State.Pid}}')
sudo ln -s /proc/${pid}/ns/net /var/run/netns/tmp-container-ns
sudo ip netns exec tmp-container-ns bash

Docker Containerのネットワークについて

Docker Container(DefaultのrunC container runtime)では、Container環境とHost環境のリソースの隔離化に、Linuxカーネルの機能であるnetwork namespaceを使います。これは1つのLinux OS(カーネル)環境でネットワークの設定を複数独立して持つことを許容してくれます。
例えば

  • PID 50のプロセスでは、
    • 10.0.0.2のipを使い
    • デフォルトゲートウェイに10.0.0.1を設定する
  • PID 51のプロセスでは、
    • 10.0.0.3のipを使い
    • デフォルトゲートウェイに10.0.0.254を設定する

ということが可能になります。

Dockerもこのようにして、各コンテナごとにnetwork namaespaceを分けることで、コンテナごとに独立したネットワーク設定を保持しています。

どうやってnetwork namespaceを作るのか

network namespaceの作成には、「ip」コマンドが利用可能です。

新しく、network namespaceを作成します

$ ip netns add newns

namespace一覧の確認

$ ip netns
newns

作成したnamespaceを指定して、好きなコマンド/プログラムを実行

$ ip netns exec newns ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

上記コマンドでは、単純に新しいnamespace環境でipアドレスの確認をしていますが、loopback interfaceのみが表示されてると思います。

ip netnsでdockerの作ったnamespaceが表示されない?

上記で、ip netnsを使えばデバッグできるのでは?と思うと思うのですが、実はdockerが作るnamespaceは ip netnsコマンドで操作できないのです。。。
ip netnsで既存のnamespaceリストを表示できますが、ここでの情報は厳密には、namespaceではありません。/var/run/netns 配下のファイル一覧を単純に表示しているだけです。

root@vagrant:/var/run/netns# touch /var/run/netns/normalfile
root@vagrant:/var/run/netns# ip netns
RTNETLINK answers: Invalid argument
RTNETLINK answers: Invalid argument
normalfile

エラーは出るので、表示する際に該当のfileがnamespaceの識別子かどうか確認しているようですがご覧の通り、namespaceとして表示されています。

正直なところ、namespaceが内部的にどのように実装されているのかまだわかっていませんが([*1]要追加調査)、スペシャルデバイス上のファイルとして作られるようです。3h/3dが具体的にどういう意味なのかなどは今後の課題とします。

vagrant@vagrant:/var/run/netns$ stat test
  File: 'test'
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: 3h/3d   Inode: 4026532349  Links: 1

ここでわかっていただきたいことは、特定のinode番号を持ったファイルがnamespaceの識別子になっており(必ずしもone hopである必要はないnamespaceを示すinodeへのsymbolic linkでも可) 通常 ip netns add で([*2]要追加調査)追加したnamespace識別子は /var/run/netns に追加されるということ

ip netns で dockerが作ったnamespaceを表示し、exec できるようにする

ここまでくればやるべきことは単純明解で、

  1. dockerが作ったnamespace識別子ファイルを探して
  2. なんとかして /var/run/netns 配下に置く

dockerが作ったnamespace識別子ファイルを探して

/proc/\${PID}/ns では、${PID}が利用しているすべての種類のnamespace識別子へのシンボリックリンクが置いてあります。のでまず該当のdocker containerのホスト側でのpidを調べます。

$ docker inspect ${container_id} --format '{{.State.Pid}}'
3456

でnamespace識別子と呼んでいるのはこれです。

$ ls -l /proc/3456/ns/net
lrwxrwxrwx 1 root root 0 Sep  2 08:25 /proc/3456/ns/net -> net:[4026531957]

/proc/3456/ns/net を /var/run/netns 配下に置く

シンボリックリンクでいいので、シンボリックリンクを置いてあげます

$ ln -s /proc/3456/ns/net /var/run/netns/docker-ns-3456

ここまでくると下記のコマンドで、dockerのコンテナのネットワーク情報で好きなコマンドが実行できます。

$ ip netns
docker-ns-3456
$ ip netns exec docker-ns-3456 bash

とここまで、dockerの作ったnetwork namespaceを指定してipコマンドでデバッグする方法を記述しましたが、簡単なbash scriptで、dockerコンテナのnetwork namespaceをすべて、 ip netns で表示、利用できるようにするbash scriptを書きました。興味がある人は是非。

下記の要調査事項については、これから調べていきたいと思います。
*1 namespaceファイルの仕組み、statのdeviceの意味を明確に
*2 特定のシステムコールの結果、カーネルが追加するのか、ユーザランドプログラムが追加するのかを明確に

10
7
0

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
10
7