LoginSignup
11
14

More than 5 years have passed since last update.

vagrantの代わりにdockerで仮想的な複数ホストを構築する際のTIPS

Last updated at Posted at 2015-12-19

複数台のホストを使ったテストをやりたいときに実際にホストを複数台用意するのは面倒なので、自身が使用しているのマシン上で仮想的に複数台のホストを用意したいと思います。
多くの場合はvagrantで複数のVMを立ち上げてテストすることが多いと思います。
しかし使っているマシンが非力だとVMを3個くらい立ち上げるだけで悲鳴をあげることがあります。
ちなみに自分のラップトップは4core4GmemなのでVMを立ち上げるのは厳しいです。

そこでdockerを使って複数ホスト(コンテナ)を立ち上げて代替させる方法とその際に役に立つ小技をメモ代わりに残しておきます。
なお、基本的にはDockerの新しい何かを解説するというわけではなく、既にいろんなところで解説されていることをまとめているだけです。

dockerのネットワーク基礎の基礎

dockerのネットワークについて詳しいことは他の記事でたくさん解説されているので省略します。

dockerはコンテナを作成すると(特に何も設定していない場合)docker0という仮想ブリッジにぶら下がる形でコンテナに対してインターフェイスとIPアドレスが割り当てられます。
コンテナ内からホスト外に出る場合はdocker0を通ってNATされて外と通信されますが、
コンテナ同士の通信はdocker0の中のネットワークで閉じているのでNATされず直接コンテナのIPアドレスで通信することができます。

$ ifconfig docker0
docker0   Link encap:Ethernet  HWaddr 02:42:a2:84:a7:93  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:a2ff:fe84:a793/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2982 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6274 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:171299 (171.2 KB)  TX bytes:8451392 (8.4 MB)

デフォルトでは172.17.0.1/16のネットワークが構築され、コンテナを起動すると(ある程度連番ではあるけど)この空間からランダムなIPアドレスがコンテナに割り当てられます。
自身のマシン(以下ホスト)から各コンテナへも相互に通信することができます。

なのでdockerを使って仮想ホストを構築する際は、特に何も考えずにコンテナを起動すれば終わりです。

3台のコンテナを起動してみる

最初にディレクトリを作成してから端末を3つ開いてdocker runで3つのコンテナを起動します。

[host] $ mkdir /tmp/shared
[host] $ docker run -it --name node1 -h node1 -v /tmp/shared:/shared ubuntu:trusty bash
[host] $ docker run -it --name node2 -h node2 -v /tmp/shared:/shared ubuntu:trusty bash
[host] $ docker run -it --name node3 -h node3 -v /tmp/shared:/shared ubuntu:trusty bash

--nameでコンテナ名を指定しておくことでコンテナの操作を簡単にしておきます。
-hでホスト名を指定してどのコンテナなのかわかりやすくしておきます。
-vでホスト側のディレクトリを各コンテナにマウントしておくことで、vagrantのSynced Folderのようなものになります。

--rmをつけてないのは間違えて落としてしまっても元に戻せるようにしてます。
もしコンテナを終了した場合は以下のコマンドを実行すれば再度コンテナに入ることができます。

## node1の場合
[host] $ docker start node1
[host] $ docker attach node1

コンテナのIPアドレスを確認

各コンテナのIPアドレスを確認したい場合はホスト側からdocker inspectで取得するのが一番良いです。

[host] $ docker inspect node1 node2 node3 | jq '.[] | {name: .Name, addr : .NetworkSettings.IPAddress}'
{
  "addr": "172.17.0.2",
  "name": "/node1"
}
{
  "addr": "172.17.0.3",
  "name": "/node2"
}
{
  "addr": "172.17.0.4",
  "name": "/node3"
}

単体でIPアドレスだけ取りたければ以下のようにします。

[host] $ docker inspect node1 | jq '.[].NetworkSettings.IPAddress'
"172.17.0.2"

コンテナ間とコンテナホスト間で通信する

node1で簡易HTTPサーバを起動してnode2とnode3から通信してみます。

まずはnode1でncを使ってHTTPサーバを起動します。

[node1] $ while :; do (echo -e "HTTP/1.0 200 OK\n\nHelloWorld" | nc -l 8080) || break; done

