本文へスキップ

CKAD 概念ノート: モニタリング、ログ、デバッグ

  • kubernetes
  • ckad
  • kubectl
  • logging
  • debugging

基準: Kubernetes v1.35 (kind kindest/node:v1.35.8 でコマンド・YAML を確認)

目次

概念

何を見ればいいか

見たいものコマンド出どころ
Pod の一覧と STATUS、RESTARTSkubectl get podsAPI server
コンテナの State、Reason、Restart Count、イベントkubectl describe pod <pod>API server (Pod + Event)
システムが持っている全情報kubectl get pod <pod> -o yamlAPI server
namespace のイベントkubectl events、kubectl get eventsEvent リソース
CPU/メモリの使用量kubectl top pod、kubectl top nodeMetrics API (metrics-server)
アプリケーションの出力kubectl logskubelet がノードに保存した stdout/stderr
コンテナの内部kubectl exec、kubectl debug実行中のコンテナ / 新しく付けたコンテナ

Pod phase と kubectl の STATUS は別の値

  • phase は API のフィールド。値は Pending、Running、Succeeded、Failed、Unknown の五つだけ
  • kubectl get pods の STATUS 列は人が見やすいように作った表示用の値。CrashLoopBackOff、Terminating、OOMKilled はここに出るが、phase の値ではない
  • 実際の原因はコンテナ単位で見る必要がある。コンテナの state は Waiting、Running、Terminated の三つで、Waiting と Terminated には Reason が付く

コンテナのログはどこにあるか

  • コンテナが stdout/stderr に書いた出力を container runtime が受け取り、kubelet がノードの /var/log/pods の下にファイルとして保存する
  • コンテナが再起動すると、kubelet はデフォルトで終了したコンテナ一つとそのログを残す → --previous で読む対象
  • Pod がノードから evict されると、コンテナとログも一緒に削除される
  • ログの rotation は kubelet の担当。containerLogMaxSize のデフォルトは 10Mi、containerLogMaxFiles のデフォルトは 5
  • kubectl logs は最新のログファイルだけを読む。40MiB 書いて 10MiB ごとに rotation されると、見えるのは最大 10MiB だけ
  • ファイルにしかログを書かないアプリだと kubectl logs には何も出ない。この場合、streaming sidecar がファイルを tail して自分の stdout に流すパターンが公式ドキュメントにある

ephemeral container

  • すでに作られた Pod に一時的に追加するコンテナ。トラブルシューティング専用
  • Pod spec に直接入れるのではなく、API の ephemeralcontainers ハンドラで追加する → kubectl edit では付けられない
  • 制約
    • ports、livenessProbe、readinessProbe は不可
    • resources は不可 (Pod のリソース割り当ては immutable)
    • 自動再起動なし、リソース・実行の保証なし
    • 一度追加すると変更・削除できない
    • static Pod では対応していない

動作の仕組み

よくある失敗状態の読み方

表示どこで見えるか意味まず見るもの
Pendingphaseスケジュール待ち、またはイメージのダウンロード中describe の Events。FailedScheduling ならリソース不足、nodeSelector の不一致、hostPort の衝突
ImagePullBackOffコンテナの Waiting Reasonイメージを取得できない。イメージ名の誤り、private registry なのに imagePullSecret がない、などイメージ名・タグ、registry へのアクセス
CrashLoopBackOffコンテナの Waiting Reason起動 → 終了を繰り返していて、再起動の back-off がかかった状態logs --previous、describe の Last State と Exit Code
OOMKilledコンテナの Terminated Reasonmemory limit を超えて kernel が終了させた。exitCode: 137resources.limits.memory、top pod

back-off のタイミング

  • コンテナの再起動: 10s、20s、40s … のように伸びていき、300 秒 (5 分) で止まる
  • 10 分間問題なく動けば back-off のタイマーはリセット
  • イメージの pull の再試行も同じように伸びていき、上限は 300 秒
  • だから直した直後でも、しばらく CrashLoopBackOff と表示されることがある。すぐ確認したいなら Pod を作り直すほうが早い

