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

Kubernetesディストリビューション — 自分で立てる

stable チャネルに従っていたらマイナーを二段飛ばしていた

TT Labで続きを見る

このラボは本物のk3sで動きます

VMの中にk3s v1.34.11+k3s1が1台起動しています。このクラスターを1.35へ1つ上げて、上げる前の準備と、上げた後の照合を実際にやってみます。最初の起動には2分前後かかります。作業ディレクトリは/root/upgradeです。

目標

k3sをマイナーバージョン1つ分アップグレードしながら、出発バージョンの記録、非推奨APIの点検、バックアップ、drainの判断を順に行い、アップグレード後に同じノードと同じワークロードが生きているかをUIDで証明します。最後に、次の1つをsystem-upgrade-controllerのPlanとして書きます。

なぜ重要なのか

インストールスクリプトを再実行すればアップグレードが終わるので、バージョンを選ぶことが軽く見られがちです。ところが、チャネル(stable)は、現在のクラスターがどのバージョンかを知らず、最新の推奨バージョンに向かいます。1.34からstableに上げると、stableが1.36の日にはマイナーバージョンを2つ飛ばしますが、Kubernetesのスキューポリシーは、インスタンスが1つだけのクラスターでもこれを許可しません。飛ばすことを防いでくれるツールはないので、人がバージョンを固定して1つずつ上げる必要があります。

そして、「上がった」ことは証拠になりません。上げる前にバージョンとUIDを書き留めておかなければ、上げた後に、同じクラスターが同じワークロードを持って上がったかを確認する方法がありません。

ステップ

  1. APIサーバーのバージョン、kubeletのバージョン、ノード名、ノードのUIDを保存してください(保存先: /root/upgrade/before.json)。
  2. upgネームスペースにDeploymentweb(nginx 1.27-alpine、レプリカ2)とPDBweb(minAvailable 1)を作成し、UIDを書いてください(書き込み先: /root/upgrade/workload.json)。
  3. apiserver_requested_deprecated_apisメトリクスを書き写し(書き込み先: /root/upgrade/deprecated.txt)、removed_by_target=を書いてください。
  4. SQLiteのDBとトークンをバックアップし(バックアップ先: /root/upgrade/backup/)、ファイルを書いてください(書き込み先: /root/upgrade/backup.json)。
  5. drainを試みて出力を残し(保存先: /root/upgrade/drain.txt)、uncordonしてからdecision=の行を追加してください。
  6. v1.35.8+k3s1にアップグレードして、ファイルを書いてください(書き込み先: /root/upgrade/upgrade.json)。
  7. アップグレード後の状態とスキューの許容範囲を書いてください(書き込み先: /root/upgrade/after.json)。
  8. Plan CRDを入れ、次の1つのバージョンに固定したPlank3s-server-nextを適用してください(ファイル: /root/upgrade/plan.yaml)。
  9. 5つの行と説明を書いてください(書き込み先: /root/upgrade/report.md)。

参考

出発バージョンを先に書く

APIサーバーのバージョン、kubeletのバージョン、ノード名、ノードのUIDを、server_version、kubelet_version、node_name、node_uidのキーで保存してください(保存先: /root/upgrade/before.json)。

アップグレードの後は、以前のバージョンをどこからも見直せません。kubectl version -o jsonのserverVersionと、ノードオブジェクトのstatus.nodeInfo、metadata.uidを見てください。UIDは、後のステップで「同じノードが上がったか」を照合する値です。

PDBが設定されたワークロードを起動する

upgネームスペースにDeploymentweb(イメージはpublic.ecr.aws/docker/library/nginx:1.27-alpine、レプリカ2)と、app=webを選ぶPodDisruptionBudgetweb(minAvailable 1)を作成し、DeploymentのUIDをdeployment_uid、pdb_min_availableのキーで保存してください(保存先: /root/upgrade/workload.json)。

PDBは、「自発的な中断(eviction)のときに、最低いくつは残してほしい」という約束です。kubectl create deploymentとkubectl create pdbで作成でき、2つのPodがReadyになってからUIDを書いてください。Docker Hubのイメージは使いません。

誰がまだ古いAPIを呼んでいるのか

APIサーバーのメトリクスからapiserver_requested_deprecated_apisの行をそのまま書き写し(書き込み先: /root/upgrade/deprecated.txt)、そのうち目標バージョン1.35までに削除される(removed_releaseが1.35以下の)ものの個数を、removed_by_target=<수>の行として書いてください(プレースホルダーは個数です)。

