本書を手に取っていただき、ありがとうございます。この本は、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)を使用しています。
本書に記載されている会社名、製品名などは、一般に各社の登録商標または商標、商品名です。会社名、製品名については、本文中では©、®、™の表示は省略しています。
本書に記載の内容は、すべて情報提供を目的として書かれています。正確な情報を記載するよう努めていますが、必ずしもすべて正確であることを保証するものではありません。本書の内容をもとに検証や開発、運用する際は、オフィシャルなサイトなど他の情報源も参考にされたうえで、ご自身の判断と責任によって行ってください。本書の情報をもとに実施した結果について、筆者はいかなる責任も負いません。
本章では、Kubernetesの基本コマンドと、調査に使用するツールについて紹介します。以降の章で紹介するトラブルシューティングの方法では、本章で紹介をしたコマンドやツール使用します。
kubectl CLIツールでKubernetesクラスターを操作できます。Kubernetesオブジェクトのステータスを確認できるので、トラブルシューティングの際に役立ちます。ここではkubectlコマンドの使い方と、トラブルシューティング時に確認するポイントを解説します。
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
...
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
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
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" "-"
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:/#
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%
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にアクセスできます。また、コマンドを利用したアクセスだけでなく、ローカルマシンのブラウザー上から動作確認することも可能になります。
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にも使用できます。
ネットワークのトラブルシューティングでは、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を使って開くことができます。
トラブルシューティングをする対象Podが特定できている場合は、そのPodを流れるパケットをキャプチャーして調査します。本項では、ツールを使用したPodのパケットキャプチャーの方法を紹介します。
ksniff3は、Podのネットワークパケットをキャプチャーするツールです。このツールは、クラスターにパケットキャプチャー用のPodをデプロイして、指定したPodのパケットをキャプチャーします。
ksniffの既定では、ツールを開始するとWiresharkが起動し、対象Podでキャプチャーされたパケットをリアルタイムで表示します。本項では、パケットのキャプチャー結果をファイルとして保存する方法を紹介します。
ksniffはkubectlのプラグインマネージャーであるKrewを使って、インストールできます。プラグインマネージャーのインストールは、KrewのユーザーガイドのInstallingページ4をご参照ください。
プラグインマネージャーのインストールが完了したら、次のコマンドでksniffをインストールします。
$ kubectl krew install sniff
それでは、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
調査対象の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 --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
Kubernetesにおいては、アプリケーションを複数個のPodとして構成し、それらのPodを複数のワーカーノードにデプロイすることが一般的です。この構成では、Serviceでトラフィックがロードバランスされるために、アプリケーションのトラフィックがどのPodに到達するか事前には予測できません。また、ノード同士でトラフィックの転送が発生する場合もあります。そのため、キャプチャーを採取する対象を単一のPodや単一のノードに限定せず、クラスター内のすべてのノードでキャプチャーを採取できると便利な場面があります。
dspcap5は、クラスターのすべてのノードでtcpdumpを実行し、キャプチャーを採取するためのスクリプトです。DaemonSetを使用して、パケットキャプチャー用のPodをクラスターの各ノードにデプロイします。図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"