CentOS7の/etc/fstabのnofailオプション(あるいはsystemd)

この記事は最終更新日から5年以上が経過しています。

結論(ショートカット)

ディスクのマウントに失敗してもブートしてほしいときには、/etc/fstabnofailオプションを付けます。

何をしたかったか

 サーバ運用を行っていると、OS用とは別にデータディスクを追加することがあると思います。アプリケーションサーバやサービスが使用するデータとOSを分けておくためです。データディスクはRAIDだったり、エンクロージャだったり、AWSでEBSだったりします。
 ところがRAIDは崩壊したり、エンクロージャは線が抜けていたり、EBSは別のインスタンスを立ち上げるときにつけ忘れたりします。そのため、CentOS6ではデータディスクは/etc/fstabで第5オプションと第6オプションを0 0にしていたのでした。(正確には起動時に無視するかどうかは第6オプション)

CentOS6での/etc/fstab(抜粋)
/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は起動していません。
emergency.png
 したがってシリアルコンソールなどの回復手段が無かった場合にはサーバまで走るか、AWSのような環境では一度ディスクイメージを操作する必要があります。泣きたくなる日ですね。

なんでこうなるの?

systemd

 適当に調べた限りでは、fstab(やmount)がsystemdの担当になり、local-fs.targetというスペシャルユニットになり、そこにこう書かれているからです。

/usr/lib/systemd/system/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が立ち上がるのはどのタイミングでしょうか。万が一にも逆順になると、dfduの結果が食い違うような割とやっかいなトラップになります。もしこうなるのならいっそ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を設定したところ、すんなり起動しました。

CentOS7での/etc/fstab(抜粋)
/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のユニット作成は簡単かつさすがの高機能です。起動しなかった場合の通知や標準出力・標準エラー出力方法は今後の課題…

/etc/systemd/system/myservice.service
[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
nishemon
Javaっ子
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
この記事にコメントはありません。
あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした