#はじめに
この記事はDocker2 Advent Calendar 2016の5日目の記事です。
このエントリではCoreOSのクラスタ環境(CoreOS+etcd+fleet)でdocker-composeを使う方法について書きます。
最近、Dockerをホスティングするための軽量OSとして、CoreOSを使うべく色々触っているのですが、標準で入っているfleetというオーケストレーションツールがシンプルでいい感じだと感じています。
しかし、同一ホストでコンテナ連携する機能が使いづらく(無いわけじゃない)感じたので、そこをdocker-compose入れてサンプルアプリケーションを立ち上げてみた際の内容になります。
ちなみに、少し古いですが、CoreOSに関する概要は以下に纏められており、参考になりました。
http://qiita.com/mopemope/items/fa9424b094aae3eac580
使用ソフトウェアとバージョン
CoreOS:stable 1185.5.0
docker:1.11.2
docker-compose:1.9.0
#CoreOSクラスタを立ち上げる
まずはCoreOSのクラスタをAWS上に立ち上げます。
CoreOSが公式にCloudFormationのテンプレートを用意しているのでそれを利用します。
以下のURLにリンクが並んでいるので好きなリージョンでLaunchします。
https://coreos.com/os/docs/latest/booting-on-ec2.html
途中の設定で出てくるDiscoveryURLというパラメータについてですが、CoreOS(がクラスタリングに使っているetcd)のクラスタは初期構築の際、リーダ選出やメンバ発見などの情報をやり取りするDiscoveryURLを指定して起動する必要があります。
このDiscoveryURLを発行するサービスをCoreOS社が提供してくれているので、基本それを利用します。
DiscoveryURLは以下のURLで発行できます。末尾のsizeのパラメータはクラスタサイズに合わせます。今回は5台でクラスタを作成します。
https://discovery.etcd.io/new?size=5
アクセスするとURLが表示されるので、それをDiscoveryURLに設定します。
立ち上がったら、適当なインスタンスにログインしてfleetctl list-machines
を打って5台認識されていればOKです。
core@ip-172-31-28-248 ~ $ fleetctl list-machines
MACHINE IP METADATA
1f23b08a... 172.31.10.104 -
6738f2ad... 172.31.28.248 -
81e310a5... 172.31.10.105 -
91cdf348... 172.31.28.247 -
d639bc34... 172.31.10.103 -
CoreOSクラスタにDocker Composeをインストールする
残念ながらCoreOSには標準でdocker-composeが入っていないのでインストールする必要があります。
しかし、CoreOSの思想は「アプリケーションはすべてコンテナで動かすべし」なのでCoreOSにはパッケージマネージャがありません。
CoreOSのSDKを使えば、追加のアプリケーションをインストールしたイメージを作ることができるのですが、わざわざ独自イメージを作成するのは少し敷居が高いです。
なので、githubのdownloadページにあるdocker-composeのバイナリを直接配置することにします。
https://github.com/docker/compose/releases
手動で入れるには以下のような手順になります。が、これを全台で展開するのは少々めんどくさいです。
core@ip-172-31-4-162 ~ $ sudo -i
ip-172-31-4-162 ~ # mkdir -p /opt/bin
ip-172-31-4-162 ~ # curl -L https://github.com/docker/compose/releases/download/1.9.0/docker-compose-`uname -s`-`uname -m` > /opt/bin/docker-compose
ip-172-31-4-162 ~ # chmod +x /opt/bin/docker-compose
ip-172-31-4-162 ~ # exit
fleetctlというコマンドを使ってクラスタ全台に展開します。fleetはCoreOSに標準で入っているオーケストレーションツールで、fleetctlはそのCLIになります。fleetはCoreOSクラスタ全体に対するinitツールのような働きをします。
少し古いですが、fleetに関する概要は以下に纏められており、参考になりました。
http://deeeet.com/writing/2014/11/20/fleet/
fleetctlはsystemdのUnitファイルを一部拡張した記述形式のファイルを用いて、クラスタにサービスをデプロイします。docker-composeのinstallで使うUnitファイルは以下になります。
[Unit]
Description=compose install Service
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
TimeoutStartSec=0
Environment=COMPOSE_VERSION="1.9.0"
ExecStartPre=-/usr/bin/sudo /usr/bin/sh -c "/usr/bin/mkdir -p /opt/bin"
ExecStartPre=-/usr/bin/sudo /usr/bin/sh -c "usr/bin/mkdir -p /opt/docker"
ExecStartPre=-/usr/bin/sudo /usr/bin/sh -c \
"curl -L https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` \
-o /opt/docker/docker-compose_${COMPOSE_VERSION}"
ExecStartPre=-/usr/bin/sudo /usr/bin/sh -c \
"/usr/bin/chmod +x /opt/docker/docker-compose_${COMPOSE_VERSION}"
ExecStartPre=/usr/bin/sudo /usr/bin/sh -c \
"/usr/bin/ln -f -s /opt/docker/docker-compose_${COMPOSE_VERSION} /opt/bin/docker-compose"
ExecStart=/opt/bin/docker-compose -v
[X-Fleet]
Global=true
UnitやServiceのセクションの記述はSystemdと同じです。
[Unit]セクションではサービス間の依存や実行の前後関係を記載します。
[Service]セクションではサービスが実行するコマンドを記載します。ExecStartPre
はExecStart
の前に実行するコマンドを記載します。=-
はコマンドが失敗しても次のコマンドに進むことを示しています。
[X-Fleet]のセクションはfleetで拡張された記述となっており、Global=trueはクラスタ全台にサービスを実行することを示しています。
上記のUnitファイルをコマンドを実行するCoreOS上に配置し、以下のコマンドでサービスの展開を行います。
core@ip-172-31-28-248 ~ $ fleetctl start compose-install.service
Unit compose-install.service
Triggered global unit compose-install.service start
core@ip-172-31-28-248 ~ $ fleetctl list-units
UNIT MACHINE ACTIVE SUB
compose-install.service 1f23b08a.../172.31.10.104 active exited
compose-install.service 6738f2ad.../172.31.28.248 active exited
compose-install.service 81e310a5.../172.31.10.105 active exited
compose-install.service 91cdf348.../172.31.28.247 active exited
compose-install.service d639bc34.../172.31.10.103 active exited
core@ip-172-31-28-248 ~ $ docker-compose -v
docker-compose version 1.9.0, build 2585387
うまく行けば、すべてのホストで一連のコマンドが実行され、actice/exitedの状態になります。
インストールを上記の形式でサービスとして展開すると、クラスタに新たノードが追加された時も、特に何もしなくても自動的にcompose-insttall.serviceが実行されて、docker-composeのバイナリが配置されます。
CloudFormationのClusterSizeを増やせばノードが追加できるので、確認ができますが、ここでは割愛します。
#Composeのサンプルプロジェクトを作成してgithubに上げる
クラスタにdocker-composeがインストールされたので、docker-composeで使うコンテナのDockerfileとdocker-compose.ymlを作成します。
サンプル用にdocker-composeのチュートリアルで使う、PythonとRedisのアプリケーションをデプロイすることにします。
アクセス回数をredisに保存してそれを表示するシンプルなアプリケーションです。詳細は上記のリンクを見てみて下さい。
以下のようにひとまとめにして、githubに上げます。
composetest
├── Dockerfile
├── README.md
├── app.py
├── docker-compose.yml
└── requirements.txt
一応docker-compose.ymlだけ中身を転記しておくと、こんな感じです。
version: '2'
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
depends_on:
- redis
redis:
image: redis
#Composeアプリをクラスタに展開する
上記のcomposetestアプリケーションをfleetctlでクラスタに展開します。
使用するUnitファイルは以下のような感じになります。
[Unit]
Description=Sample docker-compose service
After=compose-install.service
Requires=compose-install.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/opt/bin/docker-compose -f /home/core/git/composetest/docker-compose.yml kill
ExecStartPre=-/opt/bin/docker-compose -f /home/core/git/composetest/docker-compose.yml rm
ExecStartPre=-/usr/bin/mkdir /home/core/git
ExecStartPre=-/usr/bin/git clone https://github.com/kanga333/composetest.git /home/core/git/composetest
ExecStartPre=/usr/bin/git -C /home/core/git/composetest pull
ExecStartPre=/opt/bin/docker-compose -f /home/core/git/composetest/docker-compose.yml build
ExecStart=/opt/bin/docker-compose -f /home/core/git/composetest/docker-compose.yml up
ExecStop=/opt/bin/docker-compose -f /home/core/git/composetest/docker-compose.yml stop
[X-Fleet]
Conflicts=composetest@*.service
ExecStartPre
で実行している内容は、同じdocker-composeアプリが何らか原因で残っていた場合に備えてのゴミ消し、gitレポジトリの最新化、docker-composeで使用するイメージのビルドになります。
ファイル名に@がついていますが、@をつけると、共通のUnitファイルから複数のinstanceを作成することができます。
例えば、以下のコマンドを実行するとcomposetest@.service
のUnitファイルを使ったinstance1のサービスがCoreOSクラスタのいずれかのホストで起動されます。
core@ip-172-31-28-248 ~ $ fleetctl start composetest@1.service
Unit composetest@1.service inactive
Unit composetest@1.service launched on 1f23b08a.../172.31.10.104
同様に2台目、3台目は以下のようにして起動します。
core@ip-172-31-28-248 ~ $ fleetctl start composetest@2.service
Unit composetest@2.service inactive
Unit composetest@2.service launched on 6738f2ad.../172.31.28.248
core@ip-172-31-28-248 ~ $ fleetctl start composetest@3.service
Unit composetest@3.service inactive
Unit composetest@3.service launched on 81e310a5.../172.31.10.105
Unitファイルの[X-Fleet]セクションに記載してあるConflicts=composetest@*.service
はすでにcomposetest@.serviceのインスタンスが起動しているホストに起動しない事を示しています。
つまり、3台のインスタンスを冗長化のために起動したけど、うっかり同じホストで動いていた、などといった事態を防ぐことができます。
起動後、少し時間を置いてから以下のコマンドを打つとサービスが起動していることが確認できます。
core@ip-172-31-28-248 ~ $ fleetctl list-units | grep composetest
composetest@1.service 1f23b08a.../172.31.10.104 active running
composetest@2.service 6738f2ad.../172.31.28.248 active running
composetest@3.service 81e310a5.../172.31.10.105 active running
curlで接続確認します。(※AWSのSGのポート5000番を開ける必要があります。)
core@ip-172-31-28-248 ~ $ curl 172.31.10.104:5000
Hello World! I have been seen 1 times.
接続確認が取れました。無事にfleet経由でdocker-composeのアプリケーションが起動しています。
#おわりに
以上でCoreOSのクラスタ環境にdocker-composeを入れて、可動確認を取ることができました。
とはいえ、立ち上がったね、はい終わり。では使い物にならないので、サービスディスカバリとか監視とか諸々を導入する必要があります。
ちょうど、来週のDocker2アドベントカレンダーが空いているので、その辺りをDockerコンテナで導入する話を書こうかと思います。
追記
Traefikでサービスディスカバリする方法について以下に書きました。
CoreOS環境でtraefikを使ってコンテナのサービスディスカバリを行う方法