<p><!--META
{
"title": "curlでHTTP/2とJSONを操る",
"primary_category": "DevOps",
"secondary_categories": ["CLI","Linux"],
"tags": ["curl", "jq", "HTTP/2", "JSON", "systemd", "DevOps", "API", "bash"],
"summary": "DevOpsエンジニア向けに、curlでHTTP/2とJSONを安全に操作する手法、jqによるJSON処理、systemd連携を解説。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"DevOpsエンジニア必見!curlでHTTP/2とJSONを操るテクニックを徹底解説。安全なbashスクリプト、jq活用、systemd自動化、トラブルシュートまで網羅。#curl
#jq #HTTP2
#DevOps"},
"link_hints": ["https://curl.se/docs/manpage.html", "https://jqlang.github.io/jq/manual/"]
}
-->
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">curlでHTTP/2とJSONを操る</h1>
<h2 class="wp-block-heading">要件と前提</h2>
<p>本記事は、DevOpsエンジニアが <code>curl</code> コマンドを使用してHTTP/2プロトコル経由でJSON形式のAPIと安全かつ効率的に連携するための実践的な手法を解説します。特に、安全なbashスクリプティング、<code>jq</code> を用いたJSON処理、<code>systemd</code> を利用したジョブ自動化に焦点を当てます。</p>
<p><strong>前提ツール:</strong></p>
<ul class="wp-block-list">
<li><p><code>curl</code> (HTTP/2サポート版)</p></li>
<li><p><code>jq</code></p></li>
<li><p><code>bash</code> (バージョン4.x以上推奨)</p></li>
<li><p><code>systemd</code> (Linux環境)</p></li>
</ul>
<p><strong>注意事項:</strong></p>
<ul class="wp-block-list">
<li><p>本記事のスクリプトはLinux環境を想定しています。</p></li>
<li><p><code>systemd</code> サービスの設定には通常 <code>root</code> 権限が必要ですが、実行ユーザーは可能な限り非特権ユーザーを指定し、権限分離を徹底してください。</p></li>
</ul>
<h2 class="wp-block-heading">実装</h2>
<p>まず、安全で冪等な(idempotent)処理を実現するためのbashスクリプトのひな形と、<code>curl</code> および <code>jq</code> を組み合わせた具体的なAPI連携スクリプトを実装します。</p>
<h3 class="wp-block-heading">安全なbashスクリプトのフレームワーク</h3>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/usr/bin/env bash
# set -euo pipefail:
# -e: コマンドが失敗した場合、即座にスクリプトを終了
# -u: 未定義の変数が使用された場合、エラーで終了
# -o pipefail: パイプライン内で一つでも失敗したコマンドがあれば、パイプライン全体を失敗とする
set -euo pipefail
# 一時ディレクトリの作成と自動削除
# mktemp -d: 一意な名前の一時ディレクトリを作成
# trap '...' EXIT: スクリプト終了時に指定したコマンドを実行 (正常終了/異常終了問わず)
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"; echo "Temporary directory $TMP_DIR removed."' EXIT
echo "Starting API interaction script."
echo "Temporary directory: $TMP_DIR"
# APIエンドポイントの定義 (例としてhttpbin.orgを使用)
API_URL="https://httpbin.org/anything"
# TLSクライアント証明書を使用する場合 (例: 本番環境では必要)
# CLIENT_CERT="/path/to/client.crt"
# CLIENT_KEY="/path/to/client.key"
# CA_BUNDLE="/path/to/ca-bundle.crt" # サーバー証明書の検証用CAバンドル
# --- ここから具体的なAPI連携処理 ---
# 1. 送信するJSONペイロードの作成
PAYLOAD_FILE="$TMP_DIR/payload.json"
cat << EOF > "$PAYLOAD_FILE"
{
"service_name": "example-service",
"status": "active",
"timestamp": "$(date -uIs)",
"metadata": {
"version": "1.0.0",
"region": "us-east-1"
}
}
EOF
echo "Generated payload.json:"
cat "$PAYLOAD_FILE" | jq '.'
# 2. curlでのHTTP/2リクエスト送信とJSONレスポンスの受信
# --http2: HTTP/2を明示的に使用
# -sS: サイレントモード (-s) かつエラー表示 (-S)
# --retry 5: 失敗時に最大5回再試行
# --retry-delay 2: 再試行間の待ち時間を2秒
# --retry-max-time 30: 再試行を含む全処理の最大時間を30秒
# -X POST: POSTリクエスト
# -H "Content-Type: application/json": リクエストヘッダー
# --data "@$PAYLOAD_FILE": ファイルからリクエストボディを読み込む
# --connect-timeout 5: 接続確立の最大時間
# --max-time 10: 転送全体の最大時間
# (TLS設定例: --cert "$CLIENT_CERT" --key "$CLIENT_KEY" --cacert "$CA_BUNDLE")
echo "Sending HTTP/2 POST request to $API_URL..."
API_RESPONSE=$(curl --http2 -sS \
--retry 5 --retry-delay 2 --retry-max-time 30 \
--connect-timeout 5 --max-time 10 \
-X POST \
-H "Content-Type: application/json" \
--data "@$PAYLOAD_FILE" \
"$API_URL")
# エラーハンドリング: curlコマンドが失敗した場合
if [ $? -ne 0 ]; then
echo "ERROR: curl command failed." >&2
exit 1
fi
echo "API Response received."
# 3. jqを用いたJSONレスポンスの処理
# jq '. | .json.service_name, .json.status, .url' : 特定のフィールドを抽出
# jq -r: 生の文字列として出力 (クォートなし)
# jq '. | select(.json.status == "active")' : 条件フィルタリング
if echo "$API_RESPONSE" | jq -e . >/dev/null; then
echo "Processed JSON response with jq:"
echo "$API_RESPONSE" | jq -r '
.json.service_name as $service_name |
.json.status as $status |
.url as $url |
"Service: \($service_name), Status: \($status), URL: \($url)"
'
# さらに、条件に基づいて処理する例
if echo "$API_RESPONSE" | jq -e '.json.status == "active"' >/dev/null; then
echo "Service is active, proceeding with next step..."
# 後続処理 (例: データベース更新、通知など)
else
echo "Service status is not 'active'."
fi
else
echo "ERROR: API response is not valid JSON or jq processing failed." >&2
echo "Raw response: $API_RESPONSE" >&2
exit 1
fi
echo "Script finished successfully."
</pre>
</div>
<h3 class="wp-block-heading">Mermaid図</h3>
<p>API連携のフローを簡潔に示します。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["Start Script"] --> B{"Setup: Temp Dir & Trap"};
B --|Prepare Request Data|--> C["Create JSON Payload"];
C --|Send via curl|--> D{"API Endpoint(\"HTTP/2, Retry\")"};
D --|Receive JSON Response|--> E["Process Response with jq"];
E --|Log & Act on Data|--> F["Handle Business Logic"];
F --|Cleanup|--> G{"Remove Temp Dir"};
G --> H["End Script"];
</pre></div>
<h2 class="wp-block-heading">検証</h2>
<p>上記のスクリプトを <code>my_api_job.sh</code> として保存し、実行して動作を確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic"># スクリプトを実行可能にする
chmod +x my_api_job.sh
# 実行
./my_api_job.sh
</pre>
</div>
<p><strong>期待される出力:</strong></p>
<ul class="wp-block-list">
<li><p>一時ディレクトリの作成と削除メッセージ。</p></li>
<li><p>生成された <code>payload.json</code> の内容。</p></li>
<li><p><code>curl</code> によるリクエスト送信メッセージ。</p></li>
<li><p>APIからのレスポンスを <code>jq</code> で整形した結果。</p></li>
<li><p>サービスが “active” である場合の追加メッセージ。</p></li>
</ul>
<p><code>curl -v --http2 <URL></code> を実行することで、HTTP/2が利用されているかを確認できます。出力中に <code>ALPN, offering h2</code> や <code>ALPN, negotiated h2</code> といった行があれば、HTTP/2が使用されています。</p>
<h2 class="wp-block-heading">運用</h2>
<p>定期的なAPI呼び出しやバックグラウンド処理には、<code>systemd unit/timer</code> を用いるのがDevOpsのベストプラクティスです。</p>
<h3 class="wp-block-heading">systemd Unitファイルの作成</h3>
<p><code>/etc/systemd/system/my-api-job.service</code> を作成します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">[Unit]
Description=My API Interaction Job
# Networkが利用可能になるまで待機
After=network-online.target
Wants=network-online.target
[Service]
# OneShot: 処理が完了したらサービスを停止 (定期実行に適している)
Type=oneshot
# スクリプトを非特権ユーザーで実行することを強く推奨
# User=<your-non-root-user>
# Group=<your-non-root-group>
# WorkingDirectory: スクリプトが実行されるディレクトリ
WorkingDirectory=/usr/local/bin
# ExecStart: 実行するコマンド
# スクリプトのフルパスを指定
ExecStart=/usr/local/bin/my_api_job.sh
# StandardOutput, StandardError: 標準出力/エラーの出力先 (journalctlで確認可能)
StandardOutput=journal
StandardError=journal
# Restart=on-failure: 予期せぬ終了時に自動で再起動
# (Type=oneshotの場合は、基本的にTimerが再実行を制御するため不要な場合が多い)
# RestartSec=5s # 再起動までの待ち時間
[Install]
WantedBy=multi-user.target
</pre>
</div>
<p><strong>root権限と権限分離の注意点:</strong>
<code>systemd</code> サービスは通常 <code>root</code> ユーザーによって管理されますが、<code>ExecStart</code> で実行されるプロセス自体は可能な限り非特権ユーザー (<code>User=</code>, <code>Group=</code> ディレクティブで指定) で実行すべきです。これにより、スクリプトに潜在的な脆弱性があった場合でも、システム全体への影響を最小限に抑えることができます。スクリプトや関連する設定ファイル (<code>CLIENT_CERT</code> など) のパーミッションも、指定したユーザーのみが読み書きできる安全な状態に設定してください。</p>
<h3 class="wp-block-heading">systemd Timerファイルの作成</h3>
<p><code>/etc/systemd/system/my-api-job.timer</code> を作成します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">[Unit]
Description=Run My API Interaction Job Daily
[Timer]
# OnCalendar: 日次で実行 (例: 毎日午前3時30分)
OnCalendar=*-*-* 03:30:00
# OnUnitActiveSec: 前回の実行から指定時間後に実行 (例: 1時間ごと)
# OnUnitActiveSec=1h
# Persistent=true: タイマーが停止・再起動しても、最後に実行されるべきだった時刻に実行
Persistent=true
[Install]
WantedBy=timers.target
</pre>
</div>
<h3 class="wp-block-heading">インストールと有効化</h3>
<ol class="wp-block-list">
<li><p>上記スクリプトを <code>/usr/local/bin/my_api_job.sh</code> に配置し、実行権限を与えます。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo mv my_api_job.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/my_api_job.sh
</pre>
</div></li>
<li><p><code>systemd</code> 設定ファイルをリロードします。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo systemctl daemon-reload
</pre>
</div></li>
<li><p>タイマーを有効化し、起動します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">sudo systemctl enable my-api-job.timer
sudo systemctl start my-api-job.timer
</pre>
</div></li>
</ol>
<h3 class="wp-block-heading">ログの確認</h3>
<p>サービスが実行された後のログは <code>journalctl</code> で確認できます。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">journalctl -u my-api-job.service
# 直近のログのみ表示
journalctl -u my-api-job.service -n 20
</pre>
</div>
<p>タイマーの稼働状況は以下で確認できます。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">systemctl list-timers | grep my-api-job
</pre>
</div>
<h2 class="wp-block-heading">トラブルシュート</h2>
<ul class="wp-block-list">
<li><p><strong><code>curl</code> がエラーになる場合</strong>:</p>
<ul>
<li><p><code>--verbose</code> オプションを追加し、詳細な通信ログを確認します (<code>curl --verbose --http2 ...</code>)。</p></li>
<li><p>ネットワーク接続、DNS解決、ファイアウォール設定を確認します。</p></li>
<li><p>TLS証明書のエラー (<code>SSL certificate problem</code>) の場合は、<code>--cacert</code>, <code>--cert</code>, <code>--key</code> オプションが正しく設定されているか、または証明書自体が有効かを確認します。開発/テスト環境では <code>--insecure</code> も一時的に利用可能ですが、本番環境では絶対に避けてください。</p></li>
<li><p><code>--connect-timeout</code> や <code>--max-time</code> で指定した時間が短すぎる可能性があります。</p></li>
</ul></li>
<li><p><strong><code>jq</code> がJSONをパースできない場合</strong>:</p>
<ul>
<li><p><code>curl</code> の出力が本当にJSON形式であるか確認します (例: <code>echo "$API_RESPONSE" | head -n 10</code>)。サーバーからHTML形式のエラーページが返されている可能性もあります。</p></li>
<li><p><code>jq</code> の構文が正しいか確認します。<code>jq -e .</code> でパース可能か、<code>jq '.'</code> で整形できるか試行します。</p></li>
</ul></li>
<li><p><strong><code>systemd</code> サービスが起動しない/失敗する場合</strong>:</p>
<ul>
<li><p><code>journalctl -u my-api-job.service</code> でエラーログを確認します。</p></li>
<li><p><code>ExecStart</code> のスクリプトパスが正しいか、実行権限 (<code>chmod +x</code>) があるか確認します。</p></li>
<li><p><code>User=</code> と <code>Group=</code> で指定したユーザーが存在し、スクリプトや関連ファイルにアクセスできる権限があるか確認します。</p></li>
<li><p><code>systemctl status my-api-job.service</code> でサービスの現在の状態を確認します。</p></li>
</ul></li>
</ul>
<h2 class="wp-block-heading">まとめ</h2>
<p>、DevOpsエンジニアが <code>curl</code> を用いてHTTP/2とJSON形式のAPIを操作するための包括的なアプローチを解説しました。安全なbashスクリプトのベストプラクティス、<code>jq</code> による強力なJSON処理、そして <code>systemd unit/timer</code> による堅牢な自動化と運用方法を紹介しました。これらの技術を組み合わせることで、API連携処理を効率的かつセキュアに管理し、システムの信頼性と運用効率を大幅に向上させることができます。特に <code>root</code> 権限の取り扱いと権限分離には常に細心の注意を払い、最小権限の原則を適用することが重要です。</p>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
curlでHTTP/2とJSONを操る
要件と前提
本記事は、DevOpsエンジニアが curl
コマンドを使用してHTTP/2プロトコル経由でJSON形式のAPIと安全かつ効率的に連携するための実践的な手法を解説します。特に、安全なbashスクリプティング、jq
を用いたJSON処理、systemd
を利用したジョブ自動化に焦点を当てます。
前提ツール:
curl
(HTTP/2サポート版)
jq
bash
(バージョン4.x以上推奨)
systemd
(Linux環境)
注意事項:
実装
まず、安全で冪等な(idempotent)処理を実現するためのbashスクリプトのひな形と、curl
および jq
を組み合わせた具体的なAPI連携スクリプトを実装します。
安全なbashスクリプトのフレームワーク
#!/usr/bin/env bash
# set -euo pipefail:
# -e: コマンドが失敗した場合、即座にスクリプトを終了
# -u: 未定義の変数が使用された場合、エラーで終了
# -o pipefail: パイプライン内で一つでも失敗したコマンドがあれば、パイプライン全体を失敗とする
set -euo pipefail
# 一時ディレクトリの作成と自動削除
# mktemp -d: 一意な名前の一時ディレクトリを作成
# trap '...' EXIT: スクリプト終了時に指定したコマンドを実行 (正常終了/異常終了問わず)
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"; echo "Temporary directory $TMP_DIR removed."' EXIT
echo "Starting API interaction script."
echo "Temporary directory: $TMP_DIR"
# APIエンドポイントの定義 (例としてhttpbin.orgを使用)
API_URL="https://httpbin.org/anything"
# TLSクライアント証明書を使用する場合 (例: 本番環境では必要)
# CLIENT_CERT="/path/to/client.crt"
# CLIENT_KEY="/path/to/client.key"
# CA_BUNDLE="/path/to/ca-bundle.crt" # サーバー証明書の検証用CAバンドル
# --- ここから具体的なAPI連携処理 ---
# 1. 送信するJSONペイロードの作成
PAYLOAD_FILE="$TMP_DIR/payload.json"
cat << EOF > "$PAYLOAD_FILE"
{
"service_name": "example-service",
"status": "active",
"timestamp": "$(date -uIs)",
"metadata": {
"version": "1.0.0",
"region": "us-east-1"
}
}
EOF
echo "Generated payload.json:"
cat "$PAYLOAD_FILE" | jq '.'
# 2. curlでのHTTP/2リクエスト送信とJSONレスポンスの受信
# --http2: HTTP/2を明示的に使用
# -sS: サイレントモード (-s) かつエラー表示 (-S)
# --retry 5: 失敗時に最大5回再試行
# --retry-delay 2: 再試行間の待ち時間を2秒
# --retry-max-time 30: 再試行を含む全処理の最大時間を30秒
# -X POST: POSTリクエスト
# -H "Content-Type: application/json": リクエストヘッダー
# --data "@$PAYLOAD_FILE": ファイルからリクエストボディを読み込む
# --connect-timeout 5: 接続確立の最大時間
# --max-time 10: 転送全体の最大時間
# (TLS設定例: --cert "$CLIENT_CERT" --key "$CLIENT_KEY" --cacert "$CA_BUNDLE")
echo "Sending HTTP/2 POST request to $API_URL..."
API_RESPONSE=$(curl --http2 -sS \
--retry 5 --retry-delay 2 --retry-max-time 30 \
--connect-timeout 5 --max-time 10 \
-X POST \
-H "Content-Type: application/json" \
--data "@$PAYLOAD_FILE" \
"$API_URL")
# エラーハンドリング: curlコマンドが失敗した場合
if [ $? -ne 0 ]; then
echo "ERROR: curl command failed." >&2
exit 1
fi
echo "API Response received."
# 3. jqを用いたJSONレスポンスの処理
# jq '. | .json.service_name, .json.status, .url' : 特定のフィールドを抽出
# jq -r: 生の文字列として出力 (クォートなし)
# jq '. | select(.json.status == "active")' : 条件フィルタリング
if echo "$API_RESPONSE" | jq -e . >/dev/null; then
echo "Processed JSON response with jq:"
echo "$API_RESPONSE" | jq -r '
.json.service_name as $service_name |
.json.status as $status |
.url as $url |
"Service: \($service_name), Status: \($status), URL: \($url)"
'
# さらに、条件に基づいて処理する例
if echo "$API_RESPONSE" | jq -e '.json.status == "active"' >/dev/null; then
echo "Service is active, proceeding with next step..."
# 後続処理 (例: データベース更新、通知など)
else
echo "Service status is not 'active'."
fi
else
echo "ERROR: API response is not valid JSON or jq processing failed." >&2
echo "Raw response: $API_RESPONSE" >&2
exit 1
fi
echo "Script finished successfully."
Mermaid図
API連携のフローを簡潔に示します。
graph TD
A["Start Script"] --> B{"Setup: Temp Dir & Trap"};
B --|Prepare Request Data|--> C["Create JSON Payload"];
C --|Send via curl|--> D{"API Endpoint(\"HTTP/2, Retry\")"};
D --|Receive JSON Response|--> E["Process Response with jq"];
E --|Log & Act on Data|--> F["Handle Business Logic"];
F --|Cleanup|--> G{"Remove Temp Dir"};
G --> H["End Script"];
検証
上記のスクリプトを my_api_job.sh
として保存し、実行して動作を確認します。
# スクリプトを実行可能にする
chmod +x my_api_job.sh
# 実行
./my_api_job.sh
期待される出力:
curl -v --http2 <URL>
を実行することで、HTTP/2が利用されているかを確認できます。出力中に ALPN, offering h2
や ALPN, negotiated h2
といった行があれば、HTTP/2が使用されています。
運用
定期的なAPI呼び出しやバックグラウンド処理には、systemd unit/timer
を用いるのがDevOpsのベストプラクティスです。
systemd Unitファイルの作成
/etc/systemd/system/my-api-job.service
を作成します。
[Unit]
Description=My API Interaction Job
# Networkが利用可能になるまで待機
After=network-online.target
Wants=network-online.target
[Service]
# OneShot: 処理が完了したらサービスを停止 (定期実行に適している)
Type=oneshot
# スクリプトを非特権ユーザーで実行することを強く推奨
# User=<your-non-root-user>
# Group=<your-non-root-group>
# WorkingDirectory: スクリプトが実行されるディレクトリ
WorkingDirectory=/usr/local/bin
# ExecStart: 実行するコマンド
# スクリプトのフルパスを指定
ExecStart=/usr/local/bin/my_api_job.sh
# StandardOutput, StandardError: 標準出力/エラーの出力先 (journalctlで確認可能)
StandardOutput=journal
StandardError=journal
# Restart=on-failure: 予期せぬ終了時に自動で再起動
# (Type=oneshotの場合は、基本的にTimerが再実行を制御するため不要な場合が多い)
# RestartSec=5s # 再起動までの待ち時間
[Install]
WantedBy=multi-user.target
root権限と権限分離の注意点:
systemd
サービスは通常 root
ユーザーによって管理されますが、ExecStart
で実行されるプロセス自体は可能な限り非特権ユーザー (User=
, Group=
ディレクティブで指定) で実行すべきです。これにより、スクリプトに潜在的な脆弱性があった場合でも、システム全体への影響を最小限に抑えることができます。スクリプトや関連する設定ファイル (CLIENT_CERT
など) のパーミッションも、指定したユーザーのみが読み書きできる安全な状態に設定してください。
systemd Timerファイルの作成
/etc/systemd/system/my-api-job.timer
を作成します。
[Unit]
Description=Run My API Interaction Job Daily
[Timer]
# OnCalendar: 日次で実行 (例: 毎日午前3時30分)
OnCalendar=*-*-* 03:30:00
# OnUnitActiveSec: 前回の実行から指定時間後に実行 (例: 1時間ごと)
# OnUnitActiveSec=1h
# Persistent=true: タイマーが停止・再起動しても、最後に実行されるべきだった時刻に実行
Persistent=true
[Install]
WantedBy=timers.target
インストールと有効化
上記スクリプトを /usr/local/bin/my_api_job.sh
に配置し、実行権限を与えます。
sudo mv my_api_job.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/my_api_job.sh
systemd
設定ファイルをリロードします。
sudo systemctl daemon-reload
タイマーを有効化し、起動します。
sudo systemctl enable my-api-job.timer
sudo systemctl start my-api-job.timer
ログの確認
サービスが実行された後のログは journalctl
で確認できます。
journalctl -u my-api-job.service
# 直近のログのみ表示
journalctl -u my-api-job.service -n 20
タイマーの稼働状況は以下で確認できます。
systemctl list-timers | grep my-api-job
トラブルシュート
まとめ
、DevOpsエンジニアが curl
を用いてHTTP/2とJSON形式のAPIを操作するための包括的なアプローチを解説しました。安全なbashスクリプトのベストプラクティス、jq
による強力なJSON処理、そして systemd unit/timer
による堅牢な自動化と運用方法を紹介しました。これらの技術を組み合わせることで、API連携処理を効率的かつセキュアに管理し、システムの信頼性と運用効率を大幅に向上させることができます。特に root
権限の取り扱いと権限分離には常に細心の注意を払い、最小権限の原則を適用することが重要です。
コメント