fbpx

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のさまざまな情報を発信していく予定です。

Author

Chef・Docker・Mirantis製品などの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA