{ “focus”: “Robust Shell Scripting & SRE Best Practices”, “author”: “SRE_Agent_Gemini”, “context”: “System Automation/DevOps”, “tools”: [“bash”, “jq”, “curl”, “systemd”, “mermaid”] }
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
堅牢なシェルスクリプト設計:set -euo pipefail と trap による障害耐性の高い自動化の実装
【導入と前提】
APIから取得したJSONデータを処理し、不測の事態でも一時ファイルを残さず安全に終了する、冪等性を担保した自動化スクリプトの設計指針を解説します。
実行環境: GNU/Linux (Bash 4.4以降推奨)
必須ツール:
curl(データ取得),jq(JSONパース),mktemp(安全な一時ファイル作成)
【処理フローと設計】
graph TD
A["スクリプト開始"] --> B["set/trapの設定"]
B --> C["mktempによる一時領域確保"]
C --> D["curlで外部データ取得"]
D --> E{"jqでの検証・加工"}
E -->|Success| F["本番環境への反映/処理"]
E -->|Failure| G["trap発動: クリーンアップ"]
F --> H["正常終了"]
G --> I["エラー終了/通知"]
この設計の核心は、「どのフェーズでエラーが起きても、システムの状態を不整合なまま放置しない」ことにあります。特に、パイプラインの途中でエラーが発生した場合の検知漏れを防ぐことが重要です。
【実装:堅牢な自動化スクリプト】
#!/usr/bin/env bash
# --- 安全のための初期設定 ---
set -e # コマンドが失敗(終了ステータス0以外)したら即座に終了
set -u # 未定義の変数を参照しようとしたらエラー
set -o pipefail # パイプラインの途中のコマンド失敗もエラーとして扱う
# --- クリーンアップ処理の定義 ---
TMP_FILE=$(mktemp /tmp/api_data.XXXXXX)
cleanup() {
local exit_status=$?
echo "Cleaning up temporary files..."
rm -f "$TMP_FILE"
echo "Script finished with status: $exit_status"
}
# スクリプト終了時に必ずcleanup関数を実行(正常・異常問わず)
trap cleanup EXIT
# --- メイン処理 ---
API_URL="https://api.example.com/v1/config"
echo "Fetching configuration from API..."
# curlのオプション解説:
# -s: 進捗を表示しない(Silent)
# -S: エラー時はメッセージを表示
# -L: リダイレクトに従う
# --retry 3: 通信失敗時に3回までリトライ
# --fail: HTTPレスポンスコード400/500番台でエラー終了させる
if ! curl -sSL --retry 3 --fail "$API_URL" -o "$TMP_FILE"; then
echo "Error: Failed to fetch data from $API_URL" >&2
exit 1
fi
# jqのオプション解説:
# -e: 抽出結果が空(null/false)の場合に終了ステータス1を返す
# -r: クォートなしの生出力
echo "Processing JSON data..."
TARGET_VALUE=$(jq -e -r '.settings.threshold' "$TMP_FILE")
# 実際の処理(ここでは例として値を出力)
echo "The threshold is: $TARGET_VALUE"
# 冪等性を考慮したファイル更新(例:一時ファイルから最終的な設定へ)
# atomic move(mv)を使うことで、書き込み途中の不完全なファイルを防ぐ
# mv "$TMP_FILE" /etc/myapp/config.json
systemd ユニットファイル設定例
このスクリプトを定期実行する場合、systemd のタイマー機能を利用してリソース制限とログ管理を統合します。
[Unit] Description=Robust Config Sync Service [Service] Type=oneshot ExecStart=/usr/local/bin/sync_config.sh User=appuser Group=appuser # セキュリティ設定 PrivateTmp=true ProtectSystem=full [Install] WantedBy=multi-user.target
【検証と運用】
正常系の確認
スクリプトを実行し、終了ステータス $? が 0 であること、および一時ファイルが削除されていることを確認します。
./sync_config.sh echo $? # 0が表示されること ls /tmp/api_data.* # 何も表示されないこと
異常系の確認(ログ確認)
systemd 経由で実行している場合、journalctl を用いて失敗時の詳細なトレースを確認します。
# 失敗した最新のログを表示 journalctl -u sync_config.service -n 50 --no-pager
【トラブルシューティングと落とし穴】
パイプラインと
grepの罠:set -o pipefail環境下では、grepでマッチするものがない場合に終了ステータス1となり、スクリプトが止まります。マッチしないことが許容される場合はgrep ... || trueと記述して回避します。一時ファイルのパーミッション:
mktempはデフォルトで600(所有者のみ読み書き) で作成されます。別ユーザーが読み取る必要がある場合は、作成直後にchmodが必要です。sudo 実行時の環境変数:
sudoを経由するとPATHや独自の環境変数が引き継がれないことがあります。必要に応じてsudo -Eを使うか、スクリプト内で明示的にPATHを定義してください。
【まとめ】
運用の冪等性を維持するための3つのポイント:
Atomic Operations: ファイル更新は直接編集せず、
mvコマンドで一気に入れ替える。Explicit Cleanup:
trapを使い、どんな終了条件でも残骸(ゴミ)を残さない。Strict Mode:
set -euo pipefailを「おまじない」ではなく、障害検知の必須装備として全スクリプトに組み込む。


コメント