このエントリは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
注意点としては、この環境変数はコンテナ起動時に決定されるため、バックエンドのコンテナが再起動したりして接続情報が変わった時には追従してくれません。
- バックエンドコンテナを起動
- フロントエンドコンテナを起動
- バックエンドコンテナを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を使って各 Minion
の docker0
同士を直接接続する方法があります。この方法を利用する場合、 各 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