Fleetの使い方,Unitファイルの書き方
CoreOSではすべてのアプリケーションをDockerで動かす.このとき,コンテナによるサービスをCoreOSクラスタのどのマシンで起動するかをいちいち人手で決めるわけにはいけない.クラスタ内のリソースの状態や動いているサービスに基づき,適切なマシンでコンテナを動かすスケジューリングの仕組みが必要になる.
このスケジューリングとコンテナの管理にCoreOSはfleetを用いる. fleetを使うとCoreOSクラスタが1つのinit systemで動いているかのようにそれを扱うことができるようになる.開発者はどのマシンでどのDockerコンテナが動いているかを気にする必要がなくなる.
例えば,5つのコンテナを動かす必要があれば,fleetはクラスタのどこかでその5つのコンテナが動いてることを保証する.もしコンテナが動いているマシンに障害があっても,fleetはそのコンテナを別のマシンにスケジューリングしなおす(フェイルオーバー).
スケジューリングは柔軟で,マシンのRegionやRoleによって振り分けることもできるし,同じサービスを同じマシンでは動かさないようにするといった設定もできる.例えば,複数のDBコンテナを別々のマシンに分散させるといったこともできる.
DigitalOceanの“Getting Started with CoreOS”シリーズの
- How To Use Fleet and Fleetctl to Manage your CoreOS Cluster
- How to Create Flexible Services for a CoreOS Cluster with Fleet Unit Files
において,fleetを操作するためのfleettclコマンドの使い方と,その設定ファイルであるUnitファイルの書き方を良い感じに解説していたので,それらを参考にfleetの使い方をまとめておく.
まずfleetの技術的概要をまとめる,次にfleetctlコマンドによるサービスの管理方法を書く.最後にUnitファイルの書き方について説明する.
fleetの技術的概要
fleetはクラスタレベルのsystemdと捉えることができる(単一マシンのinit systemがsystemdで,クラスタのinit systemがfleet).
https://coreos.com/assets/images/media/fleet-schedule-diagram.png
fleetはengineとagentという大きく2つのコンポーネントから構成される.engineはジョブスケジューリングとクラスタサイズの変更を管理する.agentはマシンの代わりにジョブを引き受ける.Unitがクラスタに割り当てられると,agentはUnitファイルを読み込み,それを開始する.そして,systemdの状態をfleetに通知する.
バックエンドではetcdクラスタが動いており,engineとagentの協調に使われる.
fleetctlによるサービスの管理
fleetの設定ファイルは,systemdのunitファイルにfleet特有の設定(e.g., クラスタ内での分散方法など)を加えたものを利用する. このファイルの詳細は後述するとして,ここでは以下のようなHello Worldを出力しつづけるhello.serviceを利用する.
1 2 3 4 5 6 7 8 9 10 11 | |
fleetによるサービス管理はfleetctlコマンドを使って行う.サービスの起動は以下の流れで行われる.
- Unitファイルを読み込む
- クラスタ内の特定のマシンにスケジューリングする
- サービスを起動する
サービスの登録
まず,submitコマンドを使ってサービスを登録する.これは単にfleetがファイルをメモリ内に読み込むだけ.
1
| |
fleetが読み込んだunitファイルは以下で一覧できる.
1 2 3 | |
DSTATEとは望まれる状態であり,STATEは実際の状態を示す(DSTATEとSTATEが一致しているとき各種コマンドがちゃんと動いたと考えて良い).TARGETはサービスを実行するべきマシンを示す.今はまだスケジューリングをしていないので,-となる.
読み込んだファイルの内容を確認することもできる.
1 2 3 4 5 | |
submitコマンドを一度実行し,Unitファイルを変更して再びsubmitしてもアップロードは実行されない.ファイルを更新するには,一度アップロードしたものを削除する必要がある.
サービスのスケジューリング
次に,サービスをスケジューリングする.スケジューリングとは,fleetエンジンがクラスタ内でサービスを実行するのに最も適したマシンを選択することである.これはUnitファイルの[X-Fleet]セクションの既述と,クラスタ内のマシンの現在のリソース状態に基づき決定される.サービスがスケジューリングされると,Unitファイルはそのマシンに渡され,ローカルのsystemdインスタンスに読み込まれる.
スケジューリングは,loadコマンドで行う.
1 2 | |
読み込んだUnitファイルを確認する.
1 2 3 | |
TAGETセクションにサービスを実行するマシンが追加されているのが確認できる.STATEはスケジューリングされたことを示している.
list-unitsコマンドを使うと,実行中,もしくはスケジューリングされたサービスとその状態を確認することができる.
1 2 3 | |
これはsystemdから得られる情報であり,ACTIVEはサービスの状態を示し,SUBはより低レベルな状態を示す.
サービスを起動する
次にサービスを起動してみる.起動はstartコマンドで行う.
1 2 | |
Unitファイルの状態を確認する.
1 2 3 | |
次に,systemdの状態を確認する.
1 2 3 | |
ちゃんとサービスが起動したことが確認できる.
サービスの詳細の状態を得る
list-unitコマンドで,現在スケジュールされているUnitを一覧でき,list-unit-filesでfleetが知っている全てのUnitの状態を一覧できる.
一覧ではなく,各Unitの詳細をみることもできる.例えば,statusコマンドを使うと,そのUnitが動いているマシンのsystemctl statusの結果を取得することができる.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
journalコマンドを使うと,各サービスのjournalのエントリをみることもできる.
1 2 3 4 5 6 | |
tail -fみたいなこともできる.
1
| |
スケジューリングされたマシンにログインしていろいろ調査することもできる.このときスケジューリングされたマシンのIPを知る必要はなく,サービス名でログインできる.
1
| |
サービスの停止
まず,stopコマンドでサービスを停止できる.
1 2 | |
1 2 3 | |
これにより,サービスはloaded状態に戻ったことが確認できる.これはサービスは止まったが,スケジューリングされたマシンのsystemdには読み込まれた状態である.
これを削除するには,unloadコマンドを使う.
1 2 | |
1 2 3 | |
inactive状態になり,ターゲットのマシンも消えていることが確認できる.これは,スケジューリングを解除し,fleetにUnitファイルが読み込まれただけの状態である.
fleetが読み込んだUnitファイルを削除するには,destroyコマンドを使う.
1 2 | |
1 2 | |
完全に消えた.
Unitファイル
fleetによるスケジューリングはUnitファイルにより行う.UnitファイルはsystemdのUnitファイルにfleet特有の[X-Fleet]セクションを加えたものを使う.systemdについて以下の記事が詳しい.
- Systemd入門(1) - Unitの概念を理解する
- Systemd入門(2) - Serviceの操作方法
- Systemd入門(3) - cgroupsと動的生成Unitに関する小ネタ
- Systemd入門(4) - serviceタイプUnitの設定ファイル
- Systemd入門(5) - PrivateTmpの実装を見る
Unitファイルの名前
Unitファイル名には命名規則がある.string.suffixもしくはstring@instance.suffixとする.
stringは必須.Unitの名前を指定する.[a-zA-Z0-9:_.@-]+の正規表現に一致する必要があるinstanceは必須ではない.1つのUnitファイルから複数のUnitインスタンスを作成するときに利用する(例えば,hello@.serviceファイルからhello@1.service,hello@2.serviceを作る).この値はUnitファイル内から%iとして参照することもできる.[a-zA-Z0-9:_.@-]+の正規表現に一致する必要があるsuffixは必須.Unitの属性を指定する.service, socket, device, mount, automount, timer, pathのいずれか.
セクション
一般的なfleetの設定ファイルは以下のようになる(serviceの場合).
1 2 3 4 5 6 7 8 9 10 11 | |
[Unit]セクションには,Unitの依存関係/順序関係など、Unitのタイプに依存しない設定を既述する(systemdと同様).
[Service]セクションには,service固有の設定を書く.例えば,起動・停止コマンドや,サービス起動前に実行するべきコマンド,環境変数ファイルの場所などを既述する(systemdと同様).
[X-Fleet]セクションには,fleet特有の設定を書く.どのようにクラスタにスケジューリングを行うかを定義する.具体的には以下のような設定ができる.
MachineID- 特定のマシンにスケジューリングしたときに利用する.マシンのIDを指定する(fleetctl list-machines -l).MachineOf- 特定のUnitが実行されているマシンと同様のマシンにスケジューリングしたときに利用する.Unit名を指定する.MachineMetadata- マシンのメタデータに基づきスケジューリングしたいときに利用する.例えばRegionやRole, diskTypeなど.これらはcloud-configのfleet.metadataの項目で指定できる.Conflicts- 特定のUnitが実行されているマシンを避けたいときに利用する.MachineOfの逆.Global- すべてのマシンにスケジューリングしたいときに利用する.
Apacheサービス
具体例としてApacheサービスを起動するためのUnitファイルを作る.ファイル名はapache@.serviceとする.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
まず[Unit]セクションには,サービス名とこのサービスを起動するための依存を既述する.Afterにはこのサービスが起動する前に起動されているべきサービスを,Requireにはこのサービスが必要とするサービスを既述する.
Requireに書いたサービスが失敗したらこのサービスも起動に失敗する.失敗してもサービスを継続したい場合はWantを使う.
次に[Service]セクションには,具体的なサービスの起動・停止方法を書く.TimeoutStartSecを0にしてタイムアウトを無効にしている(デフォルトは90秒).これはDockerイメージのpullに時間がかかる可能性があるため.
EnvironmentFileを指定するとその中に既述された環境変数が有効になる./etc/environmentにはCOREOS_PUBLIC_IPV4といった値が定義されている.
ExecStartPreにはサービス起動前に実行するべきコマンドを定義する.=-とした場合は,失敗してもサービスの起動を続行する(一度目の起動はdocker killやdocker rmは必ず失敗する).そしてExecStartとExecStopで起動,停止コマンドを指定する.
最後に[X-Fleet]には,fleetのスケジューリングに関わる設定を既述する.X-Conflictsを指定すると,そのUnitが実行されているマシンではスケジューリングされなくなる.この場合,このapacheサービスは同じマシンにはスケジューリングされない.
さらにこのUnitファイルはテンプレートになっており,%iには動的に値が代入される.スケジューリングする際に具体的な値を入れる.例えば,以下のようにすると,起動時には80という値が代入される.
1 2 | |
テンプレートの扱い
fleetもsystemdもシンボリックリングを扱うことができる.そのため上述したapache@.serviceのスケジューリングは以下のように既述できる.
1 2 | |
これは扱うテンプレートとそのインスタンスが増えたときに管理が楽になる.例えば,templeatesとinstance,staticというディレクトリを作り,動的な変更のないUnitファイルはstatic以下に,テンプレートとして使うUnitファイルはtemplates以下に配置し,instancesにシンボリックリンクを作る.
1 2 3 | |
1 2 3 4 | |
こうしておくと,instanceは以下のように一気に起動できる.管理と運用が楽になる.
1
| |