Docker を支える Linux カーネル機能 Network Namespace から docker exec の実装が ip netns exec である雰囲気をつかむ

f:id:kyagi:20180216163134p:plain

Linux の Network Namespace を ip netns exec で操作しているうちに「docker exec の正体」がなんとなく見えてきた。

Bridge Networking Deep Dive — Docker Kubernetes Lab 0.1 documentation

デフォルトでは docker はコンテナの Network Namespace を見せないようにしている。これは docker exec からのみコンテナの Network Namespace にアクセスを許可するように意図しているものに感じられる。OOP でクラスのメンバを private や protected で保護することと似ているかもしれない。

$ docker run --rm -d --name b1 busybox sh -c "while true; do sleep 3600; done"
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
0e3030605661        busybox             "sh -c 'while true..."   About an hour ago   Up About an hour                        b1

いつも操作を忘れてしまうので、シェル関数を作っておく。docker-netns show [CONTAINER-NAME] で ip netns 経由で exec できるようになる。隠蔽された状態に戻したい時は docker-netns stash [CONTAINER-NAME] を実行する。

function docker-netns () {
  local action=$1
  local cname=$2

  case ${action} in
    show)
      docker inspect ${cname} | jq ".[].State.Pid" | xargs -i sudo ln -s /proc/{}/ns/net /var/run/netns/${cname}
      echo "Network namespace for container ${cname} is visible now."
      ;;
    stash)
      sudo rm /var/run/netns/${cname}
      echo "Network namespace for container ${cname} is invisible now."
      ;;
    *) :
      ;;
  esac

  ls -l /var/run/netns/${cname}
}
$ sudo ip netns list
$

$ docker-netns show b1
Network namespace for container b1 is visible now.
lrwxrwxrwx 1 root root 18 Jan 31 00:30 /var/run/netns/b1 -> /proc/11385/ns/net

$ sudo ip netns list
b1

$ sudo ip netns exec b1 ip a # 以下 docker exec と内容が同じ
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
91: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever

$ docker exec -it b1 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
91: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever

$ docker-netns stash b1
Network namespace for container b1 is invisible now.
ls: cannot access /var/run/netns/b1: No such file or directory

$ sudo ip netns list
$ 

$ sudo ip netns exec b1 ip route
Cannot open network namespace "b1": No such file or directory

ただ ip netns exec b1 ip a でなく ip netns exec b1 hostname だとコンテナのホスト名 0e3030605661 は返ってこずに docker host のホスト名が返ってくるので、あくまでネットワークに限定した名前空間である感覚がしている。その意味では docker exec は Network Namespace だけでなく Process Namespace やその他の名前空間をまとめて隔離した空間に対するインターフェイスなのではないか。

ip netns exec が docker exec の正体というのはただの個人的感覚でしかない。今度、知り合いのカーネルハッカーに聞いてみよう...。自分でソースを読めと言われるに決まっているのだけれど。ただこういった局所的な興味からカーネルソースに飛び込んでみるのも目的がはっきりしていていいかもしれない。(´▽`)