CrashLoopBackOff の原因 (公式ドキュメントのリスト)

  • アプリケーションのエラーで終了
  • 設定の誤り: 間違った環境変数、存在しない設定ファイル
  • メモリ・CPU の不足
  • liveness probe または startup probe の失敗

kubectl debug の三つのモード

kubectl debug <pod> --image=...               # 既存の Pod に ephemeral container を追加
kubectl debug <pod> --copy-to=<new> ...       # Pod をコピーして新しい Pod を作成 (元はそのまま)
kubectl debug node/<node> --image=...         # ノードにデバッグ用の Pod を作成
  • ephemeral container モード: --target=<container> で対象コンテナの process namespace を共有。runtime が対応していなければ、ps に対象のプロセスが見えないことがある
  • copy モード: 新しいコンテナの追加、--container で既存のコンテナの command の差し替え、--set-image でイメージの差し替えができる。--share-processes で Pod 内のコンテナ同士がプロセスを見られる
  • node モード: ノードの root filesystem が /host にマウントされ、host の IPC/Network/PID namespace を使う。privileged ではないので、必要なら --profile=sysadmin
  • --profile の値: legacy、general、baseline、restricted、netadmin、sysadmin。指定しなければ legacy が使われ、kubectl 1.35 はそのとき --profile=legacy is deprecated and will be removed in the future という警告とともに --profile=general を明示するよう案内する
  • -i を付けると新しいコンテナに自動で attach。接続が切れたら kubectl attach でつなぎ直す
  • --container を付けなければ、コンテナ名は自動生成 (debugger-xxxxx)

例

状態とイベント

kubectl get pods -o wide
kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'
kubectl describe pod <pod>

# イベントは namespace 単位
kubectl get events --sort-by='.lastTimestamp'
kubectl get events -n my-namespace
kubectl events --for pod/<pod> --watch
kubectl events --types=Warning

# メトリクス (metrics-server が必要)
kubectl top node
kubectl top pod --sort-by=memory
kubectl top pod <pod> --containers

終了の理由だけを取り出す

kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'
kubectl get pod <pod> -o go-template='{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}'
  • termination message は、コンテナが terminationMessagePath (デフォルト /dev/termination-log) に書いた内容
  • terminationMessagePolicy: FallbackToLogsOnError なら、ファイルが空でエラー終了したとき、ログの末尾 (2048 バイトか 80 行の小さいほう) を代わりに使う

ログ

kubectl logs <pod>
kubectl logs <pod> -c <container>          # multi-container の Pod
kubectl logs <pod> --previous              # 直前に終了したコンテナ (-p)
kubectl logs -f <pod>                      # ストリーミング
kubectl logs <pod> --all-containers=true
kubectl logs -l app=nginx --all-containers=true
kubectl logs deployment/nginx -c nginx-1
kubectl logs --tail=20 <pod>
kubectl logs --since=1h <pod>
kubectl logs <pod> --timestamps=true --prefix

streaming sidecar

公式ドキュメントの例を縮めた形。アプリはファイルにだけログを書き、sidecar がそのファイルを自分の stdout に流す。

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
    - name: count
      image: busybox:1.28
      args:
        - /bin/sh
        - -c
        - >
          i=0;
          while true;
          do
            echo "$i: $(date)" >> /var/log/1.log;
            i=$((i+1));
            sleep 1;
          done
      volumeMounts:
        - name: varlog
          mountPath: /var/log
    - name: count-log-1
      image: busybox:1.28
      args: [/bin/sh, -c, "tail -n+1 -F /var/log/1.log"]
      volumeMounts:
        - name: varlog
          mountPath: /var/log
  volumes:
    - name: varlog
      emptyDir: {}
kubectl logs counter count-log-1

exec

