使用 Terraform 部署 Proxmox 虚拟机

举报
yd_212422989 发表于 2026/11/06 14:20:28 2026/11/06
【摘要】 IaC 定义代码即基础设施 (IaC) 是现代 IT 运营中的一种常见实践,它使用代码来管理和配置计算基础设施,而不是使用命令行工具]或 GUI 接口进行手动硬件配置。手动硬件配置容易导致实现不一致和错误。组织可以通过使用代码自动化基础设施设置来确保其环境的可重复性和一致性。 常用 IaC 工具概述CFEngine率先使用声明式、幂等的 DSL 来安装软件包和管理服务器上的配置文件,Pup...

IaC 定义

代码即基础设施 (IaC) 是现代 IT 运营中的一种常见实践,它使用代码来管理和配置计算基础设施,而不是使用命令行工具]或 GUI 接口进行手动硬件配置。手动硬件配置容易导致实现不一致和错误。组织可以通过使用代码自动化基础设施设置来确保其环境的可重复性和一致性。

常用 IaC 工具概述

CFEngine率先使用声明式、幂等的 DSL 来安装软件包和管理服务器上的配置文件,Puppet 和 Chef 随后出现。随着时间的推移,其他工具因其可靠性、适应性和在各行业的广泛接受而日益突出。这些包括:
Ansible。最初由 于 2012 年编写,并于 2015 年被 Red Hat收购,Ansible 是一款开源的 IaC 工具套件,涵盖软件配置、配置管理和应用程序部署功能。Ansible 是无代理的,依赖于通过 SSH 或 Windows 远程管理的临时远程连接,允许执行 PowerShell。系统配置使用 YAML 定义。
AWS Cloud Development Kit (CDK)。亚马逊网络服务 (AWS) 开发的开源软件开发框架,用于使用熟悉的 [编程语言] 定义和配置云基础设施资源。支持的语言包括 C#、[Go]、Java、[JavaScript] 和 Python。[CDK] 包含一个更高级别的构造和预构建组件库,这些组件封装了一个或多个 AWS 资源及其配置。构造可用于构建更高级别的抽象,称为模式。
Chef。[Progress Chef] 是一款用 Ruby 和 Erlang 编写的配置管理工具。它使用纯 Ruby 领域特定语言 (DSL) 来编写系统配置“配方”,然后可以将这些配方分组到“cookbook”中以方便管理。它可以与 Amazon EC2、[Google Cloud]Platform (GCP)、Microsoft Azure 等集成,以自动配置和配置新机器。
OpenTofu。OpenTofu 是 HashiCorp 的 Terraform 的一个开源分支,于 2023 年 8 月创建,以应对该公司将其 IaC 产品转向更严格许可证的决定,[OpenTofu] 使用声明式 [OpenTofu 配置语言],并与 AWS、Azure 和 Google 云服务兼容。
Pulumi。Pulumi由前微软员工Joe Duffy和Eric Rudder于2017年创立,[Pulumi]是新一代IaC工具之一,主要面向云端设计。它采用命令式方法定义基础设施。开源的Pulumi CLI和SDK为用户提供了一种管理各种公共云提供商(包括AWS、Azure和GCP)的云基础设施的方法。代码可以使用多种语言编写,包括[C#]、Go、Java、JavaScript、Python、TypeScript和[YAML]。
Puppet。 [Puppet]使用其自身的声明式语言来描述系统配置。它可用于在数据中心和云基础设施中配置、修补、配置和管理操作系统和应用程序组件。
Terraform。 Terraform由HashiCorp开发,能够管理本地和云端资源。Terraform用户使用称为[HashiCorp Configuration Language]或JSON的声明式配置语言来定义和配置数据中心基础设施。它支持一系列提供商,包括AWS、Google Cloud和Microsoft Azure,非常适合云环境。
这些工具不仅有助于自动化基础设施的设置和管理,而且支持创建可重用和版本控制的基础设施代码,从而提高可扩展性和易于维护性。

本文目的

通过 Terraform 调用Proxmox VE 的 REST API 在创建虚拟机,其中使用的是cloud-init 模版

  • 将平台虚拟化
  • 快速创建销毁实验环境
  • 体验基础设施即代码 IaaS

环境概述

pve: 8.4.1
Terraform:开源的基础设施即代码工具
terraform-provider-proxmox:Terraform Proxmox Provider,通过 Proxmox VE 的 REST API 在创建虚拟机。

pve

如何安装pve此处不详细展开,具体参考本目录下关于pve的篇章

创建Cloud-Init Template

这里选用 Cloud-Init 的方式,从 cloud-init template 来克隆虚拟机。cloud-init 的虚拟机可以完成一些高级定制的初始化工作,具体参考 [Cloud Init 文档]。

下载Cloud-Init 镜像

登陆到 Proxmox VE 宿主机,下宿主机目录下下载Cloud-init镜像
官方镜像地址:

# cloud images下载地址
# centos:
http://cloud.centos.org/centos/
# ubuntu:
http://cloud-images.ubuntu.com/releases/
# debian:
https://cloud.debian.org/images/cloud/OpenStack/
# fedora:
https://alt.fedoraproject.org/cloud/
# opensuse:
https://software.opensuse.org/distributions/leap#JeOS-ports

本文中下载ubuntu 24.10版本的镜像
登录上pve主机上,在特定目录下执行

wget https://cloud-images.ubuntu.com/releases/oracular/release-20250701/ubuntu-24.10-server-cloudimg-amd64.img
创建模版主机

登录到pve主机上执行下面的命令创建一个虚拟机:

# 创建一个vmid 为9000,名字为ubuntu-24.10-template的虚拟机
qm create 9000 --cores 1 --memory 1024 --name "ubuntu-24.10-template" --net0 virtio,bridge=vmbr0
#将cloud镜像注入到9000 虚拟机
qm importdisk 9000  ubuntu-24.10-server-cloudimg-amd64.img local-lvm
# 详细设置虚拟机的一些配置
qm set 9000 --sata0 local-lvm:vm-9000-disk-0 --sata1 local-lvm:cloudinit --boot c --bootdisk sata0 --serial0 socket --vga serial0 --ciuser root --cipassword 123456  --ipconfig0 ip=dhcp,ip6=dhcp --nameserver 8.8.8.8 --searchdomain 8.8.8.8
配置模板系统

启动模版系统

qm start 9000

通过默认设置的用户,进入模版系统,优化设置,如下
CentOS

# 配置yum源
sudo sed -e 's|^mirrorlist=|#mirrorlist=|g' -e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.ustc.edu.cn/centos|g' -i.bak /etc/yum.repos.d/CentOS-Base.repo

# 设置时区
timedatectl set-timezone Asia/Shanghai

# 安装vim
yum install vim bash-completion

# 修改ssh远程
vim /etc/ssh/sshd_config
# 开启root登录(如果需要的话;这里开启了之后Cloud-Init用户就可以设置root)
PermitRootLogin yes
# 开启密钥登录
PubkeyAuthentication yes
# 密钥路径
AuthorizedKeysFile
# 开启密码登录(默认只允许密钥登录)
PasswordAuthentication yes
# 不允许空密码登录
PermitEmptyPasswords no
# 关闭连接的DNS解析
UseDNS no

Ubuntu

# 设置时区
timedatectl set-timezone Asia/Shanghai

# 设置APT仓库源
sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list

# 更新源和安装常用软件
apt update && sudo apt install vim bash-completion -y

# 更新系统
sudo apt upgrade

# 修改SSH配置
vim /etc/ssh/sshd_config
# 开启root登录(如果需要的话;这里开启了之后Cloud-Init用户就可以设置root)
PermitRootLogin yes
# 开启密钥登录
PubkeyAuthentication yes
# 密钥路径
AuthorizedKeysFile
# 开启密码登录(默认只允许密钥登录)
PasswordAuthentication yes
# 不允许空密码登录
PermitEmptyPasswords no
# 关闭连接的DNS解析
UseDNS no

配置完成之后,关闭虚拟机

qm stop 9000
转换模版

将模版系统转换成模版

qm template 9000

创建 Proxmox 用户及 API Token

使用 Proxmox VE 的 REST API 需要权限校验,有用户名密码或者 API Token 两种方式。我们选用后者,登陆到 Proxmox 宿主机,执行如下创建 API Token

pveum user token add root@pam terraform-token --privsep=0

┌──────────────┬──────────────────────────────────────┐
│ key          │ value                                │
╞══════════════╪══════════════════════════════════════╡
│ full-tokenid │ root@pam!terraform-token   │
├──────────────┼──────────────────────────────────────┤
│ info         │ {"privsep":"0"}                      │
├──────────────┼──────────────────────────────────────┤
│ value        │ e573b835-503b-4999-8806-91dbf0493d39 │
└──────────────┴──────────────────────────────────────┘

Terraform

有了 token 和 cloud-init 模板后,就是定义虚拟机了。
安装最新版本的 terraform。

wget https://releases.hashicorp.com/terraform/1.12.2/terraform_1.12.2_linux_amd64.zip unzip terraform_1.12.2_linux_amd64.zip  && mv terraform /usr/bin

创建一个空目录

mkdir ./Iac

在空目录中创建 ubuntu.tf 文件,并按步骤进行配置:

touch ubuntu.tf
配置要使用的 provider:
terraform {
  required_providers {
    proxmox = {
      source = "telmate/proxmox"
      version = "3.0.2-rc01"
    }
  }
}
配置 provider
provider "proxmox" {
  pm_tls_insecure     = true
  pm_api_url          = "https://192.168.28.165:8006/api2/json"
  pm_api_token_id     = "root@pam!iac"
  pm_api_token_secret = "e573b835-503b-4999-8806-91dbf0493d39"
}
配置虚拟机资源

可以参考[provider 的配置说明]:

  clone = "ubuntu-24.10-template"
  agent   = 0
  os_type = "linux"
  onboot  = false
  memory   = 4096
  scsihw   = "virtio-scsi-pci"
  cpu {
  cores    = 1
  sockets  = 1
  type     = "host"
  }
  disks {
    sata {
      sata0 {
        disk {
         size     = "101580M"
         storage  = "local-lvm"
    }
  }
      sata1 {
         cloudinit {
          storage  = "local-lvm"
          }
        }
}
}
  network {
    id = 0
    model  = "virtio"
    bridge = "vmbr0"
  }
  lifecycle {
    ignore_changes = [
      network,
    ]
  }
  ipconfig0 = "ip=192.168.28.19${count.index + 1}/24,gw=192.168.28.1"
  ciuser  = "manager"
  cipassword = "123456"

}
创建虚拟机

