本稿の内容は以下「How To Inspect Kubernetes Networking」の記事の前半部分を参考に、Kubernetes 1.18.6、CRIとしてDocker、CNIとしてFlannelで動作確認したものをまとめています。
前書き
Kubernetesは、サーバーノードのクラスター全体でコンテナー化されたアプリケーションを管理できるコンテナーオーケストレーションシステムです。 クラスター内のすべてのコンテナー間のネットワーク接続を維持するには、いくつかの高度なネットワークに関する技術が必要です。 この記事では、Kubernetesのネットワークを検査するためのいくつかのツールと手法について簡単に説明します。
これらのツールは、接続の問題をデバッグしたり、ネットワークスループットの問題を調査したり、Kubernetesを調べてその動作を確認したりする場合に役立ちます。
Getting Started
このチュートリアルでは、次の項目で説明する構成のKubernetesクラスターを用いた場合の結果です。 kubectlはKubernetesホストのローカルにインストールされており、そのノード上で実行することを前提としています。
ノード構成
以下の構成で実験していますが、OpenStackインスタンス以外で動かした場合も基本的に一緒です。 CRI,CNIによって若干異なります。
- OpenStackインスタンスでKubernetesクラスターを稼働
- Kubernetes CRIとしてDockerを利用
- Kubernetes CNIとしてFlannelを利用
- Kubernetes Master + Worker構成
ノードを確認
ノード一覧をコマンドを実行して確認します。
root@k8s-demo-1:~# kubectl get no NAME STATUS ROLES AGE VERSION k8s-demo-1 Ready master 3h55m v1.18.6 k8s-demo-2 Ready <none> 3h43m v1.18.6
Podネットワークの確認
まず、適当なPodを作成します。
root@k8s-demo-1:~# kubectl run --generator=run-pod/v1 --tty=true -it hello-k8s --image=alpine -- sh
WorkerノードでPodのCONTAINER ID,CONTAINER NAMEを確認します。
root@k8s-demo-2:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 27b2de56b27c alpine "sh" 10 minutes ago Up 10 minutes k8s_hello-k8s_hello-k8s_default_ef3142f3-ad83-464c-b781-b3a589e0b2e0_0
確認したCONTAINER ID,CONTAINER NAMEを用いて、コンテナーのプロセスID(上記コマンドのコンテナー名を指定)を確認
root@k8s-demo-2:~# docker inspect --format '{{ .State.Pid }}' k8s_hello-k8s_hello-k8s_default_ef3142f3-ad83-464c-b781-b3a589e0b2e0_0 3624
プロセスIDを用いて、そのプロセスのネットワーク名前空間でipコマンドを実行します。 これにより、コンテナーのNICに割り当てられたIPアドレスやポッドの仮想イーサネットインターフェイスを確認できます。 以下の場合は「eth0@if11」がそれであり、ノードのインターフェイス11に接続されており、IPアドレスとして10.244.1.5が割り当てられていることがわかります。
root@k8s-demo-2:~# nsenter -t 3624 -n ip addr 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 2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 3: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000 link/sit 0.0.0.0 brd 0.0.0.0 5: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1408 qdisc noqueue state UP group default link/ether 52:d0:a7:66:d3:53 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
ノード上でip addrコマンドを実行し、インターフェイス11を探します。
root@k8s-demo-2:~# ip a ... 11: veth75a6ccd3@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1408 qdisc noqueue master cni0 state UP group default link/ether 86:87:d3:13:bf:09 brd ff:ff:ff:ff:ff:ff link-netnsid 2 inet6 fe80::8487:d3ff:fe13:bf09/64 scope link valid_lft forever preferred_lft forever
この出力例では、11番目のインターフェースの「veth75a6ccd3」が該当のインターフェイスです。
tcpdumpコマンドでdumpを取ってみます。
root@k8s-demo-2:~# tcpdump -i veth75a6ccd3 -Q inout icmp
コンテナーの中でpingを実行
/ # ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: seq=0 ttl=111 time=3.045 ms 64 bytes from 8.8.8.8: seq=1 ttl=111 time=2.774 ms 64 bytes from 8.8.8.8: seq=2 ttl=111 time=11.785 ms ...
tcpdumpコマンドにより、PODが外部に通信する際、指定したインターフェイスが使われていることが確認できました。
root@k8s-demo-2:~# tcpdump -i veth75a6ccd3 -Q inout icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on veth75a6ccd3, link-type EN10MB (Ethernet), capture size 262144 bytes 06:32:38.008431 IP 10.244.1.5 > dns.google: ICMP echo request, id 3584, seq 0, length 64 06:32:38.011485 IP dns.google > 10.244.1.5: ICMP echo reply, id 3584, seq 0, length 64 06:32:39.008740 IP 10.244.1.5 > dns.google: ICMP echo request, id 3584, seq 1, length 64 06:32:39.011403 IP dns.google > 10.244.1.5: ICMP echo reply, id 3584, seq 1, length 64 ... ^C 16 packets captured 16 packets received by filter 0 packets dropped by kernel
「veth75a6ccd3」というインターフェイスはcni0ブリッジに集約されていることがわかります。
root@k8s-demo-2:~# brctl show bridge name bridge id STP enabled interfaces cni0 8000.d6d734a198c5 no veth4be74028 veth75a6ccd3 vethf81b42a8
cni0はKubernetes CNIのルーターのようなものです。インターフェイス名はCRI、CNIの組み合わせによっては異なりますが、規則性があるのでわかると思います。
7: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1408 qdisc noqueue state UP group default qlen 1000 link/ether d6:d7:34:a1:98:c5 brd ff:ff:ff:ff:ff:ff inet 10.244.1.1/24 brd 10.244.1.255 scope global cni0 valid_lft forever preferred_lft forever inet6 fe80::d4d7:34ff:fea1:98c5/64 scope link valid_lft forever preferred_lft forever
Pod内でtracerouteを実行すると、Workerノードのcni0に行った後に10.0.1.1を経由してパケットが流れることがわかります。
/ # traceroute 8.8.8.8 traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 46 byte packets 1 10.244.1.1 (10.244.1.1) 0.020 ms 0.012 ms 0.008 ms 2 10.0.1.1 (10.0.1.1) 0.610 ms 0.512 ms^C
WorkerノードのIPアドレスは以下のように設定されており...
2: ens2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1458 qdisc pfifo_fast state UP group default qlen 1000 link/ether fa:16:3e:e1:a0:fb brd ff:ff:ff:ff:ff:ff inet 10.0.1.167/24 brd 10.0.1.255 scope global dynamic ens2 valid_lft 71230sec preferred_lft 71230sec inet6 fe80::f816:3eff:fee1:a0fb/64 scope link valid_lft forever preferred_lft forever
このノードのゲートウェイとして設定されているIPアドレスは「10.0.1.1」であることがわかります。
root@k8s-demo-2:~# ip r default via 10.0.1.1 dev ens2 proto dhcp src 10.0.1.167 metric 100 10.0.1.0/24 dev ens2 proto kernel scope link src 10.0.1.167 10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink 10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 169.254.169.254 via 10.0.1.1 dev ens2 proto dhcp src 10.0.1.167 metric 100 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
このことから、Podからの通信はPodの仮想インターフェイスからWorkerノードのveth、cni0を経由して、ノードのルートネットと通信します。ノードのルートネットから外部のノードと通信できます。