TT Lab
はじめる
学ぶ 学習パス コース

Kubernetes運用実務

ノードを空けてアップグレード計画を立てる

TT Labで続きを見る

目標

ノード1台を安全に空にして元に戻す手順を自分の手で実行し、バージョンスキューのルールとカナリアロールアウトの計画を、ドキュメントではなく検証可能な成果物として作ります。

なぜ重要なのか

アップグレード事故の大半は、コマンドを知らないからではなく、順序と範囲を知らないから起きます。元に戻せない作業(kubeadm upgrade apply)の前には、必ずetcdスナップショットがなければなりません。コントロールプレーンのダウングレードはサポートされていないため、失敗したときの脱出口は「下げること」ではなく「スナップショットから復旧すること」だけだからです。範囲の問題は、バージョンスキューが担当します。kubeletはapiserverより低いことはできても高いことはできず、kubectlだけが唯一、上に1つの余裕が認められています。この非対称性を知らないと、「kubectlは最新を使ってもいいのに、なぜkubeletはだめなのか」といった疑問で行き詰まります。最後に、ノード作業の3段階(cordon → drain → uncordon)とPDBはセットです。PDBがなければdrainがサービスをまるごと止めてしまうことがあり、PDBが厳しすぎればdrainがいつまでも終わりません。

ステップ

  1. /root/ops/upgrade/out/versions.jsonを作成してください。最上位のキーはserver(v1.x.y形式のサーバーバージョン文字列)とnodesです。nodesはlab-node-0、lab-node-1、lab-node-2の3つをキーに持ち、値は各ノードの実際のkubeletバージョン文字列である必要があります。
  2. lab-node-1だけをcordonし、直後のノード一覧を/root/ops/upgrade/out/cordon.txtに保存してください。ファイルにlab-node-1とSchedulingDisabledが見えていて、lab-node-0はブロックされていてはいけません。そして/root/ops/upgrade/out/cordon-note.txtに、cordonがすでに起動しているPodには手を触れないという点を1行で書いてください。
  3. ネームスペースops-upgradeを作成し、その中にreplicas: 3のDeploymentweb(Podのラベルはapp=web)を作成してください。続けてPodDisruptionBudgetweb-pdbを同じネームスペースに、spec.minAvailable: 2、spec.selector.matchLabels.app: webで作成します。maxUnavailableは使わないでください。
  4. lab-node-1をdrainして、その出力を/root/ops/upgrade/out/drain.txtに保存してください。drainが終わったあと、ops-upgradeネームスペースのPodがどのノードにあるかを、/root/ops/upgrade/out/after-drain.txtに3行以上で残してください。このファイルにlab-node-1が含まれていてはならず、lab-node-0またはlab-node-2が見えている必要があります。
  5. lab-node-1をuncordonして、その出力を/root/ops/upgrade/out/uncordon.txtに保存してください。3つのノードすべてがスケジュール可能な状態で、DeploymentwebのReadyなレプリカが再び3つになっている必要があります。
  6. /opt/lab/fixtures/k8s/skew-template.yamlを/root/ops/upgrade/skew.yamlにコピーしたあと、answersの下の疑問符を埋めてください。apiserver: "1.34"は問題の前提なので、そのままにしておきます。回答の形式は"1.31"のようなマイナーバージョンの文字列で、downgrade_supportedだけは"yes"または"no"です。埋めるべきキーはmin_kubelet、max_kubelet、max_kubectl、min_kubectl、max_controller_manager、next_upgrade_target、downgrade_supportedです。
  7. /root/ops/upgrade/runbook.mdを500バイト以上で書いてください。各項目は別々の行に置き、次の順序を守る必要があります。(1)etcdスナップショットのバックアップ、(2)kubeadm upgrade plan、(3)最初のコントロールプレーンでのkubeadm upgrade apply、(4)残りのノードでのkubeadm upgrade node、そしてノード作業の区間ではdrainがuncordonより先に出てくる必要があります。マイナーバージョンを飛ばせないという制約も、文章で書いてください(例: 「一度に1つ」)。
  8. 3つのノードにupgrade.labhub.io/stageラベルを付けてください。lab-node-0はcanary、lab-node-1はbatch1、lab-node-2はbatch2です。そして/root/ops/upgrade/out/rollout.jsonに計画を書いてください。stagesは長さ3の配列で、各要素はstage(canary/batch1/batch2)とnodes(ノード名の配列)を持ちます。最初のステージはcanaryで、ノードはちょうど1台である必要があり、3つのノードすべてがどこかに割り当てられている必要があります。最上位にverify_between_stagesキーを置き、ステージの間に何を確認するかを書いてください。

参考

コントロールプレーンとノードのバージョンを収集する