第一次需要先执行 init 命令进行初始化:

terraform init

可以使用 terraform fmt 和 terraform validate 对配置文件进行格式化和校验。

[root@jumpserver IaC]# vim ubuntu.tf 
[root@jumpserver IaC]# terraform fmt
ubuntu.tf
[root@jumpserver IaC]# terraform validate
Success! The configuration is valid.

使用 terraform plan 可以查看虚拟机信息

terraform plan

通过terraform apply 创建虚拟机

[root@jumpserver IaC]# terraform apply
Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

proxmox_vm_qemu.proxmox-ubuntu[0]: Creating...
proxmox_vm_qemu.proxmox-ubuntu[0]: Still creating... [00m10s elapsed]
proxmox_vm_qemu.proxmox-ubuntu[0]: Still creating... [00m20s elapsed]
proxmox_vm_qemu.proxmox-ubuntu[0]: Still creating... [00m30s elapsed]
proxmox_vm_qemu.proxmox-ubuntu[0]: Creation complete after 35s [id=pve/qemu/110]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
删除虚拟机

虚拟机的销毁也很简单,执行 terraform destory 并输入 yes 即可。

删除指定虚拟机

在Terraform中删除5台服务器中的指定一台,可以使用terraform destroy -target命令定向删除特定资源。以下是具体步骤:

  1. 查看当前资源列表
    首先执行terraform state list查看所有已创建的虚拟机资源ID,例如:
