起動スクリプトがPIDファイルの存在を理由に OpenVPN の起動を拒否するとき、どこかでトンネルが動いていると読むのは自然である。その読みは十分に頻繁に間違うので、何かを削除する前に確認する価値がある。実行時ディレクトリのファイルは台帳にすぎず、台帳はプロセスを残さなかった異常終了や再起動、中断された停止を生き延びる。判断を決めるのは、生きているプロセスがあるか、そのプロセスはこのインスタンスのものかという二つの問いである。

ファイルではなくサービスから始める

拒否は通常 OpenVPN 自身ではなく起動スクリプトから来る。スクリプトはパスを読み、そこに何かを見つけ、設定を解析する前に止まる。したがって最初の問いはトンネルではなく起動スクリプトの契約についてである。

メッセージを出力したユニットまたはスクリプトを読み、検査するパス、宣言するサービス種別、そもそもファイルの書き込みを担っているかどうかという三つを確認する。現代的なユニットは単純監視や通知監視を使い、PIDファイルに一切触れないことがある一方、古い init スクリプトは自分で作ることがある。あるモデルの検査を別のモデルへコピーすると、まさにこの種の拒否が生じる。何かを解釈する前に、どちらのモデルが使われているかを突き止める。

そのとき初めて、問いはインスタンスが実際に動いているかどうかになる。

その識別子が自分のインスタンスのものかを確認する

ファイル内の数値は、数値が書かれたことだけを証明する。その識別子のプロセスが今存在することは証明しないし、ましてプロセスが自分のものだとも証明しないのは、識別子が再起動後に再利用されるからだ。

プロセスの起動時刻をファイルのタイムスタンプと、実行ファイルをユニットのコマンドと、サービスの所属をユニット名と比べる。三つのうちどれかが食い違えば、見ているのはトンネルではなく台帳である。

コンテナや複数の名前空間を動かすホストでは、識別子がある見え方では意味を持ち、別の見え方では存在しないことがある。プロセスが見えないことも、アカウントが検査できないのであれば、存在しない証拠にはならない。結論を出す前に、起動スクリプトが使う文脈から確認する。

systemctl status [email protected]
ps -o pid,lstart,args -p "$(cat /run/openvpn/vpn.pid)"

起動スクリプトがファイルについて何を約束したか

PIDFile= は、主プロセスの識別子をどこから読むかをサービス管理に伝える。管理側はそのファイルを書かないが、停止後に残ったファイルは削除するので、中断された終了がファイルを残すのはまさにそのためである。

この責任分担は、人が不具合として報告する二つの症状を説明する。決して現れないファイルは、デーモンまたはそのスクリプトが書き込めなかったことを意味し、監視の設定が誤っていることを意味しない。停止後も残るファイルは、停止の経路が完了しなかったことを意味する。

また、監視が主プロセスを直接追跡できる場合にこの慣習を避けるよう現在の指針が勧める理由も説明する。その指針は、デーモンの前面実行、フォーク、通知の挙動がユニットの宣言と一致しなければならないから、パッケージ済みユニットの種別を変えてよいという許可ではない。デーモンを変えずに種別だけ変えると、トンネルが動いているのに失敗を報告するユニットになる、あるいはその逆になる。両者を揃えるか、どちらも触らないことだ。systemd のサービスリファレンスが所有の分担を説明している。

きれいな停止と中断された終了

きれいな停止はデーモンに終了を求め、それを待ち、管理側に自分の台帳を片付けさせる。中断された終了、強制終了されたプロセス、停止途中の再起動はその手順の一部を飛ばし、残るのはファイルである。異常終了のあとにそれを見つけても分かるのは停止が完了しなかったことであり、何かが待ち受けていることではない。

ファイルのタイムスタンプを最後の起動と、ユニットの最後の停止と比べる。

次に生きた証拠を直接確認する。VPNポートに結び付いたソケットとアドレスを持つトンネルインターフェースは、何かが動いている証拠であり、実行時ディレクトリのテキストファイルがそうなることは決してない。どちらもなければ、ファイルが何を主張しても、何も動いていない。

習慣でファイルを削除するのをやめる

どのインスタンスもファイルを所有しておらず、起動スクリプトがこの挙動を文書化していると確認できたなら、削除は正当な復旧手順である。

起動前の恒常的な習慣にすると、回避しようとしている検査そのものを無効にする。きれいな停止のたびに古いファイルが戻るなら、書き手と停止のライフサイクルがパスの所有について一致しておらず、その不一致こそ直す価値のある不具合である。

ホストへの独立した入口を確保し、通常の管理経由で一つのインスタンスを起動し、最初の結果を読む。次の人は、動くか、自らを説明するユニットを受け取る。

関連記事