CoreOSを使ってDockerコンテナを動かす——15分でできるCoreOSクラスタの作り方

 Dockerを利用する際に、コンテナを動かすための環境の1つとしておすすめしたいのがCoreOSだ。CoreOSでは簡単にコンテナの実行環境を構築でき、さらに複数台のCoreOSマシンを組み合わせて連携させる機能も用意されている。今回はCoreOSを使ってサービスを構築するための基礎知識と、実際の作業の流れを紹介する。

Dockerなどを使ったコンテナの利用に特化したCoreOS

 近年注目されている「コンテナ」技術は、VMwareやXen、KVMといった仮想化技術と同様にハードウェアやOSとは独立した環境を構築でき、また仮想化を利用するよりも低コストで利用できることで注目を集めている。しかし、Dockerを使ってサービスを実際に運用する場合、どういった環境を用意すれば良いか迷うケースもあるだろう。その解答の1つに、コンテナを稼動させることに特化したLinuxディストリビューションであるCoreOSがある(図1)。

図1 CoreOSのWebサイト

図1 CoreOSのWebサイト

 CoreOSはコンテナを稼動させることに特化することで、それ以外の管理コストを大幅に下げることに成功している。また、クラスタ環境を構築するためのツールも標準で提供されている。今回はこのCoreOSについて、その構成技術の基本的な解説と、簡単なクラスタ環境を構築するための手順を紹介する。

CoreOSの特徴

 CoreOSはコンテナを稼動させることに特化しており、標準では必要最小限のコマンドやサービスのみが含まれている。コンテナを実行させるためのDockerや、後述するetcdやfleetといったクラスタ環境に向けたツールは標準でインストールされているものの、一般的なLinuxディストリビューションで提供されているようなパッケージ管理機構は用意されていない。標準で用意されていないアプリケーションについては、基本的にはコンテナを作成してその中で稼働させることになる。

 また、OSのアップデートはシステムイメージ全体を入れ替えることで対応し、その作業もデフォルト設定ではバックグラウンドで自動で行われるようになっている(もちろん手動で行うよう設定することも可能)。また、サービスの管理にはsystemdが採用されている。

 なお、CoreOSはデフォルトではiptablesなどのファイアウォール機能についても設定されていない。グローバルIPを付与する場合など第三者がサーバーにアクセスできる環境においては、個別にセキュリティ設定を行う必要がある。

CoreOSのインストールと環境構築

 CoreOSはコンテナの利用に特化していることもあり一般的なLinuxディストリビューションとは設定や運用の方法が大きく異なっている。そこで、以下ではまず「1台のサーバーだけでCoreOS環境を運用する」場合について紹介し、続けて「複数台のサーバーを組み合わせてクラスタを作る」場合の2つの例でCoreOSを使ったサーバー運用の例を解説していく。

 また、テスト環境にはさくらのクラウドを利用したが、それ以外の場合についても基本的な流れは同様だ。

CoreOSのインストール

 CoreOSのインストールについてはさまざまな方法があるが、実マシンの場合はCoreOSをインストールするための「coreos-install」というシェルスクリプトが用意されており、インストール用ISOイメージなどからマシンを起動してこのスクリプトを実行すれば良い(公式ドキュメント)。

 また、仮想マシンへのインストールもサポートされている。公式にVagrantVMwareqemu向けなどのイメージおよびドキュメントが公開されているので、こちらに従って作業を進めれば良い。

 ちなみに、さくらのクラウドではあらかじめCoreOSがインストールされたアーカイブも提供されており、これを利用することですぐにCoreOSを利用できるようになっている(図2)。

図2 さくらのクラウドではサーバー作成時にディスク設定でCoreOSのアーカイブを選択することで、CoreOSがインストールされた環境をすぐに用意できる

図2 さくらのクラウドではサーバー作成時にディスク設定でCoreOSのアーカイブを選択することで、CoreOSがインストールされた環境をすぐに用意できる

 この場合、管理ユーザーのパスワードやホスト名、公開鍵などを画面上で設定できるので、それぞれ適切なものを入力しておくことをおすすめする(図3)。

図3 サーバー作成時に管理ユーザーのパスワードやホスト名、公開鍵の設定が行える

図3 サーバー作成時に管理ユーザーのパスワードやホスト名、公開鍵の設定が行える

 ここでパスワードを指定した管理ユーザーは「root」ではなく、「core」というユーザー名になる点には注意したい。CoreOSではシステムの管理にrootではなくcoreユーザーを使うのが一般的となっているためだ。

 また、さくらのクラウドで提供されているCoreOSイメージについてはデフォルトでSSHやコンソールログインが可能なように設定されているが、CoreOSのデフォルト設定ではコンソールログインは無効にされている。もしコンソールからログインを行いたい場合は、カーネルのブート時に次のように「console」および「coreos.autologin」起動オプションを指定する必要がある。

console=tty0 console=ttyS0 coreos.autologin=tty1 coreos.autologin=ttyS0

CoreOSのアップデート

 インストールが完了し、ログインできるかの確認をしたら、特別な理由がない限りはOSの手動アップデートを行っておくと良いだろう。たとえばさくらのクラウドでは標準で提供されているCoreOSのバージョンが367.1.0と古いため、アップデートを行わないと後述のetcd2などが利用できない。

 アップデートを手動で実行するには、次のようにupdate_engine_clientコマンドを「-update」オプション付きで実行する。

$ sudo update_engine_client -update

 これで新しいバージョンのOSイメージがダウンロードされ、適用後に自動的に再起動が行われる。

 なお、CoreOSのバージョンは/etc/os-releaseファイルを参照することで確認できる。

$ cat /etc/os-release
NAME=CoreOS
ID=coreos
VERSION=367.1.0
VERSION_ID=367.1.0
BUILD_ID=
PRETTY_NAME="CoreOS 367.1.0"
ANSI_COLOR="1;32"
HOME_URL="https://coreos.com/"
BUG_REPORT_URL="https://github.com/coreos/bugs/issues"

CoreOSの設定

 CoreOSでは、「cloud-config」という設定ファイルを使ってサーバーの設定を行うようになっている。cloud-configファイルはYAML形式で書かれたテキストファイルで、多くの場合/usr/share/oem/ディレクトリ以下に配置される。

 CoreOSで使われるcloud-configでは、以下の設定が可能だ。

  • etcdやfleetといったCoreOSのコンポーネントの動作
  • 自動アップデートの可否やアップデート方法
  • 起動するsystemdユニット
  • coreユーザーのssh_authorized_keyファイルに追加するSSH公開鍵
  • ホスト名
  • 「core」以外のユーザー追加
  • 指定したパスへのファイル追加

 これだけを見ると限定的な設定しか行えないようにも見えるが、ファイルに指定した内容を書き出す機能を利用することで、各種設定ファイルの内容をcloud-configファイル内に記述するといったことができ、多岐にわたる設定を行える(CoreOSのドキュメント)。

 たとえば、さくらのクラウドで自動生成されるcloud-configファイルは以下のようになっている(記事執筆時のもの)。

#cloud-config

coreos:
  units:  ←起動するサービスの設定
    -
      command: restart
      name: coreos-setup-environment.service
    -
      command: restart
      name: systemd-networkd.service
    -
      command: start
      content: "[Unit]\nDescription=timezone\n[Service]\nType=oneshot\nRemainAfterExit=yes\nExecStart=/usr/bin/ln -sf ../usr/share/zoneinfo/Japan /etc/localtime\n"
      name: timezone.service

ssh_authorized_keys:  ←公開鍵の設定
  - <指定したSSH公開鍵>

hostname: web01  ←ホスト名の設定

users:  ←ユーザー関連の設定
  -
    name: core
    passwd: <指定したパスワードのハッシュ>

write_files:  ←指定した内容をファイルに書き出す設定
  -
    content: "[Match]\nName=e*\n\n[Network]\nAddress=***.***.***.***/24\nGateway=***.***.***.1\nDNS=***.***.***.3\nDNS=***.***.***.4\n"
    path: /etc/systemd/network/10-static.network

 ここでは、次のようなものが設定されている。

  • systemdを使って「coreos-setup-environment.service」および「systemd-networkd.service」サービスをリスタート
  • 「timezone.service」サービスを開始
  • coreユーザーのssh_authorized_keyファイルに指定されたSSH公開鍵を追加
  • ホスト名を「web01」に設定
  • coreユーザーのパスワードを指定したものに変更
  • 以下の内容を格納した「/etc/systemd/network/10-static.network」というファイルを作成
[Match]
Name=e*

[Network]
Address=***.***.***.***/24
Gateway=***.***.***.1
DNS=***.***.***.3
DNS=***.***.***.4

 最後の「write_files:」以下では、systemdのネットワーク設定管理機能(systemd-networkd)用設定ファイルを作成している。systemd-networkdでは、/etc/systemd/network/ディレクトリ以下に設定ファイルを配置することでネットワークインターフェイス(NIC)へのIPアドレス割り当てなどの設定を行える。この例では、「e*」というワイルドカード付き文字列にマッチするネットワークインターフェイスに対し、「[Network]」以下で指定したIPアドレスを割り当てている。

 もしNICが2つ以上ある場合、それぞれに対応した設定ファイルを/etc/systemd/network/ディレクトリ以下に配置すれば良い。たとえばeth1に対して「192.168.0.10/24」というIPアドレスを割り当てるには、次のような設定ファイルを用意する。

