2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetesを学ぶ9日間 - Day 4: Kubernetes監視スタック構築 - Prometheus + Grafana + Discord通知

Last updated at Posted at 2025-12-06

はじめに

「ジャンルなしオンラインもくもく会 Advent Calendar 2025」の6日目の記事です。

本記事は、Kubernetesクラスタ構築シリーズの第4回です。今回は、構築したクラスタに本番を意識した監視スタックをデプロイします。

本シリーズの全体構成

前回のおさらい

本記事で学べること

  • kube-prometheus-stackのHelmインストール
  • AlertManager → Discord Webhook連携
  • 専用ノードへのデプロイ(NodeSelector + Toleration)
  • Ingress経由での外部アクセス設定

1. kube-prometheus-stackとは

1.1 含まれるコンポーネント

公式Helmチャートに含まれるもの

コンポーネント 役割
Prometheus Operator CRDベースの自動化
Prometheus メトリクス収集・保存
Grafana 可視化ダッシュボード
AlertManager アラート管理・通知
Node Exporter ノードメトリクス収集
kube-state-metrics Kubernetesオブジェクトのメトリクス

一括デプロイで監視基盤が構築可能!


1.2 アーキテクチャ

監視専用ノードの利点

  • アプリケーションとのリソース競合回避
  • 安定した監視基盤
  • Taint/Tolerationで確実に分離

2. 事前準備

2.1 Helmのインストール

# Mac
brew install helm

# バージョン確認
helm version

2.2 Discord Webhookの作成

  1. Discordサーバーの設定 → 連携サービス
  2. ウェブフック → 新しいウェブフック
  3. ウェブフックURLをコピー

2.3 監視専用ノードの確認

昨日(12/5)でラベル付けしたノードを確認

※ノード名は環境に合わせて読み替えてください

# ラベル確認
kubectl get node talos-worker-3 --show-labels | grep monitoring

# Taint確認
kubectl describe node talos-worker-3 | grep Taints
# Taints: dedicated=monitoring:NoSchedule

3. Helmでのインストール

3.1 Helm Repositoryの追加

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

3.2 values.yamlの作成

実際に使用した設定ファイル

# kubernetes/infrastructure/monitoring/values.yaml
# kube-prometheus-stack Helm values
# 監視専用ノード(talos-worker-3)への配置設定付き

commonLabels:
  app.kubernetes.io/part-of: homelab-infra

# =============================================================================
# Prometheus
# =============================================================================
prometheus:
  prometheusSpec:
    # 監視専用ノードに配置
    nodeSelector:
      node-role.kubernetes.io/monitoring: "true"
    tolerations:
      - key: dedicated
        operator: Equal
        value: monitoring
        effect: NoSchedule

    # リソース制限(homelab向け)
    resources:
      requests:
        cpu: 200m
        memory: 512Mi
      limits:
        cpu: 1000m
        memory: 2Gi

    # データ保持期間
    retention: 15d
    retentionSize: "10GB"

    # ストレージ設定(StorageClass がないため emptyDir を使用)
    # Longhorn導入後にPVCに変更可能
    storageSpec: {}

    # 全Namespaceからメトリクス収集
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false

# =============================================================================
# Alertmanager
# =============================================================================
alertmanager:
  alertmanagerSpec:
    nodeSelector:
      node-role.kubernetes.io/monitoring: "true"
    tolerations:
      - key: dedicated
        operator: Equal
        value: monitoring
        effect: NoSchedule

    resources:
      requests:
        cpu: 50m
        memory: 64Mi
      limits:
        cpu: 200m
        memory: 256Mi

  # AlertManager設定
  config:
    global:
      resolve_timeout: 5m

    route:
      receiver: discord
      group_by: ['alertname', 'namespace']
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 4h
      routes:
        # Critical は即座に通知
        - match:
            severity: critical
          receiver: discord
          group_wait: 10s
          repeat_interval: 1h
        # Warning は通常通知
        - match:
            severity: warning
          receiver: discord

    receivers:
      - name: discord
        webhook_configs:
          - url: "YOUR_DISCORD_WEBHOOK_URL"  # ← 実際のURLに置き換え
            send_resolved: true

    inhibit_rules:
      - source_match:
          severity: critical
        target_match:
          severity: warning
        equal: ['alertname', 'namespace']

