目次

はじめに
本書の目的
対象読者
本書の流れ
本書の想定環境
表記関係について
免責事項
第1章 Kubernetesの基本コマンドと調査ツール
1.1 kubectlコマンド
1.2 ネットワークパケットのキャプチャーツール
第2章 アプリケーションPodのトラブルシューティング
2.1 kubectl describe podコマンドの結果を確認する
2.2 [トラブル] Podが正常に起動しない
2.3 [トラブル] コンテナーイメージのPullが成功しない
2.4 [トラブル] Podがノードにスケジューリングされない
2.5 [トラブル] Pod内で稼働中のコンテナーが再起動する
2.6 [トラブル] Deploymentによるデプロイが成功しないときのトラブルシューティング
第3章 ネットワークのトラブルシューティング
3.1 [トラブル] Serviceで公開したアプリケーションにアクセスできない
3.2 [トラブル] ServiceにIPアドレスが割り当てられない
3.3 [トラブル] Ingressで公開したアプリケーションにアクセスできない
3.4 パケットキャプチャーによるネットワークの調査
3.5 クラスター内DNSサービスのトラブルシューティング
第4章 ストレージのトラブルシューティング
4.1 kubectl describe podコマンドの結果を確認する
4.2 kubectl describe pv / pvcコマンドの結果を確認する
4.3 [トラブル] PersistentVolumeClaim <PVC名> Not found で Pod が起動しない
4.4 [トラブル] Podとディスクの存在するゾーンが異なりPodが起動しない
4.5 [トラブル] FailedMount / timed out waiting for the condition のエラーが発生する
4.6 [トラブル] Multi-Attachエラーが発生する
第5章 Kubernetesクラスター/ノードのトラブルシューティング
5.1 ノードが不調な場合の暫定対処の方法
5.2 [トラブル] ノードのドレインが進行しない
5.3 [トラブル] ノードがNotReadyステータスになる
5.4 [トラブル] ノードのディスク使用量が増加する
5.5 [トラブル] API VersionのWarningが表示される
付録A 困ったときは - オンラインの情報源とコミュニティー

はじめに

 本書を手に取っていただき、ありがとうございます。この本は、Kubernetesのトラブルシューティングについて解説する本です。

本書の目的

 近年、Kubernetesはコンテナーのオーケストレーション環境としてすっかり定着しました。マネージドのKubernetesサービスも発展し、業務利用する機会も増えてきたのではないでしょうか。アプリケーションの開発においても、独自のコンテナーイメージをビルドして、Kubernetesクラスターにデプロイすることが一般的になりました。しかしながら、ひとたびトラブルが発生すると、その対処方法に困ってしまう方もいるのではないでしょうか。

 本書はKubernetesで起こりうるトラブル事例と対処方法を解説する本です。豊富なコマンド例と図解を掲載し、エラーメッセージの読み解き方と対処方法を身につけられる内容を目指しています。

 Kubernetesは、オンプレミスをはじめとして、パブリッククラウドの各社サービスにおいても広く利用されています。本書では、クラスターが存在するクラウド環境固有のトラブルシューティング方法はなるべく掲載せず、Kubernetesそのものがもつ機能や出力されるメッセージを活用して、各種トラブルへ対処する事例を紹介します。

対象読者

 ・Kubernetesの運用をはじめて1、2年の初中級者の方

 ・Kubernetesの出力するエラーやステータスメッセージを理解したい方

 ・PodやService、ノードなどの問題発見・解決をしたい方

 ・パケットキャプチャーを使用したネットワークのデバッグができるようになりたい方

本書の流れ

 本書は次の流れで、Kubernetesのトラブルシューティングを解説していきます。

 第1章では、Kubernetesの基本コマンドと、調査で使用するツールについて紹介します。以降の章で紹介するトラブルシューティングの方法では、第1章で紹介をしたコマンドやツール使用します。

 第2章では、アプリケーションPodに関するトラブルシューティング事例を解説します。

 第3章では、ネットワークに関するトラブルシューティング事例を解説します。

 第4章では、ストレージに関するトラブルシューティング事例を解説します。

 第5章では、Kubernetesクラスターやノードに関するトラブルシューティング事例を解説します。

 本書では次の項目は扱いません。あらかじめご了承ください。

 ・Kubernetes自体の使い方

 ・Kubernetes以外のコンテナー技術

 ・クラウドプロバイダーの機能に固有のトラブルシューティング方法