/root/ops/upgrade/out/versions.jsonを作成してください。最上位のキーはserver(v1.x.y形式のサーバーバージョン文字列)とnodesです。nodesはlab-node-0、lab-node-1、lab-node-2の3つをキーに持ち、値は各ノードの実際のkubeletバージョン文字列である必要があります。

アップグレードは、現在のバージョンを正確に知ることから始まります。サーバーバージョンと各ノードのkubeletバージョンは、それぞれ別の場所から読み取ります。ノード情報はstatus.nodeInfoの下にあります。

ノード1台だけをスケジュール対象から外す

lab-node-1だけをcordonし、直後のノード一覧を/root/ops/upgrade/out/cordon.txtに保存してください。ファイルにlab-node-1とSchedulingDisabledが見えていて、lab-node-0はブロックされていてはいけません。そして/root/ops/upgrade/out/cordon-note.txtに、cordonがすでに起動しているPodには手を触れないという点を1行で書いてください。

一度に1台ずつが原則です。cordonはこれからのスケジュールを止めるだけで、すでに起動しているPodには手を触れないという点を、メモに残してください。

可用性を守るPDBを作成する

ネームスペースops-upgradeを作成し、その中にreplicas: 3のDeploymentweb(Podのラベルはapp=web)を作成してください。続けてPodDisruptionBudgetweb-pdbを同じネームスペースに、spec.minAvailable: 2、spec.selector.matchLabels.app: webで作成します。maxUnavailableは使わないでください。

3つのレプリカのうち、最低いくつが生きていなければならないかを宣言します。最小値と最大利用不可値は、同時には使えません。

ノードを空にしてPodの移動を確認する

lab-node-1をdrainして、その出力を/root/ops/upgrade/out/drain.txtに保存してください。drainが終わったあと、ops-upgradeネームスペースのPodがどのノードにあるかを、/root/ops/upgrade/out/after-drain.txtに3行以上で残してください。このファイルにlab-node-1が含まれていてはならず、lab-node-0またはlab-node-2が見えている必要があります。

drainはcordonに加えて、既存のPodを追い出します。DaemonSetとemptyDirに関するオプションがないと拒否されます。空にしたあと、Podがどのノードにあるかを保存してください。

作業が終わったノードを元に戻す

lab-node-1をuncordonして、その出力を/root/ops/upgrade/out/uncordon.txtに保存してください。3つのノードすべてがスケジュール可能な状態で、DeploymentwebのReadyなレプリカが再び3つになっている必要があります。

uncordonを忘れると、そのノードは静かに遊んでしまいます。3つのノードすべてがスケジュール可能な状態で、ワークロードも元の数に戻っている必要があります。

バージョンスキュー表を埋める

/opt/lab/fixtures/k8s/skew-template.yamlを/root/ops/upgrade/skew.yamlにコピーしたあと、answersの下の疑問符を埋めてください。apiserver: "1.34"は問題の前提なので、そのままにしておきます。回答の形式は"1.31"のようなマイナーバージョンの文字列で、downgrade_supportedだけは"yes"または"no"です。埋めるべきキーはmin_kubelet、max_kubelet、max_kubectl、min_kubectl、max_controller_manager、next_upgrade_target、downgrade_supportedです。

基準は常にapiserverです。kubeletは下に3マイナー、コントローラーは下に1マイナー、kubectlだけが上下に1マイナーです。マイナーは一度に1つずつ上げます。

アップグレードのランブックを書く

/root/ops/upgrade/runbook.mdを500バイト以上で書いてください。各項目は別々の行に置き、次の順序を守る必要があります。(1)etcdスナップショットのバックアップ、(2)kubeadm upgrade plan、(3)最初のコントロールプレーンでのkubeadm upgrade apply、(4)残りのノードでのkubeadm upgrade node、そしてノード作業の区間ではdrainがuncordonより先に出てくる必要があります。マイナーバージョンを飛ばせないという制約も、文章で書いてください(例: 「一度に1つ」)。

元に戻せない作業の前には、バックアップが来ます。最初のコントロールプレーンと残りのノードでは、使うkubeadmのサブコマンドが違うという点も書いてください。

カナリアロールアウトの計画を立ててラベルを付ける

3つのノードにupgrade.labhub.io/stageラベルを付けてください。lab-node-0はcanary、lab-node-1はbatch1、lab-node-2はbatch2です。そして/root/ops/upgrade/out/rollout.jsonに計画を書いてください。stagesは長さ3の配列で、各要素はstage(canary/batch1/batch2)とnodes(ノード名の配列)を持ちます。最初のステージはcanaryで、ノードはちょうど1台である必要があり、3つのノードすべてがどこかに割り当てられている必要があります。最上位にverify_between_stagesキーを置き、ステージの間に何を確認するかを書いてください。

計画をドキュメントだけに置かず、ノードのラベルとしてクラスターに刻んでください。最初のステージは1台で、ステージの間に何を確認するかがカナリアの核心です。