クラスターの可観測性: Prometheus、Grafana、OpenTelemetry
「測定できなければ、管理することもできません。」実稼働 Kubernetes クラスターの場合、これは それは、3 次元での可視性を実現することを意味します。 メトリクス (彼はそれぞれどれくらいの量を消費しますか コンポーネント)、 ログ (システム内で何が起こっているのか)、e トレース (リクエストがマイクロサービスを介してどのように移動するか)。プロメテウス + グラファナ + スタック Loki + OpenTelemetry は、このニーズに対するオープンソースの答えです。
この記事では、Kubernetes の完全な可観測性スタックを構築します。 kube-プロメテウス-スタック インフラストラクチャのメトリクスについては、設定します ロキ 集約ログの場合は統合します OpenTelemetry コレクター アプリケーションによって配布されたトレースを収集し、Tempo (バックエンド) に転送します。 Grafana トレースの)。その結果、統合された可観測性プラットフォームが誕生します。 グラファナで表示されます。
何を学ぶか
- kube-prometheus-stack をインストールします: Prometheus Operator、kube-state-metrics、Node Exporter
- アプリの自動スクレイピング用に ServiceMonitor と PodMonitor を作成する
- 重要なクラスター アラート (OOMKill、CrashLoopBackOff など) 用の PrometheusRule
- Kubernetes ラベルを使用した集約ログ用の Loki + Promtail
- OpenTelemetry Collector - 構成可能なテレメトリ パイプライン
- 分散トレーシング用の Grafana Tempo
- Kubernetes 用の事前構築済み Grafana ダッシュボード
- Grafana のメトリクスとログとトレースの相関関係 (エグザンプラ)
可観測性スタックのアーキテクチャ
インストールする前に、コンポーネントが相互にどのように関係しているかを理解しましょう。
- プロメテウス: HTTP スクレイピングを使用してメトリクスを収集します。データを 15 ~ 30 日間保存します
- kube-state-metrics: K8s オブジェクト (デプロイメント、ポッドなど) のステータスに関するメトリクスを公開します。
- ノードエクスポーター: ノードのハードウェア メトリクス (CPU、ディスク、ネットワーク) を公開します
- ロキ: Pod ログを集約します。コンテンツのインデックスは作成されず、ラベルのみがインデックスされます。
- プロムテール: コンテナログを Loki に送信する DaemonSet
- OpenTelemetry コレクター: アプリからトレース/メトリクス/ログを受信し、バックエンドにルーティングします。
- グラファナ時間: 分散トレースのバックエンド (トレース)
- グラファナ: メトリクス (Prometheus)、ログ (Loki)、トレース (Tempo) を表示するための統合 UI
kube-prometheus-stack のインストール
# Installa kube-prometheus-stack (include Prometheus, Alertmanager, Grafana, kube-state-metrics, Node Exporter)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# values.yaml per produzione
cat > kube-prometheus-values.yaml << 'EOF'
# Prometheus
prometheus:
prometheusSpec:
retention: 30d
retentionSize: "50GB"
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: ssd
resources:
requests:
storage: 100Gi
# Scrape tutte le ServiceMonitor/PodMonitor nel cluster
serviceMonitorSelectorNilUsesHelmValues: false
podMonitorSelectorNilUsesHelmValues: false
ruleSelectorNilUsesHelmValues: false
resources:
requests:
cpu: 500m
memory: 2Gi
limits:
cpu: 2000m
memory: 8Gi
# Alertmanager
alertmanager:
alertmanagerSpec:
storage:
volumeClaimTemplate:
spec:
storageClassName: ssd
resources:
requests:
storage: 10Gi
config:
global:
slack_api_url: "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
route:
receiver: 'slack-critical'
group_wait: 30s
group_interval: 5m
repeat_interval: 12h
routes:
- receiver: 'slack-critical'
matchers:
- alertname =~ ".*Critical.*"
- receiver: 'slack-warning'
matchers:
- severity = warning
receivers:
- name: 'slack-critical'
slack_configs:
- channel: '#alerts-critical'
send_resolved: true
title: '[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}'
text: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'
- name: 'slack-warning'
slack_configs:
- channel: '#alerts-warning'
send_resolved: true
# Grafana
grafana:
enabled: true
ingress:
enabled: true
hosts:
- grafana.company.com
persistence:
enabled: true
size: 10Gi
# Datasource Loki pre-configurato
additionalDataSources:
- name: Loki
type: loki
url: http://loki.monitoring.svc:3100
jsonData:
derivedFields:
- datasourceUid: tempo
matcherRegex: '"traceID":"(\w+)"'
name: TraceID
url: '${__value.raw}'
- name: Tempo
type: tempo
url: http://tempo.monitoring.svc:3100
# kube-state-metrics
kube-state-metrics:
metricLabelsAllowlist:
- pods=[team,environment,app]
- deployments=[team,environment,app]
EOF
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--version 65.0.0 \
-f kube-prometheus-values.yaml
# Verifica
kubectl get pods -n monitoring
kubectl get servicemonitors -A
アプリケーションスクレイピング用のServiceMonitor
プロメテウス オペレーターは次を使用します。 サービスモニター e ポッドモニター アプリケーションメトリクスのスクレイピングを動的に設定します。無駄だよ Prometheus 構成を変更します。
# servicemonitor-api-service.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: api-service-monitor
namespace: team-alpha-production
labels:
team: team-alpha # label per selezionare questo monitor
spec:
selector:
matchLabels:
app: api-service # seleziona il Service con questo label
endpoints:
- port: metrics # nome della porta nel Service
interval: 30s
path: /metrics
# Basic auth se le metriche sono protette
# basicAuth:
# username: { name: metrics-auth, key: username }
# password: { name: metrics-auth, key: password }
namespaceSelector:
matchNames:
- team-alpha-production
---
# Aggiungi la porta metrics al Service dell'applicazione
apiVersion: v1
kind: Service
metadata:
name: api-service
namespace: team-alpha-production
labels:
app: api-service
spec:
selector:
app: api-service
ports:
- name: http
port: 8080
- name: metrics # porta dedicata alle metriche
port: 9090
targetPort: 9090
クラスタークリティカルアラート
# prometheusrule-kubernetes-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: kubernetes-critical-alerts
namespace: monitoring
labels:
prometheus: kube-prometheus
spec:
groups:
- name: kubernetes.critical
rules:
# Pod in CrashLoopBackOff
- alert: PodCrashLoopBackOff
expr: |
rate(kube_pod_container_status_restarts_total[15m]) > 0
AND
kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} == 1
for: 5m
labels:
severity: critical
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} in CrashLoopBackOff"
# Pod OOMKilled
- alert: PodOOMKilled
expr: |
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1
for: 0m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} terminato per OOM"
description: "Aumenta i memory limits del container {{ $labels.container }}"
# Nodo sotto pressione di memoria
- alert: NodeMemoryPressure
expr: kube_node_status_condition{condition="MemoryPressure",status="true"} == 1
for: 2m
labels:
severity: critical
annotations:
summary: "Nodo {{ $labels.node }} sotto MemoryPressure"
# PVC quasi pieno
- alert: PersistentVolumeFillingUp
expr: |
kubelet_volume_stats_available_bytes /
kubelet_volume_stats_capacity_bytes < 0.15
for: 5m
labels:
severity: warning
annotations:
summary: "PVC {{ $labels.namespace }}/{{ $labels.persistentvolumeclaim }} al 85% della capacita"
# Deployment con zero repliche disponibili
- alert: DeploymentUnavailable
expr: kube_deployment_status_replicas_available == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Deployment {{ $labels.namespace }}/{{ $labels.deployment }} ha 0 repliche disponibili"
Loki + Promtail の集約ログ
# Installa Loki (modalita monolitica per cluster medio)
helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki \
--namespace monitoring \
--set loki.commonConfig.replication_factor=1 \
--set loki.storage.type=filesystem \
--set singleBinary.replicas=1 \
--set monitoring.selfMonitoring.enabled=false
# Installa Promtail (DaemonSet che invia log a Loki)
helm install promtail grafana/promtail \
--namespace monitoring \
--set config.clients[0].url=http://loki.monitoring.svc:3100/loki/api/v1/push \
--set config.snippets.extraScrapeConfigs='
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
pipeline_stages:
- cri: {}
- labeldrop:
- filename
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_team]
target_label: team
- source_labels: [__meta_kubernetes_pod_label_app]
target_label: app
- source_labels: [__meta_kubernetes_namespace]
target_label: namespace'
# Query Loki in Grafana (LogQL):
# Tutti i log error del team-alpha:
# {namespace="team-alpha-production"} |= "ERROR"
# Error rate per app negli ultimi 5 minuti:
# sum(rate({namespace="team-alpha-production"} |= "ERROR" [5m])) by (app)
# Log strutturati JSON - estrai campo:
# {app="api-service"} | json | level="error" | line_format "{{.message}}"
分散トレース用の OpenTelemetry コレクター
# Installa OpenTelemetry Operator
kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
---
# otel-collector.yaml - pipeline di telemetria
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: otel-collector
namespace: monitoring
spec:
mode: DaemonSet # un collector per nodo
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 1024
# Aggiunge metadati Kubernetes ai trace (namespace, pod name, etc.)
k8sattributes:
auth_type: "serviceAccount"
passthrough: false
extract:
metadata:
- k8s.pod.name
- k8s.namespace.name
- k8s.deployment.name
- k8s.node.name
labels:
- tag_name: team
key: team
from: pod
# Campionamento: mantieni solo 10% dei trace in produzione (volume alto)
probabilistic_sampler:
sampling_percentage: 10
exporters:
otlp/tempo:
endpoint: http://tempo.monitoring.svc:4317
tls:
insecure: true
prometheus:
endpoint: "0.0.0.0:8889"
const_labels:
cluster: production
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, k8sattributes, probabilistic_sampler]
exporters: [otlp/tempo]
metrics:
receivers: [otlp]
processors: [batch, k8sattributes]
exporters: [prometheus]
Grafana Tempo のインストール
# Installa Grafana Tempo
helm install tempo grafana/tempo-distributed \
--namespace monitoring \
--set storage.trace.backend=local
# Alternativa: Tempo monolitico per cluster piccoli/medi
helm install tempo grafana/tempo \
--namespace monitoring \
--set tempo.storage.trace.backend=filesystem \
--set tempo.storage.trace.local.path=/var/tempo
Kubernetes 用 Grafana ダッシュボード
Grafana には、事前に構築されたダッシュボードのカタログがあります。これらの ID を UI からインポートします (ダッシュボード > インポート):
| ダッシュボード | ID グラファナ | ユーティリティ |
|---|---|---|
| Kubernetes クラスターの概要 | 7249 | ノード別の CPU/メモリ/ポッドの概要 |
| Kubernetesのデプロイメント | 8588 | デプロイメントステータス、再起動率、レプリカ |
| ノード エクスポーター フル | 1860年 | ノードのハードウェア メトリック (CPU、ディスク、ネットワーク) |
| Kubernetes PVC | 13646 | PVCストレージの使用 |
| ロキダッシュボード | 15141 | 集約ログ、エラー率、ログエクスプローラー |
| NGINX 入力コントローラー | 9614 | リクエストレート、レイテンシ、入力ステータスコード |
メトリクス、ログ、トレースの相関関係 (サンプル)
このスタックの真の力は、 相関: のメトリクスから 待ち時間が長いため、対応するトレースに直接移動したり、トレースからログに移動したりできます。 その時のポッドの様子。これはと呼ばれます 模範的な:
# Abilita gli exemplar in Prometheus (già abilitati in kube-prometheus-stack)
# Nell'applicazione, includi il traceID nell'histogram metric:
# Go/Python con OpenTelemetry:
# Quando crei un histogram, aggiungi l'exemplar con il trace ID corrente
# Il Prometheus scraper lo raccoglie e lo conserva
# In Grafana:
# 1. Vai alla dashboard API latency
# 2. Vedi un picco di latenza
# 3. Clicca il diamante (exemplar) sul grafico
# 4. Grafana ti apre automaticamente il trace in Tempo
# 5. Dal trace, clicca sul servizio con errore
# 6. Grafana mostra i log di quel Pod in Loki per quel timestamp
# Questo flusso metriche → trace → log e il "holy grail" dell'osservabilita
Kubernetes の可観測性のベスト プラクティス
本番環境の可観測性チェックリスト
- リソースの USE メソッド: 各リソース (CPU、メモリ、ディスク、ネットワーク) について: 使用率、飽和、エラー。これらは各ノードの 3 つの基本的なアラートです
- サービスの RED メソッド: 各サービス: レート (要求/秒)、エラー (エラー率)、継続時間 (遅延)。 3 つすべてについて警告
- SLO ベースのアラート: あらゆる異常についてアラートを発するのではなく、SLO エラー バジェットを使い果たした場合にのみアラートを発します。ノイズが減り、信号が増える
- 差別化された保持率: 生のメトリクスは 15 日、月次集計は 1 年です。生のログは 7 日間、監査ログは 1 年間
- トラックのサンプリング: 運用環境でトレースを 100% 保持しないでください。コストがかかりすぎます。デバッグには 1 ~ 10% で十分です
- 一貫したラベル: 各メトリクス、ログ、トレースには、フィルタリング用のチーム、環境、アプリ、バージョンが必要です
結論と次のステップ
完全な可観測性スタック — メトリクスには Prometheus、ログには Loki、 OpenTelemetry + Trace Time — Kubernetes をブラック ボックスからシステムに変換します 理解できる。 Grafana の 3 つの信号間の相関により、時間が大幅に短縮されます。 デバッグの平均時間は数時間から数分です。
このシリーズの次回で最後の記事 — フェデレーションを使用した Kubernetes マルチクラウド Submariner — 異なるクラウド プロバイダー間で複数のクラスターを管理するという課題に対処します。 は 1 つだけであり、このシリーズのすべての概念をマルチクラスターに拡張しました。
関連シリーズ
- 可観測性と OpenTelemetry — アプリケーション計測の詳細な分析
- ArgoCD を使用した GitOps — Argo Rollouts は自動カナリア分析に Prometheus を使用します







