<p><meta/>
ID: SRE_CURL_JQ_HANDLING
VER: 1.0
AUTH: SRE_AUTO_ENG
TAG: REST_API, AUTOMATION, CURL, JQ, ERROR_HANDLING
</p>
<p>本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">curlとjqによるAPI自動実行の堅牢化:レスポンス検証と例外処理の実践</h1>
<h2 class="wp-block-heading">【導入と前提】</h2>
<p>API経由のインフラ操作や状態監視をシェルスクリプトで自動化し、HTTPエラーや期待しないJSONペイロードを検知して安全に異常終了させる手法を解説します。</p>
<ul class="wp-block-list">
<li><p><strong>OS:</strong> GNU/Linux (Ubuntu 22.04 / RHEL 9 等)</p></li>
<li><p><strong>ツール:</strong> <code>curl</code> (7.70.0以上推奨), <code>jq</code> (1.6以上)</p></li>
</ul>
<h2 class="wp-block-heading">【処理フローと設計】</h2>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["開始"] --> B{"curl実行"}
B -->|HTTP 2xx以外| C["異常終了/リトライ"]
B -->|HTTP 2xx| D["jqによるJSON検証"]
D -->|パース失敗/特定キー不在| E["異常終了"]
D -->|検証成功| F["後続処理実行"]
F --> G["終了"]
</pre></div>
<p><code>curl</code>でHTTPレイヤーの成功を担保した後、<code>jq</code>の<code>-e</code>(exit status反映)オプションを用いてアプリケーションレイヤーの値を検証します。パイプラインの途中で失敗した場合、<code>set -o pipefail</code>によりスクリプト全体が停止するように設計します。</p>
<h2 class="wp-block-heading">【実装:堅牢な自動化スクリプト】</h2>
<h3 class="wp-block-heading">1. API検証スクリプト (<code>api_check.sh</code>)</h3>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/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."
</pre>
</div>
<h3 class="wp-block-heading">2. 定期実行の設定 (<code>systemd</code> timer)</h3>
<div class="codehilite">
<pre data-enlighter-language="generic"># /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
</pre>
</div>
<h2 class="wp-block-heading">【検証と運用】</h2>
<h3 class="wp-block-heading">正常系の確認</h3>
<p>スクリプトを手動実行し、終了ステータスが <code>0</code> であることを確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">./api_check.sh
echo $? # 0が返れば正常
</pre>
</div>
<h3 class="wp-block-heading">異常系のログ確認</h3>
<p><code>systemd</code>で実行している場合、<code>journalctl</code>を用いて詳細なエラー原因(curlの終了コードやjqの検証失敗メッセージ)を追跡します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># 失敗した直近のログを確認
journalctl -u api-health-check.service -n 50 --no-pager
</pre>
</div>
<h2 class="wp-block-heading">【トラブルシューティングと落とし穴】</h2>
<ol class="wp-block-list">
<li><p><strong>トークンの露出防止</strong>:
スクリプト内に直接トークンを書かず、<code>EnvironmentFile</code>や秘密情報管理ツール(Vault等)から読み込んでください。<code>ps</code>コマンドで見えないよう配慮が必要です。</p></li>
<li><p><strong>jqのバージョン差異</strong>:
古い環境(jq 1.5未満)では <code>-e</code> オプションの挙動が一部異なる場合があります。可能な限り最新のバイナリを配置してください。</p></li>
<li><p><strong>巨大なJSONレスポンス</strong>:
数MBを超えるJSONを処理する場合、<code>jq</code>のメモリ消費が増大します。ストリーム処理(<code>jq --stream</code>)の検討が必要ですが、通常のAPIレスポンスなら逐次処理で十分です。</p></li>
<li><p><strong>プロキシ設定</strong>:
企業内ネットワークでは <code>http_proxy</code> 環境変数の影響を受けます。<code>--noproxy</code> オプションや環境変数の <code>unset</code> を適宜組み合わせてください。</p></li>
</ol>
<h2 class="wp-block-heading">【まとめ】</h2>
<p>運用の冪等性を維持するための3つのポイント:</p>
<ol class="wp-block-list">
<li><p><strong>HTTPレイヤーとContentレイヤーの二段階検証</strong>: <code>curl -f</code> だけでなく <code>jq -e</code> で中身まで保証する。</p></li>
<li><p><strong>シェルオプションの厳格化</strong>: <code>set -euo pipefail</code> を定型句として埋め込み、連鎖的なエラーを見逃さない。</p></li>
<li><p><strong>一時ファイル管理の自動化</strong>: <code>trap</code> コマンドを使用し、異常終了時でもゴミを残さない設計にする。</p></li>
</ol>
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ペイロードを検知して安全に異常終了させる手法を解説します。
【処理フローと設計】
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
【トラブルシューティングと落とし穴】
トークンの露出防止:
スクリプト内に直接トークンを書かず、EnvironmentFileや秘密情報管理ツール(Vault等)から読み込んでください。psコマンドで見えないよう配慮が必要です。
jqのバージョン差異:
古い環境(jq 1.5未満)では -e オプションの挙動が一部異なる場合があります。可能な限り最新のバイナリを配置してください。
巨大なJSONレスポンス:
数MBを超えるJSONを処理する場合、jqのメモリ消費が増大します。ストリーム処理(jq --stream)の検討が必要ですが、通常のAPIレスポンスなら逐次処理で十分です。
プロキシ設定:
企業内ネットワークでは http_proxy 環境変数の影響を受けます。--noproxy オプションや環境変数の unset を適宜組み合わせてください。
【まとめ】
運用の冪等性を維持するための3つのポイント:
HTTPレイヤーとContentレイヤーの二段階検証: curl -f だけでなく jq -e で中身まで保証する。
シェルオプションの厳格化: set -euo pipefail を定型句として埋め込み、連鎖的なエラーを見逃さない。
一時ファイル管理の自動化: trap コマンドを使用し、異常終了時でもゴミを残さない設計にする。
ライセンス:本記事のテキスト/コードは特記なき限り
CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。
コメント