Chef Infra Clientの「多重実行」を物理的に封殺せよ:極限の排他制御アーキテクチャ
現場のSRE諸君。Chefの`chef-client`がcronやsystemd timerで定期実行されている最中、CI/CDパイプラインや手動オペレーションと衝突し、`chef-client.lock`を巡る阿鼻叫喚のスタックトレースを拝んだ経験はないか?
「Chefは冪等性が担保されているから同時実行しても大丈夫」というのは、カタログスペックを信じた初心者の幻想だ。実際には、ロックファイルの競合、リソースの競合、そして何よりChefの実行プロセス自体が持つステートフルな挙動が、あなたのインフラに予期せぬ破壊をもたらす。
今回は、Chef Infra Clientの多重実行を「OSレベルで」完全に封殺し、堅牢な運用を実現するための極限テクニックを伝授する。
—
1. なぜ「Chefのデフォルト」だけでは不十分なのか
Chefには標準でPIDロック機能があるが、これには致命的な穴がある。プロセスがシグナルで強制終了された際のPIDファイルの後始末や、異なる実行コンテキスト(rootのcron vs ユーザーのデプロイ権限)間でのロックの不整合だ。
我々が目指すべきは、「OSカーネルが管理する排他ロック」である。
2. 実装テクニック:flockによる「物理的な」排他制御
最も確実なのは、Linuxの`flock`コマンドを利用することだ。これはファイルディスクリプタを用いた排他制御であり、プロセスが死んでもOSがロックを解放するため、Chef特有の「ロックファイルが残って二度とChefが動かない」という悲劇を回避できる。
推奨される実行ラッパースクリプト (`/usr/local/bin/chef-wrapper.sh`)
!/bin/bash
実行ロックファイルを定義(/run以下はtmpfsなので再起動でクリーンになる)
LOCKFILE=”/run/chef-client.lock”
-x: 排他ロック
-n: ロック取得に失敗したら即座に終了(ノンブロッキング)
実行本体
flock -xn “$LOCKFILE” -c “/usr/bin/chef-client -c /etc/chef/client.rb”
終了ステータスのハンドリング
EXIT_CODE=$?
if [ $EXIT_CODE -eq 1 ]; then
echo “$(date): Chef is already running. Skipping execution.”
# ここで監視ツール(Datadog/Mackerel)にカスタムメトリクスを飛ばすと最高だ
exit 0
elif [ $EXIT_CODE -ne 0 ]; then
echo “$(date): Chef failed with exit code $EXIT_CODE”
# アラート発報ロジックをここに
exit $EXIT_CODE
fi
3. systemd RuntimeDirectory を活用したモダンな設計
systemdで管理しているなら、`RuntimeDirectory`を使うのが最もエレガントだ。これにより、ロックファイルの管理をOSのライフサイクルに委ねることができる。
/etc/systemd/system/chef-client.service の抜粋
[Service]
ExecStart=/usr/bin/chef-client -c /etc/chef/client.rb
/run/chef-client ディレクトリを自動生成し、サービス停止時に自動削除する
RuntimeDirectory=chef-client
RuntimeDirectoryMode=0755
ロックをかける際は RuntimeDirectory 内に作成させる
4. プロの現場で差がつく「設定のベストプラクティス」
client.rb の共有ルール
設定ファイルは「環境依存」と「共通定義」を分けるのが鉄則だ。`client.rb`を巨大化させるのは悪手である。
/etc/chef/client.rb
必須の共通設定
chef_server_url ‘https://chef.example.com/organizations/prod’
log_level :info
log_location STDOUT
隠れた神設定: リトライの冪等性を高める
Chefが失敗した際、即座にリトライせずランダムな間隔を空けることでロック競合を回避する
splay 300
チーム開発の生産性を上げるTips
- VSCode神プラグイン: `Chef Extension for VS Code` は必須。特に `Test Kitchen` の連携設定を各メンバーの `settings.json` に強制共有せよ。
- キーボードショートカット: `Command + Shift + P` で `Chef: Converge` を実行できるようにタスク化する。GUIをいじる時間を削り、ターミナルから `kitchen converge` を叩くリズムを体に染み込ませろ。
5. ロック競合時の自動リトライとアラート戦略
排他制御で「スキップ」させた場合、その実行が本当に重要なら、バックグラウンドでリトライキューイングが必要だ。
1. スキップ時はログに JSON を吐く: `{“status”: “skipped”, “reason”: “lock_acquired_by_other_process”}`
2. Fluentd/Logstashで収集: このログを検知し、3分後に自動リトライするジョブを投げるか、Slackに「Chefの実行が重複しました。後続のCIを確認してください」とアラートを飛ばす。
結び:エンジニアが守るべき「聖域」
インフラ構成管理における最大の敵は「不確定要素」だ。Chefの多重実行は、インフラの整合性を揺るがす最大の不確定要素と言っても過言ではない。
今回紹介した `flock` を用いた排他制御は、シンプルだが極めて強力だ。小手先のツールに頼るのではなく、OSのプリミティブな機能と対話せよ。それが、大規模インフラを安定させる唯一の道だ。
さあ、今すぐ `chef-client` の呼び出しをラッパー経由に変更し、平穏な夜を取り戻せ。健闘を祈る。