{ “status”: “draft”, “topic”: “Robust Shell Scripting for SRE”, “engineer_level”: “Senior SRE”, “focus”: [“Error Handling”, “Resource Cleanup”, “Atomic Updates”], “tools”: [“Bash”, “jq”, “curl”, “systemd”] } 本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
堅牢なシェルスクリプト設計:エラー伝播を防ぎリソースを確実に回収する自動化手法
【導入と前提】
外部APIから取得したJSON構成に基づき、システム設定を安全かつアトミックに更新する自動化処理を、堅牢なエラー制御下で実装します。
OS: Linux (Ubuntu 22.04 LTS / RHEL 9 動作確認済)
ツール: Bash 4.4+, jq 1.6+, curl 7.68+
【処理フローと設計】
graph TD
A["開始: trap設定"] --> B["APIから構成取得: curl"]
B --> C{"取得成功?"}
C -- No --> D["エラー終了: trap発動"]
C -- Yes --> E["JSONバリデーション: jq"]
E --> F["一時ファイル作成: mktemp"]
F --> G["アトミックな配置: mv"]
G --> H["正常終了: trapによる後処理"]
この設計の核心は、処理のどの段階で失敗しても「中途半端なファイル」を残さず、かつ失敗の原因を即座に特定できる点にあります。
【実装:堅牢な自動化スクリプト】
#!/usr/bin/env bash
# --- 安全のための設定 ---
# -e: コマンドの失敗で即座に終了
# -u: 未定義変数の参照でエラー
# -o pipefail: パイプライン途中のエラーを拾う
# -E: 関数内やサブシェルでもtrapを継承する
set -euo pipefail
trap 'echo "Error on line $LINENO"' ERR
# --- 環境設定 ---
readonly API_URL="https://api.example.com/v1/config"
readonly CONFIG_PATH="/etc/myapp/config.json"
readonly TMP_FILE=$(mktemp /tmp/myapp_config.XXXXXX)
# --- クリーンアップ処理の定義 ---
# スクリプト終了時(成功/失敗問わず)に一時ファイルを削除
cleanup() {
local exit_code=$?
echo "Cleanup: Removing temporary files..."
rm -f "$TMP_FILE"
exit "$exit_code"
}
trap cleanup EXIT
# --- メイン処理 ---
echo "Fetching configuration..."
# curlオプション説明:
# -s: 進捗非表示, -S: エラー時は表示, -f: HTTPエラー(4xx/5xx)で失敗させる
# --retry: 一時的なネットワークエラー時に再試行
curl -sSfL --retry 3 "$API_URL" -o "$TMP_FILE"
echo "Validating JSON schema..."
# jqオプション説明:
# -e: フィルタ結果がnull/falseの場合に終了ステータスを1にする
if ! jq -e '.version and .settings' "$TMP_FILE" > /dev/null; then
echo "Error: Invalid JSON schema detected."
exit 1
fi
echo "Applying configuration atomically..."
# 設定反映をアトミック(原子性)に行うため、mvを使用
# sudoが必要な場合は実行権限に注意
chmod 644 "$TMP_FILE"
mv "$TMP_FILE" "$CONFIG_PATH"
echo "Operation completed successfully."
systemdによる定期実行(Timer)
このスクリプトを systemd で管理する場合のユニット定義例です。
# /etc/systemd/system/myapp-config-sync.service [Unit] Description=Sync MyApp Configuration Safe Script [Service] Type=oneshot ExecStart=/usr/local/bin/sync-config.sh User=root Group=root # /etc/systemd/system/myapp-config-sync.timer [Timer] OnCalendar=hourly Persistent=true [Install] WantedBy=timers.target
【検証と運用】
正常系確認: スクリプトを手動実行し、終了ステータスを確認します。
./sync-config.sh && echo "Success" || echo "Failed"
異常系確認(シミュレーション):
API_URLを存在しないURLに変更して実行し、TMP_FILEが残らずにError on line...が出力されることを確認します。ログ確認:
systemd経由で実行した場合は、journalctlで詳細なスタックトレースを確認可能です。journalctl -u myapp-config-sync.service
【トラブルシューティングと落とし穴】
pipefailの未設定: デフォルトのBashではcurl | jqのパイプラインにおいて、curlが失敗してもjqが成功すると全体として成功扱いになります。必ずset -o pipefailを含めてください。権限の壁:
mktempで作成される一時ファイルは実行ユーザー所有です。/etcへの配置にsudoが必要な場合、mvコマンド時に所有権やパーミッションが適切に維持されるか確認してください(installコマンドの使用も検討してください)。環境変数の露出: APIキーなどを含む場合は、スクリプト内にハードコードせず
EnvironmentFile=を用いてsystemdから読み込むか、シークレット管理ツールを併用してください。
【まとめ】
運用の冪等性と堅牢性を維持するための3つのポイント:
Fail-Fast(即時停止):
set -euo pipefailにより、予期せぬ挙動を隠蔽せず即座にエラーとする。Resource Hygiene(リソースの衛生管理):
trapを用いて、いかなる終了時にも一時ファイルやロックファイルを確実にクリーンアップする。Atomic Update(原子的な更新): 設定ファイルの書き換えは直接編集せず、一時ファイルからの
mv(リネーム)により一瞬で行う。

