REST API連携の堅牢化:curlとjqによる自動バリデーション実装

Tech
{
  "style": "SRE/DevOps professional",
  "tools": ["curl", "jq", "systemd", "bash"],
  "reliability_level": "Production-ready (Draft)",
  "security_focus": ["Environment variables", "Error handling", "Least privilege"]
}

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

REST API連携の堅牢化:curlとjqによる自動バリデーション実装

【導入と前提】

APIレスポンスの構造検証とエッジケース対応をシェルスクリプトで自動化し、監視の信頼性と運用継続性を向上させます。

  • OS: GNU/Linux (Ubuntu 22.04+, RHEL 8+ 推奨)

  • 必須ツール: curl (7.68.0+), jq (1.6+)

  • 前提: RESTful APIへの疎通権限および環境変数の設定権限

【処理フローと設計】

graph TD
A["Start: Script Execution"] --> B["Environment Check"]
B --> C["Execute curl with Retries"]
C --> D{"HTTP Status 200?"}
D -- No --> E["Logging & Alerting"]
D -- Yes --> F["jq Schema Validation"]
F -- Fail --> E
F -- Success --> G["Post-Processing / Success"]
G --> H["End: Cleanup"]
E --> H

本フローでは、まず通信レイヤーの正常性(HTTP Status)を確認し、次にデータレイヤーの整合性(JSON Schema相当のチェック)を jq で検証します。これにより、APIのサイレントフェイル(200 OKだがデータが空など)を確実に検知します。

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

#!/usr/bin/env bash

# ==============================================================================


# API Validation Script


# Description: curlでAPIを叩き、jqでレスポンスの整合性を検証する


# ==============================================================================

set -euo pipefail # エラー時に停止、未定義変数参照禁止、パイプ途中エラーの捕捉
IFS=$'\n\t'

# 設定項目(環境変数からの読み込みを推奨)

API_URL="${API_URL:-https://api.example.com/v1/health}"
API_TOKEN="${API_TOKEN:-}"
TIMEOUT_SEC=10
RETRY_COUNT=3

# 一時ファイルの管理

TMP_RES=$(mktemp)
trap 'rm -f "$TMP_RES"' EXIT # 終了時に必ず一時ファイルを削除

echo "[INFO] Starting API validation for: ${API_URL}"

# 1. APIリクエスト実行


# -s: プログレス表示抑制 / -S: エラー表示 / -f: HTTPエラー時に失敗させる


# -L: リダイレクト追従 / --retry: ネットワーク一時エラー時のリトライ

HTTP_STATUS=$(curl -s -S -L \
    -w "%{http_code}" \
    -o "$TMP_RES" \
    --max-time "${TIMEOUT_SEC}" \
    --retry "${RETRY_COUNT}" \
    -H "Authorization: Bearer ${API_TOKEN}" \
    -H "Accept: application/json" \
    "${API_URL}")

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

if [ "$HTTP_STATUS" -ne 200 ]; then
    echo "[ERROR] API request failed with status: ${HTTP_STATUS}" >&2
    cat "$TMP_RES" >&2
    exit 1
fi

# 3. jqによるペイロード検証(例:statusが'ok'かつ、data配列が空でないこと)


# -e: フィルター結果が false/null の場合に終了ステータス 1 を返す

if ! jq -e '.status == "ok" and (.data | length > 0)' "$TMP_RES" > /dev/null; then
    echo "[ERROR] Invalid API response schema or empty data." >&2
    echo "Response: $(cat "$TMP_RES")" >&2
    exit 1
fi

echo "[INFO] API validation successful."

systemdによる定期実行設定 (Timer)

/etc/systemd/system/api-checker.service

[Unit]
Description=API Health Checker
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/api-checker.sh
EnvironmentFile=/etc/default/api-checker
User=monitoring-user

/etc/systemd/system/api-checker.timer

[Unit]
Description=Run API Checker every 5 minutes

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

[Install]
WantedBy=timers.target

【検証と運用】

正常系の確認

スクリプトを直接実行し、終了コードを確認します。

./api-checker.sh
echo $? # 0であれば正常

異常系のログ確認

systemd経由で実行している場合、journalctl を使用してエラー出力を確認します。

# 特定のサービスのエラーログを直近10件表示

journalctl -u api-checker.service -n 10 --no-pager

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

  1. 秘密情報の漏洩: APIトークンをスクリプトに直書きせず、EnvironmentFile またはシークレットマネージャー(AWS Secrets Manager等)から読み込んでください。

  2. パイプラインの罠: set -o pipefail がないと、curl ... | jq の構成で curl が失敗しても jq が(空入力に対して)成功判定を出してしまうリスクがあります。

  3. ゾンビ一時ファイル: trap コマンドによるクリーンアップを忘れると、/tmp 領域を圧迫します。特に高頻度な定期実行時は注意が必要です。

  4. jqのバージョン差異: 古いOS(CentOS 7等)の jq 1.5では一部の演算子が異なる場合があるため、ポータブルな記述(例:length > 0)を優先してください。

【まとめ】

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

  1. ステータスとコンテンツの二段構え検証: HTTP 200を過信せず、必ず jq で期待するキーの存在をチェックする。

  2. リトライ戦略の実装: 一時的なネットワーク瞬断による偽陽性(False Positive)を防ぐため、curl--retry オプションを活用する。

  3. クリーンアップの徹底: trap を用いて、実行環境にゴミを残さない設計を徹底する。

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

コメント

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