Systemd で multi instance service を作る際の注意点
表題の通り、Systemdを使っている際に遭遇した問題について記載してみます。
Systemdを使う際に、一時ディレクトリとしてRuntimeDirectoryを定義し、そこをpid、sockファイル置き場にすることはよくあると思います。
サービス停止時に不要なものをクリーンしてくれるので便利なのですが、特定条件において注意が必要です。
まず、Systemd では一つのserviceファイルをテンプレートのように使用することで、複数のサービスインスタンスを管理することが可能です。
# foo@.service [Unit] Description=foo service %i [Service] Type=simple RuntimeDirectory=/var/run/foo PIDFile=/var/run/foo/foo_%i.pid ...
上記のようにファイル名を○○@.service としておくことで、systemctl start foo@baz.service
としてテンプレートのように扱うことができます。
この際、serviceファイル内の %i の部分が、baz で置換されるイメージです。
実際には %i 以外にも使用できる指定子はあるようなので、興味のある方は調べてみてください。systemd man page
# systemctl start foo@baz.service [Unit] Description=foo service baz [Service] Type=simple RuntimeDirectory=/var/run/foo PIDFile=/var/run/foo/foo_baz.pid ...
こうすることで、特定のサービスを用途ごとに比較的簡単に切り分けることができます。
察しの良い方は既にお気づきかもしれませんが、RuntimeDirectoryを %i などで分けておかないと、複数起動しているインスタンスの内、一つでも停止してしまうと、他のインスタンスの一時ファイルを巻き込んで全て削除してしまいます。上記の例であれば、PIDファイルが該当します。
なので、以下のように分けておく必要があります。
[Service] ... RuntimeDirectory=/var/run/foo_%i PIDFile=/var/run/foo_%i/%i.pid ...
こうすることで、RuntimeDirectory がインスタンスごとに分割でき、それぞれのインスタンスの停止に応じて削除されるようになります。
また、具体的なバージョンは失念してしまったのですが、比較的新しめのSystemdであれば以下のようにディレクトリ名に %i を定義することも可能です。(複数インスタンスの問題というより、fooが先に存在しないとディレクトリが作成できない制約か何かだったかもしれません)
RuntimeDirectory=/var/run/foo/%i
よくよく考えれば事前に予想できそうなものなのですが、踏むまでは気づけないものですね...
複数インスタンスの管理は便利な機能なので、ご存知なかった方は是非使ってみてください。