Terraform无法直接在KVM上“一键”创建测试集群,因其原生不支持KVM,须通过libvirt provider桥接;而AWS/Azure等云厂商provider与libvirt生态完全隔离,资源定义、镜像管理、网络模型及自动伸缩机制均不兼容,必须手动配置本地路径、Cloud-init、隔离网络及分步生成kubeadm token才能完成集群闭环。

直接用 terraform apply 无法在 KVM 上“一键”创建测试集群——因为 Terraform 本身不原生支持 KVM,必须通过 libvirt provider 桥接,且 Cloud-init 配置、网络拓扑、SSH 密钥注入这三处最容易卡住。
为什么不能直接用官方 AWS/Azure 模板套到 KVM 上
Terraform 的云厂商 provider(如 aws、azurerm)和私有虚拟化 provider(如 libvirt)是完全独立的生态。KVM 没有 REST API,libvirt provider 实际调用的是本地 virsh 命令 + QEMU/KVM 底层接口,因此:
-
resource "aws_instance"这类资源定义在 libvirt 环境下会报错Provider doesn't support resource - 所有网络、存储、镜像路径都得手动指定本地路径(如
/var/lib/libvirt/images/centos8.qcow2),不能依赖云平台自动拉取 AMI/Marketplace 镜像 - 没有现成的 “托管节点池” 或 “自动伸缩组” 概念,需用
count或for_each手动控制实例数量,再靠外部脚本或 Ansible 补齐集群初始化逻辑
必须安装并配置 libvirt provider 和基础依赖
CentOS 8.3 / Ubuntu 20.04+ 上缺一不可:
- 装好
libvirt-daemon、qemu-kvm、virt-install,并确保当前用户在libvirt组里:sudo usermod -a -G libvirt $USER - 下载对应架构的
terraform-provider-libvirt二进制(不是从 Terraform Registry 自动下载,它不支持自动安装),放进~/.terraform.d/plugins/registry.terraform.io/dmacvicar/libvirt/0.7.0/linux_amd64/并加可执行权限 - provider 声明必须显式写死版本,且启用
use_remote_connection = false(本地直连):provider "libvirt" { uri = "qemu:///system" use_remote_connection = false } - 务必运行
sudo systemctl enable --now libvirtd,否则terraform init会提示failed to connect to libvirt
Cloud-init 是批量初始化唯一可靠方式,但镜像必须预装支持
裸 CentOS 8.3 ISO 安装的镜像默认不含 cloud-init,直接启动后 SSH 连不上、hostname 不生效、磁盘不自动扩容。正确做法是:
Linux 安全审计工具,检查 SSH 配置、开放/监听端口、防火墙规则(ufw/iptables/nftables)、失败登录尝试、sudoers 权限...
- 用
virt-install基于官方 cloud image 重打包(如CentOS-8-GenericCloud-8.3-20200918.2.x86_64.qcow2),而非自己装系统 - 在
libvirt_domain资源中用user_data字段注入 base64 编码的 YAML:user_data = base64encode(templatefile("${path.module}/cloudinit.yaml", { ssh_pub_key = file("~/.ssh/id_rsa.pub") })) -
cloudinit.yaml必须包含write_files写入/etc/hosts、runcmd执行kubeadm init或k3s server启动命令——否则只是起了几台孤立的 VM,不算“集群”
批量创建多节点时,网络和主机名必须隔离
用 count = 3 创建三台节点,若共用一个 libvirt_network 定义且未设 mode = "nat",会导致 DHCP 分配冲突或 DNS 解析失败。关键点:
- 不要复用同一个
libvirt_network实例;要么用count创建多个隔离网络,要么只建一个bridge模式网络并配静态 IP - 每台 VM 的
domain名称必须唯一,否则libvirt会拒绝启动第二台:name = "k8s-node-${count.index}" - 若要用
kubeadm join,主节点的 token 和 ca cert 必须在 Cloud-init 中动态生成并分发——这意味着你得先apply主节点,output出 token,再用local-exec触发子节点 apply,不能真正“一键”完成整个集群
真正能跑通的最小闭环不是“一键”,而是:先 apply control-plane 节点 → 提取 kubeadm token create 输出 → 改写 worker 节点的 cloudinit.yaml → 再 apply worker。跳过这步,所谓“批量创建集群”只是批量起 VM 而已。










