k0sとkubeadmでKonnectivityの動きを調べてみよう #k0s #mirantis #kubernetes #konnectivity
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
k0sとは、軽量かつ使いやすい、100%オープンソースのKubernetesディストリビューションです。主な特徴としては、
- フル機能のKubernetesを構築するために必要なすべてを単一バイナリに同梱
- k0s特有の改変を加えていない、CNCF認定の純正なKubernetesをデプロイ
- 最低必要リソース1vCPU・1GBメモリ・2GBストレージのシングルノードから、HA構成の大規模クラスタまでサポート
- Konnectivityをデフォルトで有効化
などが挙げられます。より詳しい情報は公式ドキュメントをご覧ください。
本稿では、k0sのデフォルトで有効化されているKonnectivityの動きについて、それを持たないkubeadmと比較して調べてみます。
Konnectivityの簡単な説明については以前の記事「k0sでEC2マスターとVirtualboxワーカーのKubernetesクラスタを作成してみよう」をご参照ください。
kubeadmの場合(Konnectivityなし)
パブリックラウド(AWS)上のKubernetesコントロールプレーンと、ローカルPC上のVirtualBoxワーカーという構成を、kubeadmでKonnectivityなしで構築した場合を見てみましょう。
- AMI: Ubuntu Server 20.04 LTS (HVM), SSD Volume Type (x86)
- インスタンスタイプ: t2.medium (2vCPU・4GiBメモリ)
- セキュリティグループでローカルPCのIPアドレスを全許可。
これで作成したコントロールプレーン用のEC2インスタンスのグローバルIPアドレスは 54.213.94.242 とします。
ローカルPCにVagrantとVirtualBoxがインストール済みで2台のゲストが準備済みであり、グローバルIPアドレスは XXX.YYY.ZZZ.254 とします。
これらのIPアドレスは適宜読み替えてください。
まず 54.213.94.242 にて Installing kubeadm の手順に従って kubeadm をインストールしコントロールプレーンを作成します。ここではCRIにDockerではなくcontainerdを使用しており、 --control-plane-endpoint に 54.213.94.242 のFQDNを指定することがポイントです。
ubuntu@ip-172-31-17-165:~$ sudo kubeadm init --ignore-preflight-errors=NumCPU \ --pod-network-cidr=10.244.0.0/16 --service-cidr=10.244.0.0/16 \ --control-plane-endpoint=ec2-54-213-94-242.us-west-2.compute.amazonaws.com \ --cri-socket=/run/containerd/containerd.sock
これで出力された kubeadm join コマンドを、ローカルPCのVirtualBoxゲスト2台で実施します。
kubectl get nodes と netstat で状況を確認してみましょう。
ubuntu@ip-172-31-17-165:~$ kubectl get node NAME STATUS ROLES AGE VERSION ip-172-31-17-165 NotReady control-plane,master 106s v1.22.3 node01 NotReady 41s v1.22.3 node02 NotReady 9s v1.22.3 ubuntu@ip-172-31-17-165:~$ sudo netstat -atnp | grep XXX.YYY tcp 0 36 172.31.17.165:22 XXX.YYY.ZZZ.254:49670 ESTABLISHED 1213/sshd: ubuntu [ tcp6 0 0 172.31.17.165:6443 XXX.YYY.ZZZ.254:40038 TIME_WAIT - tcp6 0 0 172.31.17.165:6443 XXX.YYY.ZZZ.254:40032 ESTABLISHED 8865/kube-apiserver tcp6 0 0 172.31.17.165:6443 XXX.YYY.ZZZ.254:40060 ESTABLISHED 8865/kube-apiserver tcp6 0 0 172.31.17.165:6443 XXX.YYY.ZZZ.254:40078 ESTABLISHED 8865/kube-apiserver tcp6 0 0 172.31.17.165:6443 XXX.YYY.ZZZ.254:40020 ESTABLISHED 8865/kube-apiserver
このようにワーカー(node01,node02)がクラスタに参加できており、ワーカー側からkube-apiserverに接続できていることがわかります。
なお、この状態でCalicoをインストールしようとしても、コントロールプレーンもワーカーも同じEC2上にある場合と異なり、正常に動作しませんが、本稿では本質的な部分ではないため解決は試みません。
では、既に起動しているシステムPodに対してkubectl logsを実行してみましょう。
ubuntu@ip-172-31-17-165:~$ kubectl -n kube-system get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-78fcd69978-4s4v7 0/1 Pending 0 17m coredns-78fcd69978-n7vwc 0/1 Pending 0 17m etcd-ip-172-31-17-165 1/1 Running 4 17m 172.31.17.165 ip-172-31-17-165 kube-apiserver-ip-172-31-17-165 1/1 Running 4 17m 172.31.17.165 ip-172-31-17-165 kube-controller-manager-ip-172-31-17-165 1/1 Running 5 17m 172.31.17.165 ip-172-31-17-165 kube-proxy-f6bkh 1/1 Running 0 17m 172.31.17.165 ip-172-31-17-165 kube-proxy-p8f9f 1/1 Running 0 16m 192.168.123.22 node02 kube-proxy-w5v8x 1/1 Running 0 16m 192.168.123.21 node01 kube-scheduler-ip-172-31-17-165 1/1 Running 5 17m 172.31.17.165 ip-172-31-17-165
ここではnode01で起動しているkube-proxy-w5v8xに対してkubectl logsを実行してみます。
ubuntu@ip-172-31-17-165:~$ kubectl -n kube-system logs kube-proxy-w5v8x ^Z [1]+ Stopped kubectl -n kube-system logs kube-proxy-w5v8x
なかなか結果が返ってこないのでCTRL+Zで一旦停止し、netstatで接続状況を見てみます。
ubuntu@ip-172-31-17-165:~$ sudo netstat -atnp | grep SYN tcp 0 1 172.31.17.165:48460 192.168.123.21:10250 SYN_SENT 8865/kube-apiserver
このようにkube-apiserverから、プライベートIPアドレスを持っているローカルPCのnode01に接続しようとしています。当然これは不可能であるため、
Error from server: Get "https://192.168.123.21:10250/containerLogs/kube-system/kube-proxy-w5v8x/kube-proxy": dial tcp 192.168.123.21:10250: i/o timeout [1]+ Exit 1 kubectl -n kube-system logs kube-proxy-w5v8x
しばらくするとタイムアウトしてしまいます。kubectl execやkubectl port-forwardも同様です。
ubuntu@ip-172-31-17-165:~$ kubectl -n kube-system exec kube-proxy-w5v8x -- sh Error from server: error dialing backend: dial tcp 192.168.123.21:10250: i/o timeout ubuntu@ip-172-31-17-165:~$ kubectl -n kube-system port-forward kube-proxy-w5v8x 8888:8888 error: error upgrading connection: error dialing backend: dial tcp 192.168.123.21:10250: i/o timeout
何が起きているのでしょうか? Kubernetes公式ドキュメントを見てみましょう。
ワーカーノードはKubelet APIを受け付けるため10250/tcpを開放していなければいけません。ではKubelet APIとは何でしょうか? 簡単には先ほどのkubelet logs/exec/port-forwardに利用されます。
このように、パブリックラウド上にコントロールプレーン、ローカルPC上にワーカーという構成で単純にKubernetesをインストールすると、正常に動作しないことがわかりました。
図の青矢印のようにkubeletからkube-apiserverへの接続は可能ですが、黄矢印のようにkube-apiserverからkubeletへの接続は不可能であるためです。
k0sの場合(Konnectivityあり)
では、パブリックラウド(AWS)上のKubernetesコントロールプレーンと、ローカルPC上のVirtualBoxワーカーという構成を、k0sで構築した場合を見てみましょう。内容としては以前の記事「k0sでEC2マスターとVirtualboxワーカーのKubernetesクラスタを作成してみよう」とほぼ同一です。
- AMI: Ubuntu Server 20.04 LTS (HVM), SSD Volume Type (x86)
- インスタンスタイプ: t2.medium (2vCPU・4GiBメモリ)
- セキュリティグループでローカルPCのIPアドレスを全許可。
これで作成したコントロールプレーン用のEC2インスタンスのグローバルIPアドレスは 34.221.254.215 とします。
ローカルPCにVagrantとVirtualBoxがインストール済みで2台のゲストが準備済みであり、グローバルIPアドレスは XXX.YYY.ZZZ.254 とします。
これらのIPアドレスは適宜読み替えてください。
以前の記事「k0sでEC2マスターとVirtualboxワーカーのKubernetesクラスタを作成してみよう」と同じく、spec.api.externalAddressキーとspec.api.sansキーにEC2インスタンスのグローバルIPアドレス 34.221.254.215 を追加するだけでOKです。
ubuntu@ip-172-31-30-231:~$ k0s default-config > k0s.yaml ubuntu@ip-172-31-30-231:~$ cp k0s.yaml k0s.yaml.orig ubuntu@ip-172-31-30-231:~$ vi k0s.yaml ubuntu@ip-172-31-30-231:~$ diff -u k0s.yaml.orig k0s.yaml --- k0s.yaml.orig 2021-11-10 03:16:32.102876955 +0000 +++ k0s.yaml 2021-11-10 03:18:00.956487688 +0000 @@ -5,10 +5,12 @@ spec: api: address: 172.31.30.231 + externalAddress: 34.221.254.215 port: 6443 k0sApiPort: 9443 sans: - 172.31.30.231 + - 34.221.254.215 storage: type: etcd etcd:
これでコントロールプレーンをインストールし、手順に従ってローカルPCのVirtualBoxゲスト2台を登録し、Kubernetesクラスタを構築します。
では、kubectl get nodes と netstat で状況を確認してみましょう。
ubuntu@ip-172-31-30-231:~$ k0s kubectl get nodes NAME STATUS ROLES AGE VERSION node01 Ready 11m v1.22.3+k0s node02 Ready 11m v1.22.3+k0s ubuntu@ip-172-31-30-231:~$ sudo netstat -atnp | grep XXX.YYY tcp 0 36 172.31.30.231:22 XXX.YYY.ZZZ.254:53788 ESTABLISHED 633/sshd: ubuntu [p tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57870 ESTABLISHED 1038/kube-apiserver tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57838 ESTABLISHED 1038/kube-apiserver tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57856 ESTABLISHED 1038/kube-apiserver tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57898 ESTABLISHED 1038/kube-apiserver tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57884 ESTABLISHED 1038/kube-apiserver tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57864 ESTABLISHED 1038/kube-apiserver tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57854 ESTABLISHED 1038/kube-apiserver tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57808 ESTABLISHED 1038/kube-apiserver tcp6 0 0 172.31.30.231:8132 XXX.YYY.ZZZ.254:55562 ESTABLISHED 1086/konnectivity-s tcp6 0 0 172.31.30.231:8132 XXX.YYY.ZZZ.254:55564 ESTABLISHED 1086/konnectivity-s tcp6 0 0 172.31.30.231:6443 XXX.YYY.ZZZ.254:57886 ESTABLISHED 1038/kube-apiserver
このようにワーカー(node01,node02)がクラスタに参加できており、ワーカー側からkube-apiserverと、加えてkonnectivity-serverに接続できていることがわかります。
なお、k0sはデフォルトで自動的にKube-Routerをインストール・設定するため、kubeadmのように別途CNIプラグインをインストールする必要はありません。システムPodを見てみましょう。
ubuntu@ip-172-31-30-231:~$ k0s kubectl -n kube-system get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-77b4ff5f78-l7hkv 1/1 Running 0 20m 10.244.0.5 node01 coredns-77b4ff5f78-lddjd 1/1 Running 0 13m 10.244.0.3 node01 konnectivity-agent-gtqd7 1/1 Running 0 11m 10.244.1.2 node02 konnectivity-agent-rd98z 1/1 Running 0 12m 10.244.0.4 node01 kube-proxy-hzv4c 1/1 Running 0 13m 192.168.123.22 node02 kube-proxy-k9kx9 1/1 Running 0 14m 192.168.123.21 node01 kube-router-p749h 1/1 Running 0 13m 192.168.123.22 node02 kube-router-rdh9q 1/1 Running 0 14m 192.168.123.21 node01 metrics-server-5b898fd875-glfj2 1/1 Running 0 20m 10.244.0.2 node01
では、node01で起動しているkube-proxy-k9kx9に対してkubectl logsを実行してみます。
ubuntu@ip-172-31-30-231:~$ k0s kubectl -n kube-system logs kube-proxy-k9kx9 | head -5 I1110 01:52:46.187150 1 node.go:172] Successfully retrieved node IP: 192.168.123.21 I1110 01:52:46.187236 1 server_others.go:140] Detected node IP 192.168.123.21 I1110 01:52:46.262041 1 server_others.go:206] kube-proxy running in dual-stack mode, IPv4-primary I1110 01:52:46.262115 1 server_others.go:212] Using iptables Proxier. I1110 01:52:46.262140 1 server_others.go:219] creating dualStackProxier for iptables.
正しくログを取ることができました。kubectl logsをバックグラウンドで動作させ、ネットワークの接続状態を見てみましょう。
ubuntu@ip-172-31-30-231:~$ k0s kubectl -n kube-system logs -f kube-proxy-k9kx9 > /dev/null 2> /dev/null & [1] 1354 ubuntu@ip-172-31-30-231:~$ sudo netstat -atnp | grep SYN ubuntu@ip-172-31-30-231:~$
Konnectivityが動作していないkubeadmでのクラスタ構築では、パブリックのkube-apiserverからプライベートのkubeletに接続しようとして失敗していましたが、Konnectivityが動作しているk0sによるインストールではそのようなことは起きていないようです。
Konnectivityの動作状況をnode01で調べてみます。しかし、単純にnode01ゲスト上でnetstatしても、パブリッククラウド上のkonnectivity-serverへの接続状態は見えません。なぜなら、node01で動作しているkonnectivity-agentはPodとして動いているため、別のネットワークネームスペースに存在しているからです。konnectivity-agentのネットワークネームスペースを調べてみましょう。
まず、konnectivity-agent (proxy-agent)のPIDから、ip netns identifyコマンドを使って、ネットワークネームスペース名を割り出します。
vagrant@node01:~$ ps auxwwwf|grep '[ p]roxy-agent' root 4247 0.0 0.1 715128 13956 ? Ssl 10:54 0:01 _ /proxy-agent --logtostderr=true --ca-cert=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt --proxy-server-host=34.221.254.215 --proxy-server-port=8132 --service-account-token-path=/var/run/secrets/tokens/konnectivity-agent-token vagrant@node01:~$ sudo ip netns identify 4247 cni-2b1226ec-c0f3-1436-5440-1e1349bccee5
konnectivity-agent (proxy-agent)が動作しているネットワークネームスペースがわかったので、こちらでnetstatしてみます。
vagrant@node01:~$ sudo ip netns exec cni-2b1226ec-c0f3-1436-5440-1e1349bccee5 netstat -atnp | grep 34.221.254.215 tcp 0 0 10.244.0.4:59084 34.221.254.215:8132 ESTABLISHED 4247/proxy-agent
このようにkonnectivity-agentが、EC2インスタンス(34.221.254.215)上のkonnectivity-server (8132/tcp)に接続しています。
Konnectivityを有効にしてkubectl logs/exec/port-forwardを実行すると、kube-apiserverからkonnectivity-serverへ、konnectivity-serverに接続しているkonnectivity-agentからkubeletへと、konnectivity-server/agentを介して動作することになります。
kubeadmでクラスタ構築した場合はSet up Konnectivity serviceの手順に従ってKonnectivityを有効にする必要があるようですが、k0sならばデフォルトでKonnectivityが有効なのですぐに使うことができるようになっています。
まとめ
本稿ではKonnectivityについて、それがないkubeadmでKubernetesクラスタを構築した場合と、デフォルトで有効になるk0sでKubernetesクラスタを構築した場合のそれぞれで、kube-apiserverとkubeletの動きも含めて調べてみました。
Konnectivityによってkube-apiserverから直接疎通できないkubeletに対しても通信できるようになっています。またk0sはKonnectivityがデフォルトで有効なので、k0sの通常のクラスタ構築手順でもすぐ使うことができます。
パブリッククラウドにコントロールプレーンを置き、プライベートネットワークやIoT機器をワーカーとするKubernetesクラスタを構築する際に、軽量かつデフォルトで有効なKonnectivityを備えるk0sは適した選択肢と考えられます。本ブログでは引き続きk0sのさまざまな情報を発信していく予定です。