コンテナーのネットワークについて
1 はじめに
podmanコマンドを使ってコンテナを生成した際のネットワークについて説明します。なお、podmanコマンドの基本的な使い方は、podmanコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。
1.1 コンテナ生成前
コンテナの生成前の環境は以下です。サーバとクライアントの2台構成です。サーバは受信したパケットの宛先IPアドレスをキーにルーティングテーブルを検索します。検索した結果、自身宛ならサーバのプロセスに受信したパケットを転送します。
+---------------------- server ------------------------------------+ | | | +- servre's process -+ | | | For example: | | | | httpd, chronyd | | | +--------------------+ | | A | | | | | | | | | | | | | | | | | | | | | | | | | | +--------------------+ | | | Routing | | | +--------------------+ | | | | | | +----+ +----+ | +--- client ---+ | | | | | | | | V | | +--------+ | | +--------+ | +--| enp1s0 |--+ +-------| enp1s0 |-------------------------------------------------+ +--------+ +--------+ | .177 | .68 | | ------------------------------------------------------------------------------------- 192.168.122.0/24
1.2 コンテナ生成後
コンテナ(web1,web2)を生成すると、コンテナとホストを接続するブリッジが作成されます。サーバは受信したパケットの宛先IPアドレスをキーにルーティングテーブルを検索します。検索した結果、自身宛ならサーバのプロセスに受信したパケットを転送します。コンテナ宛てなら、受信したパケットをcni-podman0より送信します。なお、図中のデバイスの意味は以下のとおりです。
デバイス | 概要 |
---|---|
cni-podman0 | ブリッジです。宛先MACアドレスをキーにパケットの出力NICを決定するデバイスです |
vetha0a16d8a,veth8b2242ec | ブリッジに作成されたNICです |
enp1s0 | サーバ/クライアントのNICです |
eth0 | コンテナのNICです |
+---------------------- server ------------------------------------+ | | | +- servre's process -+ +---- web1 ----+ +---- web2 ----+ | | | | | | | | | | For example: | | nginx | | httpd | | | | httpd, chronyd | | | | | | | +--------------------+ | +----+ | | +----+ | | | A +----|eth0|----+ +----|eth0|----+ | | | +----+ +----+ | | | | | | | | +--------------+ +--------------+ | | | +-| vetha0a16d8a |--| veth8b2242ec |-+ | | | | +--------------+ +--------------+ | | | | | | | | | | cni-podman0 | | | | | (Bridge device) | | | +--------------------+ | | | | | Routing | -> | | | | +--------------------+ +------------------------------------+ | | | | | | +----+ +----+ | +--- client ---+ | | | | | | | | V | | +--------+ | | +--------+ | +--| enp1s0 |--+ +-------| enp1s0 |-------------------------------------------------+ +--------+ +--------+ | .177 | .68 | | ------------------------------------------------------------------------------------- 192.168.122.0/24
2 検証環境
クライアント、サーバのAlmaLinuxの版数は以下のとおりです。
[root@server ~]# cat /etc/redhat-release AlmaLinux release 8.6 (Sky Tiger)
カーネル版数は以下のとおりです。
[root@server ~]# uname -r 4.18.0-372.9.1.el8.x86_64
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
Z 参考情報
私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