コンテナネットワークで何かおかしい時に使える netshoot

# はじめに

Kubernetes (K8s) にデプロイされたコンテナ (Pod) のネットワークが、何かおかしい時、皆さんはどうされてますか？
`kubectl exec` でコンテナに入り込んで、ping を打ちたかったりしませんか？
ところが...！そのコンテナには ping はおろか、curl や ip コマンドも仕込まれていなかったり...！？

「マジかー・・・ping 仕込んだコンテナイメージ用意するかぁ・・・」とかなっていれば、ちょっと待ってください！
そんな悲しい状況下で、便利に使えるコンテナイメージがあります！

netshoot というコンテナイメージなのですが、ping はもちろん、curl や ip コマンド、はたまた iperf などのネットワークのトラブルシュートに使える様々な便利ツールが詰め込まれています。

https://github.com/nicolaka/netshoot


# 前提

今回、直接の依存関係はありませんが、[`TUNA-JP Advent Calendar 2023`](https://qiita.com/advent-calendar/2023/tuna-jp) への寄稿ということで、強引に Tanzu に絡ませるべく、vSphere with Tanzu を使ってデプロイした TKG クラスタ上で、お試ししています。

- vSphere 8.0 Update 2
- kubectl v1.26.4+vmware.wcp.0
- Kubernetes v1.25.7+vmware.3-fips.1

また、「何かおかしいな？」の題材として、下記のような nginx のコンテナを TKG クラスタ上にデプロイしています。

```yaml: nginx.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
```

```text
$ kubectl get pods -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE                                                     NOMINATED NODE   READINESS GATES
nginx-85cf7975fb-cmrzr   1/1     Running   0          10m   10.244.1.5   project01-cluster01-node-pool-1-hsn8z-74b594979f-l8g62   <none>           <none>
nginx-85cf7975fb-fscq9   1/1     Running   0          10m   10.244.2.5   project01-cluster01-node-pool-1-hsn8z-74b594979f-thnv8   <none>           <none>
nginx-85cf7975fb-jjsfl   1/1     Running   0          10m   10.244.2.4   project01-cluster01-node-pool-1-hsn8z-74b594979f-thnv8   <none>           <none>
```


# 使い方

GitHub の README が非常に分かりやすいので、そこまで難しいことは無いと思いますが、K8s 上での使い方に関しては、[`Netshoot with Kubernetes`](https://github.com/nicolaka/netshoot#netshoot-with-kubernetes) のセクションを参照ください。

https://github.com/nicolaka/netshoot#netshoot-with-kubernetes


## コンテナへのアタッチ

まず `kubectl debug` コマンドを使って、稼働中の nginx の Pod に netshoot のコンテナをアタッチしてみます。

```text
$ kubectl debug nginx-85cf7975fb-cmrzr -it --image=nicolaka/netshoot
Defaulting debug container name to debugger-ggtqg.
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (containers "nginx", "debugger-jmvt4", "debugger-ggtqg" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (containers "nginx", "debugger-jmvt4", "debugger-ggtqg" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or containers "nginx", "debugger-jmvt4", "debugger-ggtqg" must set securityContext.runAsNonRoot=true), seccompProfile (pod or containers "nginx", "debugger-jmvt4", "debugger-ggtqg" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
If you don't see a command prompt, try pressing enter.
                    dP            dP                           dP
                    88            88                           88
88d888b. .d8888b. d8888P .d8888b. 88d888b. .d8888b. .d8888b. d8888P
88'  `88 88ooood8   88   Y8ooooo. 88'  `88 88'  `88 88'  `88   88
88    88 88.  ...   88         88 88    88 88.  .88 88.  .88   88
dP    dP `88888P'   dP   `88888P' dP    dP `88888P' `88888P'   dP

Welcome to Netshoot! (github.com/nicolaka/netshoot)
Version: 0.11




 nginx-85cf7975fb-cmrzr  ~ 
 ```

 Ctrl+D や `exit` で、ターミナルを抜けると、自動でアタッチしたコンテナも削除してくれるので便利です。

また、プロンプトがアタッチした先の Pod 名になっているのと、Pod の IP アドレスを確認すると、ちゃんと狙った Pod の IP アドレス (前提のセクションで確認した `10.244.1.5`) になっていることが確認できます。

 ```text
 nginx-85cf7975fb-cmrzr  ~  ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    link/ether ee:24:6e:32:2d:dd brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.1.5/24 brd 10.244.1.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::ec24:6eff:fe32:2ddd/64 scope link
       valid_lft forever preferred_lft forever
```


## ping

とりあえず、まずは ping を打ってみましょう。
外部のルータの IP アドレス `172.19.30.254` を狙ってみます。

```text
 nginx-85cf7975fb-cmrzr  ~  ping -c 3 172.19.30.254
PING 172.19.30.254 (172.19.30.254) 56(84) bytes of data.
64 bytes from 172.19.30.254: icmp_seq=1 ttl=63 time=1.60 ms
64 bytes from 172.19.30.254: icmp_seq=2 ttl=63 time=1.10 ms
64 bytes from 172.19.30.254: icmp_seq=3 ttl=63 time=0.919 ms

--- 172.19.30.254 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.919/1.206/1.599/0.287 ms
```


## ss (netstat)

nginx で 80番ポート (HTTP) で待ち受けているはずなので、ちゃんと Listen できているのか確認してみます。

```text
 nginx-85cf7975fb-cmrzr  ~  ss -tupln
Netid              State               Recv-Q              Send-Q                           Local Address:Port                             Peer Address:Port              Process
tcp                LISTEN              0                   128                                    0.0.0.0:80                                    0.0.0.0:*
tcp                LISTEN              0                   128                                       [::]:80                                       [::]:*
```

プロセス名まで追えていない (PID Namespace が違う？) ですが、80番ポートで、何かが動いていることは確認できました。


## iproute2

ルーティングのテーブルの確認のために、`ip route` や `ip neighbor` コマンドを叩いてみます。

```text
 nginx-85cf7975fb-cmrzr  ~  ip r
default via 10.244.1.1 dev eth0
10.244.1.0/24 dev eth0 proto kernel scope link src 10.244.1.5

 nginx-85cf7975fb-cmrzr  ~  ip n
10.244.1.1 dev eth0 lladdr 0e:e3:be:9c:67:2d REACHABLE
```

今回の環境では Antrea CNI によって管理される Open vSwitch (OVS) に渡されるので、大した情報はありませんが、「ルーティングテーブルは正しい？」などを確認する際に便利そうです。


## termshark

tcpdump でも良いですが、CLI ながら Wireshark に似た UI でパケットキャプチャできる Termshark を起動しています。

```text
 nginx-85cf7975fb-cmrzr  ~  termshark -i eth0
Termshark is initializing - please wait...
(The termshark UI will start when packets are detected on eth0...)

...(略)...
```

ここでは詳しくは紹介しませんが、Termshark めっちゃ便利です！びっくりしました。

https://github.com/gcla/termshark


## iperf

もう一つ別のコンテナなり、対向してくれる端末が必要ですが、お試しで iperf を使って、スループットを計測してみます。

まず、対向する端末、例えば、もう一つ別の nginx の Pod に netshoot のコンテナをアタッチしておいて、iperf サーバーを起動しておきます。

```text
 nginx-85cf7975fb-fscq9  ~  iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size:  128 KByte (default)
------------------------------------------------------------

...(略)...
```

次に、もう一方の netshoot コンテナから、iperf クライアントを実行して、スループットを計測します。

```text
 nginx-85cf7975fb-cmrzr  ~  iperf -c 10.244.2.5
------------------------------------------------------------
Client connecting to 10.244.2.5, TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[  1] local 10.244.1.5 port 40660 connected with 10.244.2.5 port 5001
[ ID] Interval       Transfer     Bandwidth
[  1] 0.00-10.12 sec   259 MBytes   214 Mbits/sec
```

デフォルト設定であることもあって、大して良いスループットでは無いですね...
チューニングの余地はありそうですが、ここでは割愛します。
もし、オプション指定のチューニングや、結果の集計について、ご興味があれば、下記の記事も覗いていただけると嬉しいです。

https://qiita.com/iwaseyusuke/items/47f4963128ee2ca301d3


# まとめ

パッとすぐに使いそうなコマンドや操作のみを取り上げましたが、netshoot には、まだまだ他にも便利なツールが同梱されています！
「あ、こんなツールあるんだ〜」と勉強になるものもあると思いますので、「いくつ知ってるかな？」気分で覗いみても面白いと思います。