高度なjqフィルタリングによるシステムメトリクスの集計・自動化

Tech

author: SRE_Consultant style: technical_documentation_sre tools: [jq, bash, systemd] knowledge_level: advanced 本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

高度なjqフィルタリングによるシステムメトリクスの集計・自動化

【導入と前提】 大量のJSON形式ログやAPIレスポンスを、group_byreduceを用いて高速に集計し、SREの意思決定を支援する堅牢なスクリプトを構築します。

  • OS: GNU/Linux (Ubuntu/RHEL)

  • Tool: jq 1.6+, curl, bash 4.4+

【処理フローと設計】

graph TD
A["JSON Data Source"] -->|curl/cat| B["jq: group_by"]
B -->|Sort & Group| C["jq: reduce/map"]
C -->|Aggregate/Summary| D["Structured Report"]
D -->|Alert/Log| E["Monitoring System"]

group_by は内部的にソートを行うため、対象の配列が巨大な場合はメモリ消費に注意が必要です。一方、reduce は単一パスで集計を行うため、パフォーマンスを最大化する際に有効です。これらを組み合わせることで、特定のキー(例:HTTPステータス、インスタンスID)ごとの統計情報を抽出します。

【実装:堅牢な自動化スクリプト】 以下は、分散されたアクセスログ(JSON)を読み込み、ステータスコードごとにリクエスト数を集計するテンプレートスクリプトです。

#!/usr/bin/env bash

# --- 安全のためのシェルオプション ---


# -e: エラー発生時に即座に終了


# -u: 未定義変数の参照時にエラー


# -o pipefail: パイプラインの途中のエラーを拾う

set -euo pipefail

# 一時ファイル管理用のtrap

TMP_DATA=$(mktemp)
readonly TMP_DATA
trap 'rm -f "${TMP_DATA}"' EXIT

# --- 設定 ---


# ダミーデータの生成(実際はAPIリクエストやログファイル)

cat << 'EOF' > "${TMP_DATA}"
[
  {"status": 200, "latency": 120, "path": "/api/v1"},
  {"status": 500, "latency": 500, "path": "/api/v1"},
  {"status": 200, "latency": 150, "path": "/health"},
  {"status": 404, "latency": 50,  "path": "/unknown"},
  {"status": 500, "latency": 450, "path": "/api/v2"}
]
EOF

echo "--- 1. group_by による集計例 (ステータス別カウント) ---"

# group_by(field): 指定したキーで配列をネストされた配列に分割


# map({key: .[0].field, value: length}): グループごとの個数を計算

jq -r '
  group_by(.status) 
  | map({
      "status": .[0].status | tostring,
      "count": length
    })
' "${TMP_DATA}"

echo "--- 2. reduce による高度な統計計算 (レイテンシ合計) ---"

# reduce: 配列を一つずつ処理し、アキュムレータ(acc)に結果を蓄積


# リトライ処理や条件分岐を含む集計に最適

jq -r '
  reduce .[] as $item ({}; 
    .[$item.status | tostring] += {
      "count": 1,
      "total_latency": $item.latency
    } | 
    .[$item.status | tostring].count += 0 # 初期化補助
  )
' "${TMP_DATA}"

# --- 実戦的なAPI取得と集計の例 ---


# curl -L: リダイレクト追従, -s: 進捗非表示, --retry: 失敗時の再試行


# URL="https://api.example.com/metrics"


# curl -L -s --retry 3 "${URL}" | jq 'group_by(.severity) | map({(.[0].severity): length}) | add'

【検証と運用】

  1. 正常系の確認: スクリプトを実行し、標準出力にステータスコード別のJSONオブジェクトが返ることを確認します。

    ./aggregate_metrics.sh | jq -e '.[] | has("status")'
    
  2. システムログの監視: 本処理をsystemdタイマーで実行する場合、以下のコマンドで実行ログとエラーを確認します。

    journalctl -u json-aggregator.service -f
    

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

  • メモリ制約: group_by は全データをメモリに展開しソートします。数GB単位のJSONを扱う場合は、jq --stream オプションの使用や、事前に split コマンドで分割して処理することを検討してください。

  • 型の一貫性: group_by のキーに数値と文字列が混在すると、予期しないグループ化が行われます。必ず tostringtonumber で正規化してください。

  • 権限: 出力先のディレクトリへの書き込み権限や、一時ファイル /tmp の容量不足に注意してください。

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

  1. 入力チェック: jq 処理前に、入力が空でないか、正当なJSON形式かを確認する。

  2. Atomicな書き出し: 結果をファイルに保存する際は、一時ファイルに書き出した後 mv で置換し、中途半端な状態のファイルを生成しない。

  3. エラーハンドリング: パイプライン内で jq がエラーを吐いた場合、set -o pipefail によりスクリプトを停止させ、不正なデータが後続工程に流れるのを防ぐ。

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

コメント

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