Kubernetesの動作を理解するのに重要なdockerの機能

このエントリはKubernetesアドベントカレンダー2014の4日目です。

今日はdocker側にフォーカスして、Kubernetesの動作を理解するのに重要なdockerの機能を紹介します。

  • コンテナリンク
  • ネットワーク
  • ボリューム

コンテナリンク

dockerでは docker run するときに --link を指定することでコンテナ同士を接続することができます。

書式は --link コンテナ名:エイリアス です。こうすると、コンテナに環境変数として エイリアス_PORT などが渡されるので、コンテナで環境変数から接続先情報を読み込んで接続できます。

接続を受ける側は普通に EXPOSE して起動します。

[root@localhost ~]# docker run -d -P --name myredis dockerfile/redis redis-server
08f9cd8359ad3a47758ccc7a9a8f421309c65bd544faff32130d5a1472668deb
[root@localhost ~]# docker ps
CONTAINER ID        IMAGE                     COMMAND             CREATED             STATUS              PORTS                     NAMES
08f9cd8359ad        dockerfile/redis:latest   "redis-server"      2 seconds ago       Up 2 seconds        0.0.0.0:49153->6379/tcp   myredis

次に接続する側を起動します。 --link 有無で環境変数を比較すると、 --link によって環境変数が設定されていることがわかります。

[root@localhost ~]# docker run --rm centos:centos7 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=b8092c6d13a7
HOME=/root
[root@localhost ~]# docker run --rm --link myredis:redis centos:centos7 env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=b9803eb2e636
REDIS_PORT=tcp://172.17.0.2:6379
REDIS_PORT_6379_TCP=tcp://172.17.0.2:6379
REDIS_PORT_6379_TCP_ADDR=172.17.0.2
REDIS_PORT_6379_TCP_PORT=6379
REDIS_PORT_6379_TCP_PROTO=tcp
REDIS_NAME=/jolly_blackwell/redis
HOME=/root

また同時にコンテナ内の /etc/hosts に参照名とIPアドレスが追記されます。

[root@localhost ~]# docker run --rm --link myredis:redis centos:centos7 cat /etc/hosts
172.17.0.5      c5c622fa373c
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
172.17.0.2      redis

注意点としては、この環境変数はコンテナ起動時に決定されるため、バックエンドのコンテナが再起動したりして接続情報が変わった時には追従してくれません。

  1. バックエンドコンテナを起動
  2. フロントエンドコンテナを起動
  3. バックエンドコンテナをstopしてstart

した場合に、フロントエンドコンテナは古い情報を持ち続けることになります。

Kubernetesでは service を1層はさむことでこの問題を解決しています。

参考: Linking containers together - Docker Documentation

ネットワーク

参考: Advanced networking - Docker Documentation

bridgeしてコンテナが直接ローカルネットワークに参加する

dockerではコンテナとIPで通信するために、デフォルトで docker0 というbridge interfaceを作成して利用します。このとき利用するIPアドレス帯はホストが利用していないものから選択されます。

このインターフェイス docker0 を使うのをやめて直接ホストのインターフェイスからbridgeすることで、コンテナが直接LANのIPアドレスを持つことができます。ただしこの方法はGCEのAdvanced Routing機能を利用する前提のようです(試せてない)。

KubernetesのDesignドキュメントでは、dockerの起動オプションで --bridge=cbr0 --iptables=false すべしとなっています。

なお、CentOS7でbridge interfaceのcbr0を作成し、eth1に接続する方法は以下のとおりです。 (NetworkManagerがONなので nmcli を使います) 今回は作成後に 172.16.0.1/16 を付与しています。

[root@localhost ~]# nmcli con add type bridge ifname cbr0
[root@localhost ~]# nmcli con modify bridge-cbr0 bridge.stp no
[root@localhost ~]# nmcli con add type bridge-slave ifname eth1 master bridge-cbr0
[root@localhost ~]# nmcli con mod bridge-cbr0 ipv4.method manual ipv4.addresses "172.16.0.1/16"

この状態でdockerの起動オプションに --bridge=cbr0 --iptables=false を指定すればokです。 dockerをyumでインストールした場合は /etc/sysconfig/docker に記載します。 ( --selinux-enabled はもともと記載があったものです)

OPTIONS=--selinux-enabled --bridge=cbr0 --iptables=false
[root@localhost ~]# systemctl restart docker.service
[root@localhost ~]# ps aux | grep docke[r]
root      2541  1.0  1.6 208600  8452 ?        Ssl  19:55   0:00 /usr/bin/docker -d --selinux-enabled --bridge=cbr0 --iptables=false

GCEで動かすのであれば利用を検討してもよいかもしれませんね(試せてない)。

