「Inspektor Gadget」でKubernetesクラスタをデバッグする
はじめに
皆さま、はじめまして! 3-shakeのSreake事業部に所属するsatokenです。第3回目の今回は、eBPFを利用してKubernetesクラスタをデバッグする「Inspektor Gadget」というツールについて紹介します。
Kubernetesクラスタのトラシューとデバッグ
KubernetesクラスタにデプロイしたPodが起動しなかったり、Probeに失敗したり、クラッシュしたときなど、いろいろな手段を駆使して調査することになると思いますが、原因を突き止めるのはなかなか大変です。
皆さんもトラブル時は以下のような作業をされているのではと想像しています。
- kubectl logs、describeコマンドで頑張る
- execコマンドでPodに入って調査
- debug用のPodを作成して調査
- metricsやtraceなどをダッシュボードで見る
- APMなどで見る
- OSSであれば同様のエラーが出ていないかGitHubのissueを検索する
- ソースコードを読む
Inspektor Gadget
Inspektor Gadgetは、Kinvolk社*が開発したeBPFを利用したkubernetesクラスタをデバッグするツールです。2023年7月にCNCFのSandboxプロジェクトにも入っています。
*:Kinvolk社は現在マイクロソフトに買収されています。
Inspektor Gadgetの仕組み
Inspektor Gadgetをkrewでインストールすると、kubectl gadget
コマンドが使えるようになります。kubectl gadget
には以下のようなサブコマンドがあります。
$ kubectl gadget -h Collection of gadgets for Kubernetes developers Usage: kubectl-gadget [command] Available Commands: advise Recommend system configurations based on collected information audit Audit a subsystem completion Generate the autocompletion script for the specified shell deploy Deploy Inspektor Gadget on the cluster help Help about any command profile Profile different subsystems prometheus Expose metrics using prometheus script Run a bpftrace-compatible scripts snapshot Take a snapshot of a subsystem and print it sync Synchronize gadget information with your cluster top Gather, sort and periodically report events according to a given criteria trace Trace and print system events traceloop Get strace-like logs of a container from the past undeploy Undeploy Inspektor Gadget from cluster version Show version
kubectl gadget deploy
コマンドを実行するとDaemonSetがデプロイされ、各Node上のPodがNodeのカーネルにeBPFプログラムをアタッチして情報を収集します。
LinuxカーネルのVersionやConfigと関係があるため現時点で動く環境と動かない環境があります。AKS、EKS、GKEなど動作が確認されている一般的なKubernetes環境についてはDocumentのKubernetes Platform Requirementsに記載があります。
アーキテクチャの詳細はInspektor GadgetのDocumentに記載されていますので、ご興味がある方はこちらをご覧ください。
Inspektor Gadgetの動作確認
kubectl gadget deploy
コマンドでクラスタにInspektor Gadgetをインストールすると、DaemonSetとRoleなど必要な各種リソースが作成されます。
$ kubectl gadget deploy Creating Namespace/gadget... Creating ServiceAccount/gadget... Creating ClusterRole/gadget-cluster-role... Creating ClusterRoleBinding/gadget-cluster-role-binding... Creating Role/gadget-role... Creating RoleBinding/gadget-role-binding... Creating DaemonSet/gadget... Creating CustomResourceDefinition/traces.gadget.kinvolk.io... Waiting for gadget pod(s) to be ready... 0/3 gadget pod(s) ready 1/3 gadget pod(s) ready 2/3 gadget pod(s) ready 3/3 gadget pod(s) ready Retrieving Gadget Catalog... Inspektor Gadget successfully deployed
Inspektor GadgetのPodがDaemonSetで作成されます。
$ kubectl get po -n gadget NAME READY STATUS RESTARTS AGE gadget-9dd26 1/1 Running 0 2m46s gadget-jpjnl 1/1 Running 0 2m46s gadget-p7fzz 1/1 Running 0 2m46s
このgadgetのPodがAPIサーバとやり取りをしつつ、NodeのカーネルにeBPFプログラムをアタッチし、情報を収集する仕組みとなっています。
eBPFプログラムは、pkg/gadgets
以下にgadgetサブコマンドに応じたものが格納されています。trace tcp
コマンドであれば、pkg/gadgets/trace/tcp/tracer/bpf
にeBPFプログラムがあります。
trace gadgets
まずtrace gadgets
を試してみます。Pod内で実行されたコマンドなど、プロセスに関する情報やTCPなどネットワークに関する情報をトレースできます。
$ kubectl gadget trace -h Trace and print system events Usage: kubectl-gadget trace [command] Available Commands: bind Trace socket bindings capabilities Trace security capability checks dns Trace DNS requests exec Trace new processes fsslower Trace open, read, write and fsync operations slower than a threshold mount Trace mount and umount system calls network Trace network streams oomkill Trace when OOM killer is triggered and kills a process open Trace open system calls signal Trace signals received by processes sni Trace Server Name Indication (SNI) from TLS requests tcp Trace TCP connect, accept and close tcpconnect Trace connect system calls tcpdrop Trace TCP kernel-dropped packets/segments tcpretrans Trace TCP retransmissions
trace exec
、trace open
はPod内で実行されたコマンドや開かれたファイルをトレースできます。
$ kubectl gadget trace exec K8S.NODE K8S.NAMESPACE K8S.POD K8S.CONTAINER PID PPID COMM RET ARGS kind-worker default alpine alpine 54629 54619 uname 0 /bin/uname -a
trace exec
コマンドを実行しておき、別のterminalからkubectl exec
コマンドでPodでコマンドを実行すると、実行されたコマンドが出力されます。
上記の出力例では、Pod内でuname
コマンドが実行されたことが分かります。trace open
ではPod内で実行された open
システムコールがトレースされます。
以下は、kubectl exec
コマンドでcat/etc/hosts
を実行したときの出力です。
$ kubectl gadget trace open K8S.NODE K8S.NAMESPACE K8S.POD K8S.CONTAINER PID COMM FD ERR PATH kind-worker default alpine alpine 74112 runc:[2:… 5 0 /sys/kernel/mm/transpar… kind-worker default alpine alpine 74112 runc:[2:… 5 0 / kind-worker default alpine alpine 74112 runc:[2:… 5 0 /proc/sys/kernel/cap_la… kind-worker default alpine alpine 74112 runc:[2:… 5 0 /proc/filesystems kind-worker default alpine alpine 74112 runc:[2:… 5 0 /proc/self/fd kind-worker default alpine alpine 74112 runc:[2:… 5 0 /proc/self/status kind-worker default alpine alpine 74112 runc:[2:… 5 0 /etc/passwd kind-worker default alpine alpine 74112 runc:[2:… 10 0 /etc/group kind-worker default alpine alpine 74112 runc:[2:… 5 0 /etc/group kind-worker default alpine alpine 74112 runc:[2:… 5 0 /proc/self/setgroups kind-worker default alpine alpine 74112 cat 3 0 /etc/hosts
trace tcp
やtrace network
ではPod内のネットワークトラフィックがトレースされます。
$ kubectl gadget trace tcp K8S.NODE K8S.NAMESPACE K8S.POD K8S.CONTAINER T PID COMM IP SRC DST kind-worker default alpine alpine C 357939 curl 4 p/default/alpine:58984 s/default/nginx:8080 kind-worker2 default nginx nginx A 84824 nginx 4 p/default/nginx:80 p/default/alpine:58984 kind-worker2 default nginx nginx X 84824 nginx 4 p/default/nginx:80 p/default/alpine:58984 kind-worker default alpine alpine X 357939 curl 4 p/default/alpine:58984 s/default/nginx:8080 kind-worker default alpine alpine C 358298 apk 4 p/default/alpine:32950 r/146.75.114.132:443 kind-worker default alpine alpine X 358298 apk 4 p/default/alpine:32950 r/146.75.114.132:443
上記のトレース結果は、クラスタ内外にNetwork Traficが発生するようにPod内でcurlやapkコマンドを実行したものです。
$ kubectl get pod NAME READY STATUS RESTARTS AGE alpine 1/1 Running 0 4h32m nginx 1/1 Running 0 4h15m $ kubectl exec alpine -- curl -s http://nginx:8080 >> /dev/null $ kubectl exec alpine -- apk update fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/community/x86_64/APKINDEX.tar.gz v3.18.4-140-g659ef3a25fa [https://dl-cdn.alpinelinux.org/alpine/v3.18/main] v3.18.4-142-g406f54845c8 [https://dl-cdn.alpinelinux.org/alpine/v3.18/community] OK: 20076 distinct packages available
advice gadgets
次に、advise gadgets
を試してみます。advise gadgets
ではキャプチャしたログからNetwork PolicyとSeccomp profileの生成が可能です。
Network Polocyの生成を試すために、以下のコマンドでnginxのPodとServiceを作成しておきます。
$ kubectl run nginx --image=nginx:alpine -l app=nginx $ kubectl expose pod nginx --port=8080 --target-port=80
次に、advise network-policy monitor
でNamespace内で発生するネットワークトラフィックをログファイルに出力させておきます。
$ kubectl gadget advise network-policy monitor --output ./networktrace.log
curlコマンドでnginxにHTTPリクエストを送ります。
$ kubectl exec alpine -- curl -s http://nginx:8080 >> /dev/null
curlコマンドを実行したら、advise network-policy monitor
によるログの記録を停止します。
$ kubectl gadget advise network-policy monitor --output ./networktrace.log Recording 6 events into file "./networktrace.log"...^C Terminating...
advise network-policy report
でログファイルの記録からNetworkPolicyのyamlファイルを生成します。
$ kubectl gadget advise network-policy report --input ./networktrace.log > network-policy.yaml
生成されたNetworkPolicyのyamlファイルです。Serviceの名前解決をするDNSクエリとcurlコマンドのHTTPリクエストを許可したNetworkPolicyのyamlが生成されました。
$ cat network-policy.yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: creationTimestamp: null name: alpine-network namespace: default spec: egress: - ports: - port: 8080 protocol: TCP to: - podSelector: matchLabels: app: nginx - ports: - port: 53 protocol: UDP to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system podSelector: matchLabels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" kubernetes.io/name: CoreDNS podSelector: matchLabels: run: alpine policyTypes: - Ingress - Egress --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: creationTimestamp: null name: nginx-network namespace: default spec: ingress: - from: - podSelector: matchLabels: run: alpine ports: - port: 80 protocol: TCP podSelector: matchLabels: app: nginx policyTypes: - Ingress - Egress
Seccomp profileの生成を試してみます。nginxのPodが実行するシステムコールをキャプチャするようにgadget start
コマンドを実行します。-p
で対象のPodを指定します。
$ kubectl gadget advise seccomp-profile start -p nginx yCfzRG3ebBSDfnaG
nginxのPodを作成してHTTPリクエストを送ります。
$ kubectl run nginx --image=nginx:alpine pod/nginx created $ kubectl get pod NAME READY STATUS RESTARTS AGE alpine 1/1 Running 0 6h57m nginx 1/1 Running 0 39s $ kubectl exec alpine -- curl -s http://nginx:8080 >> /dev/null
gadget stop
コマンドを実行するとSeccompのjsonファイルが出力されます。
$ kubectl gadget advise seccomp-profile stop yCfzRG3ebBSDfnaG { "defaultAction": "SCMP_ACT_ERRNO", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ], "syscalls": [ { "names": [ "accept4", "access", "arch_prctl", "bind", "brk", ~~(省略)~~ "uname", "utimensat", "wait4", "write", "writev" ], "action": "SCMP_ACT_ALLOW" } ] }
その他のコマンド
今回のデモでは取り上げなかった、他のコマンドを紹介しておきます。
- profile
- 実行中のPodのblock-io、cpu、tcprttなどの各perfomans情報を一定時間取得し、ヒストグラフやスタックトレースを出力します。
- snapshot
- コマンド実行時点で起動しているPodのプロセスや利用しているソケットの情報を取得します。
- top
- 実行中のPodのblock-io、ebpf、file、tcpに関する利用状況をtopコマンドのように並べ替えてリアルタイム取得します。
- audit
- auditではseccompのprofileを使用してシステムコールを監査します。
- script
- scriptではbpftrace方式のユーザ任意のスクリプトを実行できます。
各コマンドの使い方は、公式Documentをご覧ください。
おわりに
今回は、Inspektor Gadgetというツールを紹介しました。eBPFプログラムを利用する性質上、Nodeに特権を持つDaemonSetのDeployが必要となるなど、本番で試すにはポリシー上難しいところもあるでしょう。開発環境などで試しつつ障害の原因が分からないときの奥の手として持っておくのはいかがでしょうか。
Inspektor GadgetのBlogには実際の環境でパフォーマンス問題を突き止めた例などか書かれているので、興味がある方はご覧ください。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Kubernetes環境を構築して、実際にコンテナを動かしてみよう
- NGINX Ingress Controllerの柔軟なアプリケーション制御、具体的なユースケースと設定方法を理解する
- Kubernetesの基礎
- KubernetesのConfig&Storageリソース(その1)
- KubernetesのDiscovery&LBリソース(その1)
- Oracle Cloud Hangout Cafe Season5 #3「Kubernetes のセキュリティ」(2022年3月9日開催)
- KubernetesのWorkloadsリソース(その1)
- KubernetesのDiscovery&LBリソース(その2)
- KubernetesのWorkloadsリソース(その2)
- Project CalicoをKubernetesで使ってみる:構築編