[Match]
Name=eth1

[Network]
Address=192.168.0.10/24

 この場合、cloud-configファイルの「write_files:」以下は次のようになる。

write_files:
  -
    content: "[Match]\nName=eth0\n\n[Network]\nAddress=***.***.***.***/24\nGateway=***.***.***.1\nDNS=***.***.***.3\nDNS=***.***.***.4\n"
    path: /etc/systemd/network/10-static.network
  - content: "[Match]\nName=eth1\n\n[Network]\nAddress=192.168.0.10/24\npath: /etc/systemd/network/20-eth1.network"

 ちなみに/etc/systemd/network/ディレクトリ以下に配置するファイル名は任意のものが使用できるが、今回は分かりやすいよう「20-eth1.network」という名称にしている。また、あらかじめ用意されていた設定では「[Match]」部分にワイルドカード(「e*」)が使われているため、これについても「eth0」に変更してある。

 なお、上記はあらかじめ用意されているcloud-configファイルに合わせた書き方をしているが、次のように記述することも可能だ。

write_files:
  - content: |
      [Match]
      Name=e*

      [Network]
      Address=***.***.***.***/24
      Gateway=***.***.***.1
      DNS=***.***.***.3
      DNS=***.***.***.4
    path: /etc/systemd/network/10-static.network
  - content: |
      [Math]
      Name=eth1

      [Network]
      Address=192.168.0.10/24
    path: /etc/systemd/network/20-eth1.network

cloud-configファイルの反映

 cloud-configファイルの内容を書き換えた後、「coreos-cloudinit」コマンドをroot権限で実行するとその設定が反映される。

$ sudo coreos-cloudinit --from-file /usr/share/oen/cloud-config

 また、CoreOSでは起動もしくは再起動時にもこれと同様の処理が実行され、自動的に設定が反映されるようになっている。

Dockerコンテナを作成するためのイメージファイルの用意

 前述のとおりCoreOSにおいては、基本的にアプリケーションはすべてコンテナ上で実行する。コンテナを自分で1から用意する手順については過去に紹介しているが(Docker向けのコンテナをゼロから作ってみよう「Packer」でDocker用のイメージファイルを作ってみよう)、今回はコンテナライブラリ「Docker Hub」上にあるDockerの公式リポジトリ「docker-library」で公開されているApache HTTP Server(httpd)のイメージを元にした独自のイメージを用意し、そこからコンテナを作成することにする。

 イメージの作成はCoreOS環境上ではなく、Dockerがインストールされたローカルマシンで行う。作業の流れとしては次のようになる。

 まず、適当なディレクトリ(今回は「osdn-test-webserver」という名前とする)を作成し、そこに「Dockerfile」という名前のテキストファイルを作成する。

$ mkdir osdn-test-webserver
$ cd osdn-test-webserver/
$ touch Dockerfile

 続いて、作成したDockerfile内に次のような内容を書き込む。

FROM httpd:2.4
COPY ./public-html/ /usr/local/apache2/htdocs/

 1行目の「FROM httpd:2.4」は、docker-libraryにある「httpd」イメージファイルのうち、「2.4」というタグが付けられたものをベースイメージとする、という意味だ。また、2行目の「COPY ./public-html/ /usr/local/apache2/htdocs/」は、Dockerfileと同じディレクトリ内にある「public-html」ディレクトリの中身をイメージファイル内の「/usr/local/apache2/htdocs/」ディレクトリにコピーする、という意味となる。httpdイメージで提供されるhttpdでは、このディレクトリがDocumentRoot(公開されるサーバーのルートディレクトリ)になるよう設定されている。

 続いてpublic-htmlディレクトリを作成し、ここに公開するコンテンツをコピーする。今回はテスト用としてhttpdのデフォルトページが含まれる/usr/share/httpd/noindex/ディレクトリの内容をコピーしているが、実際にはここに公開するコンテンツをコピーすることになる。