# =============================================================================
# Grafana
# =============================================================================
grafana:
  nodeSelector:
    node-role.kubernetes.io/monitoring: "true"
  tolerations:
    - key: dedicated
      operator: Equal
      value: monitoring
      effect: NoSchedule

  resources:
    requests:
      cpu: 100m
      memory: 128Mi
    limits:
      cpu: 500m
      memory: 512Mi

  # 管理者パスワード(本番ではSecret化推奨)
  adminPassword: "admin"  # TODO: 変更してください

  # Ingress設定
  ingress:
    enabled: true
    ingressClassName: nginx
    hosts:
      - grafana.homelab.local

  # サイドカー設定(ConfigMap からダッシュボード自動読み込み)
  sidecar:
    dashboards:
      enabled: true
      label: grafana_dashboard
      searchNamespace: ALL
    datasources:
      enabled: true
      label: grafana_datasource

# =============================================================================
# Node Exporter(全ノードで実行 - DaemonSet)
# =============================================================================
nodeExporter:
  enabled: true

# =============================================================================
# kube-state-metrics
# =============================================================================
kubeStateMetrics:
  enabled: true

# =============================================================================
# Prometheus Operator
# =============================================================================
prometheusOperator:
  nodeSelector:
    node-role.kubernetes.io/monitoring: "true"
  tolerations:
    - key: dedicated
      operator: Equal
      value: monitoring
      effect: NoSchedule

  resources:
    requests:
      cpu: 100m
      memory: 128Mi
    limits:
      cpu: 200m
      memory: 256Mi

# =============================================================================
# 追加のアラートルール
# =============================================================================
additionalPrometheusRulesMap:
  homelab-rules:
    groups:
      - name: homelab-node-alerts
        rules:
          # ノードダウン検知
          - alert: NodeDown
            expr: up{job="node-exporter"} == 0
            for: 2m
            labels:
              severity: critical
            annotations:
              summary: "Node {{ $labels.instance }} is down"
              description: "Node {{ $labels.instance }} has been down for more than 2 minutes."

          # CPU使用率 > 85%
          - alert: HighCpuUsage
            expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
            for: 10m
            labels:
              severity: warning
            annotations:
              summary: "High CPU usage on {{ $labels.instance }}"
              description: "CPU usage is above 85% (current: {{ $value }}%)"

          # メモリ使用率 > 85%
          - alert: HighMemoryUsage
            expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
            for: 5m
            labels:
              severity: warning
            annotations:
              summary: "High memory usage on {{ $labels.instance }}"
              description: "Memory usage is above 85% (current: {{ $value }}%)"

          # ディスク使用率 > 85%
          - alert: HighDiskUsage
            expr: (1 - (node_filesystem_avail_bytes{fstype!="tmpfs"} / node_filesystem_size_bytes{fstype!="tmpfs"})) * 100 > 85
            for: 15m
            labels:
              severity: warning
            annotations:
              summary: "High disk usage on {{ $labels.instance }}"
              description: "Disk usage is above 85% on {{ $labels.mountpoint }} (current: {{ $value }}%)"

      - name: homelab-kubernetes-alerts
        rules:
          # Pod CrashLoopBackOff
          - alert: PodCrashLoopBackOff
            expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
            for: 5m
            labels:
              severity: warning
            annotations:
              summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is in CrashLoopBackOff"
              description: "Pod has restarted {{ $value }} times in the last hour"

          # Pod Pending 状態が続く(10分以上)
          - alert: PodPendingTooLong
            expr: kube_pod_status_phase{phase="Pending"} == 1
            for: 10m
            labels:
              severity: warning
            annotations:
              summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is Pending"
              description: "Pod has been in Pending state for more than 10 minutes"

      - name: homelab-lb-alerts
        rules:
          # MetalLB Speaker Pod停止(4台中2台以上停止でCritical)
          - alert: MetalLBSpeakerDown
            expr: count(up{job="metallb-speaker"} == 1) < 3
            for: 2m
            labels:
              severity: critical
            annotations:
              summary: "MetalLB Speaker pods are down"
              description: "Only {{ $value }} MetalLB Speaker pods running"

          # Ingress Controller停止
          - alert: IngressControllerDown
            expr: kube_deployment_status_replicas_ready{deployment="ingress-nginx-controller"} == 0
            for: 2m
            labels:
              severity: critical
            annotations:
              summary: "Ingress Controller is down"
              description: "No ready replicas for ingress-nginx-controller"