docker0のアドレス帯を指定する

dockerでは docker0 で利用するIPアドレス帯を指定できます。

例えば 192.168.11.0/24 を利用したい場合にはdockerの起動オプションに --bip=CIDR を指定します。

dockerをyumでインストールした場合は /etc/sysconfig/docker に記載します。 ( --selinux-enabled はもともと記載があったものです)

OPTIONS=--selinux-enabled --bip=192.168.11.1/24

bridge interfaceを指定するとローカルネットワークに直接参加できますが、クラウド環境だとIPアドレスが自由にならない環境もありますよね。 Kubernetesではbridgeとは別の方法として、OpenVSwitchやflannelを使って各 Miniondocker0 同士を直接接続する方法があります。この方法を利用する場合、 各 Minion 間でIPアドレス帯が重複すると通信できず困ったことになるので、この方法で Minion ごとにIPアドレス帯を指定することで衝突を回避しています。 (どの Minion がどのアドレス帯を利用するかは別途決定する必要があり、 flannel を使う場合は flannel がいい感じに設定してくれます)

コンテナのネットワーク接続

dockerは docker run するときにネットワークをどのように接続するか設定できます。

--net=bridge がデフォルトですが、 --net="container:コンテナ名orコンテナID" とすることでネットワークを別コンテナに委譲できます。 Kubernetesでも使っているので覚えておいてください。

ボリューム

参考: Managing data in containers - Docker Documentation

データをコンテナの管理外に出す

dockerでは docker run するときに(もしくは Dockerfile で)、コンテナ内の特定ディレクトリをコンテナの管理外にすることができます。

コンテナ管理外となったファイルはホストのディレクトリに直接保存されます。

試しに /tmp をボリューム化してbarというファイルを配置してみます。

[root@localhost ~]# docker run -it -v /tmp --name dvtest centos:centos7 touch /tmp/bar

docker inspect でホスト上のどこに実体のディレクトリがあるか確認できます。

[root@localhost ~]# docker inspect  -f '{{ .Volumes }}' dvtest
map[/tmp:/var/lib/docker/vfs/dir/6b40f03e6b0dbaba3f5a5180bf8319dfa95c1831cd876154fbaad57a9eb230cf]
[root@localhost ~]# ls /var/lib/docker/vfs/dir/6b40f03e6b0dbaba3f5a5180bf8319dfa95c1831cd876154fbaad57a9eb230cf
bar

ホストのディレクトリをマウントする

dockerで docker run するときに -v ホスト側のディレクトリ:コンテナ上のディレクトリ でホストのディレクトリを指定すると、ホスト側のディレクトリを指定することができます。

[root@localhost ~]# ls -al /tmp/dvtest_tmp/
合計 4
drwxr-xr-x  2 root root    6 11月 30 20:52 .
drwxrwxrwt. 8 root root 4096 11月 30 20:52 ..
[root@localhost ~]# docker run -it -v /tmp/dvtest_tmp:/tmp --name dvtest centos:centos7 touch /tmp/bar
[root@localhost ~]# ls -al /tmp/dvtest_tmp/
合計 4
drwxr-xr-x  2 root root   16 11月 30 20:53 .
drwxrwxrwt. 8 root root 4096 11月 30 20:52 ..
-rw-r--r--  1 root root    0 11月 30 20:53 bar

ホスト側のデータを参照するときによく使います。 例えばローカルでWebアプリを開発をしている時にdockerでテスト動作させるためのコンテナを起動するようなシーンで利用しています。

$ docker run -d -P --name djangotest
    -v `pwd`:/opt/myapp \
    -e DJANGO_SETTINGS_MODULE=settings_dev \
    -e PYTHONPATH=/opt/app \
    netmarkjp/myapp-dev \
    python /opt/myapp/manage.py runserver 0.0.0.0:8000

別コンテナのディレクトリをマウントする

データをコンテナの管理外に出した上で、別のコンテナからマウントすることができます。

マウントする側(別のコンテナのほう)を docker run するときに --volumes-from=コンテナIDorコンテナ名 で指定します。

[root@localhost ~]# docker run --volumes-from=dvtest centos:centos7 ls /tmp
bar

これを利用して、データのバックアップをしたりします。

[root@localhost ~]# docker run --rm --volumes-from dvtest -v /tmp/backup:/backup ubuntu:trusty tar zcfp /backup/data.tar.gz /tmp
tar: Removing leading `/' from member names
[root@localhost ~]# ls -al /tmp/backup/
合計 8
drwxr-xr-x  2 root root   24 11月 30 21:02 .
drwxrwxrwt. 9 root root 4096 11月 30 21:02 ..
-rw-r--r--  1 root root  137 11月 30 21:02 data.tar.gz

See also