本書の想定環境

 本書では、主にminikube1で構築したKubernetesクラスターを使用して解説をします。Kubernetesのバージョンは1.25を使用しています。

 また本書の一部には、パブリッククラウドのマネージドKubernetesサービスを想定した解説がございます。マネージドKubernetesサービスの例として、Azure Kubernetes Service(AKS)を使用しています。

表記関係について

 本書に記載されている会社名、製品名などは、一般に各社の登録商標または商標、商品名です。会社名、製品名については、本文中では©、®、™の表示は省略しています。

免責事項

 本書に記載の内容は、すべて情報提供を目的として書かれています。正確な情報を記載するよう努めていますが、必ずしもすべて正確であることを保証するものではありません。本書の内容をもとに検証や開発、運用する際は、オフィシャルなサイトなど他の情報源も参考にされたうえで、ご自身の判断と責任によって行ってください。本書の情報をもとに実施した結果について、筆者はいかなる責任も負いません。

第1章 Kubernetesの基本コマンドと調査ツール

 本章では、Kubernetesの基本コマンドと、調査に使用するツールについて紹介します。以降の章で紹介するトラブルシューティングの方法では、本章で紹介をしたコマンドやツール使用します。

1.1 kubectlコマンド

 kubectl CLIツールでKubernetesクラスターを操作できます。Kubernetesオブジェクトのステータスを確認できるので、トラブルシューティングの際に役立ちます。ここではkubectlコマンドの使い方と、トラブルシューティング時に確認するポイントを解説します。

1.1.1 kubectl get

 kubectl getコマンドで、PodやNode、Serviceなどのリソース情報を取得できます。ここでは、Podを例に説明します。

 次のコマンド例は、Podの情報を取得した様子です。READYにはPodに含まれるコンテナー数(/の右側)と、現在正常に起動しているコンテナー数(/の左側)が表示されます。STATUSには、Podの現在のステータスが表示されます。Podが正常に動作している場合には、Runningと表示されます。RESTARTSはPod内のコンテナーが再起動した回数です。再起動が繰り返されて、回数が増加していないかを確認します。

$ kubectl get pod

NAME                     READY   STATUS    RESTARTS       AGE

nginx-6799fc88d8-h5vcs   1/1     Running   1 (6m4s ago)   153d

 コマンドのオプションで-o wideを指定すると、Podがデプロイされているノード名と、PodのIPアドレスが表示されます。

$ kubectl get pod -o wide

NAME                     READY   STATUS    RESTARTS      AGE    IP           NODE

nginx-6799fc88d8-h5vcs   1/1     Running   1 (19m ago)   153d   172.17.0.3   minikube

 -oオプションでyamlやjsonを指定すると、指定したフォーマットでKubernetesオブジェクトの情報を取得できます。Podが起動しない際に、マニフェストの書き方に問題がないかなど確認できます。次のコマンド例は、Podの情報をYAML形式で取得した例です。

$ kubectl get pod nginx-6799fc88d8-h5vcs -o yaml

apiVersion: v1

kind: Pod

metadata:

  creationTimestamp: "2021-11-26T12:00:55Z"

  generateName: nginx-6799fc88d8-

  labels:

    app: nginx

    pod-template-hash: 6799fc88d8

  name: nginx-6799fc88d8-h5vcs

  namespace: default

  ownerReferences:

  - apiVersion: apps/v1

    blockOwnerDeletion: true

    controller: true

    kind: ReplicaSet

    name: nginx-6799fc88d8

    uid: 592939c6-27fc-458b-aa9d-b8d060649314

  resourceVersion: "690751"

  uid: a8bf7cd8-4770-4dbc-91bf-f40ded9733fc

spec:

  containers:

  - image: nginx

    imagePullPolicy: Always

    name: nginx

    resources: {}

    terminationMessagePath: /dev/termination-log

    terminationMessagePolicy: File

    volumeMounts:

    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount

      name: kube-api-access-2p762

      readOnly: true

  ...

1.1.2 kubectl describe

 kubectl describeコマンドは、Kubernetesオブジェクトの詳細情報を表示します。特に、トラブルシューティングの際には、一番下に表示されるEventsフィールドを確認して、エラーメッセージが出ていないかを確認します。

 kubectl getコマンドと同様に、Pod以外にも、ServiceやNodeなどの他のオブジェクトの情報も取得できます。ここでは、Podを例にkubectl describeコマンドの出力を紹介します。次のコマンド例は、Podの詳細情報を表示した例です。コマンド結果の上部では、Pod名やIPアドレス、Podが割り当てられたノード名など、Podの基本情報が表示されます。

$ kubectl describe pod nginx-6799fc88d8-h5vcs

Name:         nginx-6799fc88d8-h5vcs

Namespace:    default

Priority:     0

Node:         minikube/192.168.49.2

Start Time:   Fri, 26 Nov 2021 21:00:55 +0900

Labels:       app=nginx

              pod-template-hash=6799fc88d8

Annotations:  <none>

Status:       Running

IP:           172.17.0.3

IPs:

  IP:           172.17.0.3

Controlled By:  ReplicaSet/nginx-6799fc88d8

 Containersフィールドには、Pod内のコンテナーの情報が出力されます。使用されているコンテナーイメージの情報や、コンテナーのステータスが変化した際の状態やタイムスタンプが確認できます。

Containers:

  nginx:

    Container ID:   docker://1129d2e9968a08b0badb399ab73bef1f7c7b3cf8c7e15396372b153b136f0b23

    Image:          nginx

    Image ID:       docker-pullable://nginx@sha256:859ab6768a6f26a79bc42b231664111317d095a4f04e4b6fe79ce37b3d199097

    Port:           <none>

    Host Port:      <none>

    State:          Running

      Started:      Fri, 29 Apr 2022 14:37:08 +0900

    Last State:     Terminated

      Reason:       Error

      Exit Code:    255

      Started:      Fri, 26 Nov 2021 21:01:12 +0900

      Finished:     Fri, 29 Apr 2022 14:36:45 +0900

    Ready:          True

    Restart Count:  1

    Environment:    <none>

    Mounts:

      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2p762 (ro)

 Conditionsフィールドには、Podの状態が表示されます。すべての項目がTrueになっていれば、Podは正常です。

Conditions:

  Type              Status

  Initialized       True

  Ready             True

  ContainersReady   True

  PodScheduled      True

 Volumesフィールドには、Podがマウントしているボリュームの情報が表示されます。

Volumes:

  kube-api-access-2p762:

    Type:                    Projected (a volume that contains injected data from multiple sources)

    TokenExpirationSeconds:  3607

    ConfigMapName:           kube-root-ca.crt

    ConfigMapOptional:       <nil>

    DownwardAPI:             true

 そのほか、次に示す出力例では、Podのリソースの設定やスケジューリングの設定をするフィールドが表示されています。

 QoS Classフィールドには、Podのスケジューリングおよび退役を決定するためのQoSクラスが設定されます。QoSクラスはユーザーが指定したコンテナーリソースのlimitsとrequestsの値に応じて、自動的に設定されます。

 Node-Selectorsフィールドには、nodeSelectorによって、Podをどのノード上に起動させるか選択するためのkey-valueペアのマップが表示されます。今回はnodeSelectorを使用していないので、<none>の値です。

 Tolerationsフィールドには、Podに設定されているTolerationが表示されます。今回ではノードにnode.kubernetes.io/not-readyとnode.kubernetes.io/unreachableのTaintが追加されたときに、Podは300秒間ノードに残存し、その後排除されるよう設定されています。TaintとTolerationについては、第2.4章「[トラブル]Podがノードにスケジューリングされない」を参照してください。

QoS Class:                   BestEffort

Node-Selectors:              <none>

Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s

                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s

 kubectl describeコマンドで最後に出力されるフィールドが、Eventsです。クラスター内のリソースに関連して何らかのエラーが発生した場合には、Eventsフィールドにエラーメッセージが出力されます。そのため、トラブルシューティングの際にはEventsフィールドの情報を活用します。

Events:

  Type    Reason          Age   From     Message

  ----    ------          ----  ----     -------

  Normal  SandboxChanged  33m   kubelet  Pod sandbox changed, it will be killed and re-created.

  Normal  Pulling         33m   kubelet  Pulling image "nginx"

  Normal  Pulled          32m   kubelet  Successfully pulled image "nginx" in 9.103530004s

  Normal  Created         32m   kubelet  Created container nginx

  Normal  Started         32m   kubelet  Started container nginx

