それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

/var,/homeに設置した systemd ユニット・ファイルが実行されない(シンボリックリンクは詰む)

/var に設置した systemdファイルが読み込まれない。いくら正しいサービスファイルを書いても、起動時にサービスが起動しないのである。

色々見ていたら、こんなエラーが遭遇した

systemdはマジ怖い

存在するファイルを実行しない

/etc/systemd/system に設置したサービス・ファイルが「存在するのに」「存在しない」と言われて、起動時に実行されなくて、ずっと悩んでた。

原因がわかった。/home,/varマウントのタイミングだった

systemdが実行されるタイミングと私達が動作チェックするタイミングが異なる。

systemdが、サービスを実行するタイミングでは、ファイルシステムの一部はマウントされてない。そう /home も /var も /optもマウントされてない。

しかし、私たちが、動作チェックをするときは、すべてがマウントされたあとにssh ログインをして動作チェックをしているのだ。

再現方法

  • /home を別ボリュームにする
  • /etc/systemd/systemに /home/takuyaへのシンボリックとしてユニットファイルを設置する

これで、おかしなことができる。ユニットファイルが存在し動作も完璧。しかしsystemdに、file not found. のエラーを吐かせることができる。マウントのタイミングが異なるのだ。

依存関係がおかしいとかではなく、単にファイルが読み出せないのである。ファイルが読めないから依存関係の定義も読めないのである。

サービスファイルのリンク先が見つからない。

$ dmesg
[   11.819468] systemd[1]: smtp_proxy.service: Failed to open /var/repos/smtp-proxy/etc/systemd/smtp_proxy.service: No such file or directory

そうなんですよね。マウント前に、サービスが起動する。リンクをたどってユニットファイルを読み込もうとしても、マウントされてないのである。だから絶対に起動しないのだ。

シンボリックリンクとマウント時点の問題点

シンボリック・リンクを辿れないで systemd service が file not found のエラーを吐く場合

/etc/systemd/system にシンボリックリンクを設置してた場合

$ls -l /etc/systemd/system
iperf3@192.168.2.5.service -> /home/takuya/samples/iperf3@.service

このように、ホームディレクトリに、サンプルで作ったservice ファイルやtimerファイルなどのユニットを設置していた場合

そして、それをシンボリック・リンクで、/etc/systemd/systemに設置した場合。

このsystemdファイルは絶対に実行されない。

homeがマウントされるより先にsystemdファイル実行される

/etc/systemd/system が利用可能になった時点で、systemdはサービスを起動する。

しかし、その時点では/homeは存在するが、マウントされてないので、 /home/takuya はまだ存在してない。

そのために、/etc/systemd/system/に設置したファイルのリンク先を辿れない。

結果として、dmesg に service file not foundが大量に記録されることになる。

/etc/systemd/system とマウントの関係に注意

マウントされるよりさきに/etc/systemd/system が実行されるので、リンクが辿れずに、サービスファイルが見つからなくなる。

サービスファイル内で、いくらafter/wanterd/require を書こうとも、そもそも、ユニットファイルが読み込まれないのだから。お手上げである。

以前は、こんな事は起きなかったのだが・・・ debian 11 をインストールしたときに、インストーラーのおすすめ通りvar homeパーティション分割したので、サービスが一切起動しない結果になった。

怖い・もういや

systemdはまじ怖い。まじめんどくさい。

systemd は窓から投げ捨てたい。っていうか、一般ユーザーがサービスユニットファイルを作るとき、マウント前に起動作業することなど全く殆ど皆無である。起動処理が終わってから実行してほしいのだ。systemdが起動処理を終えてから実行してくれ。確実に起動するエントリポイントを作って欲しい。ユーザーがマウントまで意識してサービス・ユニットファイルを書くようになってるのは絶対におかしい。

/etc/systemd/system 以外に設定箇所作って欲しい。マウントとネットワークの起動処理が、全部終わったら起動する確実なやつください。

systemdは シンボリック・リンクをつかうくせに、私達がシンボリックを使うとバグを踏むのです。別ボリュームの/homeに設置したシンボリック・リンクは、マウント前になってsystemdでは動かせないのです。当たり前だけど、油断してると絶対に気づかないんです。

何も書かなくても、シンボリックリンクなら、マウントされるまで待ってほしい。

そして、なによりマウントディスクに対し systemctl daemon-reload を掛けたのであれば、リロード時にわかるはずである。警告も何も出ない。

そして、/etc/systemd/systemに記載された内容をどこかべつの /lib/systemdにコピーしてsystemd設定ファイルをつくり起動時にロードしてくれればいいのだ。それもしてないのだ。ただただ、分割先が分かりにくい init.d でしか無い。

気づくまで何時間も溶けた・・・

Linux使うならもうdocker のコンテナに引きこもって root 権限でアプリを実行することで各種の地雷を回避するのが最高なんですよねやっぱり。

解決方法は「ない」

systemd のユニットの設定の依存関係で解決方法は、なんと提供されない。

代わりに、ユニットを作成するしか無い。

After=local-fs.targetExecStart=daemon-reload するのである。

systemdの公式レポジトリの回答である。

/etc/systemd/system/after-mount.service

これが公式見解らしい。local-fs.target(マウント後)に daemon-reload するサービスユニットが必要

systemctl cat after-mount.service
# /etc/systemd/system/after-mount.service
[Unit]
After=local-fs.target

[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl daemon-reload
ExecStart=/bin/bash -c '/usr/bin/systemctl start $(/usr/bin/systemd-escape var/job-worker/storage/app/work/outputs )'
ExecStart=/bin/bash -c '/usr/bin/systemctl start $(/usr/bin/systemd-escape var/job-worker/storage/app/work/tmpfs)'
ExecStartPost=/bin/bash -c '/usr/bin/systemctl start job-woker.service'

[Install]
WantedBy=multi-user.target

systemd はユニット間の依存関係を見直して、スッキリさせた再設計ねぇ?冗談が過ぎる。

シェルスクリプトをExecStartに積んでくれとのこと。詰んでるんだよ。詰みだよ詰み。シェルスクリプトを積んでいくならsystemdの意味ないじゃん。

ExexStartで systemctl start xxx.mount でマウントするときに、ExexStartはmountエスケープを解釈できずに落ちるから、bash -c 'systemd start $( エスケープ処理 ).mount' みたいなことまでする羽目になる。上記のような強引なサービス処理で解決するしか無い。しかし、このような強引な処理は、シャットダウン時など依存関係で他箇所に絶対影響出ますよね。

ほんとうに酷い。

systemd enable 時にマウント関係をみて順序調整くらいしてほしいですね。

2024/09/09

また、無意識にシンボリックリンクでサービスが起動しない現象を引き起こした。 このようなことが起きると、知っていても注意しててもミスる

そもそも、ターゲットが「multi-user.target(マウント済み)」である。multi-user.targetに指定したサービス・ユニットだからマウント済みでロードされるべきであり、これをマウント前に起動時にロードする仕様であるsystemd が異常だと思うわ。

マウント後にさらにtmpfsマウントするような記述が書けないんだ。init.dとやってることが変わらない。まじで滅んでほしい。

ExecStart の systemctl start xxx.mount でマウントすると、エスケープ文字(ハイフンはエスケープされる)が解釈できずに落ちるるから、bash でラップしないと動かない。ってなんですか・・・・バグですよね。

こんなザマで、/etc/fstab や /etc/resolv.conf などもsystemdで置き換えてるって正気の沙汰とは思えない。

クソandクソ

解決方法を書きつつ、愚痴を大幅加筆。