curlでHTTP/2とJSONを操る

Tech

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

curlでHTTP/2とJSONを操る

要件と前提

本記事は、DevOpsエンジニアが curl コマンドを使用してHTTP/2プロトコル経由でJSON形式のAPIと安全かつ効率的に連携するための実践的な手法を解説します。特に、安全なbashスクリプティング、jq を用いたJSON処理、systemd を利用したジョブ自動化に焦点を当てます。

前提ツール:

  • curl (HTTP/2サポート版)

  • jq

  • bash (バージョン4.x以上推奨)

  • systemd (Linux環境)

注意事項:

  • 本記事のスクリプトはLinux環境を想定しています。

  • systemd サービスの設定には通常 root 権限が必要ですが、実行ユーザーは可能な限り非特権ユーザーを指定し、権限分離を徹底してください。

実装

まず、安全で冪等な(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

期待される出力:

  • 一時ディレクトリの作成と削除メッセージ。

  • 生成された payload.json の内容。

  • curl によるリクエスト送信メッセージ。

  • APIからのレスポンスを jq で整形した結果。

  • サービスが “active” である場合の追加メッセージ。

curl -v --http2 <URL> を実行することで、HTTP/2が利用されているかを確認できます。出力中に ALPN, offering h2ALPN, 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

インストールと有効化

  1. 上記スクリプトを /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
    
  2. systemd 設定ファイルをリロードします。

    sudo systemctl daemon-reload
    
  3. タイマーを有効化し、起動します。

    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

トラブルシュート

  • curl がエラーになる場合:

    • --verbose オプションを追加し、詳細な通信ログを確認します (curl --verbose --http2 ...)。

    • ネットワーク接続、DNS解決、ファイアウォール設定を確認します。

    • TLS証明書のエラー (SSL certificate problem) の場合は、--cacert, --cert, --key オプションが正しく設定されているか、または証明書自体が有効かを確認します。開発/テスト環境では --insecure も一時的に利用可能ですが、本番環境では絶対に避けてください。

    • --connect-timeout--max-time で指定した時間が短すぎる可能性があります。

  • jq がJSONをパースできない場合:

    • curl の出力が本当にJSON形式であるか確認します (例: echo "$API_RESPONSE" | head -n 10)。サーバーからHTML形式のエラーページが返されている可能性もあります。

    • jq の構文が正しいか確認します。jq -e . でパース可能か、jq '.' で整形できるか試行します。

  • systemd サービスが起動しない/失敗する場合:

    • journalctl -u my-api-job.service でエラーログを確認します。

    • ExecStart のスクリプトパスが正しいか、実行権限 (chmod +x) があるか確認します。

    • User=Group= で指定したユーザーが存在し、スクリプトや関連ファイルにアクセスできる権限があるか確認します。

    • systemctl status my-api-job.service でサービスの現在の状態を確認します。

まとめ

、DevOpsエンジニアが curl を用いてHTTP/2とJSON形式のAPIを操作するための包括的なアプローチを解説しました。安全なbashスクリプトのベストプラクティス、jq による強力なJSON処理、そして systemd unit/timer による堅牢な自動化と運用方法を紹介しました。これらの技術を組み合わせることで、API連携処理を効率的かつセキュアに管理し、システムの信頼性と運用効率を大幅に向上させることができます。特に root 権限の取り扱いと権限分離には常に細心の注意を払い、最小権限の原則を適用することが重要です。

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

コメント

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