1.1.3 kubectl logs

 kubectl logsコマンドで、Pod内のコンテナーのログが確認できます。Podに複数のコンテナーがある場合は、-cオプションでログを取得するコンテナー名を指定します。

 次のコマンド例は、nginx Podのログを表示した例です。プロセスが開始された際のログが確認できます。

$ kubectl logs nginx-6799fc88d8-h5vcs

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration

/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/

/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh

10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf

10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf

/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh

/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh

/docker-entrypoint.sh: Configuration complete; ready for start up

2022/04/29 05:37:08 [notice] 1#1: using the "epoll" event method

2022/04/29 05:37:08 [notice] 1#1: nginx/1.21.6

2022/04/29 05:37:08 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)

2022/04/29 05:37:08 [notice] 1#1: OS: Linux 5.10.47-linuxkit

2022/04/29 05:37:08 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576

2022/04/29 05:37:08 [notice] 1#1: start worker processes

2022/04/29 05:37:08 [notice] 1#1: start worker process 32

 次のコマンド例は、yatteiki-webapp Podに内包されたyatteiki-catlogコンテナーのログをストリームで表示した例です。-cオプションで、yatteiki-catlogコンテナー名を指定してコンテナーのログを表示しています。-fオプションで、ログをストリームで標準出力に表示できます。次の例では、コンテナーログに出力されたアクセスログがストリームで標準出力に表示されていることが確認できます。

$ kubectl logs yatteiki-webapp -c yatteiki-catlog -f

