# 介绍一个小工具：Inspektor Gadget

> 各位好，今天 6 月 26 号，吃了么您呐。

例行查看 krew index 的时候，发现有个新插件 `gadgit`，翻翻来历，居然是 [Kinvolk](https://kinvolk.io/) 的作品，公司不太出名，印象里最早做服务网格 Benchmark 的就是他。插件功能介绍很简单：`Collection of gadgets for Kubernetes developers`，但是用法就很了不得了，非常有字数越小事越大的感觉：

```text
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
  snapshot    Take a snapshot of a subsystem and print it
  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 pod from the past
  undeploy    Undeploy Inspektor Gadget from cluster
  version     Show version
```

过滤掉辅助功能，可以看到几个主要内容：

* advise：根据搜集信息，推荐系统配置内容
    
* audit：对子系统进行审计
    
* profile：对不同子系统进行侧写
    
* snapshot：给子系统进行快照并打印
    
* top：根据制定规则，搜集、排序和周期性地报告事件
    
* trace：跟踪和打印系统事件
    
* traceloop：获取类似 strace 格式的历史日志
    

其实说了跟没说一样是不是？不如一条条看过去了。

## 部署

首先使用 krew 安装这个插件：

```plaintext
$ kubectl krew install gadget
Updated the local copy of plugin index.
Installing plugin: gadget
...
 | Use this plugin:
 |      kubectl gadget
...
 |  | $ kubectl gadget deploy | kubectl apply -f -
...
WARNING: You installed plugin "gadget" from the krew-index plugin repository.
...
```

上文可以看到，使用插件之前要安装到及群里 `kubectl gadget deploy | kubectl apply -f -`，可以看到，除了 RBAC 内容之外，还有 Daemonset 和 CRD 这两个东西。为了跟踪 Pod 行为，Inspektor Gadget 把 BPF 程序附加到内核函数上，当函数被执行时，内核也会运行这些被注入的程序。因此，BPF 程序需要检测触发该函数的系统调用，是否来自 Inspektor Gadget 的追踪目标。为了做到这一点，程序在包含要追踪的 Pod 列表的 BPF Map 中查找当前的 cgroup id，如果没有找到，程序会提前退出。最后，BPF程序收集要追踪的信息，例如，系统调用参数，并将它们 Ring Buffer 或 BPF Map。Inspektor Gadget 的用户空间工具在 Ring Buffer 或 BPF 地图上监听或读取，并获取新的事件。追踪结束后，BPF 程序将会被删除。

## Network Policy Advise

这个功能由 Monitor 和 Report 两个部分构成，分别是启动特定命名空间内工作负载的网络监听，生成跟踪记录；以及根据跟踪记录生成网络策略两部分，例如：

```plaintext
$ kubectl gadget advise network-policy monitor  --output /tmp/result.txt
Node "gke-gcp-vlab-k8s-default-pool-d3fe3442-pw6v" ready.
Node "gke-gcp-vlab-k8s-default-pool-d3fe3442-9hsc" ready.
Node "gke-gcp-vlab-k8s-default-pool-d3fe3442-nj0k" ready.
^C
Stopping...

$ more /tmp/result.txt
{"type":"ready"}
{"type":"ready"}
{"type":"ready"}
{"type":"connect","remote_kind":"pod","port":2021,"local_pod_namespace":"gadget","local_pod_name":"gadget-dzb7g","local_pod_labels":{"controller-revision-hash":"8f55cc94f","k8s-app":"gadget","pod-template-generation":"1"},"remote_pod_namespace":"kube-system","remote_pod_name":"pdcsi-node-lpqln","remote_pod_labels":{"controller-revision-hash":"69cdc7c487","k8s-app":"gcp-compute-persistent-disk-csi-driver","pod-template-generation":"1"},"debug":"4649087588182 cpu#1 connect 3293 otelsvc 10.138.15.229:33032 10.138.15.229:2021 4026531992\n"}
...
```

执行一段时间后使用 Ctrl+C 终止命令，可以看到指定的输出文件中包含了一堆类似 JSON 的记录内容，可以用这个文件生成网络策略：

```plaintext
$ kubectl gadget advise network-policy report --input=/tmp/result.txt
...
      podSelector:
        matchLabels:
          k8s-app: konnectivity-agent
    ports:
    - port: 10250
      protocol: TCP
  podSelector:
    matchLabels:
      k8s-app: gadget
  policyTypes:
  - Ingress
  - Egress
```

可以看到，网络策略已经生成。

## Seccomp Profile Advise

这一功能是用 `advise seccomp-profile` 模块完成的，这个模块有三个子命令，分别是 `start`、`list` 和 `stop`，例如要跟踪一个 Calico Pod：

```plaintext
$ kubectl gadget advise seccomp-profile start --podname=calico-node-t6hwg
HAmaTrPcxTLDNfSo
$ kubectl gadget advise seccomp-profile list
NAMESPACE      NODE(S)                                                                                                                         POD                  CONTAINER    TRACEID
kube-system    gke-gcp-vlab-k8s-default-pool-d3fe3442-9hsc,gke-gcp-vlab-k8s-default-pool-d3fe3442-nj0k,gke-gcp-vlab-k8s-default-pool-d3fe3442-pw6v    calico-node-t6hwg                 HAmaTrPcxTLDNfSo
```

上面 `start` 命令执行后出现的 `HAmaTrPcxTLDNfSo` 就是跟踪 ID，开始一段时间之后，可以调用 `stop` 命令结束跟踪，跟踪结束后会显示这个 Pod 的 Seccomp：

```plaintext
kubectl gadget advise seccomp-profile stop HAmaTrPcxTLDNfSo
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": [
    "SCMP_ARCH_X86_64",
    "SCMP_ARCH_X86",
    "SCMP_ARCH_X32"
  ],
  "syscalls": [
    {
      "names": [
        "accept4",
        "access",
        "arch_prctl",
...
 ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}
```

## Profile

这个模块包括 `block-io` 和 `cpu` 两个指令，例如监控某个节点的 `block-io`：

```plaintext
 kubectl gadget profile block-io --node=gke-gcp-vlab-k8s-default-pool-d3fe3442-9hsc
Tracing block device I/O... Hit Ctrl-C to end.^C

     usecs               : count     distribution
         0 -> 1          : 0        |                                        |
         2 -> 3          : 0        |                                        |
         4 -> 7          : 0        |                                        |
         8 -> 15         : 0        |                                        |
        16 -> 31         : 0        |                                        |
        32 -> 63         : 0        |                                        |
        64 -> 127        : 1        |                                        |
       128 -> 255        : 1        |                                        |
       256 -> 511        : 0        |                                        |
       512 -> 1023       : 2        |                                        |
      1024 -> 2047       : 54       |****************                        |
      2048 -> 4095       : 44       |*************                           |
      4096 -> 8191       : 49       |***************                         |
      8192 -> 16383      : 128      |****************************************|
     16384 -> 32767      : 118      |************************************    |
     32768 -> 65535      : 11       |***                                     |
     65536 -> 131071     : 5        |*                                       |
```

可以看到以微秒为单位的统计记录和分布情况。`cpu` 子命令的用法如下，其中 `-K` 开关意思是只关注内核空间的内容：

```plaintext
kubectl gadget profile cpu -p calico-node-t6hwg -K
Capturing stack traces... Hit Ctrl-C to end.^C

calico-node;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;vfs_write;pipe_write;__wake_up_sync_key;_raw_spin_unlock_irqrestore;_raw_spin_unlock_irqrestore 1
calico-node;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_read;vfs_read;pipe_read;anon_pipe_buf_release;anon_pipe_buf_release 1
ip 1
calico-node;entry_SYSCALL_64_after_hwframe;do_syscall_64;__se_sys_nanosleep;get_timespec64;_copy_from_user;copy_user_generic_unrolled;copy_user_generic_unrolled 1
calico-node 9
```

## Snapshot

Snapshot 模块分为 `process` 和 `socket` 两个子命令，分别用于记录进程和网络。（`v0.5.1` 版本的 `process` 子命令好像无法工作）。

```plaintext
$ kubectl gadget snapshot socket \  
    --node=gke-gcp-vlab-k8s-default-pool-d3fe3442-pw6v \
    -o custom-columns=namespace,pod,protocol,status
NAMESPACE       POD             PROTOCOL STATUS
kube-system     calico-node-zjpl5 TCP      ESTABLISHED
kube-system     calico-node-zjpl5 TCP      ESTABLISHED
kube-system     calico-node-zjpl5 TCP      ESTABLISHED
kube-system     calico-node-zjpl5 TCP      ESTABLISHED
kube-system     calico-node-zjpl5 TCP      ESTABLISHED
kube-system     calico-node-zjpl5 TCP      ESTABLISHED
kube-system     calico-node-zjpl5 TCP      ESTABLISHED
```

## Top

这个模块有三个子命令，`block-io`、`tcp` 和 `file`，跟 Linux 系统的 `top` 命令类似，例如下面的命令列出的 top file:

```plaintext
$ kubectl gadget top file \
    -o custom-columns=container,pid,comm,reads
CONTAINER        PID     COMM             READS
fluentbit        3737    flb-pipeline     1
fluentbit        3737    flb-pipeline     1
fluentbit        3737    flb-pipeline     2
gke-metrics-agent 56606   otelsvc          2
fluentbit        3737    flb-pipeline     1
fluentbit        3737    flb-pipeline     1
fluentbit        3737    flb-pipeline     2
gke-metrics-agent 56606   otelsvc          2
fluentbit        3737    flb-pipeline     1
fluentbit        3737    flb-pipeline     2
```

## Trace

这个模块针对系统事件进行跟踪，目前支持包括：

* bind：Scoket 绑定
    
* capabilities：Capability 检查
    
* dns：DNS 请求
    
* exec：新进程
    
* fsslower：`open`、`read`、`write` 和 `fsync` 操作时长超过阈值
    
* mount：`mount` 和 `umount` 操作
    
* oomkill：OOM Killer 被触发
    
* open：`open` 系统调用
    
* signal：跟踪进程收到的信号
    
* sni: TLS 请求中的 SNI
    
* tcp：TCP 的 `connect`、`accept` 和 `close`
    
* tcpconnect：`connect` 调用
    

例如对 `open` 的跟踪：

```plaintext
$ kubectl gadget trace open -o custom-columns=container,path

CONTAINER        PATH
fluentbit        /var/log/containers
fluentbit        /var/log/pods
fluentbit        /var/log/containers
fluentbit        /var/log/pods
fluentbit        /var/run/google-fluentbit/pos-files
...
csi-driver-registrar /usr/bin/runc
csi-driver-registrar /sys/kernel/mm/hugepages
...
```

## 然后

以后没 eBPF 支持连 Ops 都不好做了？
