Kubespray と Terraform でクラスターを構築する
1.35.8 から 1.36.4 へ — 同じノード、新しいバージョン
目標
kubespray v2.32.0でKubernetes 1.35.8クラスターを構築してワークロードを起動しておき、インベントリのバージョンを1.36.4に変更して、upgrade-cluster.ymlで1つ上げます。上げる前後を「写真」として残し、何が変わって何がそのままかを証拠で語ります。
なぜ重要なのか
Kubernetesは、おおむね4か月ごとにマイナーバージョンが出て、サポート期間が過ぎるとセキュリティパッチが途切れます。そのため、アップグレードは1回やって終わりの作業ではなく、定期的な作業です。 kubesprayは、アップグレードをインストールと同じ宣言として扱います。インベントリのバージョンを変更して、プレイブックを実行します。その代わり、順序があります。マイナーバージョンを飛ばさず、kubesprayのタグを飛ばさず、ノードごとにcordon → drain → 更新 → uncordonを行います。ノードが1台だと、drainの間ワークロードが止まることも、このラボで自分で見ます。 インストールとアップグレードを合わせて、約13分待ちます。余裕が足りなければ、セッションを延長してください。
ステップ
/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.ymlを実行して、出力の全体を残してください(約7分、保存先:/root/ks/logs/cluster-1.log)。インベントリのkube_versionは1.35.8です。終わったら、APIサーバーのバージョンがv1.35.8である必要があります。- defaultネームスペースにDeployment
web(イメージはregistry.k8s.io/e2e-test-images/agnhost:2.59、引数はnetexec --http-port=8080、レプリカ2)を作成して、2つのPodがReadyになるようにしてください。そのあと次のフィールドを書いてください(書き込み先:/root/ks/upgrade/before.json)。server_version、kubelet_version、etcd_version(etcd --versionのバージョン)、coredns_image(coredns Deploymentのイメージ)、node_uid、web_pod_uids(webのPodのUIDをソートした配列)です。 kube_versionを1.36.4に変更してください(対象:/root/ks/inventory/lab/group_vars/k8s_cluster/k8s-cluster.yml)。-eで渡さずに、ファイルを直します。ansible-inventory --host node1が1.36.4を返す必要があります。/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini upgrade-cluster.ymlを実行して、出力の全体を残してください(約6分、保存先:/root/ks/logs/upgrade.log)。PLAY RECAPがfailed=0で、APIサーバーとkubeletがどちらもv1.36.4で、/etc/kubernetes/kubeadm-config.yamlのkubernetesVersionもv1.36.4である必要があります。- ステップ2と同じフィールドで、ファイルを書いてください(書き込み先:
/root/ks/upgrade/after.json)。そして、same_node(ノードのUIDがそのままか)、web_pods_recreated(webのPodのUIDがすべて変わったか)、etcd_changed(etcdのバージョンが変わったか)、coredns_changed(corednsのイメージが変わったか)を、ブール値で書いてください(書き込み先:/root/ks/upgrade/diff.json)。 /root/ks/logs/upgrade.logから、upgrade/pre-upgradeとpost-upgradeロールのタスクを探して、次のフィールドを書いてください(書き込み先:/root/ks/upgrade/drain.json)。cordon_changed、drain_changed、uncordon_changed(それぞれ「Cordon node」「Drain node」「Uncordon node」のタスクがchangedを出したか、ブール値)、drain_sec(「Drain node」のタスクにかかった秒、TASKS RECAPの値を四捨五入した整数、一覧になければ0)、node_schedulable_now(現在のノードのspec.unschedulableが真ではないか、ブール値)です。- 次のフィールドを書いてください(書き込み先:
/root/ks/upgrade/next.json)。running(現在のAPIサーバーのバージョン)、max_supported(このkubesprayのバージョンがインストールできる最も高いKubernetesのバージョン、チェックサム一覧の最初のキー)、needs_new_kubespray(次のマイナーバージョンに上げるには、kubesprayを次のタグに先に上げる必要があるか、ブール値)です。
参考
- kubespray v2.32.0が
/opt/ks/kubesprayに、インベントリが/root/ks/inventory/lab/inventory.ini(kube_version 1.35.8)に用意されています。 - よくあるミス:
-e kube_version=1.36.4だけで上げて、インベントリをそのままにすることです。次の人が、古いバージョンが書かれたインベントリでcluster.ymlを実行することになります。 - よくあるミス: アップグレードが途中で失敗した後、ノードがcordonされたまま残っていることに気づかないことです。
kubectl get nodeのSchedulingDisabledを確認してください。 - ドキュメント: Kubespray: Upgrading Kubernetes・Kubernetes: Version Skew Policy・Kubernetes: Upgrading kubeadm clusters
1つ下(1.35.8)で構築する
/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.ymlを実行して、出力の全体を残してください(約7分、保存先: /root/ks/logs/cluster-1.log)。インベントリのkube_versionは1.35.8です。終わったら、APIサーバーのバージョンがv1.35.8である必要があります。
アップグレードを練習するには、1つ下のバージョンが先にある必要があります。このkubesprayのバージョンのデフォルト値は1.36.4なので、インベントリに1.35.8を書いておかないと、最初から1.36.4がインストールされます。
上げる前の写真
defaultネームスペースにDeploymentweb(イメージはregistry.k8s.io/e2e-test-images/agnhost:2.59、引数はnetexec --http-port=8080、レプリカ2)を作成して、2つのPodがReadyになるようにしてください。そのあと次のフィールドを書いてください(書き込み先: /root/ks/upgrade/before.json)。server_version、kubelet_version、etcd_version(etcd --versionのバージョン)、coredns_image(coredns Deploymentのイメージ)、node_uid、web_pod_uids(webのPodのUIDをソートした配列)です。
アップグレードの後に何が変わって何がそのままかを語るには、上げる前の値を先に残す必要があります。etcdはPodではなくホストのサービスなので、kubectlではなくバイナリでバージョンを尋ねます。採点ツールは、この記録がアップグレードより先に書かれたかどうかを、バージョン番号で照合します。
バージョンはインベントリで上げる
kube_versionを1.36.4に変更してください(対象: /root/ks/inventory/lab/group_vars/k8s_cluster/k8s-cluster.yml)。-eで渡さずに、ファイルを直します。ansible-inventory --host node1が1.36.4を返す必要があります。
kubesprayのアップグレードのドキュメントは、インベントリにバージョンを書いてあるなら、upgrade-cluster.ymlを実行する前にその値を直すよう求めています。-eだけで上げると、インベントリには古いバージョンが残り、次に誰かがそのインベントリでcluster.ymlを実行すると、クラスターとインベントリが食い違った状態から始まります。
upgrade-cluster.yml
/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini upgrade-cluster.ymlを実行して、出力の全体を残してください(約6分、保存先: /root/ks/logs/upgrade.log)。PLAY RECAPがfailed=0で、APIサーバーとkubeletがどちらもv1.36.4で、/etc/kubernetes/kubeadm-config.yamlのkubernetesVersionもv1.36.4である必要があります。
upgrade-cluster.ymlは、すでにあるクラスターにだけ使うプレイブックで、ノードごとにcordon → drain → 更新 → uncordonを行います。ノードが1台だと、drainする間にPodの行き先がなく、しばらくPendingになります。1台構成のクラスターのアップグレードは、そのまま中断時間だという意味です。待っている間に、ログのPLAYの見出しを追ってみてください。
上げた後の写真
ステップ2と同じフィールドで、ファイルを書いてください(書き込み先: /root/ks/upgrade/after.json)。そして、same_node(ノードのUIDがそのままか)、web_pods_recreated(webのPodのUIDがすべて変わったか)、etcd_changed(etcdのバージョンが変わったか)、coredns_changed(corednsのイメージが変わったか)を、ブール値で書いてください(書き込み先: /root/ks/upgrade/diff.json)。
Kubernetesを1つ上げても、すべてのコンポーネントが一緒に上がるわけではありません。kubesprayは、etcdとCoreDNSのバージョンを、Kubernetesのマイナーバージョンごとの表(etcd_supported_versions、coredns_supported_versions)で選びます。2つのバージョンが表で同じ値を指していれば、そのまま残ります。
ログで読み取るdrain
/root/ks/logs/upgrade.logから、upgrade/pre-upgradeとpost-upgradeロールのタスクを探して、次のフィールドを書いてください(書き込み先: /root/ks/upgrade/drain.json)。cordon_changed、drain_changed、uncordon_changed(それぞれ「Cordon node」「Drain node」「Uncordon node」のタスクがchangedを出したか、ブール値)、drain_sec(「Drain node」のタスクにかかった秒、TASKS RECAPの値を四捨五入した整数、一覧になければ0)、node_schedulable_now(現在のノードのspec.unschedulableが真ではないか、ブール値)です。
profile_tasksのTASKS RECAPの一覧には、時間のかかったタスクだけが出ます。drainが一覧にないなら、すぐに終わったという意味です。アップグレードが途中で失敗すると、ノードがcordonされたまま残ることがあるので、終わった後にunschedulableを確認する習慣が必要です。
次の1つはどこから来るのか
次のフィールドを書いてください(書き込み先: /root/ks/upgrade/next.json)。running(現在のAPIサーバーのバージョン)、max_supported(このkubesprayのバージョンがインストールできる最も高いKubernetesのバージョン、チェックサム一覧の最初のキー)、needs_new_kubespray(次のマイナーバージョンに上げるには、kubesprayを次のタグに先に上げる必要があるか、ブール値)です。
kubesprayは、タグごとに受け入れるバージョンをチェックサムで固定します。現在のバージョンが一覧の一番上なら、このkubesprayではこれ以上は上げられず、ドキュメントの「複数回のアップグレード」の節のとおり、kubesprayのタグを1つずつ上げてから、upgrade-cluster.ymlを実行する必要があります。タグを飛ばすことは、サポートされていません。