3.3 Namespaceの作成とPodSecurity設定

Node Exporterはホストネットワークを使用するため、privileged設定が必要

# Namespace作成
kubectl create namespace monitoring

# PodSecurityをprivilegedに設定(Node Exporter用)
kubectl label namespace monitoring pod-security.kubernetes.io/enforce=privileged --overwrite

3.4 Helmインストール

helm install kube-prometheus prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --values kubernetes/infrastructure/monitoring/values.yaml

3.5 デプロイ確認

kubectl get pods -n monitoring -o wide

出力例:

NAME                                                     READY   STATUS    NODE
alertmanager-kube-prometheus-kube-prome-alertmanager-0   2/2     Running   talos-worker-3
kube-prometheus-grafana-xxx                              3/3     Running   talos-worker-3
kube-prometheus-kube-prome-operator-xxx                  1/1     Running   talos-worker-3
kube-prometheus-kube-state-metrics-xxx                   1/1     Running   talos-worker-2
kube-prometheus-prometheus-node-exporter-xxx             1/1     Running   (各ノード)
prometheus-kube-prometheus-kube-prome-prometheus-0       2/2     Running   talos-worker-3

ポイント: Prometheus, Grafana, AlertManager, Operatorが監視専用ノード(talos-worker-3)に配置されていることを確認。


4. Ingress経由での外部アクセス設定

4.1 Prometheus/AlertManager用Ingressの作成

Grafanaはvalues.yamlでIngressを有効化済み。PrometheusとAlertManager用に追加のIngressを作成。

# kubernetes/infrastructure/monitoring/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: prometheus
  namespace: monitoring
  labels:
    app.kubernetes.io/part-of: homelab-infra
spec:
  ingressClassName: nginx
  rules:
    - host: prometheus.homelab.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: kube-prometheus-kube-prome-prometheus
                port:
                  number: 9090
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: alertmanager
  namespace: monitoring
  labels:
    app.kubernetes.io/part-of: homelab-infra
spec:
  ingressClassName: nginx
  rules:
    - host: alertmanager.homelab.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: kube-prometheus-kube-prome-alertmanager
                port:
                  number: 9093
kubectl apply -f kubernetes/infrastructure/monitoring/ingress.yaml

4.2 hostsファイル設定

MBPの/etc/hostsに追加

echo "10.0.0.210  grafana.homelab.local prometheus.homelab.local alertmanager.homelab.local" | sudo tee -a /etc/hosts

注: 10.0.0.210 はMetalLBで割り当てられたIngress ControllerのExternal IP。


4.3 アクセス確認

# 動作確認(HTTPコード308が戻り値でした)
curl -s -o /dev/null -w "%{http_code}" -H "Host: grafana.homelab.local" http://10.0.0.210

curl -s -o /dev/null -w "%{http_code}" -H "Host: prometheus.homelab.local" http://10.0.0.210

