关于 kh-assistant
kh-assistant 是面向 terraform-hcloud-kube-hetzner 的专家 Agent 技能,用于回答集群部署与运维问题。它会优先匹配专门技能,并依据项目文档和最新发布信息提供指引。
主要功能
- 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 助手
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 | 使用 Hetzner 救援模式挂载并检查节点,无需通过 SSH 连接节点 | /debug-node |
| 使用 Terraform 和 OpenTofu 验证模块更改 | test-changes | 针对提供的测试根配置运行格式化、验证、兼容性、示例和计划门禁 | /test-changes |
| 实现 GitHub issue(仅限维护者) | fix-issue | 获取并验证 issue、实现根因修复、测试,并保留贡献者署名 | /fix-issue <number> |
| 对 GitHub issue 进行分类并回复(仅限维护者) | triage-issue | 检查证据和重复问题、对报告进行分类,并起草适当的回复/操作 | /triage-issue <number> |
| 审查 pull request(仅限维护者) | 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 默认使用上游 stable 通道;如果需要可复现性或保留 v2 次版本,请固定版本/通道 |
| 节点操作系统 | 全新节点池默认使用 Leap Micro;现有 MicroOS 节点池仍受支持,并会在常规 v2 升级中保留 |
| 插件版本 | 未设置插件版本输入项时,将使用经过审核的确定性模块矩阵;latest 是选择上游浮动版本行为的显式选项 |
关键规则
必须遵守 — 绝不可违反
| 规则 | 说明 |
|---|---|
| 至少 1 个控制平面 | control_plane_nodepools 必须至少包含一个 count >= 1 的条目 |
| 仅使用受支持的操作系统 | 新节点默认使用 Leap Micro;MicroOS 仅用于旧版支持/升级。绝不要建议使用 Ubuntu、Debian 或其他通用操作系统映像。 |
| 网络区域覆盖范围 | network_region 必须包含所有节点位置 |
| 高可用时控制平面数量必须为奇数 | 使用 1、3 或 5 个,绝不要使用 2 或 4 个(法定人数要求) |
| 自动扩缩器是独立的 | autoscaler_nodepools 独立于 agent_nodepools |
| 始终使用最新版本 | 始终获取并使用最新的发行标签 |
需要避免的常见错误
| 错误 | 正确做法 |
|---|---|
control_plane_nodepools 为空 | 至少配置一个,且 count >= 1 |
| 为实现“高可用”配置 2 个控制平面 | 使用 3 个(法定人数要求为奇数) |
| 建议使用 Ubuntu/Debian | 默认使用 Leap Micro;仅对旧版或明确指定的节点池使用 MicroOS |
位置不在 network_region 中 | network_region 必须涵盖所有位置 |
| 混淆自动扩缩器与 Agent | 自动扩缩器节点池完全独立 |
| 使用旧版本 | 务必先检查最新版本 |
| 在 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 专用 Networks 之间无法路由 |
| 将外部 Tailscale 钩子当作节点传输方式 | 集群传输使用 node_transport_mode = "tailscale";仅将 node_connection_overrides 用于用户自有的运维访问 |
| 将 Cloudflare Mesh/WARP 当作受支持的节点传输方式 | kube-hetzner 管理的安全节点传输使用 Tailscale;Cloudflare Access/Tunnel 仅用于外部运维人员/应用访问 |
| 假设单个 Hetzner Network 可以容纳超过 100 个节点 | 分散到多个 Hetzner Networks,并统计所有连接,包括控制平面、静态 Agent、自动扩缩器 max_nodes、NAT 路由器和负载均衡器 |
| 承诺在单个项目中静态分布 1 万个节点 | Hetzner 分布组每组可容纳 10 台服务器,每个项目最多 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 |
| 在低信任节点上启用内置镜像仓库镜像 | 仅用于信任级别相同的集群;提醒用户注意凭据共享和标签投毒 |
| 因单个工作负载被拒绝而全局禁用 SELinux | 遵循 docs/selinux.md:收集 AVC,尝试使用 udica,最后才考虑设置每个节点池的 selinux = false |
| 假设 RKE2 控制平面需要 8GB 内存 | v3 按规格预留的 kubelet 资源使 4GB cx23 控制平面可行;生产环境仍应为工作负载预留充足空间 |
| 拆除时手动删除云资源 | 首先使用 scripts/destroy.sh;scripts/cleanup.sh 是强制清理的后备方案,会清理令牌所属的整个 HCloud 项目 |
v3 拓扑速查
| 需求 | 建议 |
|---|---|
| 小型开发环境 | 单个控制平面、一个小型 Agent 节点池;非必要时不配置入口 |
| 常规高可用 | 3 个控制平面、至少 2 个 Agent、一个主 Hetzner Network |
| 仅限专用网络 | 在主 Network 上配置 NAT 路由器和专用控制平面负载均衡器 |
| 安全 API/SSH | 设置 node_transport_mode = "tailscale",并关闭公共 API/SSH 防火墙来源 |
| 受 Cloudflare 保护的运维人员/应用访问 | 在 kube API、SSH、Rancher、Grafana 或入口前使用用户管理的 Cloudflare Access/Tunnel;节点传输仍使用 Hetzner 专用网络或 Tailscale |
| 超过 100 个 Cloud 节点 | 使用 Tailscale 节点传输方式,并为每 100 个节点的预算配置一个外部 Hetzner Network 分片 |
| 1 万节点参考配置 | 优先采用自动扩缩器的 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 中修复
- 零 Agent 集群的 apply 后计划失败(#2236、#2238): v3.0.0 中设置了
agent_nodepools = []的集群在后续计划时会因no change found for terraform_data.agents而失败。请升级到 v3.0.1 或更高版本;该版本通过单个 Agent ID 聚合器传递就绪状态触发器,升级时不会替换或重新运行安装后就绪检查。 - 静态 Agent 被分配到错误的子网(#2239): v3.0.0 会将主网络中的静态 Agent 分配到控制平面子网。v3.0.1 或更高版本恢复了按节点池分配地址的行为。在 v3.0.0 上首次创建的集群需要按照发行说明执行滚动 Agent 迁移:对一个 Agent 执行 cordon/drain,针对该 Agent 执行 apply,使其原地分离并重新连接专用 NIC,并重启 k3s/RKE2 配置;确认它在目标子网上处于
Ready状态后,取消 cordon,然后继续处理其他 Agent。如果接口映射没有恢复,请重启以便kh-rename-interface.service进行验证。从 v2.x 升级的常规集群会保留 v2 的 IP 公式,不受影响。
常见错误模式
| 错误 | 原因 | 解决方案 |
|---|---|---|
cannot sum empty list | control_plane_nodepools 为空或所有数量均为 0 | 至少添加一个数量 >= 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 是否与现有 CIDR 重叠 |
cloud-init failed | 缺少 Leap Micro/MicroOS 快照、区域错误、架构错误或发行版标签错误 | 使用 packer 在正确的区域和架构中重新创建快照,并选择 k3s/RKE2 SELinux 变体 |
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 示例中当前采用的最低配置。在基于大小设置 kubelet 预留资源后,RKE2 + Leap
Micro cx23 控制平面配置已在实际环境中验证,详见 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 中的精确工作流。
核心规则:
- 编辑前备份状态。
- 使用
MIGRATION.md重写仅适用于 v2 的输入。 - 谨慎反转正向 / 负向布尔值。
- 保留首次应用的兼容性冻结,除非运维人员有意选择更改拓扑 / 运行时:
k3s_channel = "stable"是 v3 默认值,但 v2 升级通常会固定为k3s_channel = "v1.33"或k3s_version;未设置的附加组件版本将采用经过审查且确定性的默认值;network_subnet_mode保持为per_nodepool;node_transport_mode保持为hetzner_private。 - 移除
network_id = 0;在 v3 中,省略或设为 null 表示主 Network。 - 移除控制平面的
network_id;控制平面始终使用主 Network。 - 对于安全的 Tailnet 访问或私有多网络扩展,优先选择
node_transport_mode = "tailscale"。对于 v2 到 v3 的升级,请在基础升级后,通过单独且经过审计的计划引入大规模多网络扩展。Tailscale 模式会让 Kubernetes 节点 IP 保持在 Hetzner 私有地址上,并可在禁用 Tailscale 子网路由 SNAT 的情况下通告节点私有/32路由。Tailscale 模式下的活动 Agent / 自动扩缩器节点池必须设置network_scope = "primary"或network_scope = "external",以便在计划阶段发现无效的同根外部 Network 配置并报错。 - 暂时不要建议将 Calico 与 Tailscale 节点传输模式搭配使用。Flannel 是首个受支持的方案;Cilium 在此传输模式下仍明确属于实验性功能。
- 对于 Cloudflare,只推荐用于 kube API、SSH、Rancher、Grafana 或入口流量的外部 Access / Tunnel 模式。不要建议将 Cloudflare Mesh / WARP 用作 kube-hetzner 节点传输方式,也不要虚构 Cloudflare 提供商输入项。
- 运行
terraform fmt -recursive、terraform init -upgrade、terraform validate和terraform plan -out=v3-upgrade.tfplan。 - 运行
MIGRATION.md中受保护的 hcloud 禁止销毁检查关卡;其中包含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
}
生产级高可用配置(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 网络的私有集群时使用。不要将较旧的外部覆盖网络模式用于 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 模式要求每个启用的 Agent/自动扩缩容节点池都显式设置
network_scope = "primary"或"external"。省略或设为 nullnetwork_id时使用"primary";搭配外部network_id时使用"external",包括同一根模块中的hcloud_network.*.id。 - 在此版本中,Tailscale 节点传输不支持设置
cluster_ipv6_cidr/service_ipv6_cidr;Tailscale 集群应将 IPv6 Pod/Service CIDR 留空。 - 使用外部
network_scope节点池时,Tailnet ACL 必须自动批准所通告的 Hetzner 节点私有/32路由。 - 此模块会为节点/CNI 流量禁用 Tailscale 子网路由 SNAT。
- 首先支持 Flannel VXLAN;Cilium 需要启用实验性标志;Calico 不受支持。
- 单主网络集群可使用 Hetzner 托管私有 LB;外部
network_scope节点池需要公共 LB 目标,或使用非 Hetzner/私有替代方案。 - 此模块的 NAT 路由器仅向主 Hetzner 网络提供出站流量;外部网络上的 Tailscale 节点池需要公共出站流量或外部引导路径。
- 大型示例位于
examples/tailscale-node-transport/large-scale-200.tf.example和examples/tailscale-node-transport/massive-10000-nodes.tf.example。 - 200 节点静态示例包含
3 个控制平面 + 97 个主网络 Agent + 100 个位于同一外部网络的 Agent;两个网络都恰好达到 Hetzner 的 100 个连接上限,placement group 会自动分片为 21 个组。 - 10,000 个总节点的参考配置为
3 个控制平面 + 7 个静态系统 Agent + 90 个主网络自动扩缩容工作节点 + 99 个外部网络 * 每个网络 100 个自动扩缩容工作节点。这是配额/设计参考,并非可随意采用的默认配置。 - 大型集群推荐的暴露模型会关闭公共 Kubernetes API 和 SSH,不使用公共托管 Web 入口(除非明确要求),并依赖 Tailnet 访问。节点仍可保留公共 IPv4/IPv6,用于 Tailscale 引导和直接 UDP/41641 WireGuard 连接;要实现真正无公共 IP 的多网络集群,每个外部网络都需要私有出站流量和外部 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!