CoreOSのクラスタ環境(CoreOS+etcd+fleet)でdocker-composeを使う方法

  • 10
    いいね
  • 0
    コメント

はじめに

この記事は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です。

fleetctl
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

手動で入れるには以下のような手順になります。が、これを全台で展開するのは少々めんどくさいです。

install_docker-compose
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ファイルは以下になります。

compose-install.service
[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]セクションではサービスが実行するコマンドを記載します。ExecStartPreExecStartの前に実行するコマンドを記載します。=-はコマンドが失敗しても次のコマンドに進むことを示しています。
[X-Fleet]のセクションはfleetで拡張された記述となっており、Global=trueはクラスタ全台にサービスを実行することを示しています。

上記のUnitファイルをコマンドを実行するCoreOS上に配置し、以下のコマンドでサービスの展開を行います。

install_docker-compose
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
composetest
├── Dockerfile
├── README.md
├── app.py
├── docker-compose.yml
└── requirements.txt

一応docker-compose.ymlだけ中身を転記しておくと、こんな感じです。

docker-compose.yml
version: '2'
services:
  web:
    build: .
    ports:
     - "5000:5000"
    volumes:
     - .:/code
    depends_on:
     - redis
  redis:
    image: redis

Composeアプリをクラスタに展開する

上記のcomposetestアプリケーションをfleetctlでクラスタに展開します。
使用するUnitファイルは以下のような感じになります。

composetest@.service
[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クラスタのいずれかのホストで起動されます。

start_instance1
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台目は以下のようにして起動します。

start_instance2-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を使ってコンテナのサービスディスカバリを行う方法

この投稿は Docker2 Advent Calendar 20165日目の記事です。