kubectl get --raw /metricsで、APIサーバーのメトリクスを見られます。ラベルのうちremoved_releaseが空なら、非推奨になっただけで削除の予定はないという意味です。このクラスターは、何もしなくても1行が出ます。

DBとトークンを一緒にバックアップする

SQLiteのデータストアをバックアップし(バックアップ先: /root/upgrade/backup/state.db)、サーバートークンもバックアップして(バックアップ先: /root/upgrade/backup/token)、バックアップしたバージョンとトークンのハッシュをk3s_version、token_sha256のキーで書いてください(書き込み先: /root/upgrade/backup.json)。

k3sのSQLiteは、/var/lib/rancher/k3s/server/db/にあります。使用中のDBは、ファイルのコピーよりも、sqlite3のオンラインバックアップのコマンドのほうが安全です。トークンがなぜ必要かは、理論の記事のバックアップの節をもう一度見てください。

drainが終わらない

ノードをkubectl drain --ignore-daemonsets --delete-emptydir-data --timeout=40sで空にしてみて、出力の全体を保存し(保存先: /root/upgrade/drain.txt)、そのあとノードをuncordonしてください。最後の行に、決定をdecision=upgrade-without-drainまたはdecision=add-node-firstとして書きます。

drainは、cordonしたあとeviction APIでPodを追い出し、evictionはPDBを守ります。ノードが1台なら、追い出されたPodの代わりのPodがどこへ行くべきかを考えてみてください。drainが失敗で終わっても、出力はファイルに残す必要があります。

マイナーバージョンを1つだけ上げる

インストールスクリプトを目標バージョンのタグから取得し、INSTALL_K3S_VERSION=v1.35.8+k3s1でアップグレードして、結果をfrom、to、node_uidのキーで書いてください(書き込み先: /root/upgrade/upgrade.json)。

チャネルの代わりにバージョンを固定します。インストールスクリプトは、raw.githubusercontent.comのk3s-io/k3sリポジトリから、そのバージョンのタグのinstall.shを取得します。このVMの設定は/etc/rancher/k3s/config.yamlにあるので、もう一度渡す必要はありません。終わったらAPIが応答するまで待ってから、値を書いてください。

同じものが上がったかを照合する

アップグレード後の状態を、server_version、deployment_uid、web_ready、web_restarts(webのPodの再起動の合計)、traefik_deployed、kubelet_allowed、kubectl_allowedのキーで書いてください(書き込み先: /root/upgrade/after.json)。最後の2つは、現在のAPIサーバーのバージョンに対してスキューポリシーが許容するマイナーバージョンの一覧("1.xx"の文字列)です。

ワークロードはUIDで、設定はtraefikが復活したかどうかで照合します。スキューポリシーで、kubeletはAPIサーバーよりいくつ低いバージョンまで、kubectlは上下いくつまで許されるかは、理論の記事の表を見てください。

次の1つをPlanとして書く

system-upgrade-controller v0.20.1のcrd.yamlを適用し、system-upgradeネームスペースにPlank3s-server-nextを作成して適用してください(ファイル: /root/upgrade/plan.yaml)。現在のバージョンのすぐ次のマイナーバージョンのk3sをversionで固定し、サーバーノードだけを選び、1台ずつcordonして上げるようにします。

CRDは、GitHubリリースの固定バージョンのアドレスから取得します。コントローラーは起動しないので、Planは実行されず、保存されるだけです。CRDはversionもchannelもないPlanをそのまま受け入れるので、適用が成功したからといって正しいPlanであるわけではありません。k3s公式ドキュメントのサーバーPlanの例で、channelを何に置き換えるべきかを考えてください。

stableに従っていたらいくつ飛ばしていたか

initial=、current=、next_plan=、stable_channel=(現在k3sのstableチャネルが指しているバージョン)、stable_minor_jump=(出発バージョンからそのバージョンまでのマイナーバージョンの差)の5行と、なぜチャネルの代わりにバージョンを固定したかの説明を150字以上書いてください(書き込み先: /root/upgrade/report.md)。

チャネルが指すバージョンは、JSONで取得できます(https://update.k3s.io/v1-release/channels)。マイナーバージョンの差は、マイナーの数字の差です。説明には、スキューポリシーと、何がその飛ばしを防いでくれなかったかを入れてください。