3 検証結果
サーバのNICを確認します。ループバックデバイス(lo)とサーバ外部との通信用のNIC(enp1s0)が確認できます。
[root@server ~]# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:98:95:eb brd ff:ff:ff:ff:ff:ff
コンテナのイメージを確認します。nginxとhttpdの最新イメージが存在することがわかります。なお、イメージのダウンロード方法は、podmanコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/library/nginx latest 670dcc86b69d 10 days ago 146 MB
docker.io/library/httpd latest 444f7df01ce9 2 weeks ago 149 MB
web1という名前のコンテナを起動します。
[root@server ~]# podman run -d -p 8080:80 --name web1 670dcc86b69d
3881981d35e65364dd1df5b0fd2a2850714a9214cf5a09ae51b0f5f933a7c7b3
コンテナを起動すると、ブリッジ(cni-podman0)とNIC(vetha0a16d8a)が作成されたことがわかります。
[root@server ~]# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:98:95:eb brd ff:ff:ff:ff:ff:ff
3: cni-podman0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 72:24:37:26:77:17 brd ff:ff:ff:ff:ff:ff
4: vetha0a16d8a@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master cni-podman0 state UP mode DEFAULT group default
link/ether 6a:f0:d2:db:20:0e brd ff:ff:ff:ff:ff:ff link-netns netns-db3d0237-4de3-5827-ee92-25e760337c1d
なお、brctlコマンドを実行すると、cni-podman0はブリッジ、vetha0a16d8aはブリッジに作成されたNICであることがわかります。
[root@server ~]# brctl show
bridge name bridge id STP enabled interfaces
cni-podman0 8000.722437267717 no vetha0a16d8a
次に、web2という名前のコンテナを起動します。
[root@server ~]# podman run -d -p 9090:80 --name web2 444f7df01ce9
311285f82dbae08a17323bab1e6e6d560b21bbfd6716551aa3d0f8911edb704f
コンテナを起動すると、NICがさらに1つ(veth8b2242ec)作成されたことがわかります。
[root@server ~]# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 52:54:00:98:95:eb brd ff:ff:ff:ff:ff:ff
3: cni-podman0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 72:24:37:26:77:17 brd ff:ff:ff:ff:ff:ff
4: vetha0a16d8a@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master cni-podman0 state UP mode DEFAULT group default
link/ether 6a:f0:d2:db:20:0e brd ff:ff:ff:ff:ff:ff link-netns netns-db3d0237-4de3-5827-ee92-25e760337c1d
5: veth8b2242ec@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master cni-podman0 state UP mode DEFAULT group default
link/ether e6:8c:c8:4d:81:6e brd ff:ff:ff:ff:ff:ff link-netns netns-d229252d-f395-e935-1c2a-0e2cc4080cfa
なお、brctlコマンドを実行すると、ブリッジにNIC(veth8b2242ec)が追加されたことがわかります。
[root@server ~]# brctl show
bridge name bridge id STP enabled interfaces
cni-podman0 8000.722437267717 no vetha0a16d8a
veth8b2242ec
次にpsコマンドを実行します。コンテナが2つ(web1,web2)動作していることがわかります。
[root@server ~]# podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3881981d35e6 docker.io/library/nginx:latest nginx -g daemon o... 2 minutes ago Up 2 minutes ago 0.0.0.0:8080->80/tcp web1
311285f82dba docker.io/library/httpd:latest httpd-foreground About a minute ago Up About a minute ago 0.0.0.0:9090->80/tcp web2
コンテナweb1で動作しているプロセスを確認します。nginxプロセスが動作していることがわかります。
[root@server ~]# podman top web1
USER PID PPID %CPU ELAPSED TTY TIME COMMAND
root 1 0 0.000 2m30.808444783s ? 0s nginx: master process nginx -g daemon off;
nginx 32 1 0.000 2m30.808765576s ? 0s nginx: worker process
nginx 33 1 0.000 2m30.808847992s ? 0s nginx: worker process
nginx 34 1 0.000 2m30.808919483s ? 0s nginx: worker process
nginx 35 1 0.000 2m30.808999017s ? 0s nginx: worker process
コンテナweb2で動作しているプロセスを確認します。www-data(httpd)プロセスが動作していることがわかります。
[root@server ~]# podman top web2
USER PID PPID %CPU ELAPSED TTY TIME COMMAND
root 1 0 0.000 1m35.137631946s ? 0s httpd -DFOREGROUND
www-data 9 1 0.000 1m35.138004623s ? 0s httpd -DFOREGROUND
www-data 10 1 0.000 1m35.138121226s ? 0s httpd -DFOREGROUND
www-data 11 1 0.000 1m35.138194124s ? 0s httpd -DFOREGROUND
次にiptablesのDNATターゲットを確認します。コンテナを起動すると、下記DNATターゲットが作成されることがわかります。作成されるDNATターゲットのルールは以下の意味になります。受信したTCPパケットを以下のように変換します。
・宛先ポート番号が8080→宛先IPアドレスを10.88.0.2、宛先ポート番号を80に変換
・宛先ポート番号が8090→宛先IPアドレスを10.88.0.3、宛先ポート番号を80に変換
[root@server ~]# iptables -t nat -nvL |grep DNAT
pkts bytes target prot opt in out source destination
Chain CNI-HOSTPORT-DNAT (2 references)
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:10.88.0.2:80
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:9090 to:10.88.0.3:80
DNAT変換をしたあと、ルーティングテーブルの検索処理が実行されます。ルーティングテーブルを確認すると、以下のようになっています。宛先が10.88.0.0/16のパケットはcni-podman0 デバイスから、宛先が192.168.122.0/24のパケットはenp1s0から送信することがわかります。つまり、コンテナ宛てのパケットはcni-podman0デバイスから送信されることがわかります。
[root@server ~]# ip r
default via 192.168.122.1 dev enp1s0 proto dhcp metric 100
10.88.0.0/16 dev cni-podman0 proto kernel scope link src 10.88.0.1
192.168.122.0/24 dev enp1s0 proto kernel scope link src 192.168.122.68 metric 100
クライアントからweb1コンテナのnginxに対してhttpアクセスします。なお、curlコマンドの使い方は、curlコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@client ~]# curl -I http://192.168.122.68:8080
HTTP/1.1 200 OK
Server: nginx/1.23.1
Date: Mon, 01 Aug 2022 10:25:52 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 19 Jul 2022 14:05:27 GMT
Connection: keep-alive
ETag: "62d6ba27-267"
Accept-Ranges: bytes
DNATターゲットを確認します。DNATで処理したパケット数が1増加したことがわかります。
[root@server ~]# iptables -t nat -nvL |grep DNAT
pkts bytes target prot opt in out source destination
Chain CNI-HOSTPORT-DNAT (2 references)
1 60 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:10.88.0.2:80
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:9090 to:10.88.0.3:80
次に、クライアントからweb2コンテナのhttpdに対してhttpアクセスします。
[root@client ~]# curl -I http://192.168.122.68:9090
HTTP/1.1 200 OK
Date: Mon, 01 Aug 2022 10:35:51 GMT
Server: Apache/2.4.54 (Unix)
Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
ETag: "2d-432a5e4a73a80"
Accept-Ranges: bytes
Content-Length: 45
Content-Type: text/html
DNATターゲットを確認します。DNATで処理したweb2宛てのhttpパケット数が1増加したことがわかります。
[root@server ~]# iptables -t nat -nvL |grep DNAT
pkts bytes target prot opt in out source destination
Chain CNI-HOSTPORT-DNAT (2 references)
1 60 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:10.88.0.2:80
1 60 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:9090 to:10.88.0.3:80
次に、tsharkを使って、NICを通過するhttpパケットを確認してみます。なお、tsharkコマンドのインストール方法、使い方は、tsharkコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。まず、tsharkでパケットキャプチャが可能なインタフェース一覧を表示してみます。ブリッジデバイス、コンテナと接続するNICが、キャプチャ可能なインタフェースとして表示されています。
[root@server ~]# tshark -D
Running as user "root" and group "root". This could be dangerous.
1. veth8b2242ec
2. enp1s0
3. cni-podman0
4. vetha0a16d8a
-snip-
ブリッジとweb1コンテナを接続するNICでパケットをキャプチャしてみます。
[root@server ~]# tshark -i vetha0a16d8a -nn
Running as user "root" and group "root". This could be dangerous.
Capturing on 'vetha0a16d8a'
クライアントからweb1コンテナのnginxに対してhttpアクセスします。
[root@client ~]# curl -I http://192.168.122.68:8080
web1コンテナのnginxに対してhttpアクセスすると、下記HTTPパケットのやり取りが確認できます。1,2,3番のパケットを確認すると、クライアントからサーバのweb1コンテナのnginxに対してSYNパケットを送信することで、3 way Handshakeを実行して、TCPコネクションを確立していることがわかります。そして、8番のパケットで、クライアントからサーバのweb1コンテナのnginxに対してFINパケットを送信して、TCPコネクションの開放を開始しています。なお、8,9,10のパケットをやり取りすることでTCPコネクションを開放します。なお、TCPコネクションの確立、解放の様子は、TCPコネクションの確立、解放シーケンス - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# tshark -i vetha0a16d8a -nn
Running as user "root" and group "root". This could be dangerous.
Capturing on 'vetha0a16d8a'
1 0.000000000 192.168.122.177 → 10.88.0.2 TCP 74 57960 → 80 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=372010414 TSecr=0 WS=128
2 0.000116548 10.88.0.2 → 192.168.122.177 TCP 74 80 → 57960 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=2845155008 TSecr=372010414 WS=128
3 0.001329368 192.168.122.177 → 10.88.0.2 TCP 66 57960 → 80 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=372010416 TSecr=2845155008
4 0.001448952 192.168.122.177 → 10.88.0.2 HTTP 150 HEAD / HTTP/1.1
5 0.001659216 10.88.0.2 → 192.168.122.177 TCP 66 80 → 57960 [ACK] Seq=1 Ack=85 Win=29056 Len=0 TSval=2845155010 TSecr=372010416
6 0.003723081 10.88.0.2 → 192.168.122.177 HTTP 304 HTTP/1.1 200 OK
7 0.004638309 192.168.122.177 → 10.88.0.2 TCP 66 57960 → 80 [ACK] Seq=85 Ack=239 Win=30336 Len=0 TSval=372010420 TSecr=2845155012
8 0.005427533 192.168.122.177 → 10.88.0.2 TCP 66 57960 → 80 [FIN, ACK] Seq=85 Ack=239 Win=30336 Len=0 TSval=372010420 TSecr=2845155012
9 0.005777478 10.88.0.2 → 192.168.122.177 TCP 66 80 → 57960 [FIN, ACK] Seq=239 Ack=86 Win=29056 Len=0 TSval=2845155014 TSecr=372010420
10 0.006914414 192.168.122.177 → 10.88.0.2 TCP 66 57960 → 80 [ACK] Seq=86 Ack=240 Win=30336 Len=0 TSval=372010422 TSecr=2845155014