migrate-v2-to-v3 について
既存のkube-hetzner Terraformルートをv2からv3へアップグレードする運用者向けの移行スキルです。stateのバックアップと設定変更を慎重に進め、apply前にインフラの破壊的な置換を計画で確認します。
主な機能
- 01v2設定入力の監査
- 02v3の変数名への対応付け
- 03stateのバックアップと検証の案内
- 04破壊的変更の計画確認
- 05Kubernetesアップグレード方針の提示
活用シーン
- 01既存クラスターのv3移行準備
- 02廃止されたv2モジュール入力の特定
- 03アップグレード前のリソース置換リスク確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mysticaltech/ terraform-hcloud-kube-hetzner --skill migrate-v2-to-v3インストール後、Claude Code で/migrate-v2-to-v3を入力して読み込まれたか確認します。
詳細
kube-hetzner v2 から v3 への移行
目的
既存の kube-hetzner クラスターを v2.x から v3.x へ安全に移行します。
このスキルは、新規クラスターではなく、実際に使用されている Terraform ルートを対象としています。インフラストラクチャの維持と、Terraform の検証エラーを対処可能な形にすることを優先します。
公式リファレンス
編集する前に、次の資料を確認してください。
docs/v2-to-v3-migration.md- 運用移行手順書docs/v3-release-evidence.md- v2.21 から v3 への実際のアップグレードの証拠MIGRATION.md- 正式な旧変数から新変数への対応表CHANGELOG.md- v3 のアップグレードに関する注意事項とリリース情報variables.tf- v3 の正確な入力仕様と検証ルールkube.tf.example- 最新の v3 設定例scripts/v2_to_v3_migration_assistant.py- 静的な設定とプランの監査docs/selinux.md- SELinux ポリシー、AVC の証拠、プールごとの代替策scripts/destroy.shとscripts/cleanup.sh- 破棄と孤立リソースのクリーンアップ
安全上のルール
- ユーザーがプランの確認後に明示的に依頼しない限り、
terraform applyを実行しないでください。 - モジュールのバージョンを変更する前に、必ず state をバックアップしてください。
- v3 アップグレードのプランにある
destroy、replace、forces replacementを決して無視しないでください。 - K3s チャネルポリシーを予告なく変更しないでください。
MIGRATION.mdに記載されているとおり、v2 のk3s_channelのデフォルトはv1.33でしたが、v3 のデフォルトはstableです。一方、自動 Kubernetes アップグレードは引き続きデフォルトで有効です。ユーザーは最初の v3 の適用前に選択する必要があります。 hcloud_network、hcloud_network_subnet、hcloud_server、hcloud_load_balancer、hcloud_primary_ip、hcloud_placement_group、hcloud_volume、hcloud_firewallの置換は、理由が説明されるまでブロッカーとして扱ってください。- 本番環境のインプレースアップグレードでは、
MIGRATION.mdがオペレーター向けの安全性に関する契約です。破棄を許さない jq ゲートが正式な基準です。これを緩和したり、省略したりしないでください。 - 移行中または移行後に、コントロールプレーンやエージェントのノードプールをリストの途中に並べ替えたり追加したりしないでください。ノードリソースのアドレスはインデックスをキーとするため、ユーザーが state の移行やブルー/グリーン再構築を意図的に計画している場合を除き、末尾にのみ追加してください。
- v3 の検証エラーを検証の回避で「修正」しないでください。設定を修正してください。
- カスタムネットワーク、プライベート専用、Robot/vSwitch、またはマルチネットワークのクラスターでは、インプレースプランが明らかに安全でない場合、ブルー/グリーン方式を推奨してください。
必要な入力
次の項目を確認してください。
- Terraform ルートのパス。
- 現在のモジュールのソース/バージョン。
- 移行先の v3 タグ。
- Kubernetes ディストリビューションの方針:k3s を維持するか、RKE2 の再構築/移行を v2 から v3 への基本モジュールアップグレードとは別に計画するか。
- K3s のバージョン/チャネルの方針:
k3s_versionを固定する、k3s_channel = "v1.33"を設定する、または v3 のstableを意識的に受け入れる。 - ノードに、
ssh_public_key/ssh_additional_public_keys以外で個別に設定された root SSH キーがあるかどうか。 terraform_data.initial_readinessのために、Terraform が既存のすべてのノードへ SSH 接続できるかどうか。プライベート専用、NAT、踏み台の各トポロジーを含みます。- これが稼働中の本番クラスターかどうか。
- NAT ルーター、プライベート専用ノード、Robot/vSwitch、オートスケーラー、Cilium、Longhorn、または外部ネットワークを使用しているかどうか。
- SELinux によるワークロードの拒否が現在のインシデントの一部かどうか。該当する場合は AVC の証拠を収集し、SELinux を全体で無効にするのではなく、
docs/selinux.mdに従ってください。
ワークフロー
1. 現在のルートを調査する
ユーザーの Terraform ルートで、次を実行します。
terraform version
terraform providers
rg -n 'module "kube-hetzner"|source\\s*=|version\\s*=' .
v2 専用の入力を特定します。
uv run python /path/to/kube-hetzner/scripts/v2_to_v3_migration_assistant.py --root .
rg -n 'kubernetes_distribution_type|k3s_token|secrets_encryption|initial_k3s_channel|install_k3s_version|initial_rke2_channel|install_rke2_version|automatically_upgrade_k3s|sys_upgrade_controller_version|additional_k3s_environment|kubeapi_port|k3s_registries|k3s_kubelet_config|k3s_audit_policy_config|k3s_audit_log_path|k3s_audit_log_maxage|k3s_audit_log_maxbackup|k3s_audit_log_maxsize|k3s_exec_server_args|k3s_exec_agent_args|k3s_global_kubelet_args|k3s_control_plane_kubelet_args|k3s_agent_kubelet_args|k3s_autoscaler_kubelet_args|subnet_amount|placement_group_disable|block_icmp_ping_in|disable_hetzner_csi|load_balancer_disable_ipv6|load_balancer_disable_public_network|use_control_plane_lb|combine_load_balancers|control_plane_lb_type|control_plane_lb_enable_public_interface|control_plane_lb_enable_public_network|lb_hostname|robot_ccm_enabled|hetzner_ccm_use_helm|enable_hetzner_ccm_helm|cilium_loadbalancer_acceleration_mode|enable_wireguard|k8s_config_updates_use_kured_sentinel|keep_disk_agents|keep_disk_cp|use_private_bastion|disable_kube_proxy|disable_network_policy|disable_selinux|k3s_prefer_bundled_bin|placement_group_compat_idx|disable_ipv4|disable_ipv6|autoscaler_disable_ipv4|autoscaler_disable_ipv6|existing_network_id|enable_x86|enable_arm|k3s_encryption_at_rest|autoscaler_labels|autoscaler_taints|extra_kustomize_' .
アシスタントが最初の確認で使う主要なレポートです。ルートで通常と異なる書式、Terragrunt、生成された HCL、または分割された tfvars を使用している場合は、rg コマンドで手動の照合を行います。
2. バックアップする
terraform state pull > "terraform-state-before-kube-hetzner-v3-$(date +%Y%m%d%H%M%S).json"
find . -maxdepth 1 -name '*.tf' -print
ユーザーの設定を直接編集する場合は、変更した .tf ファイルのコピーを保管してください。
3. 設定を書き換える
全体の対応表はMIGRATION.mdを参照してください。重要な変換は次のとおりです。
- ディストリビューションに依存しない入力名に変更します。例:
k3s_token->cluster_token、kubernetes_distribution_type->kubernetes_distribution。 - 否定形のブール値を反転します。
disable_kube_proxy->enable_kube_proxydisable_network_policy->enable_network_policydisable_selinux->enable_selinux- nodepool の
disable_ipv4/disable_ipv6->enable_public_ipv4/enable_public_ipv6 autoscaler_disable_ipv4/disable_ipv6->autoscaler_enable_public_ipv4/autoscaler_enable_public_ipv6
existing_network_id = ["123"]をexisting_network = { id = 123 }に置き換えます。enable_x86/enable_armをenabled_architecturesに置き換えます。- コントロールプレーンの
network_idを削除します。コントロールプレーンはプライマリネットワークのみを使用します。 network_id = 0を削除します。v3 ではプライマリネットワークは省略するか、null にします。- 通常のインプレース v2 アップグレードでは、デフォルトの
network_subnet_mode = "per_nodepool"を維持します。これはリリース済みの v2 サブネットリソースに対応しています。新しいクラスターを作成する場合、またはサブネットトポロジーを意図的に変更する場合にのみ、network_subnet_mode = "shared"を使用します。 placement_group_compat_idxをplacement_group_indexに置き換えます。extra_kustomize_*をuser_kustomizationsに置き換えます。enable_iscsid、k3s_encryption_at_rest、autoscaler_labels、autoscaler_taintsを削除します。hetzner_ccm_use_helm/enable_hetzner_ccm_helmを削除します。v3 は常に HelmChart マニフェストを通じて Hetzner CCM をインストールします。- 書き換え時に既存の nodepool の順序を変更したり、挿入したりしないでください。新しい nodepool は末尾にのみ追加します。リストの順序を変えたい場合は作業を中止し、別途、状態の移行またはブルー/グリーン設計の問題として扱ってください。
4. 初回適用の方針を決める
v3 を初めて適用する前に、次の項目を解決してください。
- Kubernetes チャネルの方針。
MIGRATION.mdには、デフォルトが通知なく変更されたことが記載されています。v2 ではk3s_channel = "v1.33"、v3 ではk3s_channel = "stable"が使用され、automatically_upgrade_kubernetesは引き続きデフォルトで有効です。次のうち、必ず1つを選択してください。k3s_versionを固定する- v2 のマイナーチャネルを維持するため、
k3s_channel = "v1.33"を設定する stableを追従することを明示的に受け入れる
- アドオンのバージョン方針。v2 でアドオンのバージョンが未設定/浮動の場合、アップストリームの最新バージョンが解決されていました。v3 でアドオンのバージョンが未設定の場合は、kube-hetzner がレビューした決定的なデフォルトマトリックスが使用されます。アップグレード計画に、一度限りのアドオンバージョン変更が表示される場合があります。浮動バージョンの動作を維持するには、
latestを明示的に設定する必要があります。 - SSH 認証鍵の方針。v3 を初めて適用すると、
terraform_data.ssh_authorized_keysが/root/.ssh/authorized_keysを調整します。デフォルトでは、構成外で追加された不明な鍵を保持しつつ、ssh_public_keyまたはssh_additional_public_keysから削除されたモジュール管理の鍵を失効させます。モジュール管理の鍵だけに厳密に置き換えたい場合に限り、ssh_authorized_keys_exclusive = trueを設定してください。 - ノードへの到達性。
terraform_data.initial_readinessは、既存のすべてのコントロールプレーンノードとエージェントノードに SSH 接続します。プライベートネットワークのみを使用する場合、NAT を使用する場合、または踏み台経由のみで接続する場合は、適用前に Terraform からすべてのノードに到達できるようにしてください。
2026-07-04/05 に実施した標準的な本番環境のアップグレードでは、最初の v3 プランに必要だったのは、モジュールソースの切り替えとk3s_channel = "v1.33"のみでした。
5. 初期化して検証する
terraform fmt -recursive
terraform init -upgrade
terraform validate -no-color
tmpdir="$(mktemp -d)"
rsync -a --exclude .git --exclude .terraform --exclude .terraform-tofu ./ "$tmpdir"/
(cd "$tmpdir" && tofu init -backend=false -input=false && tofu validate -no-color)
rm -rf "$tmpdir"
terraform validateは、モジュールが読み込まれることを確認します。v3 の変数間の移行ガードはterraform_data.validation_contractで適用されるため、無効な組み合わせや置き換えのリスクを確認するには、保存したterraform planによる検証が必要です。
検証に失敗した場合は、設定を変更する前に、variables.tf内の該当する変数と検証ブロックを確認してください。
6. 計画
terraform plan -out=v3-upgrade.tfplan
terraform show v3-upgrade.tfplan
terraform show -json v3-upgrade.tfplan > v3-upgrade-plan.json
uv run python /path/to/kube-hetzner/scripts/v2_to_v3_migration_assistant.py --root . --plan-json v3-upgrade-plan.json --strict
厳格な監査では、サーバーのネットワーク切り離しのみの操作もブロックし、インラインのプライベート/パブリックネットワーク設定が変更または不明な場合にも警告します。サーバーの置き換えがないからといって、IP/MAC アドレスの維持、ゲストの永続的なルーティング、クォーラムに安全なロールアウトが保証されるわけではありません。MIGRATION.md#network-and-ssh-transition-limits に従い、既存の SSH ポートを維持し、移行前に Cilium の有効な値を比較してください。
jq が利用可能な場合は、破壊的なアクションを一覧表示します。
terraform show -json v3-upgrade.tfplan \
| jq -r '.resource_changes[]? | select(.change.actions | index("delete")) | "\(.address): \(.change.actions | join(","))"'
稼働中の v2 → v3 移行では、保護対象インフラストラクチャのゲートを実行してください。Terraform の置き換えは、delete を含むアクションリストに表示されます。このゲートはブロック対象のリソースのみを出力します。出力がある場合は、処理を中止してください。
terraform show -json v3-upgrade.tfplan \
| jq -r '
.resource_changes[]?
| select(.type as $type | [
"hcloud_server",
"hcloud_network",
"hcloud_network_subnet",
"hcloud_load_balancer",
"hcloud_volume",
"hcloud_primary_ip",
"hcloud_placement_group",
"hcloud_firewall"
] | index($type))
| select(.change.actions | index("delete"))
| "\(.address): \(.type) \(.change.actions | join(","))"
'
v2 の名前変更後、計画では hcloud_server、hcloud_network、hcloud_network_subnet、hcloud_load_balancer、hcloud_volume、hcloud_primary_ip、hcloud_placement_group、および hcloud_firewall に対する destroy/replace アクションがゼロでなければなりません。これらのタイプがいずれか表示された場合は、適用する前に処理を中止して原因を調査してください。
次の想定されるアクションについて、健全な初回の v3 計画を慌てて中止しないでください。
- トリガーキーの追加による、冪等な k3s/RKE2 kustomization の再実行 1 回
- readiness、SSH 認証済みキーの調整、バリデーター、および destroy クリーンアップのための新しい
terraform_dataリソース kube-hetzner/osなどのサーバーラベルのインプレース更新- ファイアウォールルールの更新
7. 特殊ケースの解釈
- v2.19 より前のクラスターからの NAT ルーターのプライマリ IP の置き換えは、適用前に状態の移行が必要になる場合があります。
existing_networkとvswitch_idを併用する場合、モジュール管理のexpose_routes_to_vswitchを持つことはできません。false に設定するか、ルート公開を手動で有効にしてください。- プライベート専用クラスターには、動作する NAT/踏み台/コントロールプレーンの参加パスが必要です。
- マルチネットワークのパブリックオーバーレイはラボ専用のプレビューです。これには
multinetwork_mode = "cilium_public_overlay"、enable_experimental_cilium_public_overlay = true、およびcni_plugin = "cilium"が必要です。ライブデータパスの E2E が合格するまで、本番アップグレードには推奨しないでください。 - Tailscale ノードトランスポートは静的に検証され、プランマトリックスでカバーされていますが、ライブの Hetzner/Tailscale E2E はまだ保留中です。評価および個別の監査済みプランには
node_transport_mode = "tailscale"を使用してください。静的なチェックだけで本番トポロジーを認定しないでください。 - Tailscale モードでは、Kubernetes ノード IP を Hetzner のプライベートアドレスに保持し、Tailscale のサブネットルート SNAT を無効にした状態で、ノードプライベートの
/32ルートをアドバタイズできます。シングルネットワーククラスターではルートアドバタイズを無効にできますが、外部のnetwork_scope = "external"ノードプールにはルートアドバタイズと Tailnet の自動承認が必要です。 - 外部エージェント/オートスケーラーの Network ID は、正の Hetzner Network ID でなければなりません。省略または null はプライマリ Network を意味します。Tailscale モードでは、アクティブなエージェント/オートスケーラーノードプールも
network_scope = "primary"またはnetwork_scope = "external"を設定する必要があり、これにより同一ルートの外部 Network ID がterraform plan中に検証されます。 cilium_gateway_api_enabled、embedded_registry_mirror、新しい Tailscale マルチネットワークシャード、外部 Cloudflare Access/Tunnel ルーティングなどの新しいオプションの v3 機能を、最初のインプレース v2-to-v3 適用と同じタイミングで追加しないでください。ただし、オペレーターがブルー/グリーン/トポロジー変更の移行を明示的に受け入れる場合を除きます。まずクリーンにアップグレードし、その後、別のレビュー済みプランでこれらの機能を追加してください。- Cloudflare Access/Tunnel は外部アクセスパターンにすぎません。Cloudflare プロバイダーの入力をでっち上げたり、移行中に kube-hetzner ノードトランスポートとして Cloudflare Mesh/WARP を推奨したりしないでください。
- SELinux は v3 でもデフォルトで有効のままです。ワークロードの拒否については、
docs/selinux.mdを使用してください。AVC 行を収集し、まず udica で生成されたワークロードポリシーを試し、再現可能な AVC 証拠がある場合にのみ上流モジュールポリシーを提案し、プールごとのselinux = falseは最後の手段としてのみ使用してください。 - ユーザーが後でクラスターを破棄する場合は、Terraform ルートから
scripts/destroy.shを使用してください。これは、既知の無害なイングレス LB デタッチ競合(デュアル CCM と Terraform の所有権によるresource_already_detaching/422)のみを自動再試行し、その後、ラベルなしのプライマリ IP、状態外の自動スケールノード、および正確な管理対象 LB 名についての読み取り専用のオーファンレポートを出力します。scripts/cleanup.shは、レポートで残存物が示された後、または状態がすでに破壊されている場合にのみ、強制的なフォールバックとして使用してください。これはトークンの HCloud プロジェクト全体をクラスター専用として扱います。ドライランを確認し、永続データは意図的にのみ含めてください。 - オートスケーラーが作成したサーバーは Terraform の状態に含まれておらず、ネットワーク/サブネットの削除を妨げる可能性があります。これらのオーファンは、コントロールプレーンが停止した後にのみ削除するか、先にオートスケーラープールを
min_nodes = 0にスケールしてください。Cluster Autoscaler が稼働している間にこれらを早く削除すると、再作成される可能性があります。 - 移行後に Cilium Gateway API を追加する場合は、
cni_plugin = "cilium"とenable_kube_proxy = falseが必要です。 - 移行後に組み込みレジストリミラーを追加する場合は、ノードが同等の信頼を持つレジストリピアであり、重要なイメージはダイジェストを使用すべきであることを警告してください。
8. レポート
移行レポートを返します:
## kube-hetzner v2 -> v3 migration report
- Terraform root:
- Source version:
- Target version:
- Terraform/OpenTofu version:
- hcloud provider version:
- K3s channel/version intent:
- Kubernetes distribution intent:
- Addon version intent:
- SSH authorized_keys policy:
- SELinux intent and AVC evidence, if relevant:
- Inputs changed:
- Inputs removed:
- Manual state actions:
- Validation result:
- Plan result:
- Protected hcloud delete/replace gate:
- Replacements/destroys:
- Expected first-apply actions:
- Blockers:
- Recommendation:
適用ポリシー
以下を実行するのは:
terraform apply v3-upgrade.tfplan
ユーザーの明示的な承認後、かつプランレポートに説明のつかない破壊的なアクションがない場合のみにしてください。