curl -s -o /dev/null -w "%{http_code}" -H "Host: alertmanager.homelab.local" http://10.0.0.210

ブラウザでアクセス

AlertManager画面

AlertManager

Prometheus画面

Prometheus


5. Grafanaダッシュボード

5.1 デフォルトで含まれるダッシュボード

kube-prometheus-stack には多数のダッシュボードがプリインストール

ダッシュボード 用途
Kubernetes / Compute Resources / Cluster クラスタ全体のリソース
Kubernetes / Compute Resources / Node ノード別リソース
Node Exporter / Nodes ノードの詳細メトリクス
Alertmanager / Overview アラート状況

5.2 ダッシュボードへのアクセス

  1. http://grafana.homelab.local にアクセス
  2. admin / admin でログイン
  3. 左メニュー → Dashboards → Browse
  4. 任意のダッシュボードを選択

Grafanaダッシュボード画面

Grafana


6. Discord通知のテスト

6.1 アラートの確認

Prometheusのアラート画面で設定したルールを確認:

open http://prometheus.homelab.local/alerts

6.2 テスト用の高負荷Podデプロイ

# high-cpu-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: high-cpu-test
  namespace: default
spec:
  containers:
  - name: stress
    image: polinux/stress
    command: ["stress"]
    args: ["--cpu", "2", "--timeout", "300s"]
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 200m
        memory: 256Mi
kubectl apply -f high-cpu-pod.yaml

約5分後、Discordにアラート通知が届きます。

Discord通知の例

Discord通知


7. トラブルシューティング

7.1 Prometheusが起動しない

症状: Pod が Pending のまま

確認事項:

# イベント確認
kubectl describe pod -n monitoring prometheus-kube-prometheus-kube-prome-prometheus-0

# よくある原因
# 1. PVC が Pending → storageSpec: {} で emptyDir を使用
# 2. NodeSelector が合わない → ラベル確認
# 3. Toleration が不足 → Taint 確認

7.2 Node Exporterが起動しない

症状: PodSecurity違反エラー

kubectl get events -n monitoring | grep node-exporter

解決策

kubectl label namespace monitoring pod-security.kubernetes.io/enforce=privileged --overwrite

7.3 Discord通知が届かない

確認事項

# Webhook URL の確認
kubectl get secret -n monitoring alertmanager-kube-prometheus-kube-prome-alertmanager \
  -o jsonpath='{.data.alertmanager\.yaml}' | base64 -d | grep url

# AlertManager ログ確認
kubectl logs -n monitoring alertmanager-kube-prometheus-kube-prome-alertmanager-0

# 手動テスト
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"content":"Test from AlertManager"}' \
  YOUR_DISCORD_WEBHOOK_URL

8. まとめ

本記事では、Kubernetesクラスタに本番を意識した監視スタックを構築しました。

達成したこと

項目 状態
kube-prometheus-stack デプロイ
監視専用ノードへの配置
Discord Webhook 連携
Ingress 経由の外部アクセス
カスタムアラートルール

Week 1 完了!

所感

  • 設定ファイルは相変わらず読みづらいね...でも通知できたのでよかったが監視設定などもう少し踏み込みたかったがボリューの都合で別で考えたい
  • kube-prometheus-stack は一発で監視基盤が揃うので便利。NMS/Nagios/Zabbixを個別にセットアップしていた時代を思うと隔世の感
  • リソース設定はとりあえずの値だが、ホームラボ規模では問題なく動作している
  • アラートのしきい値設計は昔の知識がベースになっているので、Kubernetes ネイティブな考え方(自己修復前提)も今後やりたいかな

9. 次回予告: TLS証明書管理

明日(12/7)は Week 1 最終回: cert-manager による TLS 証明書管理 です。

学べること:

  • cert-manager のインストールと設定
  • 自己署名証明書の発行
  • Ingress への TLS 適用

参考リンク

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?