Proxmox VE Cloud-Init 完全指南:从模板到克隆,SSH密钥注入、IP自动配置与踩坑实录

告别手动装系统!深入掌握 Proxmox VE 的 Cloud-Init 功能——镜像准备、模板制作、SSH 密钥注入、静态 IP 自动配置,以及 vendor.yaml 高级定制与常见踩坑全解。

前言

你是否有过这种体验:新建一台虚拟机,挂 ISO、等安装界面、选语言、设分区、配用户、改 SSH 配置、敲 ip addr 看 DHCP 分配的地址……整个过程少则五分钟,多则半小时。当你有 5 台、10 台虚拟机要批量部署时,这简直是噩梦。

Cloud-Init 就是来终结这种手工劳动的。它是云厂商(AWS、Azure、GCP)的标准初始化方案,也是你在 Proxmox VE 上实现 “一次配置、秒级克隆” 的关键工具。本文将从原理到实战,带你彻底掌握 PVE 上的 Cloud-Init 自动化部署。


1. Cloud-Init 是什么?

Cloud-Init 是 Canonical 开发的开源工具,负责在虚拟机首次启动时完成初始化工作。它的工作流程非常简单:

1
VM 首次启动 → 读取 metadata/userdata → 执行配置 → 完成初始化 → 禁用自身

支持的操作:

  • 🏷️ 设置主机名
  • 👤 创建用户、设置密码、注入 SSH 公钥
  • 🌐 配置静态 IP / DHCP / DNS
  • 📦 安装软件包、执行脚本
  • 💾 扩容磁盘、挂载文件系统
  • 📝 写入任意文件

在 PVE 中,Cloud-Init 通过 虚拟 CD-ROM (IDE 设备) 向 VM 注入配置文件,标准名为 NoCloud 数据源。


2. 准备 Cloud-Init 镜像

PVE 内置了主流发行版的 Cloud-Init 镜像下载工具,你也可以手动导入。

2.1 使用 PVE 内置下载(推荐)

在 PVE Web UI 中,选择 local 存储 → CT 模板 / ISO 镜像,或者直接用命令:

1
2
3
4
5
6
7
# 查看可用的 Cloud-Init 镜像
pveam available | grep -i cloud

# 输出示例:
# system   ubuntu-22.04-standard_22.04-1_amd64.tar.gz
# system   debian-12-standard_12.2-1_amd64.tar.gz
# ...

但注意:pveam 下载的是 LXC 模板,不是 Cloud-Init VM 镜像。要获取 Cloud-Init VM 镜像,直接下载官方 .qcow2.img 文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Ubuntu 22.04 Cloud-Init 镜像
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img \
  -O /var/lib/vz/template/iso/jammy-cloudimg-amd64.img

# Debian 12 Cloud-Init 镜像
wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 \
  -O /var/lib/vz/template/iso/debian-12-cloudimg-amd64.qcow2

# Rocky Linux 9
wget https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2 \
  -O /var/lib/vz/template/iso/rocky-9-cloudimg-amd64.qcow2

2.2 调整镜像大小

官方 Cloud 镜像通常只有 2-5 GB,你需要扩容才能用:

1
2
# 扩容到 32GB
qemu-img resize /var/lib/vz/template/iso/jammy-cloudimg-amd64.img 32G

⚠️ 必须在导入 PVE 之前扩容。导入后再扩容应使用 qm resize,但磁盘格式必须是 qcow2 且无快照。

2.3 导入到 Proxmox VE

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 创建 VM(ID=9000,作为模板)
qm create 9000 \
  --name ubuntu-2204-cloudinit-template \
  --memory 2048 \
  --cores 2 \
  --net0 virtio,bridge=vmbr0 \
  --ostype l26

# 导入磁盘
qm importdisk 9000 /var/lib/vz/template/iso/jammy-cloudimg-amd64.img local-lvm

# 附加磁盘为 scsi0
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0

# 添加 Cloud-Init 驱动器(关键步骤!)
qm set 9000 --ide2 local-lvm:cloudinit

# 设置启动顺序
qm set 9000 --boot order=scsi0

# 启用 QEMU Guest Agent
qm set 9000 --agent enabled=1

# 添加串口控制台(方便 web console 调试)
qm set 9000 --serial0 socket --vga serial0

此时你就有了一个 “裸” 模板 VM,接下来配置 Cloud-Init 参数。


3. Cloud-Init 核心配置

3.1 基础配置:用户、密码、SSH

1
2
3
4
5
6
7
8
9
# 设置用户名和密码
qm set 9000 --ciuser ubuntu
qm set 9000 --cipassword "YourSecurePassword123"

# 注入 SSH 公钥(推荐,避免密码登录)
qm set 9000 --sshkeys ~/.ssh/id_ed25519.pub

# 设置主机名
qm set 9000 --cihostname cloud-template

💡 最佳实践:模板中只注入 SSH 公钥,不要设置密码。克隆后再按需修改。

