3ノード — join・drain・kubelet 復旧
目標
ノード3台にsshで入ってkubeadmクラスターを組み立て、ワーカー1台を空にしてメンテナンスしたあと、再起動して戻ってこないもう1台のワーカーを復活させます。 CKAの実技が求める「複数のノードを行き来して作業する手」を、本物のVM3台で身につけます。
なぜ重要なのか
1台のクラスターでは、ノードが何なのか見えません。ノードが複数になった瞬間、3つのことが変わります。
1つ目、ノードのアドレスが問題になります。このラボの3つのVMは、最初のNICのアドレスがすべて10.0.2.2で、実際のクラスターネットワークは、2つ目のNIC(eth1)です。
kubeletの--node-ip、APIサーバーのadvertiseアドレス、CNIのトンネル用NICが、すべてeth1を指す必要があります。複数NICのサーバーでKubernetesを立ち上げるときに、実際に最も多く間違える箇所です。
2つ目、メンテナンスが生まれます。ノードを停止する前にPodを空にするdrainと、元に戻すuncordonは、運用の日常です。
3つ目、kubeletはPodではなく、systemdのサービスです。ノードがNotReadyのとき、kubectlでは原因に届かず、そのノードに入って、サービスを見る必要があります。
ステップ
- controlplaneで
ssh node01・ssh node02で各ノードに入り、hostnameとeth1のIPv4アドレスを確認してください。3つのノードを1行ずつ이름 주소の形式で、controlplane・node01・node02の順に書いてください(プレースホルダーは名前とアドレスです。保存先:/root/cluster/nodes.txt)。 - controlplaneで、
kubeadm initを--kubernetes-version v1.36.4 --apiserver-advertise-address <controlplane 의 eth1 주소> --pod-network-cidr 172.20.0.0/16 --service-cidr 172.21.0.0/16で実行し(プレースホルダーはcontrolplaneのeth1アドレスです)、/etc/kubernetes/admin.confをコピーしてください(コピー先:/root/.kube/config)。controlplaneノードのInternalIPが、eth1のアドレスである必要があります。 - あらかじめ取得しておいたFlannel v0.28.9のマニフェストで(場所:
/root/cluster/kube-flannel.yml)、net-conf.jsonのNetworkを172.20.0.0/16に変更し、kube-flannelコンテナのargsに--iface=eth1を追加して、適用してください。controlplaneがReadyになり、CoreDNSがAvailableである必要があります。 - controlplaneで参加コマンドを作成し(
kubeadm token create --print-join-command)、node01とnode02で実行してください。3つのノードがすべてReadyで、node01・node02のInternalIPが、それぞれのeth1のアドレスである必要があります。 - defaultネームスペースにDeployment
webを、イメージregistry.k8s.io/e2e-test-images/agnhost:2.53、コマンド/agnhost netexec --http-port=8080、レプリカ2で作成してください。2つのPodがワーカーでReadyで、controlplaneから各PodのIPの:8080/hostnameが、そのPod名を返す必要があります。 - node01を
kubectl drainで空にして(DaemonSetのPodは残しておきます)、その出力の全体を保存してください(保存先:/root/cluster/drain.log)。node01にDaemonSetではないPodが1つもなく、webは2つのPodがどちらもReadyである必要があります。 - node02を再起動してください(
ssh node02 reboot)。戻ってきたあと、node02がNotReadyのまま残る原因をnode02で見つけて直し、次の再起動でも自力で戻ってくるようにしてください。 - node01を再びスケジュール可能にしてください。3つのノードがすべてReadyでスケジュール可能で、Flannel DaemonSetが3つのノードで準備できており、
webの2つのPodがReadyである必要があります。
参考
- このターミナルはcontrolplaneです。ほかのノードは、
ssh node01・ssh node02で入り、exitで戻ります。3つのノードとも、rootです。 - 3つのノードに、containerd 2.3.5とkubeadm・kubelet・kubectl 1.36.4がインストールされており、ランタイムの設定(SystemdCgroup・ip_forward・br_netfilter)とイメージの取得は、終わっています。クラスターは、まだありません。
- 範囲は、Pod
172.20.0.0/16・サービス172.21.0.0/16です。このVMたちが動いている外側のクラスターが、10.244/16・10.96/12を使っているからです。 - よくあるミス: advertiseアドレスなしにinitすることです。ワーカーが10.0.2.2:6443でAPIサーバーを探しに行き、自分自身につながろうとします。元に戻すには、
kubeadm reset -fから行う必要があります。 - よくあるミス: ステップ7で、
systemctl start kubeletだけを行うことです。今は戻ってきますが、次の再起動で同じことが起こります。 - セッションが終わると、3つのVMが一緒に消えます。60分を超えたら、画面の+時間で延長してください。
3つのノードに入って2つ目のNICを確認する
controlplaneでssh node01・ssh node02で各ノードに入り、hostnameとeth1のIPv4アドレスを確認してください。3つのノードを1行ずつ이름 주소の形式で、controlplane・node01・node02の順に書いてください(プレースホルダーは名前とアドレスです。保存先: /root/cluster/nodes.txt)。
ip -4 addrを見ると、NICが2つあります。最初のNIC(enp1s0)のアドレスが、3つのノードすべてで同じであることを確認してみてください。そのアドレスでは、ノードを区別できません。ssh node01 'ip -4 -o addr show eth1'のように、コマンドだけを送ってもかまいません。
eth1のアドレスでコントロールプレーンを立ち上げる
controlplaneで、kubeadm initを--kubernetes-version v1.36.4 --apiserver-advertise-address <controlplane 의 eth1 주소> --pod-network-cidr 172.20.0.0/16 --service-cidr 172.21.0.0/16で実行し(プレースホルダーはcontrolplaneのeth1アドレスです)、/etc/kubernetes/admin.confをコピーしてください(コピー先: /root/.kube/config)。controlplaneノードのInternalIPが、eth1のアドレスである必要があります。
advertiseアドレスを指定しないと、kubeadmはデフォルトのルートのNIC(10.0.2.2)を選びます。そうすると、ワーカーがそのアドレスでAPIサーバーを探しに行って、自分自身につながります。kubelet側のアドレスは、/etc/default/kubeletの--node-ipが決めます。すでに書かれているので、読んでみてください。
FlannelにどのNICでトンネルを作るかを教える
あらかじめ取得しておいたFlannel v0.28.9のマニフェストで(場所: /root/cluster/kube-flannel.yml)、net-conf.jsonのNetworkを172.20.0.0/16に変更し、kube-flannelコンテナのargsに--iface=eth1を追加して、適用してください。controlplaneがReadyになり、CoreDNSがAvailableである必要があります。
Flannelは、デフォルトのルートのNICでVXLANを作ります。そのNICのアドレスが3つのノードすべてで同じだと、トンネルの宛先がすべて自分自身になります。ノードはReadyなのに、ほかのノードのPodに届かない症状です。argsは、--kube-subnet-mgrの行の下に、同じインデントで1行追加します。
ワーカー2台を参加させる
controlplaneで参加コマンドを作成し(kubeadm token create --print-join-command)、node01とnode02で実行してください。3つのノードがすべてReadyで、node01・node02のInternalIPが、それぞれのeth1のアドレスである必要があります。
参加コマンドは、ワーカーでrootとして実行します。ssh node01 '<조인 명령>'です(プレースホルダーは参加コマンドです)。参加が終わっても、FlannelのPodがそのノードで起動するまで、Readyは少し遅れます。参加の出力のWARNINGの行も、読んでおいてください。あとで役に立ちます。
別のノードのPodに実際に届くかを見る
defaultネームスペースにDeploymentwebを、イメージregistry.k8s.io/e2e-test-images/agnhost:2.53、コマンド/agnhost netexec --http-port=8080、レプリカ2で作成してください。2つのPodがワーカーでReadyで、controlplaneから各PodのIPの:8080/hostnameが、そのPod名を返す必要があります。
kubectl create deployment web --image=… --replicas=2 -- /agnhost netexec --http-port=8080の1行で済みます。controlplaneにはcontrol-planeテイントがあるので、Podはワーカーにだけ行きます。curlが止まるなら、FlannelのNICから疑ってください。
node01をメンテナンスのために空にする
node01をkubectl drainで空にして(DaemonSetのPodは残しておきます)、その出力の全体を保存してください(保存先: /root/cluster/drain.log)。node01にDaemonSetではないPodが1つもなく、webは2つのPodがどちらもReadyである必要があります。
ドレインは、cordon(スケジュール禁止)+エビクションです。Flannel・kube-proxyのようなDaemonSetのPodは、エビクトしてもすぐ同じノードに再び作られるので、--ignore-daemonsetsでスキップします。エビクトされたwebのPodは、node02に移っていきます。出力は、2>&1 | teeで、画面とファイルの両方に残せばよいです。
再起動したnode02が戻ってこない
node02を再起動してください(ssh node02 reboot)。戻ってきたあと、node02がNotReadyのまま残る原因をnode02で見つけて直し、次の再起動でも自力で戻ってくるようにしてください。
ノードがNotReadyなら、そのノードのkubeletから見ます。systemctl status kubeletです。今起動しているか(active)と、起動時に有効になるか(enabled)は、別の質問です。参加したときに出たWARNINGを、覚えていますか。
メンテナンスを終えて元に戻す
node01を再びスケジュール可能にしてください。3つのノードがすべてReadyでスケジュール可能で、Flannel DaemonSetが3つのノードで準備できており、webの2つのPodがReadyである必要があります。
drainを元に戻すのは、uncordonです。元に戻しても、すでにnode02に移ったPodが、ひとりでに戻ってくることはありません。スケジューラーは、新しいPodを配置するときにだけ判断します。