<p><meta/>
author: SRE_Consultant
style: technical_documentation_sre
tools: [jq, bash, systemd]
knowledge_level: advanced
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">高度なjqフィルタリングによるシステムメトリクスの集計・自動化</h1>
<p>【導入と前提】
大量のJSON形式ログやAPIレスポンスを、<code>group_by</code>と<code>reduce</code>を用いて高速に集計し、SREの意思決定を支援する堅牢なスクリプトを構築します。</p>
<ul class="wp-block-list">
<li><p>OS: GNU/Linux (Ubuntu/RHEL)</p></li>
<li><p>Tool: jq 1.6+, curl, bash 4.4+</p></li>
</ul>
<p>【処理フローと設計】</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
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"]
</pre></div>
<p><code>group_by</code> は内部的にソートを行うため、対象の配列が巨大な場合はメモリ消費に注意が必要です。一方、<code>reduce</code> は単一パスで集計を行うため、パフォーマンスを最大化する際に有効です。これらを組み合わせることで、特定のキー(例:HTTPステータス、インスタンスID)ごとの統計情報を抽出します。</p>
<p>【実装:堅牢な自動化スクリプト】
以下は、分散されたアクセスログ(JSON)を読み込み、ステータスコードごとにリクエスト数を集計するテンプレートスクリプトです。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">#!/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'
</pre>
</div>
<p>【検証と運用】</p>
<ol class="wp-block-list">
<li><p><strong>正常系の確認</strong>:
スクリプトを実行し、標準出力にステータスコード別のJSONオブジェクトが返ることを確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">./aggregate_metrics.sh | jq -e '.[] | has("status")'
</pre>
</div></li>
<li><p><strong>システムログの監視</strong>:
本処理をsystemdタイマーで実行する場合、以下のコマンドで実行ログとエラーを確認します。</p>
<div class="codehilite">
<pre data-enlighter-language="generic">journalctl -u json-aggregator.service -f
</pre>
</div></li>
</ol>
<p>【トラブルシューティングと落とし穴】</p>
<ul class="wp-block-list">
<li><p><strong>メモリ制約</strong>: <code>group_by</code> は全データをメモリに展開しソートします。数GB単位のJSONを扱う場合は、<code>jq --stream</code> オプションの使用や、事前に <code>split</code> コマンドで分割して処理することを検討してください。</p></li>
<li><p><strong>型の一貫性</strong>: <code>group_by</code> のキーに数値と文字列が混在すると、予期しないグループ化が行われます。必ず <code>tostring</code> や <code>tonumber</code> で正規化してください。</p></li>
<li><p><strong>権限</strong>: 出力先のディレクトリへの書き込み権限や、一時ファイル <code>/tmp</code> の容量不足に注意してください。</p></li>
</ul>
<p>【まとめ】
運用の冪等性を維持するための3つのポイント:</p>
<ol class="wp-block-list">
<li><p><strong>入力チェック</strong>: <code>jq</code> 処理前に、入力が空でないか、正当なJSON形式かを確認する。</p></li>
<li><p><strong>Atomicな書き出し</strong>: 結果をファイルに保存する際は、一時ファイルに書き出した後 <code>mv</code> で置換し、中途半端な状態のファイルを生成しない。</p></li>
<li><p><strong>エラーハンドリング</strong>: パイプライン内で <code>jq</code> がエラーを吐いた場合、<code>set -o pipefail</code> によりスクリプトを停止させ、不正なデータが後続工程に流れるのを防ぐ。</p></li>
</ol>
author: SRE_Consultant
style: technical_documentation_sre
tools: [jq, bash, systemd]
knowledge_level: advanced
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
高度なjqフィルタリングによるシステムメトリクスの集計・自動化
【導入と前提】
大量のJSON形式ログやAPIレスポンスを、group_byとreduceを用いて高速に集計し、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'
【検証と運用】
正常系の確認:
スクリプトを実行し、標準出力にステータスコード別のJSONオブジェクトが返ることを確認します。
./aggregate_metrics.sh | jq -e '.[] | has("status")'
システムログの監視:
本処理をsystemdタイマーで実行する場合、以下のコマンドで実行ログとエラーを確認します。
journalctl -u json-aggregator.service -f
【トラブルシューティングと落とし穴】
メモリ制約: group_by は全データをメモリに展開しソートします。数GB単位のJSONを扱う場合は、jq --stream オプションの使用や、事前に split コマンドで分割して処理することを検討してください。
型の一貫性: group_by のキーに数値と文字列が混在すると、予期しないグループ化が行われます。必ず tostring や tonumber で正規化してください。
権限: 出力先のディレクトリへの書き込み権限や、一時ファイル /tmp の容量不足に注意してください。
【まとめ】
運用の冪等性を維持するための3つのポイント:
入力チェック: jq 処理前に、入力が空でないか、正当なJSON形式かを確認する。
Atomicな書き出し: 結果をファイルに保存する際は、一時ファイルに書き出した後 mv で置換し、中途半端な状態のファイルを生成しない。
エラーハンドリング: パイプライン内で jq がエラーを吐いた場合、set -o pipefail によりスクリプトを停止させ、不正なデータが後続工程に流れるのを防ぐ。
ライセンス:本記事のテキスト/コードは特記なき限り
CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。
コメント