$ mkdir public-html
$ cp -r /usr/share/httpd/noindex/* public-html/

 最後に「docker build」コマンドを実行してイメージを作成する。

$ sudo docker build -t osdn-test-webserver .

 「-t」オプションは作成するイメージの名称を指定するものだ。任意の名前が指定可能だが、今回は「osdn-test-webserver」を指定している。

 作成したイメージは、「docker images」コマンドで確認できる。今回はイメージにタグを指定していないため、自動的に「latest」というタグが付けられている。

$ sudo docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
osdn-test-webserver   latest              ab4dd8db2360        18 minutes ago      167.7 MB

 最後に「docker save」コマンドを使ってイメージをファイルに保存する。以下では「osdn-test-webserver.tar」というファイル名とした。

$ sudo docker save osdn-test-webserver:latest > osdn-test-webserver.tar

イメージファイルからコンテナを作成して実行する

 続いて、CoreOS上で作成したイメージファイルからコンテナを作成して実行してみよう。

 まず、CoreOSマシン上に先ほど作成したイメージファイル(osdn-test-webserver.tar)をコピーする。scpコマンドなどを使っても良いが、今回はCoreOSマシンからアクセスできるサーバー上にイメージファイルを置き、「http://example.com/pub/osdn-test-webserver.tar」というURLでHTTP経由でダウンロードできるように設定、これをcurlコマンドでダウンロードした。

$ curl http://example.com/pub/osdn-test-webserver.tar > osdn-test-webserver.tar

 続いてダウンロードしたイメージファイルを「docker load」コマンドでロードする。

$ docker load -i osdn-test-webserver.tar

 ロードしたイメージは「docler images」コマンドで確認できる。

$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
osdn-test-webserver   latest              ab4dd8db2360        3 days ago          167.7 MB

 イメージからコンテナを作成して実行するには、「docker run」コマンドを使用する。

$ docker run -t -i -p 80:80 --rm osdn-test-webserver
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.11. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.11. Set the 'ServerName' directive globally to suppress this message
[Mon Jul 13 10:45:49.008778 2015] [mpm_event:notice] [pid 1:tid 140486329022336] AH00489: Apache/2.4.12 (Unix) configured -- resuming normal operations
[Mon Jul 13 10:45:49.009169 2015] [core:notice] [pid 1:tid 140486329022336] AH00094: Command line: 'httpd -D FOREGROUND'

 ここでは、「-t」オプションを使用して疑似TTYを割り当て、「-i」オプションで対話的に起動したコンテナを操作できるよう指定している。また、「-p 80:80」オプションでは、コンテナの80番ポートに対し外部から80番ポートでアクセスできるよう、「–rm」オプションではコンテナの終了後自動的にそのコンテナを削除するよう指定している。

 コンテナの実行後に「http://<このCoreOSマシンのIPアドレス>/」というURLにWebブラウザでアクセスすると、イメージ作成時にコピーしたコンテンツが表示されるはずだ。

 なお、この場合はコンテナがコンソールのフォアグラウンドで実行され、Ctrl-Cを入力するとdockerが終了してサービスも停止される。バックグラウンドでコンテナを実行させるには、「-i」および「-t」オプションの代わりに「-d」オプションを指定する。

$ docker run -d -p 80:80 --name osdn-test-webserver osdn-test-webserver
fc0a51b050a469b41d4a331bf26bd7f626a9c255e8b90eeb6dd720baf81d6d17

 このとき「–name」オプションで名前を指定することで、コンテナに名前を付けることができる。ここでは「osdn-test-webserver」という名前を指定しており、以後はこれを使ってコンテナの停止や再起動、削除が行える。

$ docker ps ←コンテナの稼働状況を見る
CONTAINER ID        IMAGE                        COMMAND              CREATED              STATUS              PORTS                NAMES
fc0a51b050a4        osdn-test-webserver:latest   "httpd-foreground"   About a minute ago   Up 2 seconds        0.0.0.0:80->80/tcp   osdn-test-webserver
$ docker stop osdn-test-webserver ←コンテナを停止させる
osdn-test-webserver
$ docker restart osdn-test-webserver ←コンテナを再起動させる
osdn-test-webserver
$ docker stop osdn-test-webserver ←コンテナを停止させる
osdn-test-webserver
$ docker rm osdn-test-webserver ←コンテナを削除する
osdn-test-webserver

コンテナのサービス化

 コンテナ上でサーバーを稼動させる場合、そのコンテナをサービスとして登録しておくと管理が容易になる。CoreOSの場合、/etc/systemd/system/ディレクトリ内に「ユニットファイル」と呼ばれる設定ファイルを用意するだけで、コンテナを簡単にサービスとして登録できる。

 たとえば上記のhttpdコンテナの場合、以下のような設定ファイルを用意すれば良い。今回はこれを「osdn-test-webserver.service」という名前で/etc/systemd/system/ディレクトリ内に保存した。

[Unit]
Description=OSDN Test Web Server
After=docker.service
Requires=docker.service
 
[Service]
ExecStartPre=-/usr/bin/docker kill osdn-test-webserver
ExecStartPre=-/usr/bin/docker rm osdn-test-webserver
ExecStartPre=/usr/bin/curl http://example.com/pub/osdn-test-webserver.tar -o /tmp/osdn-test-webserver.tar
ExecStartPre=/usr/bin/docker load -i /tmp/osdn-test-webserver.tar
ExecStart=/usr/bin/docker run --name osdn-test-webserver -p 80:80 osdn-test-webserver
ExecStop=/usr/bin/docker stop osdn-test-webserver

[Install]
WantedBy=multi-user.target

 この設定ファイルは3つのセクションから構成されている。まず「Unit」セクションでは、サービスに関する情報を記述する。ここでは「Description」でサービスの説明を、「After」および「Requires」でサービスの実行に必要なサービスを指定している。

[Unit]
Description=OSDN Test Web Server
After=docker.service
Requires=docker.service

 「Service」セクションでは、サービスの実行時に行われる処理を定義する。「ExecStartPre」はサービスの起動前に実行する処理、「ExecStart」はサービスの起動時に実行する処理、「ExecStop」ではサービスの停止時に実行する処理を記述する。

[Service]
ExecStartPre=-/usr/bin/docker kill osdn-test-webserver
ExecStartPre=-/usr/bin/docker rm osdn-test-webserver
ExecStartPre=/usr/bin/curl http://example.com/pub/osdn-test-webserver.tar -o /tmp/osdn-test-webserver.tar
ExecStartPre=/usr/bin/docker load -i /tmp/osdn-test-webserver.tar
ExecStart=/usr/bin/docker run --name osdn-test-webserver -p 80:80 osdn-test-webserver
ExecStop=/usr/bin/docker stop osdn-test-webserver

 この例では、サービスの実行前に「docker kill」および「docker rm」コマンドを実行して既存のコンテナをいったん削除し、続けてcurlコマンドでイメージファイルをダウンロードしてロードする、という処理を行っている。このようにすることで、イメージをアップデートした場合にサービスを再起動するだけで自動的にコンテナもアップデートできるようになる。

 なお、「ExecStartPre」行では「=」の直後に「-」を指定することで、そのコマンドが失敗した場合でも処理を継続できるようになる。この例では、もし指定のコンテナが存在しない場合に「docker kill」および「docker rm」コマンドが失敗するが、それでも後続の処理は実行される。

 また、サービスの実行時には「docker run」コマンドを、停止時には「docker stop」コマンドを実行してコンテナの起動および停止を行うように指定している。

 最後の「Install」セクションでは、設定情報をどこに格納するかの情報を指定するものだ。詳しくはドキュメント(「man systemd.unit」コマンドなどで参照可能)を見ていただきたいが、今回のように「WantedBy=multi-user.target」と指定することで、いわゆる「ランレベル3」(通常のマルチユーザーモード)でこのサービスが使われるよう設定が行われる。

[Install]
WantedBy=multi-user.target

 ユニットファイルを作成したら、「systemctl enable」コマンドでサービスを登録する。

 $ sudo systemctl enable /etc/systemd/system/osdn-test-webserver.service
Created symlink from /etc/systemd/system/multi-user.target.wants/osdn-test-webserver.service to /etc/systemd/system/osdn-test-webserver.service.

 これで指定したコンテナをサービスとして実行できるようになり、「systemctl start」コマンドや「systemctl stop」コマンドでコンテナの開始や停止が行えるようになる。

 $ sudo systemctl start osdn-test-webserver.service
 $ sudo systemctl stop osdn-test-webserver.service

 なお、もしサービスが正しく立ち上がらない場合は、CoreOSのシステムログを確認する「journalctl」コマンドでログをチェックしてみると良いだろう。

cloud-configファイルに上記のサービス設定を記述する

 さて、先の例では/etc/systemd/system/ディレクトリ以下に直接設定ファイルを配置することでサービスを登録したが、この設定をcloud-configファイルに記述することも可能だ。この場合、cloud-configファイルの「unites:」以下に次のような内容を追加すれば良い。

coreos:
  units:
    -
      name: osdn-test-webserver.service
      enable: true
      content: |
        [Unit]
        Description=OSDN Test Web Server
        After=docker.service
        Requires=docker.service
         
        [Service]
        ExecStartPre=-/usr/bin/docker kill osdn-test-webserver
        ExecStartPre=-/usr/bin/docker rm osdn-test-webserver
        ExecStartPre=/usr/bin/curl http://hylom.net/pub/osdn-test-webserver.tar -o /tmp/osdn-test-webserver.tar
        ExecStartPre=/usr/bin/docker load -i /tmp/osdn-test-webserver.tar
        ExecStart=/usr/bin/docker run --name osdn-test-webserver -p 80:80 osdn-test-webserver
        ExecStop=/usr/bin/docker stop osdn-test-webserver
        
        [Install]
        WantedBy=multi-user.target

複数台への拡張

 CoreOSが備えるfleetという機構を利用することで、複数台のCoreOSマシンをクラスタ化して横断的にコンテナを管理することが可能になる。続いてはこの機能について解説をしていこう。

 今回前提とする環境は、次の図4のようなものだ。

図4 今回想定する環境

図4 今回想定する環境

 この環境は、クラスタに対し各種操作を行うための管理/作業用ホストと、クラスタを構成する複数台のホスト、そしてルータとファイアウォール、ロードバランサなどの役割を持つホストから構成されている。管理/作業用ホストとクラスタ構成ホストはローカルネットワークで接続されており、クラスタ構成ホストの操作やそれらへのログインは管理/作業用ホスト経由で行う形となる。

 管理/作業用ホストについてはetcdやfleetなどが利用できる任意のOSを利用できるが、CoreOSを利用したほうが設定が容易であるため、今回はCoreOSを利用する。

 なお、今回はルーターやファイアウォール、ロードバランサの設定などについては紹介しない。Linuxを使ったロードバランサ構築については「Linux Virtual Server」と「Keepalived」で作る冗長化ロードバランサという記事で以前紹介しているほか、さくらのクラウドやさくら専用サーバではルーターおよびロードバランサの提供も行われているので、それらのドキュメントなどを参照してほしい。

fleetとは

 実際に環境構築の手順を紹介する前に、fleetがどのようなものなのかを簡単に解説しておこう。

 fleetは複数のホストを対象に、横断的にサービスの管理を行えるシステムだ。これを利用することでサービスの起動や停止といった操作や、サービスの稼働状況の確認などさまざまな管理をリモートから行える。systemdをクラスタ向けに拡張したようなアーキテクチャを持ち、systemdと同様、ユニットと呼ばれる単位でサービスを管理する。

 また、fleetはクラスタの管理にetcdという分散型Key-Valueストアを使用する。etcdはクラスタ間でのデータ共有のために開発されたKey-Valueストアで、etcdでのクラスタ設定がそのままfleetに反映されるようになっている。

 etcdおよびfleetのどちらもCoreOSでは標準でインストールされており、簡単な設定だけで利用が可能だ。

マスター側etcdの設定

 fleetを利用してクラスタを作成するには、まずetcdでのクラスタ設定が必要だ。CoreOSにおいては、etcdの設定についてもcloud-configファイルで行える。

 なお、記事執筆時のetcdの最新版はバージョン2.0.13で、これは「etcd2」と呼ばれている。CoreOSでは互換性維持のためバージョン0.4.7も利用できるものの、こちらは廃止予定となっているので、本記事ではetc2を利用している。

 etcdではクラスタを静的に作成する方法と、「ディスカバリ」と呼ばれる仕組みを利用して動的に作成する方法が用意されている。前者はあらかじめクラスタを構成するすべてのマシンのIPアドレスが決まっていることが前提となるが、動的にマシンが増減することが多いクラウド環境では利用しにくい。そのため今回は動的にクラスタを作成する方法を説明する。

 動的にクラスタを作成する場合、クラスタとは別にディスカバリ用のetcdが必要となる。CoreOSではそのための「discovery.etcd.io」というサービスも提供されているが、今回は管理/作業用ホスト上にディスカバリ用のetcdを用意することで対応する。

 管理/作業用ホスト上でのetcdの設定は、cloud-configファイルの「coreos:」および「units:」以下に次の項目を追加することで行う。

coreos:
  etcd2:
    name: master01
    initial-advertise-peer-urls: http://<管理/作業用ホストのIPアドレス>:2380
    listen-peer-urls: http://<管理/作業用ホストのIPアドレス>:2380
    listen-client-urls: http://<管理/作業用ホストのIPアドレス>:2379,http://127.0.0.1:2379,http://<管理/作業用ホストのIPアドレス>:4001,http://127.0.0.1:4001
    initial-cluster-token: osdn-test-webserver
    initial-cluster: master01=http://<管理/作業用ホストのIPアドレス>:2380
    initial-cluster-state: new
  
  
  units:
    -
      command: restart
      name: etcd.service

 cloud-configファイルにこれらの項目を追加したら、coreos-cloudinitコマンドを実行して設定の反映およびetcdサービスの起動を行っておく。etcdが正しく動作しているかどうかは、次のように「etcdctl cluster-health」コマンドを実行することで確認できる。ここで問題がなければ「cluster is healthy」というメッセージが表示される。

$ etcdctl cluster-health
cluster is healthy
member 7e9741dbb084423487dff89cd4922981 is healthy

 なお、etcdは設定や操作などを行うクライアントとの通信に2379番ポートおよび4001番ポートを、クラスタ間での通信に2380番ポートを利用する。通信はHTTPベースのものを利用しており、たとえば次のようにcurlコマンドを使ってetcdサーバーに接続できるかどうかを確認できる。

$ curl -L http://<管理/作業用ホストのIPアドレス>:2379/version
etcd 2.0.10

 etcdサービスが稼働したら、続いて「etcdctl set」コマンドを実行してクラスタを識別するためのクラスタキーと初期クラスタサイズを登録しておく。

etcdctl set /discovery/<クラスタキー>/_config/size <初期クラスタサイズ>

 クラスタキーには任意の文字列を指定できる。また、初期クラスタサイズは最初に稼働させるクラスタ構成ホストの数を指定するものだ。ここで指定した台数の構成ホストが稼働するまでクラスタは利用できないので、実際に最初に稼動させるクラスタ数と等しい数を指定しておく。

 たとえばクラスタキーを「osdn-test-cluster」、クラスタの初期サイズを2とする場合、次のようになる。

$ etcdctl set /discovery/osdn-test-cluster/_config/size 2

 設定した値は「etcdctl get」コマンドで確認できる。

$ etcdctl get /discovery/osdn-test-cluster/_config/size
2

クラスタ構成ホスト側etcdの設定

 続いて、クラスタ構成ホスト側のetcdの設定を行う。こちらも管理/作業用ホストと同様、cloud-configファイルで設定を行う。具体的には、以下の内容を「coreos:」以下に追加する。

coreos:
  etcd2:
    initial-advertise-peer-urls: http://<そのホストのIPアドレス>:2380
    listen-client-urls: http://<そのホストのIPアドレス>:2379,http://127.0.0.1:2379,http://<そのホストのIPアドレス>:4001,http://127.0.0.1:4001
    advertise-client-urls: http://<そのホストのIPアドレス>:2379
    listen-peer-urls: http://<そのホストのIPアドレス>:2380
    discovery: http://<管理/作業用ホストのIPアドレス>:2379/v2/keys/discovery/osdn-test-cluster
  
  
  units:
    -
      command: restart
      name: etcd2.service

 それぞれのクラスタ構成ホストでこの設定を追加して適用した後、適当なクラスタ構成ホストで「etcctl cluster-health」コマンドを実行すると、次のようにクラスタの稼動状態とメンバーの情報が表示されるはずだ。

$ etcdctl cluster-health
cluster is healthy
member b8cba30e2543d7ea is healthy
member c62b0b3bec30a1a7 is healthy

fleetの設定

 etcdでのクラスタ設定が行えていれば、fleetサービスを起動するだけでfleetが利用可能になる。fleetサービスはsystemctlコマンドで起動できる。

 $ sudo systemctl start fleet

 手動で毎回起動するのは面倒なので、cloud-configファイルの「units:」以下に次の内容を追加して自動起動するよう設定しておくのが良いだろう。

  units:
      command: restart
      name: fleet.service

 fleetの操作は「fleetctl」コマンドで行える。たとえばクラスタ上のマシン一覧を確認するには、「list-machines」サブコマンドを使用する。

$ fleetctl list-machines
MACHINE         IP              METADATA
789b8e8a...     192.168.1.100   -
d5cb5165...     192.168.1.101   -

fleetを使ってサービスを起動する

 それでは、fleetを使ってサービスを起動してみよう。まず、クラスタ上のマシン上でfleetで起動するサービスを定義したユニットファイルを適当なディレクトリに作成する。このとき、ファイル名は「<サービス名>@.service」というものにする。たとえば「osdn-test-webserver」というサービスであれば、「osdn-test-webserver@.service」となる。今回作成したユニットファイルの中身は次のようになっている。

[Unit]
Description=OSDN Test Web Server
After=docker.service
Requires=docker.service

[Service]
ExecStartPre=-/usr/bin/docker kill osdn-test-webserver-%i
ExecStartPre=-/usr/bin/docker rm osdn-test-webserver-%i
ExecStartPre=/usr/bin/curl http://hylom.net/pub/osdn-test-webserver.tar -o /tmp/osdn-test-webserver.tar
ExecStartPre=/usr/bin/docker load -i /tmp/osdn-test-webserver.tar
ExecStart=/usr/bin/docker run --name osdn-test-webserver-%i -p 80:80 osdn-test-webserver
ExecStop=/usr/bin/docker stop osdn-test-webserver-%i

[Install]
WantedBy=multi-user.target

[X-Fleet]
X-Conflicts=osdn-test-webserver@*.service

 fleet向けのユニットファイルの中身は、基本的にはsystemd向けのユニットファイルとほぼ同じだ。ただし、「%i」という変数を使用している点が異なる。この変数は、サービスの起動時にそのインスタンス番号に置換される。

 また、追加されている「X-Fleet」というセクションはfleet特有の設定を記述するセクションだ。ここでは「X-Conflicts」という設定項目を使い、1つのホスト上で同時に起動できない(競合する)サービスを指定している。ここで「osdn-test-webserver@*.service」を指定することで、1つのマシン上で複数のosdn-test-webserverサービスが稼動しないようにしている。

 作成したユニットファイルは、「fleetctl submit」コマンドでfleet内に登録できる。

$ fleetctl submit osdn-test-webserver@.service

 登録したユニットファイルは、「fleetctl list-unit-files」コマンドで確認できる。

$ fleetctl list-unit-files
UNIT                            HASH    DSTATE          STATE           TARGET
osdn-test-webserver@.service    ee2e399 inactive        inactive        -

 登録したサービスを起動するには、「fleetctl start」コマンドを実行する。このとき引数にはサービス名を指定するのだが、その際に「@」の後にインスタンス番号を指定できる。たとえば次のように実行すると、インスタンス番号1および2の2つのインスタンスを生成できる。このサービスでは複数のインスタンスが同一のマシン上で実行されないよう指定されているため、各インスタンスはクラスタに属する異なる2つのマシン上で実行される。

$ fleetctl start osdn-test-webserver@{1..2}.service
Unit osdn-test-webserver@1.service launched on 789b8e8a.../192.168.1.100
Unit osdn-test-webserver@2.service launched on d5cb5165.../192.168.1.101

 稼動しているサービス一覧は「list-units」サブコマンドで確認できる。

$ fleetctl list-units
UNIT                            MACHINE                         ACTIVE  SUB
osdn-test-webserver@1.service   789b8e8a.../192.168.1.100       active  running
osdn-test-webserver@2.service   d5cb5165.../192.168.1.101       active  running

 サービスを停止させるには、「stop」サブコマンドを使用する。

$ fleetctl stop osdn-test-webserver@1.service
Unit osdn-test-webserver@1.service loaded on 789b8e8a.../192.168.1.100
$ fleetctl list-units
UNIT                            MACHINE                         ACTIVE          SUB
osdn-test-webserver@1.service   789b8e8a.../192.168.1.100       inactive        dead
osdn-test-webserver@2.service   d5cb5165.../192.168.1.101       active          running

 また、「destroy」サブコマンドでサービスを破棄できる。

 $ fleetctl destroy osdn-test-webserver@1.service
Destroyed osdn-test-webserver@1.service
$ fleetctl list-units
UNIT                            MACHINE                         ACTIVE  SUB
osdn-test-webserver@2.service   d5cb5165.../192.168.1.101       active  running

別マシンからfleetを操作する

 さて、今まではクラスタ構成ホスト上からクラスタの操作を行っていたが、クラスタに属していないホストからクラスタの操作を行うことも可能だ。たとえば管理/作業用ホストからクラスタの操作を行うには、次のように「FLEETCTL_ENDPOINT」環境変数に操作したいクラスタ構成ホストどれか1つのURLを指定する。

$ export FLEETCTL_ENDPOINT=http://192.168.1.100:2379

 これでクラスタをfleetctlコマンドで操作できるようになる。

$ fleetctl list-machines
MACHINE         IP              METADATA
789b8e8a...     192.168.1.100   -
d5cb5165...     192.168.1.101   -

独特の流儀はあるが管理や構成のコストは低いCoreOS

 さて、ここまで簡単ながらCoreOSを使ったクラスタ構成について紹介してきたが、cloud-configやetcd、fleetといった独特の流儀が多いため、やや取っつきにくい印象もある。とはいえ、設定ファイル1つでほぼすべての設定が完了し、クラスタも容易に構成できる点は魅力的だろう。

 今回紹介はしていないが、fleetではクラスタ構成ホストを監視し、ホストが停止したらそこで稼働していたサービスを別のホストで起動させる、といったような管理機能も用意されており、柔軟にクラスタの管理が可能だ。コンテナを利用してサービスを運用したい場合、CoreOSとfleetの組み合わせは十分検討に値するメリットがあると言える。

 なお、CoreOSは現在も活発に開発が進められているため、公式ドキュメントについても一部情報が古かったり、記述が不足しているものがある。いっぽうでIssueトラッカーなどによるサポートについてはそれなりに機能しているので、もし何かトラブルに遭遇したらそちらをチェックしてみると良いだろう。