プロキシが動いている最中のシャットダウン停止が VPN の不具合であることはまれで、画面に見える最後のものが VPN であっても同じだ。プロキシの背後のアプリケーションは、まもなく消える経路をまだ待っており、トンネルを所有するユニットにはまだ閉じるよう指示が出ていない。遅いサービスに見えるものは、たいてい順序の問題である。順序を正すほうが、タイムアウトを変えるより多くの事例を解決する。

逆順に外側へ進み、まず利用側、次にプロキシ、最後にトンネルを、それぞれを起動したコンポーネントで停止させる。

停止する前にユニットを洗い出す

通常の実行に関わるアプリケーションのユニット、プロキシのユニット、VPN の所有者を書き出す。独自の後始末を持つリゾルバや補助ツールも加えるのは、深夜に忘れられるのが決まってそれらだからだ。この一覧がなければ、最初に打つ停止コマンドは犠牲者を無作為に選ぶことになる。

名前は弱い証拠にすぎない。ユーザープロキシ、システムプロキシ、別アカウントのトンネルが同名でも、ライフサイクルはまったく無関係でありうる。

作業を終わらせてから利用側を止める

計画的な停止はアプリケーションから始まり、要求を完了させるか取り消させるのを待ってから、サービスのユニット経由でプロキシを止める。トンネルが消えた後もアプリケーションを動かし続けるべきなら、まもなく消える経路にぶら下げたままにせず、意図的にそう構成する。

停止中に飛んでいた要求は、次回起動時の再試行の嵐がまさにそこから生まれるのだから、記録に残す。

ExecStop と ExecStopPost は別の道具である

ExecStop= は同期的な停止操作であり、ExecStopPost= は起動に失敗した後でも走る後片付けである。プロセスに信号を送って戻るだけの停止コマンドでは、利用側がまだ書き出している間に systemd が先へ進んでしまう。どんな場合でも必要な処理は後者に入れ、停止を妨げる必要のある処理は同期側に入れる。パッケージのファイルをその場で編集せず、実効ユニットとそのドロップインを確認する。

依存関係グラフは停止ジョブを順序づける。非同期スクリプトを同期にはせず、遠隔のアプリケーションにデータを確定させることもしない。

停止に上限を設け、その上限の代償を知る

TimeoutStopSec= は停止コマンドとサービス自身の終了の両方に上限を課し、期限が切れると systemd は強制終了へ移る。値はアプリケーションの実際の書き出し挙動から選び、達したときに失われうるものを書き留める。速く電源を切るために選ばれた数値は、正体を隠したデータ損失の設定である。

停止がなお遅いときは、ユニット、その子プロセス、ジャーナルの間隔、タイムアウトの結果を記録する。すべてのタイムアウトを短くしたり大雑把な強制終了に走ったりすると、次の起動時にきれいな停止が壊れた状態に変わる。

所有者経由で切断し、三つの場合を試す

利用側が止まったら、トンネルを所有する管理側、つまり systemd のインスタンス、NetworkManager のアクティブ接続、ベンダーのサービスのいずれかに切断を依頼する。無関係な OpenVPN プロセスを殺しても、経路を提供したセッションが必ず消えるわけではなく、再起動ポリシーを持つプロキシは経路を解体している最中に要求を作り直すことがある。始める前にそのポリシーを計画的停止と調整する。

通常の停止、要求が動いている状態での停止、トンネルが先に落ちる停止を試す。期限付きで再現できる手順は、一度しか効かない素早い停止に勝る。

関連記事