kubectl exec <pod> -- ls /
kubectl exec <pod> -c <container> -- cat /etc/config/app.conf
kubectl exec -it <pod> -- sh
  • -- の後ろがコンテナで実行するコマンド
  • イメージに sh がなければ executable file not found in $PATH で失敗 → kubectl debug に移る

debug

# shell のないイメージに ephemeral container を追加
kubectl run ephemeral-demo --image=registry.k8s.io/pause:3.1 --restart=Never
kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo
kubectl describe pod ephemeral-demo        # "Ephemeral Containers:" の項目に追加される

# 起動してすぐ死ぬコンテナ: コピーで command を shell に差し替える
kubectl run --image=busybox:1.28 myapp -- false
kubectl debug myapp -it --copy-to=myapp-debug --container=myapp -- sh

# コピーにデバッグツールのコンテナを追加 + プロセス共有
kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug

# コピーのすべてのコンテナのイメージを差し替え
kubectl debug myapp --copy-to=myapp-debug --set-image=*=ubuntu

# ノード
kubectl debug node/mynode -it --image=ubuntu

# 後片付け
kubectl delete pod myapp myapp-debug

紛らわしいものの比較

exec、ephemeral container、copy

kubectl execkubectl debug <pod> --imagekubectl debug <pod> --copy-to
対象実行中の既存のコンテナ既存の Pod に新しいコンテナを追加新しい Pod
イメージに shell が必要必要不要 (デバッグ用イメージを使う)不要
コンテナが死んでいても不可可能可能 (command の差し替え)
元の Pod の変更なしephemeral container が残る (削除不可)なし
後片付けなしPod を消すまで残るコピーした Pod を削除

—target と —share-processes

--target=<container>--share-processes
使うモードephemeral container--copy-to
範囲指定したコンテナ一つの process namespaceコピーした Pod のすべてのコンテナ
条件container runtime の対応が必要Pod の設定で適用

logs のオプション

オプション意味注意
-cコンテナを指定コンテナが一つなら省略できる
-p、--previous直前に終了したインスタンスkubelet が残しておいた終了済みのコンテナ一つが対象
-fストリーミング終えるには Ctrl+C
--all-containersすべてのコンテナ-l と一緒に使うと複数の Pod のすべてのコンテナ
-llabel selector同時リクエストは --max-log-requests でデフォルト 5 個まで

get events と events

kubectl get eventskubectl events
時系列の並び--sort-by='.lastTimestamp'デフォルトの出力が最近のイベントの一覧
特定のリソースだけdescribe の Events で代わりに見る--for pod/<name>
タイプで絞る---types=Warning
見続ける--watch--watch

試験のポイント

まず打つコマンド

kubectl get pods -A | grep -v Running
kubectl describe pod <pod> | grep -A10 -E 'State|Events'
kubectl logs <pod> -c <container> --previous
kubectl get events -n <ns> --sort-by='.lastTimestamp'
  • Pending は logs を見ても意味がない。コンテナが起動前なのでログがない。describe の Events から
  • CrashLoopBackOff は今のコンテナが立ち上がってすぐ死んでいる途中なので、logs が空のことがある。--previous
  • OOMKilled は describe の Last State の Reason と Exit Code 137、そして resources.limits.memory を一緒に見る
  • イベントは namespace 単位。-n を付けないと default namespace しか見ない
  • kubectl top がエラーを出したら、コマンドより先に Metrics API (metrics-server) がデプロイされているかを疑う

kubectl explain

kubectl explain pod.status.containerStatuses.state            # waiting / running / terminated
kubectl explain pod.status.containerStatuses.lastState        # 再起動前の終了 reason、exitCode
kubectl explain pod.spec.containers.terminationMessagePolicy  # File / FallbackToLogsOnError
kubectl explain pod.spec.ephemeralContainers                  # kubectl debug が追加する container
kubectl explain event                                         # reason、involvedObject、type

参考ドキュメント