Docker Orchestration Toolsのチュートリアル
この度DockerCon EU 2015に参加してきました。
のハンズオンラボの内容が面白そうだったのですが、私は自社で出店していたブースのお守りもあったため、当日はあまりできませんでした。ですので、帰国後自分で楽しんでみることにしました。
今回はAzureで動作させるときの手順を書いてみたいと思います。
Lab 2 : Docker Orchestrationこれが、Docker社が公開していた、ハンズオンです。
Swarm、Compose、そしてInterlockを使って、Swarmの環境にスケールするWeb-DBアプリケーションをデプロイするデモです。いろいろはまりましたが楽しかったです。
1. インスタンスの準備
実はこのチュートリアル、本来はAWSでインスタンスが提供されるようになっていました。
環境を作ってわかったのですが、そこまで行くのが結構めんどくさいからでした。
普通だと、docker-machineでちょろっとインスタンス用意したらDockerの環境環境構築など簡単です。ところが、本チュートリアルは、TLSを使わない前提になっているのですが、最新のdocker-machineはTLSをオフにすることができないようです。
つまり、TLSを使わないDocker環境を作らないといけないわけです。
また、チュートリアルでは3つのインスタンスを使いますが、最後の一つのインスタンスはスケールアウトするときにポートをたくさん使います。ですので、ポートをレンジで開ける必要がありますが、例えばAzureのクラシックVMはこれが得意ではありません。実際にやろうと思うと、PowerShellでちょろっとやらないといけないようです。
また、最初は、Ubuntu 14.04(LTS)を使うつもりでしたが、Dockerの標準のインストール手順で実施すると、その通りにできない感じでしたので、これを機会に、15.04に取り組んで見ることにしました。
Ubuntu 15.04のVMをブートすればよいですが、classic VMではなく、ResourceManagerのVMをブートしましょう。classic VMだと、たくさんのポートを開けるのは面倒ですが、ResourceManagerのVMを使うと、ポートを範囲指定することができます。
わたしはこのチュートリアルのためにswarm-master, swarm-agent-01, swarm-agent-02というインスタンスを3台起動しました。
2. Dockerのインストール
Dockerのインストールは下記の手順に従うとあっさりできました。
わたしが実施した内容をメモとして書いておきます。3台のマシンとも同じ手順です。
apt-getのリポジトリに使うキーを取得
$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
apt-getのリポジトリを追加
/etc/apt/sources.list.d/docker.list
ファイルを下記のようにする
# Ubuntu Vivid 15.04
deb https://apt.dockerproject.org/repo ubuntu-vivid main
リポジトリを更新しておく
$ sudo apt-get update
おすすめパッケージ群のインストール
$ sudo apt-get install linux-image-extra-$(uname -r)
Docker Engineのインストール
$ sudo apt-get update
$ sudo apt-get install docker-engine
$ sudo service docker start
動作確認
$ sudo docker run hello-world
これでまずはあっさり動きました。
3. Dockerを他のマシンから使えるようにするための設定
Swarmを使うときは、Dockerを他のマシンから使えるようにする必要があります。今回実施したチュートリアルは、TLSを使わないようになっているので、TLSを使わずにdockerの設定をします。一般的に出回っているのは、Ubuntu 14.04の手順なので、Ubuntu 15.04だと若干手順が異なります。
これは、Ubuntu 15.04からUpstartの代わりにSystemdが使われるようになったからです。大抵のインターネットの手順には次のように書いています。これは15.04では、動作しない方法です。デーモンを動かすときに、ソケットと、2375ポートをリッスンするようにしています。
$ sudo nano /etc/default/docker
# Use DOCKER_OPTS to modify the daemon startup options
DOCKER_OPTS="-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --label=environment=staging"
代わりに次の手順を使います。
/etc/systemd/system/docker-tcp.socket
というファイルを作ります。systemdでは
このようにファイルを作って、ポートのリッスンを有効にします。
[Unit]
Description=Docker Socket for the API
[Socket]
ListenStream=2375
BindIPv6Only=both
Service=docker.service
[Install]
WantedBy=sockets.target
その後
$ sudo systemctl enable docker-tcp.socket
$ sudo systemctl enable docker.socket
$ sudo systemctl stop docker
$ sudo systemctl start docker-tcp.socket
$ sudo systemctl start docker
テストは次のようにすると、ネットワーク経由で、dockerがアクセスできるようになっているかを確認できます。
$ docker -H tcp://127.0.0.1:2375 ps
手元のクライアントから、このdockerホストを操作指定場合、クラウドを利用しているときなど2375ポートをオープンする必要があります。なおこの設定ができていたらお手元のクライアントから。
$ export DOCKER_HOST=xxxxxxx:2375
を設定することで、リモートからdockerを操作することができます。
ただし、平文で通信していますので、実際の運用ではTLSを使ってください。TLSを使っている場合、ポート番号は2376になります。
Setting Docker’s DOCKER_OPTS on Ubuntu 15.04
Enable Remote API on Docker hosts running systemd (like Ubuntu 15.04)
4. SwarmとComposerのインストール
swarm-masterのサーバーに、SwarmとComposerをインストールします。
Composerのインストール (swarm-master)
Composerのインストールはとても簡単です。dockerを外部から使える設定
にしている場合は、お手元のコンピュータにインストールしても結構です。
今回は、swarm-masterのサーバーにインストールしてみました。
master $ sudo -i
master # curl -L https://github.com/docker/compose/releases/download/1.5.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
master # chmod +x /usr/local/bin/docker-compose
master # exit
master $ docker-compose --version
docker-compose version: 1.5.1
agentサーバーへ環境のラベルを設定する (swarm-agent-0x)
今回の環境では、
- swarm-master : Swarmのマスター
- swarm-agent-01 : Staging環境
- swarm-agent-02 : Production環境
となっています。これらの環境の区別をするために、swarm-agent-01, swarm-agent-02でそれぞれ次の設定を行います。
それぞれの環境にdockerデーモン起動時に--label=environment=staging
と--label=environment=production
のラベルをつけます。そのためには、先ほどのsystemdを利用します。
/lib/systemd/system/docker.service
のファイルを次のように編集します。ポイントは、EnvironmentFileの追加と、ExecStartのところへの $DOCKER_OPTSの追加です。
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target docker.socket
Requires=docker.socket
[Service]
Type=notify
EnvironmentFile=/etc/default/docker
ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
[Install]
WantedBy=multi-user.target
/etc/default/docker
ファイルにも次の行を追加します
swarm-agent-01サーバ
DOCKER_OPTS="--label=environment=staging"
swarm-agent-02サーバ
DOCKER_OPTS="--label=environment=production"
その上で、swarm-agent-01, swarm-agent-02の両方のサーバーでdockerデーモンを再起動します。止めようとすると、エラーが出るときがありますが、そのエラーメッセージにかかれている、systemctlコマンド(systemctl daemon-reload)を実行すると問題なくなります。
$ sudo systemctl stop docker-tcp.socket
$ sudo systemctl daemon-reload
$ sudo systemctl stop docker
$ sudo systemctl start docker-tcp.socket
$ sudo systemctl start docker
swarm-masterでswarmのインストール
swarmのトークンを振り出します。このトークンはクラスタを区別するためのユニークな識別子です。そして、下記の指定でswarmのマネージサーバーを起動します。これ自体がdockerのコンテナです。尚、本チュートリアルでは、3375ポートはオープンする必要があります。
master $ export DOCKER_HOST=127.0.0.1:2375
master $ export TOKEN=$(docker run --rm swarm create)
master $ docker run -d -p 3375:2375 swarm manage token://$TOKEN
エージェントサーバーのswarmへの追加
2台のエージェントサーバーをswarmに追加します。2台のエージェントサーバーのプライベートアドレスを記入します。Resource Managerを利用してVMを起動したときのプライベートアドレスの発見方法はAzureの新ポータルからswarm-masterの場合
swarm-master > Settings > Network interfaces でパブリックと、プライベートのポートを見ることができます。プライベートIPは同じリソースグループの間であればInboundポートの設定をしなくてもアクセス可能です。
master $ docker run -d swarm join --addr=<swarm-agent-01_private_ip:2375> token://$TOKEN
master $ docker run -d swarm join --addr=<swarm-agent-02_private_ip:2375> token://$TOKEN
動作確認。エージェントサーバーが追加されていることがわかります。下記のように認識されないケースは、docker logs などで原因を探ってください。わたしは、token://$TOKEN
のところを間違えてtoken//$TOKEN
としていて、エラーがでないため、ちょっとはまりました。
$ docker -H tcp://0.0.0.0:3375 info
Containers: 0
Images: 0
Role: primary
Strategy: spread
Filters: health, port, dependency, affinity, constraint
Nodes: 2
node-1: ************.compute.amazonaws.com:2375
└ Containers: 3
└ Reserved CPUs: 0 / 1
└ Reserved Memory: 0 B / 3.859 GiB
└ Labels: executiondriver=native-0.2, kernelversion=3.19.0-26-generic, operatingsystem=Ubuntu 14.04.3 LTS, storagedriver=aufs
node-2: *****************.eu-central-1.compute.amazonaws.com:2375
└ Containers: 2
└ Reserved CPUs: 0 / 1
└ Reserved Memory: 0 B / 3.859 GiB
└ Labels: executiondriver=native-0.2, kernelversion=3.19.0-26-generic, operatingsystem=Ubuntu 14.04.3 LTS, storagedriver=aufs
CPUs: 2
Total Memory: 7.718 GiB
Name: 5dc162006656
5. デプロイの実施
今はdockerコンテナとして、swarm managerが動作しているので、DockerHostを変更しておきます。
master $ export DOCKER_HOST={swarm-master-PrivateIP}:3375
今回dockerから、サンプルが提供されているのでそれを使ってみます。適当なディレクトリで下記のファイルを展開してください。
master $ git clone https://github.com/nicolaka/dockchat.git
このような感じになっています。
master $ cd dockchat/
swarm-master:~/dockchat$ tree
.
├── docker-compose.yml
├── Dockerfile
├── README.md
├── requirements.txt
├── static
│ └── style.css
├── templates
│ └── form_action.html
└── webapp.py
本家のチュートリアルでは、docker-composeのみで動作させる手順も載っていますが、割愛して、いきなりswarmの用の手順を書いておきます。
最初にDockerHubにログインしてください。
master $ docker login
DockerHubのユーザ名+dockchatというアプリ名をつけてタグをつけます。そして、DockerHubにpushします。
master $ docker build -t {Your_Docker_Hub_username}/dockchat:v1 .
master $ docker push {Your_Docker_Hub_username}/dockchat:v1
次に、docker-compose.ymlを、staging.docker-compose.ymlとしてコピーして次のように変更します。environmentがdbとwebに追加されているのと、web:の次の行が、build: . からimageに変更されて、先ほどDockerHubにあげたイメージを取得するようになっています。
# Mongo DB
db:
image: mongo
expose:
- 27017
command: --smallfiles
environment:
- "constraint:environment==staging"
# Python App
web:
image: <your_Docker_Hub_username>/dockchat:v1
ports:
- "5000:5000"
links:
- db:db
environment:
- "constraint:environment==staging"
stagingへのデプロイ
ステージングへデプロイしてみましょう。
master $ docker-compose -f staging.docker-compose.yml -p dockchat_staging pull
master $ docker-compose -f staging.docker-compose.yml -p dockchat_staging up -d
Creating dockchatstaging_db_1
Creating dockchatstaging_web_1
master $ docker-compose -f staging.docker-compose.yml -p dockchat_staging ps
Name Command State Ports
---------------------------------------------------------------------------------------
dockchatstaging_db_1 /entrypoint.sh --smallfiles Up 27017/tcp
dockchatstaging_web_1 python webapp.py Up 10.0.10.77:5000->5000/tcp
これでステージング(swarm-agent-01)へデプロイがなされました。興味があればswarm-agent-01のinboundの5000番ポートを開けると、swarm-agent-01のパブリックIP:5000でチャットアプリの画面が見れます。
productionへのデプロイ
同じく、production.docker-compose.yml
を作成します。本番では、interlockのHAProxyプラグインを使って、スケールと、負荷分散を実装します。
# Mongo DB
db:
image: mongo
expose:
- 27017
command: --smallfiles
environment:
- "constraint:environment==production"
# Python App
web:
image: nicolaka/dockchat:v1
ports:
- "5000"
links:
- db:db
environment:
- "constraint:environment==production"
- INTERLOCK_DATA={"hostname":"dockchat.com","domain":"dockchat.com"}
interlock:
image: ehazlett/interlock:latest
ports:
- "80:80"
volumes:
- /var/lib/docker:/etc/docker
environment:
- "constraint:environment==production"
command: "--swarm-url tcp://$DOCKER_HOST --debug --plugin haproxy start"
同じようにプロダクションにもデプロイします。その前に、ポートを公開しておきましょう。Interlockは、どうやら、30000以上のポートを使うようなので、そこはAzureの新ポータルで公開しておきましょう。
わたしは30000-65535を公開してみました。swarm-agent-02 > Settings > inbound security rules で、次のように設定しました。これは、Resource ManagerのVMでしかレンジ設定できないので注意が必要です。同様に、80番ポートも公開しておきます。Interlockは、HAProxyなので、このポートから実際のポートに割り振りを行います。ちなみに本当は、2375のポートを開ける必要は本来ありません。
Name : Swarm
Priority : 1200
Protocol : TCP
Source port range : *
Destination port range : 30000-65535
Action : Allow
master $ docker-compose -f production.docker-compose.yml -p dockchat_production pull
master $ docker-compose -f production.docker-compose.yml -p dockchat_production up -d
master $ Creating dockchatproduction_db_1
Creating dockchatproduction_web_1
Creating dockchatproduction_interlock_1
master $ docker-compose -f production.docker-compose.yml -p dockchat_production ps
Name Command State Ports
--------------------------------------------------------------------------------------------------------
dockchatproduction_db_1 /entrypoint.sh --smallfiles Up 27017/tcp
dockchatproduction_interlock_1 /usr/local/bin/interlock - ... Up 443/tcp, 10.0.11.50:80->80/tcp
dockchatproduction_web_1 python webapp.py Up 10.0.11.50:32769->5000/tcp
手元のマシンの、/etc/hostsを下記のように書き換えます。プロダクションサーバーの
IPアドレス(swarm-agent-02のアドレス)をdockchat.comとして指定してみてください。
mylaptop$ vim /etc/hosts
(+) {PUBLIC_IP_OF_NODE-2} dockchat.com
最後にスケールアップします。注意事項としては、このスケールアウトでは、ポートが自動で割り振られ
ます。スケールアウト時にこのポートが空いている必要があります。そうすれば、Interlockが認識してくれます。
そうでないと、せっかくスケールアウトしても、Interlockが認識してくれません。
machine $ docker-compose -f production.docker-compose.yml -p dockchat_production scale web=10
Creating and starting 2 ... done
Creating and starting 3 ... done
Creating and starting 4 ... done
Creating and starting 5 ... done
Creating and starting 6 ... done
Creating and starting 7 ... done
Creating and starting 8 ... done
Creating and starting 9 ... done
Creating and starting 10 ... done
machine $ docker-compose -f production.docker-compose.yml -p dockchat_production ps
Name Command State Ports
--------------------------------------------------------------------------------------------------------
dockchatproduction_db_1 /entrypoint.sh --smallfiles Up 27017/tcp
dockchatproduction_interlock_1 /usr/local/bin/interlock - ... Up 443/tcp, 10.0.11.50:80->80/tcp
dockchatproduction_web_1 python webapp.py Up 10.0.11.50:32769->5000/tcp
dockchatproduction_web_10 python webapp.py Up 10.0.11.50:32771->5000/tcp
dockchatproduction_web_2 python webapp.py Up 10.0.11.50:32770->5000/tcp
dockchatproduction_web_3 python webapp.py Up 10.0.11.50:32775->5000/tcp
dockchatproduction_web_4 python webapp.py Up 10.0.11.50:32777->5000/tcp
dockchatproduction_web_5 python webapp.py Up 10.0.11.50:32774->5000/tcp
dockchatproduction_web_6 python webapp.py Up 10.0.11.50:32773->5000/tcp
dockchatproduction_web_7 python webapp.py Up 10.0.11.50:32772->5000/tcp
dockchatproduction_web_8 python webapp.py Up 10.0.11.50:32776->5000/tcp
dockchatproduction_web_9 python webapp.py Up 10.0.11.50:32778->5000/tcp
さて、お手元のブラウザで dockchat.com
にアクセスしてみてください。リロードごとに違うインスタンスが使われているのがわかると思います。
また、dockchat.com/haproxy?stats
を見ると、管理画面を見ることができます。
ちなみにユーザ/パスワードを聞かれたら stats/interlock
です。
6. 障害時には?
さて、スケールアウトのときになぜか画面が出てこないとかの問題に出会ったら、先の管理画面にちゃんとサーバーが認識されているか?というのと、ログを見ることをしてみましょう。
master $ docker-compose -f production.docker-compose.yml -p dockchat_production logs
という感じでログを見ることができます。
ではみなさまハッピーコーディングを!