「Inspektor Gadget」でKubernetesクラスタをデバッグする

2024年1月24日(水)
satoken
第3回の今回は、eBPFを利用してKubernetesクラスタをデバッグする「Inspektor Gadget」について紹介します。

はじめに

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

株式会社スリーシェイク Sreake事業部
2023年5月に3-shakeのSreake事業部にJoinし、自社Saasと顧客向けのSREとして活動中。

連載バックナンバー

仮想化/コンテナ技術解説
第4回

「Odigos」でノーコードの分散トレーシングを実現する

2024/3/14
第4回の今回は、アプリケーションのソースコードを変更せずに分散トレーシングを実現できる「Odigos」について紹介します。
仮想化/コンテナ技術解説
第3回

「Inspektor Gadget」でKubernetesクラスタをデバッグする

2024/1/24
第3回の今回は、eBPFを利用してKubernetesクラスタをデバッグする「Inspektor Gadget」について紹介します。
仮想化/コンテナ技術解説
第2回

ドメインを考慮した柔軟なPodの配置を実現する「Balancer」

2023/12/6
第2回目の今回は、ドメインを考慮した柔軟なPodの配置を実現する「Balancer」について紹介します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています