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
|
|