コンテナプロセスがLISTENしているポートの確認

起動しているコンテナプロセスのLISTEN PORTを確認する方法です。
(やり方自体は他にも色々あるので一例ということで)

# 環境
kind:v0.11.1 + kubenetes:v1.23.0
kindイメージには[kindest/node:v1.23.0](https://hub.docker.com/layers/kindest/node/v1.23.0/images/sha256-2f93d3c7b12a3e93e6c1f34f331415e105979961fcddbe69a4e3ab5a93ccbb35?context=explore)を使用しました。[Dockerfile](https://github.com/kubernetes-sigs/kind/blob/main/images/base/Dockerfile)
事情はわかりませんがruncやcontainerdはkindがbuildしたものを使ってるみたいです・・・なんでだろ。

# アプリケーションimage
[gcr.io/google-samples/hello-app:1.0](gcr.io/google-samples/hello-app@sha256:e7389f0a331243818bc5ad057e8de0c797fe368f3076b173d6f573477bad3fc1)
hello world返すだけのイメージをデプロイしました。
runしたはいいですが、LISTEN PORTがわかりません。

# 試したこと

LISTEN PORTを確認するのに以下を行いました。

## hello-appのドキュメント/ソースを探す

LISTEN PORTは何番だよ！みたいなドキュメントを期待してました。どこかにはあるのだろうと思いますがすぐには見つからず。ソースもすぐには見つからず。

## kindノードでコンテナCLIを使う

コンテナランタイム実行ノードにシェルログインできる場合に可能。kind workerノードにはコンテナCLIとしてcrictlが入ってました。

### サブコマンドinspect

コンテナID対象にinspectを実施すると

~~~bash
# crictl inspect c4c88276ca445 | jq -r '.info.runtimeSpec.process.env[]'
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=helloworld
PORT=8080
(以下略)
~~~
8080番だそうです。(もしかしたらbuild時のEXPOSEを見てるだけなのかも・・・)

### サブコマンドlogs

ログ確認が一番手っ取り早いですかねー。出てないことがあるかも知れんですが。
hello-appはログに出してくれていました。
kubectlかコンテナCLIで確認です。

~~~bash
# crictl logs c4c88276ca445
2021/12/29 06:22:34 Server listening on port 8080
2021/12/29 08:18:18 Serving request: /
~~~

## hello-appコンテナ内でconfigを探す

ポート番号を指定しているようなファイルを期待していましたが見つからず。goだからハードコードしてるんですかね。

## hello-appコンテナ内でlsofを試す

### busybox

デフォルトのlsofでは期待していた出力にならず、LISTEN PORTは表示されませんでした。busyboxだからですね。

~~~bash
# lsof
1       /hello-app      0       /dev/null
1       /hello-app      1       pipe:[1004247]
1       /hello-app      2       pipe:[1004248]
1       /hello-app      3       socket:[1004271]
1       /hello-app      4       anon_inode:[eventpoll]
23      /bin/busybox    0       /dev/pts/0
23      /bin/busybox    1       /dev/pts/0
23      /bin/busybox    2       /dev/pts/0
23      /bin/busybox    10      /dev/tty
~~~

### 別途インストールしたlsof

というわけで、別途lsofをパッケージインストールしてみます。

~~~bash
# cat /etc/os-release 
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.15.0
PRETTY_NAME="Alpine Linux v3.15"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
# apk update
# apk add lsof
(1/1) Installing lsof (4.94.0-r0)
Executing busybox-1.34.1-r3.trigger
OK: 6 MiB in 15 packages
# lsof -i -P
COMMAND   PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
hello-app   1 root    3u  IPv6 1004271      0t0  TCP *:8080 (LISTEN)
~~~

8080だそうです。・・・TYPEがIPv6になってますね。

## hello-appコンテナ内の/proc下でがんばる

面倒な方法ですが、何らかの理由でコンテナCLIでダメだったり、lsofをインストールできない場合もこれでいけます。

### hello-appのfdを確認

hello-appのコンテナ内のPIDは1でした。/proc/1の下を覗きます。

~~~bash
# ls -l /proc/1/fd
total 0
lrwx------    1 root     root            64 Dec 29 06:22 0 -> /dev/null
l-wx------    1 root     root            64 Dec 29 06:22 1 -> pipe:[1004247]
l-wx------    1 root     root            64 Dec 29 06:22 2 -> pipe:[1004248]
lrwx------    1 root     root            64 Dec 29 06:22 3 -> socket:[1004271]
lrwx------    1 root     root            64 Dec 29 06:22 4 -> anon_inode:[eventpoll]
~~~

lsofで確認できたsocket(1004271)と同一ですね、当たり前ですが。

### net/tcp6を確認

使用環境のk8sクラスタ内ではIPv6でお話しているようなので、/proc/1/net/tcpではなくて/proc/1/net/tcp6を見る必要がありました。

~~~bash
# cat /proc/1/net/tcp6
  sl  local_address                         remote_address                        st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
   0: 00000000000000000000000000000000:1F90 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 1004271 1 0000000000000000 100 0 0 10 0
~~~

"local_address"の"00000000000000000000000000000000:1F90"というのがbindしているaddress:portになりそうです。一行しかエントリがないので自明ではありますが、"inode"がfdで確認したsocket番号1004271に一致しています。

### 10進に変換

1F90というのが16進数っぽいので、10進数に変換します。

~~~bash
> echo "ibase=16; 1F90" | bc 
8080
~~~

というわけで、8080にbindしていることがわかります。

### 念の為確認

8080をcurlでつついてみます。

~~~bash
# curl 10.244.4.2:8080
Hello, world!
Version: 1.0.0
Hostname: helloworld
~~~

やりました。

## 番外編: port scan

たいへん行儀の悪い方法ですが、考えられなくはないということで。

~~~bash
# nmap 10.244.4.2     
Starting Nmap 7.80 ( https://nmap.org ) at 2021-12-29 18:34 JST
Nmap scan report for 10.244.4.2
Host is up (0.000018s latency).
Not shown: 999 closed ports
PORT     STATE SERVICE
8080/tcp open  http-proxy
~~~

# 結論

コンテナプロセスのLISTEN PORTを確認するには、
- ドキュメントを当たる
- ソースを当たる
- コンテナランタイムに訊く
- アプリケーションコンテナ内でlsof
- アプリケーションコンテナ内で/procを漁る
番外: ポートスキャン

の順に試してみればよさそうです。