10.244.0.17 - - [20/Oct/2022:21:48:53 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.81.0" "-"

10.244.0.17 - - [20/Oct/2022:21:48:53 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.81.0" "

 -pオプションを付与することで、前回実行されたコンテナーのログを取得できます。Pod内のコンテナーが再起動した場合や、その後CrashLoopBackOffステータスになった際には、-pオプションを付与することで前回終了したコンテナーのログが確認できます。次のコマンド例は、RESTARTSカウントが1回のPodで、現在実行されているコンテナーのログと、再起動前に実行されていたコンテナーのログを取得した例です。

$ kubectl get pod yatteiki-pod -n yatteiki

NAME                            READY   STATUS             RESTARTS       AGE

yatteiki-pod                    1/1     Running            1 (12s ago)    42s


# 現在実行されているコンテナーのログを取得

$ kubectl logs logs yatteiki-pod

Yatteiki Start


# -p オプションを付与し、前回実行されたコンテナーのログを取得

$ kubectl logs yatteiki-pod -p

Yatteiki Start

Yatteiki 待機中 1 回目

Yatteiki 待機中 2 回目

 ...

Yatteiki 待機中 9 回目

Yatteiki 待機中 10 回目

Yatteiki Failed

1.1.4 kubectl exec

 kubectl execコマンドは、Pod内のコンテナーに対してコマンドを実行します。コンテナー内でLinuxコマンドを実行して、コンテナーの動作や外部へのネットワーク通信のデバッグが可能です。

 次のコマンド例は、Pod内のコンテナーで /bin/bashを実行してシェルを取得している様子です。-itオプションを付与することで、Podをインタラクティブシェルとして実行しています。その後、パケットキャプチャーをするためのtcpdumpツールをインストールして実行しています。

$ kubectl exec -it nginx-6799fc88d8-h5vcs -- /bin/bash

root@nginx-6799fc88d8-h5vcs:/# apt-get update && apt-get install -y tcpdump

root@nginx-6799fc88d8-h5vcs:/# tcpdump -tttt -l -i eth0 -A -n -s 0 dst port 33424

 また、複数のコンテナーがあるPodの場合、-cオプションを付与することで、特定のコンテナーに対してコマンドを実行できます。

$ kubectl exec yatteiki-webapp -c yatteiki-catlog -- cat /var/log/nginx/access.log

10.244.0.17 - - [20/Oct/2022:21:48:53 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.81.0" "-"

1.1.5 kubectl run

 kubectl runコマンドは、コンテナーイメージを指定して新しいPodを作成します。トラブルシューティングでは、PodやServiceの動作を確認するために、クラスター内にデバッグ用のPodをデプロイする際に使用します。

 次のコマンド例は、busyboxイメージを使用してbusybox Podを作成した例です。-itオプションを付与することで、Podをインタラクティブシェルとして実行しています。

$ kubectl run -it --rm busybox --image=busybox

If you don't see a command prompt, try pressing enter.

/ #

 また、-rmオプションを付与しているため、シェルを終了すると、Podは自動的に削除されます。

/ # exit

Session ended, resume using 'kubectl attach busybox -c busybox -i -t' command when the pod is running

pod "busybox" deleted

 kubectl runコマンドで作成したPodでは、コンテナーイメージで設定されている既定のアプリケーションやコマンドが起動します。そのため、実行したいコマンドやシェルがある場合には、コンテナー起動時に実行するコマンドを指定する必要があります。--commandオプションを付与することで、コンテナー起動時に実行するコマンドを設定できます。kubectl runコマンドでシェルを実行する際は、--commandオプションに/bin/bashを指定することで、コンテナー起動時に/bin/bashが実行され、シェルを利用できます。/bin/shなど、bash以外のシェルやコマンドも指定可能です。

$ kubectl run -it yatteiki-run --image=nginx --rm --restart=Never --command -- /bin/bash

If you don't see a command prompt, try pressing enter.

root@yatteiki-run:/#

1.1.6 kubectl top

 kubectl topコマンドは、Podやノードが使用している現在のリソース量(CPU、メモリー)を表示します。

 次のコマンド例は、kubectl top podコマンドでPodが現在使用しているリソース量を取得した様子です。

$ kubectl top pod nginx-6799fc88d8-h5vcs

NAMESPACE     NAME                               CPU(cores)   MEMORY(bytes)

default       nginx-6799fc88d8-h5vcs             3m           114Mi

 同様に、ノードで使用されているリソース量はkubectl top nodesコマンドで取得できます。

$ kubectl top nodes

NAME                                CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%

aks-nodepool1-20356098-vmss000000   132m         6%     2491Mi          54%

aks-nodepool1-20356098-vmss000001   136m         7%     2471Mi          54%

1.1.7 kubectl port-forward

 PodやServiceに割り当てられたIPアドレス(ClusterIP)には、クラスターの外から直接アクセスできません。kubectl port-forwardコマンドは、クラスター上のPodやServiceへアクセスする際に使用するコマンドです。ローカルマシンのポートをPodやServiceのポートに転送します。

 次のコマンド例では、ローカルマシンの8080番ポートをnginx Podの80番ポートに転送しています。

$ kubectl port-forward nginx-6799fc88d8-h5vcs 8080:80

Forwarding from 127.0.0.1:8080 -> 80

Forwarding from [::1]:8080 -> 80

 ローカルマシンの8080番ポートにアクセスをすると、Podから返されたレスポンスが確認できます。

$ curl -I 127.0.0.1:8080

Handling connection for 8080

HTTP/1.1 200 OK

Server: nginx/1.21.6

Date: Fri, 29 Apr 2022 08:16:46 GMT

Content-Type: text/html

Content-Length: 615

Last-Modified: Tue, 25 Jan 2022 15:03:52 GMT

Connection: keep-alive

ETag: "61f01158-267"

Accept-Ranges: bytes

 kubectl port-forwardコマンドを利用することで、クラスターにデバッグ用のPodをデプロイせずに、ローカルマシンの環境からアプリケーションPodやServiceにアクセスできます。また、コマンドを利用したアクセスだけでなく、ローカルマシンのブラウザー上から動作確認することも可能になります。

1.1.8 kubectl debug

 kubectl debugコマンドは、Kubernetesクラスターのノードへアクセスしたいときに使用します。主にメンテナンスやログ収集、その他のトラブルシューティング時の操作で使用します。コマンドを実行すると、ノード上に特権コンテナーがデプロイされ、シェルに接続されます。

$ kubectl debug node/minikube -it --image=busybox

Creating debugging pod node-debugger-minikube-jvgkd with container debugger on node minikube.

If you don't see a command prompt, try pressing enter.

/

/ # ls

bin   dev   etc   home  host  proc  root  sys   tmp   usr   var

 特権コンテナー上でchroot /hostコマンドを実行すると、ノード上のファイルシステムすべてにアクセスができます。

/ # chroot /host

root@minikube:/# ls

Release.key  bin  boot  data  dev  docker.key  etc  home  kic.txt  kind  lib  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

 journalctlコマンドで、kubeletやコンテナーランタイムのログが確認できます。Podが正常に起動しないときなどに確認します。詳細は第5.3.1章「ノードのステータスとログの確認方法」をご参照ください。

root@minikube:~# journalctl -u kubelet -o cat

Started kubelet: The Kubernetes Node Agent.

  ...

I1105 03:29:57.117002    1414 server.go:413] "Kubelet version" kubeletVersion="v1.25.3"

 その他に、kubectl debugコマンドは、実行中のPodをデバッグしたいとき1にも使用できます。

1.2 ネットワークパケットのキャプチャーツール

 ネットワークのトラブルシューティングでは、Podやノードを流れるネットワークパケットをキャプチャーして、調査に使用することがあります。本節では、特定のPod、特定のノード、すべてのノードからパケットをキャプチャーする方法をそれぞれ紹介します。

 キャプチャーファイルを分析する際には、IPアドレスの情報をもとに、通信元と通信先のPodやノードを特定します。そのため、キャプチャーを採取する時点でのリソースの一覧をkubectlコマンドで出力し、クラスター内で使用されているIPアドレスをあとから確認できるように記録しておきましょう。

# ノードの一覧を出力

$ kubectl get nodes -o wide > nodes.txt


# Podの一覧を出力

$ kubectl get pods -o wide -A > pods.txt


# Serviceの一覧を出力

$ kubectl get services -o wide -A > services.txt

 本節で紹介する方法により採取したキャプチャーファイルは、Wireshark2を使って開くことができます。

1.2.1 ksniffで特定のPodからパケットをキャプチャーする

 トラブルシューティングをする対象Podが特定できている場合は、そのPodを流れるパケットをキャプチャーして調査します。本項では、ツールを使用したPodのパケットキャプチャーの方法を紹介します。

 ksniff3は、Podのネットワークパケットをキャプチャーするツールです。このツールは、クラスターにパケットキャプチャー用のPodをデプロイして、指定したPodのパケットをキャプチャーします。

 ksniffの既定では、ツールを開始するとWiresharkが起動し、対象Podでキャプチャーされたパケットをリアルタイムで表示します。本項では、パケットのキャプチャー結果をファイルとして保存する方法を紹介します。

ksniffのインストール

 ksniffはkubectlのプラグインマネージャーであるKrewを使って、インストールできます。プラグインマネージャーのインストールは、KrewのユーザーガイドのInstallingページ4をご参照ください。

 プラグインマネージャーのインストールが完了したら、次のコマンドでksniffをインストールします。

$ kubectl krew install sniff

ksniffでパケットをキャプチャーする

 それでは、ksniffでパケットをキャプチャーしてみましょう。まずは、パケットキャプチャーを採取する対象のPod名を取得します。

$ kubectl get pods -n {ネームスペース}

 次のコマンドで、キャプチャーする対象Podが存在するネームスペースとPod名を指定して、パケットキャプチャーを開始します。キャプチャー結果はcapture.pcapファイルに出力されます。

$ kubectl sniff -n {ネームスペース} {Pod名} -p -o capture.pcap

 次のメッセージが表示されたことを確認します。これでキャプチャーが開始されました。対象Podへ通信をして、調査したい事象を何度か再現します。

INFO[0002] pod: 'ksniff-jvppg' created successfully on node: '{ノード名}'

INFO[0002] output file option specified, storing output in: 'capture.pcap'

INFO[0005] starting remote sniffing using privileged pod

INFO[0002] executing command: '[/bin/sh -c

  ...

 キャプチャーを停止するには、Ctrl + Cキーを押下し、実行したkubectl sniffコマンドを終了します。キャプチャー結果はcapture.pcapファイルに出力されています。

 最後に、ksniffによりデプロイされたキャプチャー用Podを削除して完了です。

$ kubectl get pods

NAME                READY   STATUS      RESTARTS   AGE

ksniff-jvppg        1/1     Running     0          86s

  ...


$ kubectl delete pod ksniff-jvppg

1.2.2 tcpdumpで特定のノードからパケットをキャプチャーする

 調査対象のPodが同じノードで複数稼働している場合や、リクエストがノード自体に到達しているかどうかを確認するためには、ワーカーノードから採取したパケットキャプチャーの調査が有効です。

 本節では、tcpdumpツールを使用して、ワーカーノードからパケットをキャプチャーする方法を紹介します。

ノードにアクセスする

 はじめに、kubectl get nodesコマンドを使用して、パケットをキャプチャーするノードの名前を確認します。

$ kubectl get nodes -o wide

 kubectl debugコマンドを使用して、対象のノードにアクセスをします。このコマンドを実行すると、対象のノードにデバッグ用Podがデプロイされ、特権コンテナーが起動します。コンテナーの起動が完了すると、シェルに接続されます。

$ kubectl debug node/{ノード名} -it --image=ubuntu

Creating debugging pod node-debugger-{ノード名}-vsnwf with container debugger on node {ノード名}.

If you don't see a command prompt, try pressing enter.

root@{ノード名}:/#

 シェルが利用できるようになったら、次のようにchroot /hostコマンドを実行します。これにより、ノード上のコマンドが利用可能となります。

root@{ノード名}:/# chroot /host

#

tcpdumpでパケットをキャプチャーする

 ノードへの接続を確立したら、tcpdumpツールがインストールされていることを確認します。ツールがインストールされている場合には、次のようなメッセージが表示されます。

# tcpdump --version

tcpdump version 4.9.3

libpcap version 1.8.1

OpenSSL 1.1.1  11 Sep 2018

 command not foundエラーとなる場合には、tcpdumpをインストールします。Ubuntu/Debian系のLinux OSでは、次のコマンドでインストールできます。

# apt-get update && apt-get install tcpdump

 パケットキャプチャーを開始するには、次のコマンドを実行します。「Got」の表示のあとにキャプチャーされたパケット数が表示され、カウントが増えていることを確認します。

# tcpdump -i any -s 0 -vvv -w /capture.pcap

tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes

Got 371

 コマンド例で使用しているtcpdumpのオプションの意味は、次の表のとおりです。

オプション 説明
-i パケットをキャプチャーするネットワークインターフェースを指定します。
ここでは any を指定し、Podごとに作成されるネットワークインターフェースもキャプチャーの対象としています
-s キャプチャーするパケットサイズを指定します。
0と指定すると1パケットあたり最大のサイズ(65535バイト)でパケットを取得できます
-vvv 詳細な情報を表示します
-w 指定したパスにキャプチャー結果をファイルとして保存します

 これで、キャプチャーが開始されました。対象Podやノードへの通信をして、調査したい事象を何度か再現します。

 パケットキャプチャーを停止するには、Ctrl + Cキーを押します。キャプチャーされたパケット数が表示され、コマンドが停止します。

# tcpdump -i any -s 0 -vvv -w /capture.pcap

tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes

^C2675 packets captured

2881 packets received by filter

0 packets dropped by kernel

キャプチャーファイルをローカルマシンにダウンロードする

 パケットキャプチャーが完了したら、出力されたファイルを、ノードからローカルマシンにダウンロードします。ふたつ目のコンソールを開き、デバッグ用Podの名前を確認します。

$ kubectl get pods

NAME                                READY   STATUS    RESTARTS   AGE

node-debugger-{ノード名}-vsnwf       1/1     Running   0          13m

  ...

 kubectl cpコマンドに、デバッグ用Podの名前とファイルパスを指定し、ファイルをダウンロードします。コピー元のファイルパスに /hostを指定することで、ノード上に存在するファイルを取得できます。

$ kubectl cp node-debugger-{ノード名}-vsnwf:/host/capture.pcap capture.pcap

tar: Removing leading `/' from member names

1.2.3 dspcapですべてのノードからパケットをキャプチャーする

 Kubernetesにおいては、アプリケーションを複数個のPodとして構成し、それらのPodを複数のワーカーノードにデプロイすることが一般的です。この構成では、Serviceでトラフィックがロードバランスされるために、アプリケーションのトラフィックがどのPodに到達するか事前には予測できません。また、ノード同士でトラフィックの転送が発生する場合もあります。そのため、キャプチャーを採取する対象を単一のPodや単一のノードに限定せず、クラスター内のすべてのノードでキャプチャーを採取できると便利な場面があります。

 dspcap5は、クラスターのすべてのノードでtcpdumpを実行し、キャプチャーを採取するためのスクリプトです。DaemonSetを使用して、パケットキャプチャー用のPodをクラスターの各ノードにデプロイします。図1.1はdspcapの仕組みを図示したものです。

図1.1: dspcapによるパケットキャプチャーの仕組み

スクリプトの入手

 GitHubからdspcapのリポジトリーを取得します。リポジトリーには、キャプチャーを開始するdspcap-startスクリプトと、キャプチャーを停止するdspcap-stopスクリプトが含まれています。

 次のコマンド例では、gitコマンドを使用してリポジトリーをクローンしています。Webブラウザーを使用して、GitHubのページから直接スクリプトのファイルをダウンロードする方法でも問題ありません。

$ git clone https://github.com/tdihp/dspcap.git

Cloning into 'dspcap'...

remote: Enumerating objects: 28, done.

remote: Counting objects: 100% (28/28), done.

remote: Compressing objects: 100% (22/22), done.

remote: Total 28 (delta 8), reused 21 (delta 6), pack-reused 0

Unpacking objects: 100% (28/28), 6.57 KiB | 960.00 KiB/s, done.

$ cd dspcap

$ ls

README.md  dspcap-start  dspcap-stop

パケットキャプチャーの開始

 パケットキャプチャーを開始するには、次のようにdspcap-startスクリプトを実行します。

$ bash dspcap-start

daemonset.apps/dspcap created

Waiting for daemon set "dspcap" rollout to finish: 0 of 2 updated pods are available...

Waiting for daemon set "dspcap" rollout to finish: 1 of 2 updated pods are available...

daemon set "dspcap" successfully rolled out

Capture started. Use dspcap-stop to stop capture and collect outcome.

 クラスターにDaemonSetがデプロイされ、パケットキャプチャーを行うコンテナーが各ワーカーノードにPodとして展開されます。Podの起動が確認できたら、調査対象の通信をして事象を何度か再現します。

$ kubectl get pods -o wide

NAME           READY   STATUS    RESTARTS   AGE   IP           NODE

dspcap-dwj6k   1/1     Running   0          10s   10.240.0.4   node01

dspcap-lj94z   1/1     Running   0          10s   10.240.0.5   node02

パケットキャプチャーの停止

 パケットキャプチャーを停止するには、次のようにdspcap-stopスクリプトを実行します。キャプチャー結果のファイルのダウンロード、およびキャプチャー用Podのクリーンアップ(DaemonSetの削除)が自動的に行われます。スクリプトを実行したディレクトリーにdspcapディレクトリーが作成され、ノードごとにキャプチャー結果のファイルが保存されます。

$ bash dspcap-stop

run killing for pod/dspcap-dwj6k

killing pcap

run killing for pod/dspcap-lj94z

killing pcap

downloading for pod/dspcap-dwj6k

dspcap/

dspcap/2022-04-29T03:15.node01.pcap

dspcap/

dspcap/2022-04-29T03:15.node01.pcap

downloading for pod/dspcap-lj94z

dspcap/

dspcap/2022-04-29T03:15.node02.pcap

dspcap/

dspcap/2022-04-29T03:15.node02.pcap

cleanup for pod/dspcap-dwj6k

cleanup for pod/dspcap-lj94z

daemonset.apps "dspcap" deleted

All done, cheers.


キャプチャーサイズの制限

 dspcapの既定では、キャプチャーファイルの容量が肥大化するのを防ぐために、採取するパケットのサイズに制限がかけられています。TCP/IPヘッダーのほかに、HTTPのヘッダーやレスポンスボディも含めてキャプチャーを採取したい場合には、サイズ制限を外す必要があります。

 サイズ制限を外すためには、dspcap-startスクリプト内に記述されているTCPDUMP_ARGSの行を、次のコード例のように編集します。dspcap-stopスクリプトには、変更が必要な箇所はありません。

# 変更前
TCPDUMP_ARGS="-i any -s 100 -C 100"

# 変更後 (-s および -C オプションを削除します)
TCPDUMP_ARGS="-i any"
試し読みはここまでです。
この続きは、製品版でお楽しみください。