はじめに
皆さま、はじめまして! 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には実際の環境でパフォーマンス問題を突き止めた例などか書かれているので、興味がある方はご覧ください。
- この記事のキーワード