堅牢なシェルスクリプト設計:エラー伝播を防ぎリソースを確実に回収する自動化手法

Tech

{ “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

【検証と運用】

  1. 正常系確認: スクリプトを手動実行し、終了ステータスを確認します。

    ./sync-config.sh && echo "Success" || echo "Failed"
    
  2. 異常系確認(シミュレーション): API_URL を存在しないURLに変更して実行し、TMP_FILE が残らずに Error on line... が出力されることを確認します。

  3. ログ確認: 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つのポイント:

  1. Fail-Fast(即時停止): set -euo pipefail により、予期せぬ挙動を隠蔽せず即座にエラーとする。

  2. Resource Hygiene(リソースの衛生管理): trap を用いて、いかなる終了時にも一時ファイルやロックファイルを確実にクリーンアップする。

  3. Atomic Update(原子的な更新): 設定ファイルの書き換えは直接編集せず、一時ファイルからの mv(リネーム)により一瞬で行う。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました