社内の横断チームとして、
「エンジニアが快適に開発できる環境をつくる」
ことをミッションにしています。
その一環として、Jenkins ベースの CI / CD 基盤や GitLab などの 開発環境そのものを「社内向けサービス」 とみなし、エンジニアを「ユーザ」として扱って SLO(Service Level Objective、サービスレベル目標) を意識した可観測基盤を構築しました。
この基盤は、既にZabbixによるインフラ監視が導入されていたため、これを活用しつつ、OSSツールを組み合わせて可観測基盤を構築しました。
なお、記事中のホスト名(obsv-server / zabbix-server など)は、説明用の例です。
想定読者
- CI / CD や GitLab を「社内向けサービス」としてちゃんと運用したい
- OpenTelemetry / Jaeger / Loki / Grafana / Zabbix を組み合わせた構成例を知りたい
- 「監視」はあるけど、開発者体験(DevEx)や SLO まで踏み込みたい
という SRE / プラットフォームエンジニア / 内製開発チーム向けです。
背景:開発環境も立派な「サービス」だった
横断チームで面倒を見ている主な開発環境はこんな感じです。
- Jenkins:CI 基盤(静的解析や自動テスト)
- Jenkins:CD 基盤(ビルドやデプロイ)
- GitLab:ソースコード管理
- 開発対象のアプリケーション:
- 開発用共有環境(コンテナ構成)
- 検証用環境(コンテナ構成)
- 可観測基盤を構成する各種 OSS コンテナ群(Jaeger / Loki / Grafana など)
- 監視:Zabbix(インフラ監視)
ここで困っていたのは、例えばこんな状況でした。
- Jenkins が詰まると、各プロジェクトの開発が全部止まる
- マルチブランチ + Git pull で、ストレージがじわじわ逼迫
- 「最近 CI が重い」という声に対しても、メトリクスがないため、CI/CD のパフォーマンス低下を定量的なデータで説明できない
本番プロダクト側はそれなりに SLO や監視が整っている一方で、開発環境の監視には改善の余地があり、「落ちたら復旧する」「重くなったらチューニングする」レベルに留まっていました。
そこで発想を変えて、
CI / CD・Git などの開発環境も「社内向けサービス」
その利用者であるエンジニアを「ユーザ」とみなし、
SLO を意識して可観測性を整える
という方針に切り替えました。
要件整理:何を観測したかったのか?
まずは「何を知りたいのか」をざっくり言葉にしました。
観測したかったもの
-
サービスの実行状況
- ジョブ成功率・失敗率
- ジョブキュー待ち時間
- 各ステージの実行時間
- コンテナの稼働状況
-
リソース・ストレージ
- ディスク使用率
- メモリ使用率
- リソース逼迫の状況
-
ログ・トレース
- どのステージで遅くなっているか
- 失敗時に何が起きていたか
設計ポリシー
- できるだけ OSS を組み合わせて構成する
- メトリクス収集は既存の Zabbix を使い、二重化しない
- 「入口」「出口」を後から差し替えやすい構造にする
- 「開発環境向け」なので、運用コストも現実的な範囲に収める
全体アーキテクチャ
最終的に、ざっくり以下の構成に落ち着きました。
- Jenkins(CI / CD)
- OpenTelemetry プラグインでトレース・ログを送信
- OpenTelemetry Collector(以下、OTel Collector)
- Jenkins から OTLP(トレース/ログ)を受信
- Jaeger(トレース)と Loki(ログ)に転送
- Jaeger + Cassandra
- トレースデータの永続化
- Grafana Loki
- ログの保存・検索
- Grafana
- Jaeger / Loki / Zabbix をデータソースとして統合ダッシュボード化
- アラート設定 + Slack 通知
- Zabbix(既存監視)
- 各サーバの CPU / メモリ / ストレージなどのメトリクス取得
- Grafana からデータソースとして参照
役割分担を一言でいうと:
メトリクス:Zabbix
ログ:Loki
トレース:Jaeger
ダッシュボード:Grafana
という構図です。
OTel Collector の構成
OTel Collector には、「トレースとログのハブ」という役割に専念させています。
メトリクスはあえて扱わず、Zabbix に完全に任せる構成です。
config.yaml のイメージ
/etc/otelcol-contrib/config.yaml は、ざっくりこんな構成です。
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4320"
http:
endpoint: "0.0.0.0:4330"
exporters:
otlp:
endpoint: obsv-server:4317
tls:
insecure: true
loki:
endpoint: obsv-server:3100/loki/api/v1/push
debug:
verbosity: detailed
processors:
batch:
send_batch_size: 1024
timeout: 5s
extensions:
zpages:
endpoint: 0.0.0.0:7777
health_check: {}
service:
extensions: [zpages, health_check]
pipelines:
traces:
receivers: [otlp]
processors: []
exporters: [otlp, debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug, loki]
telemetry:
logs:
level: "debug"
metrics:
level: detailed
address: ":8888"
ポイント:
-
receivers.otlp- gRPC:
4320 - HTTP:
4330
- gRPC:
-
pipelinesはtracesとlogsのみ(メトリクスは扱わない) -
exporters- Jaeger(OTLP gRPC)へトレース送信
- Loki の HTTP API へログをプッシュ
OTel Collector の役割は一言で言うと、
Jenkins →(OTLP)→ OTel Collector → Jaeger / Loki
の「真ん中の中継+フォーマット変換」です。
Jenkins:OpenTelemetry プラグインでトレース・ログ送信
Jenkins 側では、次のプラグインを利用しています。
- OpenTelemetry
- Remoting monitoring with OpenTelemetry
設定例(System 設定):
-
OTLP Endpoint:
http://obsv-server:4330- OTel Collector の OTLP/HTTP ポート(4330) に合わせています
-
Authentication:なし(社内ネットワーク内で完結)
OTel Collector 側の設定では、
- gRPC:
0.0.0.0:4320 - HTTP:
0.0.0.0:4330
としているので、今回は Jenkins → OTLP/HTTP → OTel Collector という構成です。
(gRPC を使いたい場合は、Jenkins 側の設定を gRPC にして obsv-server:4320 を指定する想定)
これにより:
- ジョブ単位のトレースが取れる
- 各ステージの所要時間・エラーがトレースに紐づく
- ジョブ履歴画面から OpenTelemetry アイコン経由でトレースを辿れる
といった形で、Jenkins が可観測基盤の入口になります。
Jaeger + Cassandra:トレースの永続化
トレーシングのバックエンドには Jaeger を採用しました。
-
Jaeger all-in-one コンテナを
obsv-server上に起動 -
ストレージには Cassandra を利用
- Jaeger 用の Keyspace を作成
- Jaeger 公式スキーマを実行してテーブル作成
Jaeger の UI から直接トレースを見ることもできますが、運用上は
Grafana から Jaeger をデータソースとして参照
する使い方に寄せています。
理由:
- ダッシュボード上で「メトリクス → トレース → ログ」と辿りたい
- ツールを行き来するより、Grafana に寄せた方が運用しやすい
Loki:ログストア(ログだけ見る、トレースは Jaeger に任せる)
ログは Grafana Loki に集約しています。
- Jenkins のコンソールログ
- OTel Collector 経由で送られてくるアプリケーションログ(必要に応じて)
ここで重要なのは:
- Loki はログ(Log)を扱うツール
- トレース(Trace)を扱うのは Jaeger
という役割分担です。
OTel Collector はクライアントから OTLP(トレース/ログ) を受け取り、Loki Exporter が Loki の HTTP API に対してログをプッシュする構成になっています。
ラベル設計は最初から作り込みすぎず、例えば:
job="jenkins"- Jenkins ジョブ名
- ビルド番号
くらいから始めて、運用の中で「欲しい切り口」が見えてきたところから増やしていきました。
Grafana:ダッシュボードとアラートのハブ
Grafana は、
- Jaeger(トレース)
- Loki(ログ)
- Zabbix(メトリクス)
をまとめて扱うハブとして使っています。
データソース構成
-
Jaeger
- URL:
http://obsv-server:16686/ - 「Trace to logs」を設定し、トレースから Loki のログにジャンプできるようにする
- URL:
-
Loki
- URL:
http://obsv-server:3100/
- URL:
-
Zabbix
- URL:
http://zabbix-server/api_jsonrpc.php(例)
- URL:
代表的なダッシュボード
Alerting(Slack 連携)
-
閾値を超えたら Slack の特定チャンネルへ通知
-
Contact point(通知先)とメッセージテンプレートを定義しておく
-
例:
- アラートは Zabbix のデータ送信間隔、および稼働時間を踏まえて設定し、
「まだメトリクスが届いていないだけで No Data 判定になる」パターンを避けています。
Zabbix:メトリクスの収集
メトリクスの収集はすべて Zabbix に任せています。
- 各サーバに Zabbix エージェントを導入
- CPU / メモリ / ディスク / ネットワークなどを計測
- Zabbix 本体でもアラートを設定できるが、
今回は 可視化と通知は Grafana に寄せる 方針
こうすることで:
- エージェントの運用は既存の監視チームのフローに乗せられる
- 新たにメトリクス収集スタックを増やさなくて済む
- 可視化とアラートのルールを Grafana 側で統一管理できる
というメリットがありました。
「エンジニアをユーザとみなした」SLO の考え方
ガチガチな数値 SLO というよりは、まずは「エンジニアの体感」に近いラインを決めています。
例:
-
ジョブの実行状況
- 平日日中のジョブ成功率:99% 以上
- 平日日中のジョブ成功率:99% 以上
-
メモリ、および、ストレージの使用率:
- 70% 超:Warning
- 90% 超:Critical
これらを可観測基盤でモニタしつつ、
超えたら Slack にアラート、という運用にしています。
イメージとしては、
「エンジニアがイライラし始めるライン」をSLO の目安として可視化する
感じです。
実際に効いたところ
1. ストレージ逼迫を「兆候」で捉えられるようになった
マルチブランチの CD 基盤では、ブランチ数や実行回数に比例して、
- Jenkins ワークスペース
- アーティファクト
- Git クローン
などがストレージを消費します。
導入前は、
-
メトリクスがないため、パフォーマンス低下を定量的データで把握できていなかった
→ 「最近パイプラインが重い気がする」と感じてから確認に行くと、ストレージ使用率 95% 超え
→ 慌てて古いジョブやアーティファクトを削除
といった、従来の事後対応中心の運用から脱却する必要がありました。
導入後は、
-
Zabbix → Grafana でストレージ使用率のトレンドを可視化
-
余裕を持って
- ローテーション設定見直し
- クリーンアップジョブの追加
- ストレージ増強を検討
といった 事前の一手 を打てるようになりました。
2. Jenkins の「なんとなく遅い」をデータで説明できるようになった
「最近 Jenkins 重くない?」という声に対しても、
-
トレース(Jaeger)
どのステージで時間がかかっているか -
ログ(Loki)
そのタイミングでエラー・リトライが発生していないか -
メトリクス(Zabbix)
その時間帯の ストレージ / メモリ の状況はどうか
を一緒に見ることで、
- 特定時間帯にジョブが集中しすぎている
- 一部ジョブのステップが無駄に重い
- ストレージの余裕がなくなりそう
といったことを 定量的に説明 できるようになりました。
3. チーム内に「サービスとしての開発環境」という意識が根付いた
一番効いたのは、横断チーム側のマインドセットかもしれません。
- CI / CD / Git / 可観測基盤を「社内向けプロダクト」として扱う
- 社内エンジニアを「そのプロダクトのユーザ」とみなす
- ユーザ体験を守るために SLO と可観測性を整える
という視点で会話できるようになり、
「とりあえず動いていれば OK」
から
「サービスとしてどんなレベルを維持するか」
という運用にシフトしてきています。
まとめ
- Jenkins / GitLab / 各種開発サーバといった 社内開発環境を「社内向けサービス」 と捉え直し、エンジニアをユーザとみなして SLO を意識した可観測基盤を構築しました
- メトリクスは既存の Zabbix をメインに据えつつ、トレースとログを OpenTelemetry+Jaeger+Loki で扱い、Grafana で統合しています
- その結果、ストレージ逼迫やジョブ遅延を“兆候”で捉えられるようになり開発者、開発者体験に直結する SLO を意識した運用に一歩近づけました
同じように「開発環境をちゃんとしたサービスとして運用したい」と考えている方のヒントになればうれしいです。
補足:Cassandra は開発環境向けの 1 ノード構成
ここで使っている Cassandra は、「開発環境の可観測基盤」という位置づけのため 1 ノード構成 で運用しています。
本番用途で Jaeger + Cassandra を採用する場合は、レプリカ数や障害時の自動復旧を考えると 3 ノード以上のクラスタ構成が推奨 されます。
一方で、今回の構成では次のような割り切りをしています。
- 対象は開発環境のトレースであり、一部トレースの欠損は許容する
- 保存期間も比較的短めにし、ストレージ要件を抑える
- 追加のサーバを増やさず、既存の仮想基盤上のリソースで収める
つまり、「まずは開発者が困っている CI/CD の見えにくさを解消する」ことを優先し、 可用性やスケール要件は本番プロダクトほどは求めていない、というバランスになっています。
本番環境で同様の構成を検討する場合は、この 1 ノード構成をそのまま真似するのではなく、 Cassandra のクラスタ構成やストレージ設計を自分たちの SLO・障害許容度に合わせて見直すことをおすすめします。
補足:Jenkins 単体+プラグインとしなかった理由
よくある疑問として、
「Jenkins のプラグインを入れれば、監視やメトリクスはだいたい見えるのでは?」
というものがあります。実際、Jenkins 単体でもジョブの実行時間や成功率、ノード負荷、ビルドログの閲覧などはある程度カバーできます。
しかし、今回対象にしているのは CD 用 Jenkins だけでなく、CI 用 Jenkins、GitLab、コンテナ上の SaaS アプリ(開発者用/UI 単体テスト用)、さらにそれらを支えるインフラや可観測基盤のコンテナ群まで含めた「開発者が日常的に触る一連の環境」です。
Jenkins 単体+プラグインだけで観測しようとすると、次のような限界がありました。
- 視点が Jenkins に閉じてしまい、GitLab や SaaS アプリ、インフラの状態を含めた「エンドツーエンドのサービスレベル」として捉えにくい
- Zabbix のメトリクス/アプリログ/トレースを 1 つのダッシュボードで突き合わせて「この時間帯に何が起きていたか」を追うことが難しい
- 可観測性の仕組みを Jenkins プラグイン前提にすると、将来 CI/CD ツールや構成を変えたときに監視基盤ごと作り直すリスクがある
- 監視系プラグインを積みすぎると Jenkins 自身の負荷・安定性にも影響しやすい
そこで、Jenkins は「ジョブを実行してトレース/ログを出力するだけ」とし、OTel Collector / Jaeger / Loki / Grafanaを組み合わせて、開発環境全体を可視化できる構成としています。






