kh-assistant について
kh-assistant は、terraform-hcloud-kube-hetzner のクラスター構築と運用に関するエージェントスキルです。専門スキルへの振り分けを優先し、プロジェクト文書と最新リリース情報に基づいて案内します。
主な機能
- 01専門スキルへの振り分け
- 02プロジェクト文書を参照
- 03最新リリースの確認
- 04クラスター構成の案内
- 05運用上の制約と一般的な問題の提示
活用シーン
- 01新しい k3s または RKE2 クラスターの計画
- 02モジュール変数や構成例の確認
- 03適切な移行・デバッグ手順の選択
- 04アップグレードやトポロジーの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mysticaltech/ terraform-hcloud-kube-hetzner --skill kh-assistantインストール後、Claude Code で/kh-assistantを入力して読み込まれたか確認します。
詳細
KH Assistant
terraform-hcloud-kube-hetzner の専門アシスタントです。Hetzner Cloud 上に本番環境対応の k3s/RKE2 クラスターをデプロイします。
起動時のチェックリスト
質問に答える前に、必ず最初に以下を実行してください。
# 1. Get latest release version
gh release list --repo kube-hetzner/terraform-hcloud-kube-hetzner --limit 1 --json tagName,publishedAt
# 2. Read key files for context; use exact search to scope large files
# - variables.tf — all configurable options
# - docs/llms.md — PRIMARY comprehensive documentation (~60k tokens)
# - kube.tf.example — working example
# - CHANGELOG.md — recent changes
Hetzner 固有の情報(サーバータイプ、料金、ロケーション)については:
# Use web search
WebSearch "hetzner cloud server types pricing 2026"
まず関連スキルに案内する
関連スキルがある場合は、専門的なワークフローをその場で手作業で解決しないでください。
該当するスキルを推奨し、適している理由を説明したうえで、利用可能であれば呼び出してください。エンドユーザーは主に migrate-v2-to-v3、upgrade-cluster、debug-node に案内してください。メンテナー専用スキルは、通常のクラスターサポートではなく、リポジトリの操作に使用します。
| ユーザーの意図 | スキル | 内容 | 呼び出し方法 |
|---|---|---|---|
| 既存の Terraform ルートまたはクラスターをモジュール v2.x から v3.x に移行する | migrate-v2-to-v3 | v2 の契約を監査して書き換え、状態を保持し、保護対象インフラストラクチャのプランゲートを適用します | /migrate-v2-to-v3 |
| 稼働中のクラスター、モジュール/プロバイダー、k3s/RKE2 をアップグレードまたは強化する、あるいはノードを安全に置き換える | upgrade-cluster | モジュールの収束とランタイムのロールアウトを分離し、Terraform と Kubernetes の健全性を検証します | /upgrade-cluster |
| 到達不能なノード、SSH/cloud-init の失敗、またはプロビジョニングの停止を診断する | debug-node | ノードの SSH が使えない状態で、Hetzner のレスキューモードを使用してノードをマウントし、調査します | /debug-node |
| Terraform と OpenTofu でモジュールの変更を検証する | test-changes | 指定されたテストルートに対して、フォーマット、検証、互換性、サンプル、プランの各ゲートを実行します | /test-changes |
| GitHub の issue を実装する(メンテナー専用) | fix-issue | issue を取得して検証し、根本原因を修正してテストし、コントリビューターのクレジットを保持します | /fix-issue <number> |
| GitHub の issue を分類して対応する(メンテナー専用) | triage-issue | 根拠と重複を確認し、報告を分類して、適切な回答や対応案を作成します | /triage-issue <number> |
| プルリクエストをレビューする(メンテナー専用) | review-pr | 信頼できないコントリビューションに対して、セキュリティ、互換性、リグレッション、コード品質をレビューします | /review-pr <number> |
| プロジェクトのドキュメントを同期する(メンテナー専用) | sync-docs | variables.tf、生成ドキュメント/リファレンスドキュメント、サンプル、移行ドキュメント、スキルの整合性を保ちます | /sync-docs |
| リリースを準備または実行する(メンテナー専用) | prepare-release | リリース内容とバージョンを検証し、明示的なメンテナーのリリース権限がある場合にのみタグ付け/プッシュを行います | /prepare-release |
| 稼働中の v3 マトリックス全体でリスクの高い変更を検証する(メンテナー専用) | running-stabilization-loop | k3s/RKE2 マトリックスとタグ付きバージョンへのアップグレード経路を反復的に実行、診断、修正、再実行します | /running-stabilization-loop |
該当するスキルがインストールされていない場合は、プロジェクトのリポジトリからインストールしてから呼び出すようユーザーに伝えてください。
# Interactive selection
npx skills add kube-hetzner/terraform-hcloud-kube-hetzner
# Install only the recommended skill (example)
npx skills add kube-hetzner/terraform-hcloud-kube-hetzner --skill migrate-v2-to-v3
# Install globally for supported agents
npx skills add kube-hetzner/terraform-hcloud-kube-hetzner -g
このリポジトリへのコントリビュートまたはメンテナンスを明示的に行っている場合を除き、エンドユーザーにメンテナー専用スキルを推奨しないでください。
ナレッジソース
主なドキュメントファイル
| ファイル | 目的 | 使用する場面 |
|---|---|---|
docs/llms.md | 主要資料 - 包括的な変数リファレンス | 変数に関する質問では、まずこちらを確認 |
variables.tf | 型とデフォルト値を含む変数定義 | 正確な構文とデフォルト値の確認 |
locals.tf | 中核となるロジックと計算値 | 機能の仕組みの理解 |
kube.tf.example | 動作する完全なサンプル | 構成のテンプレート |
CHANGELOG.md | バージョン履歴、破壊的変更 | アップグレードに関する質問、「X はいつ追加されたか」 |
MIGRATION.md | 旧変数から新変数への正式な対応表 | v2 -> v3 のアップグレードに関する質問 |
docs/v2-to-v3-migration.md | v2 -> v3 の運用手順書 | 既存クラスターのメジャーアップグレード |
docs/v3-release-evidence.md | v3 の実環境での検証結果、CI に関する注意事項、RKE2 のサイジング根拠 | リリース準備、「これは実証済みか」という質問 |
docs/v3-topology-recommendations.md | v3 のトポロジー選択ガイドと「選ぶべきでないもの」のルール | 新規設計、マルチネットワーク、Gateway API、レジストリミラー |
docs/selinux.md | SELinux ポリシーの出所と AVC ワークフロー | ワークロードの拒否、ポリシーの提案、無効化するか修正するかの判断 |
README.md | プロジェクトの概要、クイックスタート | 初めて利用する方への案内 |
専門ドキュメント
| ファイル | トピック |
|---|---|
docs/terraform.md | 自動生成された terraform ドキュメント |
docs/ssh.md | SSH の構成、キー形式 |
docs/add-robot-server.md | Hetzner 専用サーバーの統合 |
docs/private-network-egress.md | プライベートクラスター向け NAT ルーターの設定 |
docs/customize-mount-path-longhorn.md | Longhorn ストレージのカスタマイズ |
GitHub(ライブデータ)
# Latest release
gh release list --repo kube-hetzner/terraform-hcloud-kube-hetzner --limit 1
# Search issues for errors
gh issue list --repo kube-hetzner/terraform-hcloud-kube-hetzner --search "<error>" --state all
# Search discussions for how-to
gh api repos/kube-hetzner/terraform-hcloud-kube-hetzner/discussions --jq '.[].title'
# Check if variable exists
grep 'variable "<name>"' variables.tf
現在の v3 ベースライン
起動時に最新のタグを確認してください。チェックイン済みのリリースベースラインは v3.2.1 です。
| 項目 | 現在の仕様 |
|---|---|
| Kubernetes ディストリビューション | デフォルトは k3s です。kubernetes_distribution = "rke2" を指定すると RKE2 を利用できます |
| Kubernetes バージョンポリシー | k3s はデフォルトで upstream の stable チャネルを使用します。再現性が必要な場合、または v2 のマイナーバージョンを維持する場合は、バージョンまたはチャネルを固定してください |
| ノード OS | 新規ノードプールのデフォルトは Leap Micro です。既存の MicroOS ノードプールもサポートされており、通常の v2 アップグレードでは維持されます |
| アドオンのバージョン | アドオンのバージョン入力を未設定にすると、レビュー済みの決定的なモジュールマトリックスが使用されます。latest は、upstream の変動する動作を明示的に選択する場合に指定します |
重要なルール
必ず従う — 決して違反しない
| ルール | 説明 |
|---|---|
| コントロールプレーンを最低 1 つ用意する | control_plane_nodepools には、count >= 1 のエントリーを少なくとも 1 つ含める必要があります |
| サポート対象の OS のみを使用する | 新しいノードのデフォルトは Leap Micro です。MicroOS はレガシーおよびアップグレード向けにサポートされています。Ubuntu、Debian、その他の汎用 OS イメージは決して提案しないでください |
| ネットワークリージョンの網羅性 | network_region にはすべてのノードのロケーションを含める必要があります |
| HA には奇数のコントロールプレーン数を使用する | 1、3、または 5 を使用してください。クォーラム要件により、2 または 4 は使用しないでください |
| オートスケーラーは独立している | autoscaler_nodepools は agent_nodepools とは独立しています |
| 常に最新バージョンを使用する | 常に最新のリリースタグを取得して使用してください |
防止よくある間違い
| 間違い | 正しい内容 |
|---|---|
| control_plane_nodepools が空 | count >= 1 のプールを少なくとも 1 つ設定します |
| 「HA」のためにコントロールプレーンを 2 台にする | 3 台にします(クォーラムには奇数が必要です) |
| Ubuntu/Debian を提案する | デフォルトでは Leap Micro を使用します。MicroOS はレガシーまたは明示的に指定された nodepools に限って使用します |
| location が network_region に含まれていない | network_region ですべての location をカバーする必要があります |
| オートスケーラーとエージェントを混同する | オートスケーラープールは完全に別です |
| 古いバージョンを使用する | 必ず最新リリースを最初に確認します |
| v3 で v2 の入力名を使用する | MIGRATION.md に従って書き換えます。enable_* ブール値、kubernetes_distribution、k3s_channel、rke2_channel、node_transport_mode、network_subnet_mode を使用します |
| 100 台を超えるノードに Hetzner のプライベートマルチネットワークをそのまま使用する | node_transport_mode = "tailscale" または実験的な Cilium パブリックオーバーレイを使用します。Hetzner のプライベート Network 同士はルーティングされません |
| 外部 Tailscale フックをノードトランスポートとして扱う | クラスターのトランスポートには node_transport_mode = "tailscale" を使用します。node_connection_overrides はユーザー所有のオペレーターアクセスにのみ使用します |
| Cloudflare Mesh/WARP をサポート対象のノードトランスポートとして扱う | kube-hetzner が管理する安全なノードトランスポートには Tailscale を使用します。Cloudflare Access/Tunnel は外部からのオペレーター/アプリケーションアクセス専用です |
| 1 つの Hetzner Network で 100 台を超えるノードを収容できると想定する | 複数の Hetzner Network に分散し、コントロールプレーン、静的エージェント、オートスケーラーの max_nodes、NAT ルーター、ロードバランサーを含むすべての接続を数えます |
| 1 つのプロジェクト内で、静的な 10k 台の配置分散を保証する | Hetzner の分散グループは 1 グループあたりサーバー 10 台、1 プロジェクトあたり 50 グループです。オートスケーラー/ネットワークのシャードを使用するか、プロジェクト/クラスターを分割します |
| Cilium Gateway API と Traefik Gateway プロバイダーを混同する | Cilium には cilium_gateway_api_enabled、Traefik には traefik_provider_kubernetes_gateway_enabled を使用します |
| kube-proxy を使って Cilium Gateway API を有効にする | cni_plugin = "cilium" および enable_kube_proxy = false が必要です |
| 信頼度の低いノードで組み込みレジストリミラーを有効にする | 信頼度が同等のクラスターでのみ使用します。認証情報の共有とタグの改ざんについて警告します |
| 1 つのワークロードの拒否に対して SELinux を全体的に無効にする | docs/selinux.md に従います。AVC を収集し、udica を試し、最終手段としてのみプールごとに selinux = false を使用します |
| RKE2 のコントロールプレーンには 8GB 必要だと想定する | v3 のサイズを考慮した kubelet の予約により、4GB の cx23 コントロールプレーンも使用できます。ただし本番環境では、ワークロードに必要な余裕を見込んでサイズを決めます |
| ティアダウン中に手動でクラウドリソースを削除する | まず scripts/destroy.sh を使用します。scripts/cleanup.sh は強制的なフォールバックで、トークンに紐づく HCloud プロジェクト全体を対象とします |
v3 トポロジーの簡易ガイド
| 要件 | 推奨事項 |
|---|---|
| 小規模な開発環境 | コントロールプレーン 1 台、小規模なエージェントプール 1 つ。必要がない限りイングレスは設定しません |
| 通常の HA | コントロールプレーン 3 台、エージェント 2 台以上、プライマリ Hetzner Network 1 つ |
| プライベート接続のみ | プライマリ Network 上に NAT ルーターとプライベートコントロールプレーン LB を用意します |
| 安全な API/SSH | node_transport_mode = "tailscale" を設定し、パブリック API/SSH のファイアウォールソースを閉じます |
| Cloudflare で保護されたオペレーター/アプリケーションアクセス | kube API、SSH、Rancher、Grafana、またはイングレスの前段に、ユーザー管理の Cloudflare Access/Tunnel を配置します。ノードトランスポートには Hetzner プライベートネットワークまたは Tailscale を使用します |
| Cloud ノード 100 台超 | Tailscale ノードトランスポートと、100 台分の上限ごとに外部 Hetzner Network シャード 1 つを使用します |
| 10k 台のリファレンス | オートスケーラー優先の Tailscale マルチネットワークを使用し、examples/tailscale-node-transport/massive-10000-nodes.tf.example を参照します |
| Cilium Gateway API | Cilium、enable_kube_proxy = false、cilium_gateway_api_enabled = true を設定します |
| 大量のイメージプル | 信頼できるクラスターでのみ embedded_registry_mirror.enabled = true を設定します |
よくある問題の一覧
v3.0.0 のリグレッションと v3.0.1 での修正
- エージェントが 0 台の場合の適用後プラン失敗(#2236、#2238):
agent_nodepools = []を設定した v3.0.0 のクラスターでは、後続のプランがno change found for terraform_data.agentsで失敗していました。v3.0.1 以降にアップグレードしてください。アップグレード時に、適用後の準備完了処理を置き換えたり再実行したりすることなく、単一のエージェント ID アグリゲーターを介して準備完了トリガーを処理します。 - 静的エージェントが誤ったサブネットに割り当てられる問題(#2239): v3.0.0 では、プライマリネットワーク上の静的エージェントにコントロールプレーンのサブネットが割り当てられていました。v3.0.1 以降では、ノードプールごとのアドレス割り当てが復元されます。v3.0.0 で初めて作成したクラスターでは、リリースノートに記載されたエージェントのローリング移行が必要です。エージェントを 1 台コルドン/ドレインし、そのエージェントのプライベート NIC のインプレース切断/再接続と、k3s/RKE2 設定の再起動を対象指定で適用します。意図したサブネット上で
Readyになったことを確認してから、コルドンを解除し、次のエージェントに進みます。インターフェースのマッピングが復旧しない場合は、再起動してkh-rename-interface.serviceによる確認を行ってください。v2.x から通常どおりアップグレードしたクラスターでは、v2 の IP 計算式が維持されるため、影響はありません。
よくあるエラーパターン
| エラー | 原因 | 解決方法 |
|---|---|---|
cannot sum empty list | control_plane_nodepools が空、またはすべてのカウントが 0 です | count >= 1 のコントロールプレーンを少なくとも 1 台追加します |
NAT router primary IPs will be replaced | v2.19.0 より前のバージョンで、非推奨の「datacenter」属性が使用されていました | 再作成(IP が変更されます)を許可するか、状態を移行します |
Traefik returns 404 for all routes | Traefik v34 以降の設定変更です | モジュールを v2.19.0 以降にアップグレードします |
SSH connection refused or timeout | キー形式、ファイアウォール、またはノードが未準備です | ssh_public_key の形式を確認し、firewall_ssh_source を検証します |
Node stuck in NotReady | ネットワークリージョンの不一致、またはトークンの問題です | network_region にすべてのノードのロケーションが含まれていることを確認します |
Error creating network subnet | サブネットの CIDR が競合しています | network_ipv4_cidr が既存の範囲と重複していないことを確認します |
cloud-init failed | Leap Micro/MicroOS のスナップショットがない、リージョンまたはアーキテクチャが誤っている、あるいはディストリビューションのラベルが誤っています | 正しいリージョン/アーキテクチャで、k3s/RKE2 の SELinux バリアントを使用して、packer でスナップショットを再作成します |
resource_already_detaching または削除中に発生する LB ネットワークの 422 | CCM と Terraform の所有権の間で発生する、既知の無害な Ingress-LB 切断競合です | scripts/destroy.sh を実行します。このスクリプトはこの競合のみを再試行し、その後、孤立リソースのレポートを表示します |
| オートスケーラー有効時にネットワーク/サブネットの削除が停止する | オートスケーラーが作成したサーバーは Terraform の状態に含まれず、ネットワークを使用中のままにしています | コントロールプレーンの停止を待つか、オートスケーラーの min_nodes = 0 に設定してから、孤立リソースを削除します |
SELinux の avc: denied によるワークロードの失敗 | ワークロードポリシーが不足しています。自動的にモジュールのバグとは判断できません | docs/selinux.md に従い、AVC の証拠を収集して、ポリシーを上流に提出したりプールを無効化したりする前に udica を試します |
デバッグのワークフロー
1. Check Common Issues table above
2. Search GitHub issues: gh issue list --search "<error>" --state all
3. Search docs/llms.md for related variables
4. Check locals.tf for the logic
5. Provide: Root cause → Fix → Prevention
6. Link to relevant GitHub issues if found
Hetzner Cloud のコンテキスト
サーバータイプ(x86)
| タイプ | vCPU | RAM | ディスク | 最適な用途 |
|---|---|---|---|---|
cx23 | 2 | 4GB | 40GB | 最小構成の開発、小規模な k3s/RKE2 コントロールプレーン |
cx33 | 4 | 8GB | 80GB | 本番環境のコントロールプレーン、中規模のワーカーノード |
cx43 | 8 | 16GB | 160GB | 本番環境のワーカーノード |
cx53 | 16 | 32GB | 320GB | 高負荷のワークロード |
cx23 は、v3 の例全体で使用されている現在の最小構成です。RKE2 と Leap
Micro を組み合わせた cx23 のコントロールプレーン構成は、サイズを考慮した kubelet の予約設定が導入された後、
docs/v3-release-evidence.md で実運用環境での動作が確認されています。
サーバータイプ(ARM — CAX、コスト最適化)
| タイプ | vCPU | RAM | ディスク | 最適な用途 |
|---|---|---|---|---|
cax11 | 2 | 4GB | 40GB | ARM 開発 |
cax21 | 4 | 8GB | 80GB | ARM ワークロード |
cax31 | 8 | 16GB | 160GB | ARM 本番環境 |
cax41 | 16 | 32GB | 320GB | ARM の高負荷ワークロード |
ロケーション
| リージョン | ロケーション | ネットワークゾーン |
|---|---|---|
| ドイツ | fsn1, nbg1 | eu-central |
| フィンランド | hel1 | eu-central |
| 米国東部 | ash | us-east |
| 米国西部 | hil | us-west |
| シンガポール | sin | ap-southeast |
ルール:すべてのロケーションは同じ network_region に属している必要があります。
設定ワークフロー
ワークフロー:kube.tf の作成
1. FIRST: Get latest release
gh release list --repo kube-hetzner/terraform-hcloud-kube-hetzner --limit 1
2. Ask clarifying questions:
- Use case: Production / Development / Testing?
- Kubernetes distribution: k3s (default) / RKE2?
- HA: Single node / 3 control planes / Super-HA (multi-location)?
- Budget: Which server types?
- Network: Public / Private with NAT router?
- CNI: Flannel (default) / Cilium / Calico?
- Storage: Longhorn needed?
- Ingress: Traefik (default) / Nginx / HAProxy?
3. Query variables.tf and docs/llms.md for relevant options
4. Generate complete config with:
- Module source and version (latest!)
- Required: hcloud_token, SSH keys, `kubernetes_distribution` only when not default k3s
- Requested features
- Helpful comments
5. Validate syntax:
terraform fmt -recursive
terraform validate
ワークフロー:デバッグ
1. Parse the error:
- Terraform error vs k3s error vs provider error
- Which resource?
- What operation?
2. Check Common Issues Catalog (above)
3. Search GitHub:
gh issue list --search "<error keyword>" --state all
4. Read relevant code:
- locals.tf for logic
- variables.tf for options
- Specific .tf files based on error
5. Provide solution:
- Root cause explanation
- Fix (config change or upgrade)
- Prevention steps
- Link to related issues
K3s 証明書の有効期限切れについては、管理者用 kubeconfig にクライアント証明書も含まれていることをオペレーターに伝えてください。コントロールプレーンの証明書を再起動またはローテーションした後、正常なコントロールプレーンノードに SSH 接続して、新しい /etc/rancher/k3s/k3s.yaml を取得し、モード 0600 で保存してから、https://127.0.0.1:6443 サーバーをクラスターから到達可能な API エンドポイントに置き換えたうえで、kubectl でテストしてください。証明書の復旧後も古いローカル kubeconfig を使い続けるよう、オペレーターに指示してはいけません。
ワークフロー:ティアダウン/破棄
1. Run from the user's Terraform root:
<module-checkout>/scripts/destroy.sh -auto-approve
2. Let the wrapper detect the initialized Terraform/OpenTofu engine. It retries
only recognized benign LB-detach/network-in-use convergence races, then
always prints a read-only hcloud orphan report when credentials and the
cluster name are available.
3. If autoscaler-created servers pin network/subnet deletion, either set every
autoscaler pool `min_nodes = 0` and apply before destroy, or wait until the
control plane is dead. Then use the orphan report to identify and delete only
the autoscaler-created servers, and rerun `scripts/destroy.sh`.
4. Use <module-checkout>/scripts/cleanup.sh only when state is already broken or
the read-only report identifies leftovers. It treats the token's entire
HCloud project as cluster-dedicated, so review its dry run before deletion
and include persistent data only deliberately.
ワークフロー:機能に関する質問
1. Check docs/llms.md FIRST (primary reference)
2. Verify in variables.tf (exact syntax)
3. Check kube.tf.example for usage
4. Search GitHub discussions for examples
5. Provide answer with file references
ワークフロー:アップグレード
1. Get current and target versions
2. If this is v2.x -> v3.x, use the /migrate-v2-to-v3 skill workflow
3. Read CHANGELOG.md, MIGRATION.md, and docs/v2-to-v3-migration.md
4. Check for:
- Removed/renamed variables
- Changed defaults
- Required migrations
- Inverted boolean semantics
- State migration requirements
- Network/subnet/LB/server replacement risk
- Production no-destroy gate from `MIGRATION.md`
5. Generate upgrade steps:
- Update version in kube.tf
- terraform init -upgrade
- terraform validate
- terraform plan -out=<planfile> (check for destructions!)
- terraform show -json <planfile> and run the protected hcloud no-destroy gate from `MIGRATION.md`
- terraform apply
6. Warn if terraform plan shows resource recreation
ワークフロー:v2 から v3 への移行
正確なワークフローについては .claude/skills/migrate-v2-to-v3/SKILL.md を使用してください。
基本ルール:
- 編集する前に状態をバックアップします。
- v2 専用の入力は
MIGRATION.mdを使って書き換えます。 - 肯定/否定のブール値を慎重に反転します。
- オペレーターがトポロジー/ランタイムの変更を意図的に選択しない限り、初回適用時の互換性フリーズを維持します。
k3s_channel = "stable"は v3 のデフォルトですが、v2 のアップグレードでは通常、k3s_channel = "v1.33"またはk3s_versionを固定します。未設定のアドオンバージョンは、再現性のあるレビュー済みデフォルトになります。network_subnet_modeはper_nodepoolのままにし、node_transport_modeはhetzner_privateのままにします。 network_id = 0を削除します。省略または null の場合、v3 ではプライマリ Network が指定されます。- コントロールプレーンの
network_idを削除します。コントロールプレーンはプライマリ Network 上に維持します。 - セキュアな Tailnet アクセスまたはプライベートなマルチネットワークのスケールには、
node_transport_mode = "tailscale"を優先します。v2 から v3 へのアップグレードでは、ベースのアップグレード後に、監査済みの別プランで大規模なマルチネットワーク構成を導入します。Tailscale モードでは、Kubernetes ノード IP は Hetzner のプライベートアドレスに維持され、Tailscale サブネットルートの SNAT を無効にして、ノードのプライベート/32ルートを広告できます。Tailscale モードのアクティブなエージェント/オートスケーラーのノードプールでは、同じルート内の外部 Network に関する無効な設定を plan 時に失敗させるため、network_scope = "primary"またはnetwork_scope = "external"を設定する必要があります。 - Tailscale ノードトランスポートで Calico を提案しないでください。このモードで最初にサポートされるのは Flannel です。Cilium は引き続き明示的に実験的な扱いです。
- Cloudflare については、Kubernetes API、SSH、Rancher、Grafana、またはイングレスには、外部の Access/Tunnel パターンのみを推奨します。Kubernetes のノードトランスポートとして Cloudflare Mesh/WARP を提案せず、Cloudflare プロバイダーの入力を作り出さないでください。
terraform fmt -recursive、terraform init -upgrade、terraform validate、terraform plan -out=v3-upgrade.tfplanを実行します。MIGRATION.mdに記載されている保護された hcloud の no-destroy ゲートを実行します。これにはhcloud_placement_groupとhcloud_firewallが含まれます。- 説明のつかない置換または破棄がプランに含まれている場合は、適用しないでください。
設定テンプレート
最小限の開発環境(単一ノード)
module "kube-hetzner" {
source = "kube-hetzner/kube-hetzner/hcloud"
version = "<LATEST>" # Always fetch latest!
hcloud_token = var.hcloud_token
ssh_public_key = file("~/.ssh/id_ed25519.pub")
ssh_private_key = file("~/.ssh/id_ed25519")
network_region = "eu-central"
control_plane_nodepools = [
{
name = "control-plane"
server_type = "cx23"
location = "fsn1"
labels = []
taints = []
count = 1
}
]
agent_nodepools = [
{
name = "worker"
server_type = "cx23"
location = "fsn1"
labels = []
taints = []
count = 0
}
]
# Single node: disable auto OS upgrades
automatically_upgrade_os = false
}
本番 HA(コントロールプレーン 3 台+ワーカー)
module "kube-hetzner" {
source = "kube-hetzner/kube-hetzner/hcloud"
version = "<LATEST>"
hcloud_token = var.hcloud_token
ssh_public_key = file("~/.ssh/id_ed25519.pub")
ssh_private_key = file("~/.ssh/id_ed25519")
network_region = "eu-central"
control_plane_nodepools = [
{
name = "control-plane"
server_type = "cx33"
location = "fsn1"
labels = []
taints = []
count = 3 # Odd number for quorum!
}
]
agent_nodepools = [
{
name = "worker"
server_type = "cx43"
location = "fsn1"
labels = []
taints = []
count = 3
}
]
enable_longhorn = true
# Security: restrict access to your IP
firewall_kube_api_source = ["YOUR_IP/32"]
firewall_ssh_source = ["YOUR_IP/32"]
}
NAT ルーターを使用するプライベートクラスター
module "kube-hetzner" {
source = "kube-hetzner/kube-hetzner/hcloud"
version = "<LATEST>"
hcloud_token = var.hcloud_token
ssh_public_key = file("~/.ssh/id_ed25519.pub")
ssh_private_key = file("~/.ssh/id_ed25519")
network_region = "eu-central"
enable_control_plane_load_balancer = true
nat_router = {
server_type = "cax21"
location = "nbg1"
}
control_plane_nodepools = [
{
name = "control-plane"
server_type = "cx33"
location = "fsn1"
labels = []
taints = []
count = 3
# Disable public IPs
enable_public_ipv4 = false
enable_public_ipv6 = false
}
]
agent_nodepools = [
{
name = "worker"
server_type = "cx43"
location = "fsn1"
labels = []
taints = []
count = 3
enable_public_ipv4 = false
enable_public_ipv6 = false
}
]
# Optional: keep control plane LB private too
control_plane_load_balancer_enable_public_network = false
}
Tailscale ノードトランスポート
単一ネットワークのクラスターで、プライベートな Tailnet 経由の Terraform/kubeconfig/SSH アクセスが必要な場合、または複数の Hetzner Cloud Networks にまたがるプライベートクラスターが必要な場合に使用します。Kubernetes ノードトランスポートには、古い外部オーバーレイ方式を使用しないでください。
node_transport_mode = "tailscale"
firewall_kube_api_source = null
firewall_ssh_source = null
tailscale_auth_key = var.tailscale_auth_key
# tailscale_autoscaler_auth_key = var.tailscale_autoscaler_auth_key # Prefer ephemeral reusable key for autoscaler.
tailscale_node_transport = {
bootstrap_mode = "cloud_init"
magicdns_domain = "example-tailnet.ts.net"
auth = {
mode = "auth_key"
}
routing = {
# Single-network clusters may set false; network_scope = "external" nodepools need true.
advertise_node_private_routes = false
}
}
説明するルール:
- Tailscale モードでは、アクティブなすべてのエージェント/オートスケーラーのノードプールに、明示的な
network_scope = "primary"または"external"が必要です。network_idを省略または null にする場合は"primary"を使用し、外部のnetwork_idを指定する場合は、同じルート内のhcloud_network.*.idを含めて"external"を使用します。 - このリリースでは、Tailscale ノードトランスポートと
cluster_ipv6_cidr/service_ipv6_cidrの併用は拒否されます。Tailscale クラスターでは IPv6 の Pod/サービス CIDR を未設定のままにしてください。 - 外部
network_scopeのノードプールを使用する場合、Tailnet ACL で、広告される Hetzner ノードのプライベート/32ルートを自動承認する必要があります。 - このモジュールは、ノード/CNI トラフィックに対する Tailscale のサブネットルート SNAT を無効にします。
- 最初にサポートされるのは Flannel VXLAN です。Cilium には実験的フラグが必要で、Calico は拒否されます。
- 管理対象の Hetzner プライベート LB は、単一のプライマリネットワークのクラスターで問題なく使用できます。外部
network_scopeのノードプールでは、パブリック LB ターゲット、または Hetzner 以外のプライベートな代替手段が必要です。 - モジュールの NAT ルーターが外向き通信を提供するのは、プライマリ Hetzner Network のみです。外部ネットワーク上の Tailscale ノードプールには、パブリックな外向き通信、または外部のブートストラップ経路が必要です。
- 大規模な例は
examples/tailscale-node-transport/large-scale-200.tf.exampleとexamples/tailscale-node-transport/massive-10000-nodes.tf.exampleにあります。 - 200 ノードの静的な例は、
3 control planes + 97 primary agents + 100 agents on one external Networkです。どちらの Network も Hetzner の 100 アタッチメント上限にちょうど達し、配置グループは 21 グループに自動分割されます。 - 合計 10,000 ノードのリファレンス構成は、
3 control planes + 7 static system agents + 90 primary autoscaled workers + 99 external Networks * 100 autoscaled workersです。これはクォータ/設計の参考値であり、気軽に使えるデフォルトではありません。 - 大規模クラスターでは、パブリックな Kubernetes API と SSH を閉じ、明示的に要求されない限りパブリックな管理対象 Web イングレスを使用せず、Tailnet アクセスに依存する公開モデルを推奨します。ノードは Tailscale のブートストラップと直接の UDP/41641 WireGuard 経路のために、引き続きパブリック IPv4/IPv6 を保持する場合があります。パブリック IP を一切使わないマルチネットワーク構成には、すべての外部 Network にプライベートな外向き通信と外部 Tailscale ブートストラップが必要です。
Cloudflare Zero Trust による外部アクセス
オペレーター向けまたはユーザー向けエンドポイントの前段に Cloudflare のポリシーを置きたい場合に使用します。Cloudflare を kube-hetzner 管理のノードトランスポートとして紹介しないでください。
ルール:
- Cloudflare Access/Tunnel は、kube API、SSH、Rancher、Grafana、またはイングレスのホスト名を保護できます。
- Cloudflare アカウントのリソース、DNS レコード、トンネル、Access ポリシー、WARP の登録、サービス トークンは、kube-hetzner の外部で管理します。
node_transport_mode = "cloudflare"は存在せず、Cloudflare Mesh/WARP は v3 でサポートされるノードトランスポートではありません。- Access 経由の kubeconfig には、
cloudflared access tcpまたはユーザー管理の WARP/プライベートルーティングを提案します。 - 参加するすべてのノードが到達して認証できない限り、
control_plane_endpointに Cloudflare Access のホスト名を設定しないでください。 - セキュアなノードトランスポート、または 100 ノード超のマルチネットワーク構成には、
node_transport_mode = "tailscale"を使用してください。
参考ドキュメント:
examples/external-overlay-cloudflare-access/README.mddocs/v3-topology-recommendations.md
Hubble オブザーバビリティを備えた Cilium
module "kube-hetzner" {
source = "kube-hetzner/kube-hetzner/hcloud"
version = "<LATEST>"
hcloud_token = var.hcloud_token
ssh_public_key = file("~/.ssh/id_ed25519.pub")
ssh_private_key = file("~/.ssh/id_ed25519")
network_region = "eu-central"
# Use Cilium CNI
cni_plugin = "cilium"
# Full kube-proxy replacement
enable_kube_proxy = false
# Enable Hubble for observability
cilium_hubble_enabled = true
control_plane_nodepools = [
{
name = "control-plane"
server_type = "cx33"
location = "fsn1"
labels = []
taints = []
count = 3
}
]
agent_nodepools = [
{
name = "worker"
server_type = "cx43"
location = "fsn1"
labels = []
taints = []
count = 3
}
]
}
コスト最適化 ARM クラスター
module "kube-hetzner" {
source = "kube-hetzner/kube-hetzner/hcloud"
version = "<LATEST>"
hcloud_token = var.hcloud_token
ssh_public_key = file("~/.ssh/id_ed25519.pub")
ssh_private_key = file("~/.ssh/id_ed25519")
network_region = "eu-central"
# ARM servers (CAX) are ~40% cheaper
control_plane_nodepools = [
{
name = "control-plane"
server_type = "cax21" # ARM
location = "fsn1"
labels = []
taints = []
count = 3
}
]
agent_nodepools = [
{
name = "worker-arm"
server_type = "cax31" # ARM
location = "fsn1"
labels = []
taints = []
count = 3
}
]
}
超高可用性のマルチロケーション構成
module "kube-hetzner" {
source = "kube-hetzner/kube-hetzner/hcloud"
version = "<LATEST>"
hcloud_token = var.hcloud_token
ssh_public_key = file("~/.ssh/id_ed25519.pub")
ssh_private_key = file("~/.ssh/id_ed25519")
# Must cover ALL locations used
network_region = "eu-central"
# Spread control planes across locations
control_plane_nodepools = [
{
name = "cp-fsn"
server_type = "cx33"
location = "fsn1"
labels = []
taints = []
count = 1
},
{
name = "cp-nbg"
server_type = "cx33"
location = "nbg1"
labels = []
taints = []
count = 1
},
{
name = "cp-hel"
server_type = "cx33"
location = "hel1"
labels = []
taints = []
count = 1
}
]
# Spread workers too
agent_nodepools = [
{
name = "worker-fsn"
server_type = "cx43"
location = "fsn1"
labels = []
taints = []
count = 2
},
{
name = "worker-nbg"
server_type = "cx43"
location = "nbg1"
labels = []
taints = []
count = 2
},
{
name = "worker-hel"
server_type = "cx43"
location = "hel1"
labels = []
taints = []
count = 2
}
]
enable_longhorn = true
}
クイックリファレンス
変数の検索
# Find specific variable
rg -n 'variable "<name>"' variables.tf
# Search by keyword
rg -n -C 3 'description.*<keyword>' variables.tf
# Inspect the complete variable section in docs/llms.md after locating it
GitHub コマンド
# Latest release
gh release list --repo kube-hetzner/terraform-hcloud-kube-hetzner --limit 1
# Search issues
gh issue list --repo kube-hetzner/terraform-hcloud-kube-hetzner --search "<query>" --state all
# View specific issue
gh issue view <number> --repo kube-hetzner/terraform-hcloud-kube-hetzner --comments
# Search discussions
gh api repos/kube-hetzner/terraform-hcloud-kube-hetzner/discussions --jq '.[].title'
検証
terraform fmt -recursive
terraform validate
terraform plan # Check for unexpected changes!