node2とnode3からnode1に対してアクセスします。

[node2] $ curl -D - 172.17.0.2:8080
HTTP/1.0 200 OK

HelloWorld

[node3] $ curl -D - 172.17.0.2:8080
HTTP/1.0 200 OK

HelloWorld

次にホスト側からコンテナ(node1)にアクセスします。

[host] $ curl -D - 172.17.0.2:8080
HTTP/1.0 200 OK

HelloWorld

次にホスト側でHTTPサーバを立ててnode1からアクセスしてみます。
docker0経由で来ていることを明確にするために172.17.0.1でlistenしています。

[host] $ while :; do (echo -e "HTTP/1.0 200 OK\n\nHelloWorld" | nc -l 172.17.0.1 8080) || break; done

[node1] $ curl -D - 172.17.0.1:8080
HTTP/1.0 200 OK

HelloWorld

ホストとコンテナ間でファイルを共有する

コンテナ起動時に-vオプションで/tmp/sharedをマウントしているのでホスト側から/tmp/shared以下にファイルをおけば、各コンテナから参照することができます。コンテナ側は/sharedにマウントされています。

[host] $ echo "hello" > /tmp/shared/foo

[node1] $ cat /shared/foo 
hello
[node2] $ cat /shared/foo 
hello
[node2] $ cat /shared/foo 
hello

ディレクトリをマウントしていない場合やファイルを一度移動させたりするのが面倒な場合はdocker cpコマンドを使えばホストとコンテナ間で直接ファイルを移動できます。

node1でファイルを生成してホスト側にコピーします。

[node1] $ echo "hello world" > /root/output.txt

[host] $ docker cp node1:/root/output.txt /tmp/ 
[host] $ cat /tmp/output.txt
hello world

今度にホスト側のファイルをnode2にコピーします。

[host] $ docker cp /tmp/output.txt node2:/root/

[node2] $ cat /root/output.txt
hello world

ちょっとしたスクリプトを書けば実行ファイルを配ったりするのが楽です。

[host] $ for N in node{1..3}; \
     do docker cp /usr/local/bin/script.sh $N:/usr/local/bin/script.sh; \
   done

各コンテナに対してコマンドを実行する

いちいち各ノードの端末を開いてコマンドを実行するのは面倒なのでホスト側から操作したいこともあります。
docker execコマンドで各コンテナにアタッチしてコマンドを実行することができます。

[host] $ docker exec -it node1 hostname
node1

これもスクリプトを書いておけば各コンテナの状態を簡単に確認できます。

[host] $ for N in node{1..3}; do \
    echo "==$N=="; \
    docker exec -it $N hostname;\
  done
==node1==
node1
==node2==
node2
==node3==
node3

単体のコマンドを実行するというよりもvagrant sshのようにログインして調査したい場合はdocker execでbashなどを実行すればだいたいそのような挙動になります。

[host] $ docker exec -it node1 bash

通信をキャプチャする

コンテナの通信は必ずdocker0を通るのでホスト側からtcpdumpでdocker0をキャプチャすると全ての通信を見ることができます。

[host] $ sudo tcpdump -i docker0

ホストを増やす

起動したときのイメージからパッケージのインストールなどの操作をたくさん行った場合、ホストを追加して再度同様のコマンドを実行しなおすのは面倒です。
docker commitでコンテナからイメージを作り直し、そのイメージからコンテナを起動することですぐにホストを追加することができます。

node1からイメージを作りnode4を起動する。

[host] $ docker commit node1 nodeimage
[host] $ docker run -it --name node4 -h node4 -v /tmp/shared:/shared nodeimage bash 

node4のIPアドレスを確認と追加でインストールしていたcurlが入っているか確認する。

[host] $ docker inspect node4 | jq '.[].NetworkSettings.IPAddress'
"172.17.0.5"
[host] $ docker exec -it node4 sh -c 'dpkg -l | grep curl'
ii  curl                            7.35.0-1ubuntu2                  amd64        command line tool for transferring data with URL syntax
ii  libcurl3:amd64                  7.35.0-1ubuntu2                  amd64        easy-to-use client-side URL transfer library (OpenSSL flavour)
11
14
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
11
14