#結論(ショートカット)
ディスクのマウントに失敗してもブートしてほしいときには、/etc/fstab
でnofail
オプションを付けます。
#何をしたかったか
サーバ運用を行っていると、OS用とは別にデータディスクを追加することがあると思います。アプリケーションサーバやサービスが使用するデータとOSを分けておくためです。データディスクはRAIDだったり、エンクロージャだったり、AWSでEBSだったりします。
ところがRAIDは崩壊したり、エンクロージャは線が抜けていたり、EBSは別のインスタンスを立ち上げるときにつけ忘れたりします。そのため、CentOS6ではデータディスクは/etc/fstabで第5オプションと第6オプションを0 0
にしていたのでした。(正確には起動時に無視するかどうかは第6オプション)
/dev/system/root / ext3 defaults 1 1
/dev/system/var /var ext3 noatime 1 2
tmpfs /dev/shm tmpfs size=4G 0 0
...
# 追加ディスク
/dev/xvda /data ext4 defaults 0 0
こうしておくと、起動時に/dev/xvda
ディスクが無いか、破損してmount
できなくてもサーバは起動してくれます。ところがCentOS7はそんな事情を組んでくれず、0 0
だろうとあっさりemergencyモードに入ります。この状態では当然sshdは起動していません。
したがってシリアルコンソールなどの回復手段が無かった場合にはサーバまで走るか、AWSのような環境では一度ディスクイメージを操作する必要があります。泣きたくなる日ですね。
#なんでこうなるの?
systemd
適当に調べた限りでは、fstab
(やmount)がsystemdの担当になり、local-fs.target
というスペシャルユニットになり、そこにこう書かれているからです。
[Unit]
Description=Local File Systems
...
Conflicts=shutdown.target
OnFailure=emergency.target
...
#回避方法
local-fs.target
に手を出すのはヤバそうですし、/
や/var
などがmount
できないなら確かにエマージェンシーになっても仕方ないでしょう。
力技回避としてはnoauto
にして 手動mount
が思い当ります。cronの@reboot
とかが良さそうですね。しかしできればアプリケーションサーバやサービスの起動前に確実性を持ってmount
していてほしいです。
cronの@reboot
が立ち上がるのはどのタイミングでしょうか。万が一にも逆順になると、df
とdu
の結果が食い違うような割とやっかいなトラップになります。もしこうなるのならいっそmount
してくれない方がマシなようにも思います。
##解決策
諦めてサービスやアプリケーションサーバを起動するsupervisordのsystemdのユニットの依存関係にmount
を実行するように記述するか?と思っていろいろ探してると今回の挙動に関する説明を見つけました。
> man systemd.mount
http://www.freedesktop.org/software/systemd/man/systemd.mount.html
(※URLはCentOS7とはバージョンが異なるので注意)
これによると、どうやら新しい/etc/fstab
にはいくつかのマウントオプションが加わったようです。今回やりたいことからすると、使えそうなのは次の2つのどちらかでしょう。以下意訳
nofail
nofail付のマウントはlocal-fs.targetやremote-fs.targetにとってWantsであり、Requiresではありません。したがって、このマウントポイントのマウントに失敗してもブートは続行されます。
x-systemd.automount
そのファイルシステム用のautomountエントリーが作られます。詳細はsystemd.automount(5)
無視させるにはnofail
で十分そうです。nofail
を設定したところ、すんなり起動しました。
/dev/system/root / xfs defaults 1 1
/dev/system/var /var xfs defaults 1 2
...
# 追加ディスク
/dev/xvda /data xfs nofail 0 0
ちなみに、nofail
自体は大昔からあり、起動時に無視してもよい場合はnofail
にするのが正しい(?)流儀のようです。CentOS6でも、fstabやmountの英語版のmanpageにはnofail
のことが(ブートには触れられていませんが)書かれています…(; ・3・)アルェー
#おまけ
なお、systemdではmount
コマンドを実行するユニットを作るのではなく、mountユニットという専用のユニットタイプがあります。automount用のユニットタイプもあります。~~なぜそこまでsystemdがやるのか…というかsystemdってホント何なの。~~このmountユニットはユニットファイルを書いてもいいらしいのですが、/etc/fstab
を読んで自動で作ってくれるので、いつもどおり/etc/fstab
に書くのが推奨されています。そりゃそうだ。
ところで、依存関係をきちんと考えると、本来はmount
に失敗した時にはアプリケーションサーバを立ち上げたくはありません。かといってsupervisordのユニットファイルにsupervisordの子であるアプリケーションサーバの依存関係を書くのは不本意でしょう。(supervisordユニットはrc.local
に依存しているのでrc.local
を使うことはできますが、rc.local
ももはやレガシーです。)
いろいろ考えるとsupervisord(の類)は基本的にはお役御免で、systemdでアプリケーションサーバを立ち上げるのがベストということになります。いや、そういう意味でmountをsystemdがやる必要があるのはわかるんですが…systemdのユニット作成は簡単かつさすがの高機能です。起動しなかった場合の通知や標準出力・標準エラー出力方法は今後の課題…
[Unit]
Description=My Service
#マウントできない場合は起動させない systemd.unit(5)
RequiresMountsFor=/data
# => または、Requires/After?(noautoの場合)
#ディスクのディレクトリ構造を起動条件に含めることも可能 systemd.unit(5)
ConditionPathExists=/data/myapp
[Service]
ExecStart=/path/to/app
Restart=always
TimeoutSec=60
User=http
#こういう指定/制限が可能 systemd.exec(5), systemd.service(5)
LimitNOFILE=2048
[Install]
WantedBy=multi-user.target