3.2 网络配置:DHCP vs 静态 IP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# DHCP(默认)
qm set 9000 --ipconfig0 ip=dhcp

# 静态 IP
qm set 9000 --ipconfig0 ip=192.168.1.100/24,gw=192.168.1.1

# IPv6 SLAAC
qm set 9000 --ipconfig0 ip=dhcp,ip6=auto

# 多网卡
qm set 9000 \
  --net0 virtio,bridge=vmbr0 \
  --net1 virtio,bridge=vmbr1 \
  --ipconfig0 ip=192.168.1.100/24,gw=192.168.1.1 \
  --ipconfig1 ip=10.0.0.100/24

3.3 DNS 配置

1
2
qm set 9000 --nameserver "8.8.8.8 1.1.1.1"
qm set 9000 --searchdomain "home.lab"

3.4 转换为模板

配置完成后,转换为模板以保护基础镜像:

1
qm template 9000

现在 9000 变成了只读模板,无法直接启动。


4. 从模板克隆部署

4.1 完整克隆

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 从模板 9000 克隆为 VM 101
qm clone 9000 101 \
  --name web-server-01 \
  --full true \
  --storage local-lvm

# 修改 Cloud-Init 参数(每台 VM 独立配置)
qm set 101 --cihostname web-01
qm set 101 --ipconfig0 ip=192.168.1.101/24,gw=192.168.1.1
qm set 101 --sshkeys ~/.ssh/id_ed25519.pub
qm set 101 --ciuser admin

# 启动
qm start 101

4.2 链接克隆(节省空间)

1
2
3
4
5
6
7
8
qm clone 9000 102 \
  --name web-server-02 \
  --full false \
  --storage local-lvm

qm set 102 --cihostname web-02
qm set 102 --ipconfig0 ip=192.168.1.102/24,gw=192.168.1.1
qm start 102

❗ 链接克隆依赖模板磁盘,删除模板前必须先 qm template 9000 --disk scsi0,size=32G 将所有链接克隆转为完整磁盘。

4.3 批量部署脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
TEMPLATE_ID=9000
NAMES=("web-01" "web-02" "db-01" "cache-01")
IPS=("192.168.1.101" "192.168.1.102" "192.168.1.103" "192.168.1.104")

for i in "${!NAMES[@]}"; do
  VMID=$((200 + i))
  echo "Creating ${NAMES[$i]} (VMID=$VMID, IP=${IPS[$i]})..."

  qm clone $TEMPLATE_ID $VMID \
    --name "${NAMES[$i]}" \
    --full true \
    --storage local-lvm

  qm set $VMID \
    --cihostname "${NAMES[$i]}" \
    --ipconfig0 "ip=${IPS[$i]}/24,gw=192.168.1.1" \
    --sshkeys ~/.ssh/id_ed25519.pub

  qm start $VMID
  echo "✅ ${NAMES[$i]} started"
done

5. 高级定制:vendor.yaml 与 user.yaml

PVE 的 Web UI 只能配置基础参数。要实现安装软件包、写入文件、执行脚本,你需要自定义 cicustom

5.1 创建自定义 Cloud-Init 文件

在 PVE 存储中创建 Snippets:

1
2
3
4
5
# 确保 snippets 存储存在
pvesm status | grep snippets
# 如果不存在,在 /etc/pve/storage.cfg 中定义

mkdir -p /var/lib/vz/snippets

创建 user.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# /var/lib/vz/snippets/ubuntu-user.yaml
#cloud-config
hostname: {{ hostname }}
manage_etc_hosts: true

users:
  - name: admin
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: sudo, docker
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...

package_update: true
package_upgrade: true

packages:
  - qemu-guest-agent
  - htop
  - vim
  - curl
  - wget
  - git
  - net-tools
  - ca-certificates
  - gnupg

write_files:
  - path: /etc/sysctl.d/99-custom.conf
    content: |
      net.core.default_qdisc=fq
      net.ipv4.tcp_congestion_control=bbr
      vm.swappiness=1
    permissions: '0644'

  - path: /etc/ssh/sshd_config.d/99-hardening.conf
    content: |
      PermitRootLogin prohibit-password
      PasswordAuthentication no
      X11Forwarding no
    permissions: '0644'

runcmd:
  - sysctl --system
  - systemctl restart sshd
  - timedatectl set-timezone Asia/Shanghai
  - [mkdir, -p, /opt/apps]

final_message: "Cloud-Init 初始化完成!系统于 $UPTIME 秒后启动。"

5.2 关联自定义配置

1
qm set 101 --cicustom "user=local:snippets/ubuntu-user.yaml"

你还可以分离 vendor.yaml(厂商配置)和 network-config(网络配置):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 自定义网络配置
cat > /var/lib/vz/snippets/static-net.yaml << 'EOF'
version: 2
ethernets:
  ens18:
    dhcp4: false
    addresses:
      - 192.168.1.101/24
    gateway4: 192.168.1.1
    nameservers:
      addresses: [223.5.5.5, 8.8.8.8]