proxmox_vm_qemu.server[0]
proxmox_vm_qemu.server[1]
proxmox_vm_qemu.server[2]
proxmox_vm_qemu.server[3]
proxmox_vm_qemu.server[4]

  1. 定向删除特定实例
    假设要删除第3台服务器(索引为2),执行:
terraform destroy -target=proxmox_vm_qemu.server[2] [2][6]
  1. 确认操作
    终端会提示确认,输入yes后仅会删除指定的虚拟机,其他4台服务器保持不变2。

附录

qm 创建命令

VM_ID=101
# 创建虚拟机
qm create $VM_ID --cores 4 --memory 4096 --name ubuntu --net0 virtio,bridge=vmbr0
# 给虚拟机导入镜像
qm importdisk $VM_ID jammy-server-cloudimg-amd64.img local-lvm
# 创建磁盘到local-lvm
qm set $VM_ID --sata0 local-lvm:vm-$VM_ID-disk-0
# 创建cloudinit
qm set $VM_ID --sata1 local-lvm:cloudinit
# 设置默认启动项
qm set $VM_ID --boot c --bootdisk sata0
# 设置vga
qm set $VM_ID --serial0 socket --vga serial0
# 设置root密码
qm set $VM_ID --ciuser root --cipassword 123123
# 配置静态网络
#qm set $VM_ID  --ipconfig0 ip=10.0.10.123/24,gw=10.0.10.1,ip6=dhcp
# 配置DHCP网络
qm set $VM_ID --ipconfig0 ip=dhcp,ip6=dhcp
# 配置DNS
qm set $VM_ID --nameserver 223.5.5.5
qm set $VM_ID --searchdomain 223.5.5.5
# 转换为模板
qm template $VM_ID
# 克隆
qm clone 101 103 --name cby

qm命令详解

qm monitor <vmid>         # 连接到虚拟机控制监视器
qm clone <vmid> <newid> [OPTIONS]  # 克隆虚拟机
qm start <vmid>               # 启动实例
qm shutdown <vmid>      # 优雅停止实例 发送关机命令
qm wait <vmid> [time]   wait until vm is stopped
qm stop <vmid>               # 停止实例 强制停止
qm reset <vmid>              # 重启实例 相当于stop然后再start
qm suspend <vmid>        # 暂停实例
qm resume <vmid>         # 恢复实例
qm cad <vmid>               #发送按键 ctrl-alt-delete
qm destroy <vmid>        # 销毁实例(删除所有已使用/拥有的卷)
qm unlock <vmid>         # 清除迁移/备份锁
qm status <vmid>          # 显示实例状态
qm cdrom <vmid> [<device>] <path>  set cdrom path. <device is ide2 by default>
qm cdrom <vmid> [<device>] eject   eject cdrom
qm unlink <vmid> <volume>  delete unused disk images
qm vncproxy <vmid> <ticket>  open vnc proxy
qm vnc <vmid>           start (X11) vncviewer (experimental)
qm showcmd <vmid>     # 显示命令行(调试信息)
qm list                             # 列出所有虚拟机
qm startall                       # 启动所有虚拟机 当onboot=1时
qm stopall [timeout]       # 停止所有虚拟机(默认超时为3分钟)


qm [create|set] <vmid>  # 创建虚拟机
    --memory  <MBYTES>    memory in MB (64 - 8192)
    --sockets <N>         set number of CPU sockets <N>
    --cores <N>           set cores per socket to <N>
    --ostype NAME         specify OS type
    --onboot [yes|no]     start at boot
    --keyboard XX         set vnc keyboard layout
    --cpuunits <num>      CPU weight for a VM
    --name <text>         set a name for the VM
    --description <text>  set VM description
    --boot [a|c|d|n]      specify boot order
    --bootdisk <disk>     enable booting from <disk>
    --acpi (yes|no)       enable/disable ACPI
    --kvm (yes|no)        enable/disable KVM
    --tdf (yes|no)        enable/disable time drift fix
    --localtime (yes|no)  set the RTC to local time
    --vga (gd5446|vesa)   specify VGA type
    --vlan[0-9u]          MODEL=XX:XX:XX:XX:XX:XX[,MODEL=YY:YY:YY:YY:YY:YY]
    --ide<N>              [volume=]volume,[,media=cdrom|disk]
                          [,cyls=c,heads=h,secs=s[,trans=t]]
                          [,cache=none|writethrough|writeback]
                          [,snapshot=on|off][,cache=on|off][,format=f]
                          [,werror=enospc|ignore|report|stop]
                          [,rerror=ignore|report|stop]
                          [,backup=no|yes]
    --ide<N> <GBYTES>     create new disk
    --ide<N> delete       remove drive - destroy image
    --ide<N> undef        remove drive - keep image
    --cdrom <file>        is an alias for --ide2 <file>,media=cdrom
    --scsi<N>             [volume=]volume,[,media=cdrom|disk]
                          [,cyls=c,heads=h,secs=s[,trans=t]]
                          [,snapshot=on|off][,format=f]
                          [,cache=none|writethrough|writeback]
                          [,werror=enospc|ignore|report|stop]
                          [,backup=no|yes]
    --scsi<N> <GBYTES>    create new disk
    --scsi<N> delete      remove drive - destroy image
    --scsi<N> undef       remove drive - keep image
    --virtio<N>           [volume=]volume,[,media=cdrom|disk]
                          [,cyls=c,heads=h,secs=s[,trans=t]]
                          [,snapshot=on|off][,format=f]
                          [,cache=none|writethrough|writeback]
                          [,werror=enospc|ignore|report|stop]
                          [,rerror=ignore|report|stop]
                          [,backup=no|yes]
    --virtio<N> <GBYTES>  create new disk
    --virtio<N> delete    remove drive - destroy image
    --virtio<N> undef     remove drive - keep image
    




pveperf    # 基准脚本
pvesr list    # 列出存储复制作业
ha-manager status   # 查看HA状态
pvecm nodes    # 查看集群节点
pvecm status    # 查看集群状态
pve-firewall compile  # 查看防火墙规则
pve-firewall localnet  # 输出本地网络信息
pve-firewall restart    # 重启防火墙
pve-firewall stop       # 停止防火墙
pve-firewall start      # 启动防火墙



pvesh get /version   # 查看集群
pvesh get /cluster/resources  #查看集群资源状况
pvesh get /nodes   # 显示所有node
pvesh get /nodes/<nodeid>/qemu   # 显示某节点中的虚拟机
pvesh get /nodes/<nodeid>/qemu/<vmid>/status/current    # 显示某虚拟机状态
pvesh create /nodes/<nodeid>/qemu/<vmid>/status/start    # 开启一个虚拟机 [注意是create哦,不是get了]
pvesh get /nodes/<nodeid>/lxc/<ctid>/snapshot    # 显示某节点下容器快照
pvesh get /nodes/<nodeid>/disks/zfs    # 显示某节点的ZFS存储区
pvesh get /nodes/<nodeid>/disks/list  # 显示某节点的磁盘列表

问题列表

1.wget通过清华源 下载镜像,报401

原本打算通过国内源在下cloud-init镜像,但是通过pve下载工具或者wget 下载都报401错误
在网上搜索之后的方法也未奏效(添加 user-agent)
建议直接使用官方镜像源

2.terraform plan 报错

在配置检查没有问题的情况下,terraform plan报以下错误

Stack trace from the terraform-provider-proxmox_v2.9.13 plugin: panic: interface conversion: interface {} is string, not float64

在github 的issue上查询之后发现了答案

2.9.13 and 2.9.12 have the same problem. I have searched high and low for some mysterious string value that should be a float, but nothing so far. Double checked ssh-keys for improper encoding but no dice.

[@cwilson613](https://github.com/cwilson613) 2.x release is broken, please try 3.0.2-rc01

解决方法是使用3.x版本的插件,在required_providers中声明插件版本为3.0.2-rc01,如下

terraform {
  required_providers {
    proxmox = {
      source = "telmate/proxmox"
      version = "3.0.2-rc01"
    }
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。