curlとjqによるAPI自動実行の堅牢化:レスポンス検証と例外処理の実践

Tech

ID: SRE_CURL_JQ_HANDLING VER: 1.0 AUTH: SRE_AUTO_ENG TAG: REST_API, AUTOMATION, CURL, JQ, ERROR_HANDLING

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

curlとjqによるAPI自動実行の堅牢化:レスポンス検証と例外処理の実践

【導入と前提】

API経由のインフラ操作や状態監視をシェルスクリプトで自動化し、HTTPエラーや期待しないJSONペイロードを検知して安全に異常終了させる手法を解説します。

  • OS: GNU/Linux (Ubuntu 22.04 / RHEL 9 等)

  • ツール: curl (7.70.0以上推奨), jq (1.6以上)

【処理フローと設計】

graph TD
    A["開始"] --> B{"curl実行"}
    B -->|HTTP 2xx以外| C["異常終了/リトライ"]
    B -->|HTTP 2xx| D["jqによるJSON検証"]
    D -->|パース失敗/特定キー不在| E["異常終了"]
    D -->|検証成功| F["後続処理実行"]
    F --> G["終了"]

curlでHTTPレイヤーの成功を担保した後、jq-e(exit status反映)オプションを用いてアプリケーションレイヤーの値を検証します。パイプラインの途中で失敗した場合、set -o pipefailによりスクリプト全体が停止するように設計します。

【実装:堅牢な自動化スクリプト】

1. API検証スクリプト (api_check.sh)

#!/usr/bin/env bash

# -e: エラーで即停止, -u: 未定義変数参照で停止, -o pipefail: パイプ途中のエラーを拾う

set -euo pipefail

# 終了時に一時ファイルを確実に削除

readonly TMP_RES=$(mktemp)
trap 'rm -f "$TMP_RES"' EXIT

# 設定項目(環境変数からの取得を推奨)

API_ENDPOINT="${API_ENDPOINT:-https://api.example.com/v1/status}"
AUTH_TOKEN="${AUTH_TOKEN:-default_token}"

echo "Checking API status..."

# curlの実行


# -s: 進捗非表示, -S: エラー時のみ表示, -f: HTTP 4xx/5xxでエラー終了


# -L: リダイレクト追従, --retry: 一時的エラー時の再試行


# -w: レスポンスコードを標準出力の最後に付与

HTTP_CODE=$(curl -sSfL \
    -H "Authorization: Bearer ${AUTH_TOKEN}" \
    -H "Accept: application/json" \
    --retry 3 \
    --retry-delay 2 \
    -w "%{http_code}" \
    -o "$TMP_RES" \
    "$API_ENDPOINT")

# HTTPステータスコードの検証

if [ "$HTTP_CODE" -ne 200 ]; then
    echo "Error: Unexpected HTTP status $HTTP_CODE" >&2
    exit 1
fi

# jqによるJSON内容の検証


# -e: フィルタの結果がnullまたはfalseの場合に終了コード1を返す


# .status == "healthy" を検証

if ! jq -e '.status == "healthy"' "$TMP_RES" > /dev/null; then
    echo "Error: API status is not healthy." >&2
    cat "$TMP_RES" >&2
    exit 2
fi

# 必要な値の抽出(-r: クォートなし出力)

VERSION=$(jq -r '.version' "$TMP_RES")
echo "Success: System version $VERSION is healthy."

2. 定期実行の設定 (systemd timer)

# /etc/systemd/system/api-health-check.service

[Unit]
Description=API Health Check Service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/api_check.sh
Environment="API_ENDPOINT=https://api.example.com/health"
EnvironmentFile=/etc/default/api-credentials # トークン等を隔離保存
User=monitoring-user

# /etc/systemd/system/api-health-check.timer

[Unit]
Description=Run API Health Check every 5 minutes

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target

【検証と運用】

正常系の確認

スクリプトを手動実行し、終了ステータスが 0 であることを確認します。

./api_check.sh
echo $? # 0が返れば正常

異常系のログ確認

systemdで実行している場合、journalctlを用いて詳細なエラー原因(curlの終了コードやjqの検証失敗メッセージ)を追跡します。

# 失敗した直近のログを確認

journalctl -u api-health-check.service -n 50 --no-pager

【トラブルシューティングと落とし穴】

  1. トークンの露出防止: スクリプト内に直接トークンを書かず、EnvironmentFileや秘密情報管理ツール(Vault等)から読み込んでください。psコマンドで見えないよう配慮が必要です。

  2. jqのバージョン差異: 古い環境(jq 1.5未満)では -e オプションの挙動が一部異なる場合があります。可能な限り最新のバイナリを配置してください。

  3. 巨大なJSONレスポンス: 数MBを超えるJSONを処理する場合、jqのメモリ消費が増大します。ストリーム処理(jq --stream)の検討が必要ですが、通常のAPIレスポンスなら逐次処理で十分です。

  4. プロキシ設定: 企業内ネットワークでは http_proxy 環境変数の影響を受けます。--noproxy オプションや環境変数の unset を適宜組み合わせてください。

【まとめ】

運用の冪等性を維持するための3つのポイント:

  1. HTTPレイヤーとContentレイヤーの二段階検証: curl -f だけでなく jq -e で中身まで保証する。

  2. シェルオプションの厳格化: set -euo pipefail を定型句として埋め込み、連鎖的なエラーを見逃さない。

  3. 一時ファイル管理の自動化: trap コマンドを使用し、異常終了時でもゴミを残さない設計にする。

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

コメント

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