EOF

qm set 101 --cicustom "user=local:snippets/ubuntu-user.yaml,network=local:snippets/static-net.yaml"

📌 cicustom 的格式是 key=storage:path,其中 key 可以是 uservendornetworkmeta


6. 实时查看 Cloud-Init 日志

VM 启动后,通过 PVE Console 或 SSH 查看初始化进度:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查看 Cloud-Init 状态(在 VM 内执行)
cloud-init status
# 输出: status: done

# 详细日志
tail -f /var/log/cloud-init-output.log
tail -f /var/log/cloud-init.log

# 分析初始化耗时
cloud-init analyze show
cloud-init analyze blame

在 PVE 宿主机上也可以通过 qm terminal 连接:

1
qm terminal 101

7. 常见发行版的 Cloud-Init 差异

发行版默认用户名镜像后缀注意事项
Ubuntuubuntu-cloudimg-amd64.imgAgent 默认安装;密码登录默认禁用
Debiandebian (12+) / root (11)-genericcloud-amd64.qcow2需手动安装 qemu-guest-agent
Rocky Linuxrocky-GenericCloud-*.qcow2SELinux 默认 enforcing
AlmaLinuxalmalinux-GenericCloud-*.qcow2与 Rocky 类似
Fedorafedora-Cloud-Base-*.qcow2启动后默认扩容根分区
CentOS Streamcentos-GenericCloud-*.qcow2注意 EOL 日期
Arch Linuxarch社区提供滚动更新,无固定版本
Alpine Linuxalpine-cloud-*.qcow2使用 busybox,无 systemd

💡 不同发行版对 package_updatepackage_upgrade 的行为不同。Ubuntu/Debian 正常,Alpine 的 apk 需要额外处理。


8. 踩坑记录

❌ 坑 1:Cloud-Init 驱动器未添加

现象:VM 启动后卡在 Probing EDD (edd=off to disable)... 或直接进入 cloud-init 超时等待。

根因:忘记添加 IDE Cloud-Init 驱动器。

1
2
# 修复
qm set <VMID> --ide2 local-lvm:cloudinit

❌ 坑 2:修改配置后不生效

现象:修改了 --ciuser--ipconfig0,重启 VM 后没变化。

根因:Cloud-Init 只在首次启动运行!这是最容易被忽视的机制。

解决方案:重置 Cloud-Init 状态并重新生成 ISO:

1
2
3
4
5
6
# 方法 1:在 VM 内重置
cloud-init clean --logs --reboot

# 方法 2:在 PVE 上重新生成配置驱动器
qm set <VMID> --ide2 none             # 移除旧的
qm set <VMID> --ide2 local-lvm:cloudinit  # 重建

❌ 坑 3:qemu-guest-agent 未安装导致 IP 不可见

现象:PVE Web UI 的 Summary 中不显示 VM IP 地址。

解决:在 user.yamlpackages 中添加 qemu-guest-agent,并在 PVE 中启用:

1
qm set <VMID> --agent enabled=1

❌ 坑 4:DHCP 获取不到 IP

现象:Debian Cloud 镜像启动后 ip a 看不到 IP。

根因:Debian Cloud 镜像默认不启用网卡 DHCP。

解决:确保 ipconfig0 设置正确,或在自定义 network-config 中显式配置:

1
2
3
4
version: 2
ethernets:
  ens18:
    dhcp4: true

❌ 坑 5:模板磁盘格式不兼容

现象qm importdisk 后磁盘格式是 raw,无法做快照。

解决

1
2
3
4
5
# 先转换格式再导入
qemu-img convert -f qcow2 -O qcow2 source.img dest.qcow2

# 或者在 PVE 中移动磁盘时指定格式
qm move-disk <VMID> scsi0 local-lvm --format qcow2

9. 总结

Cloud-Init 是 Proxmox VE 自动化部署的核心工具,掌握它可以让你:

维度结论
推荐工作流下载 Cloud 镜像 → resize → 导入 PVE → 配置 SSH 公钥 + user.yaml → 转为模板 → 克隆部署
最佳实践模板只设 SSH 公钥,不设密码;克隆后按需修改 hostname/IP
核心命令qm createqm importdiskqm set --ide2 cloudinitqm templateqm clone
⚠️ 最易出错忘记 --ide2 cloudinit、修改配置后不重置、qemu-guest-agent 未安装
🔗 关联阅读PVE LXC 容器完全指南 · PVE ZFS 存储配置 · WireGuard VPN 部署

一键从模板克隆 + SSH 密钥注入 + IP 自动配置 = 30 秒出一台可用 VM。配得好,一天部署 50 台不是梦。


本文基于 Proxmox VE 8.2 + Ubuntu 22.04 / Debian 12 Cloud 镜像验证。Cloud-Init 版本:24.1+

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计