技术文章2026 年 5 月 19 日约 3.1 分钟

KVM 虚拟机初始化:cloud-init 自动配置主机

用 cloud-init 自动创建用户、写入 SSH 公钥和安装基础软件,减少 KVM 虚拟机手工配置。

手工给每台虚拟机装完系统再改主机名、加用户、写 SSH 公钥、装软件,既慢又容易漏。cloud-init 专门解决「首次开机配置」:它在虚拟机第一次启动时读取元数据,自动完成初始化。这篇文章讲它在 KVM 下怎么用。

#cloud-init 的数据来源

cloud-init 会从多个来源找配置,KVM 下最常用的是挂一个 seed ISO(cloud-init 官方叫 NoCloud 数据源):一个光盘镜像里放两个文件:

  • user-data:你要执行的配置(用户、SSH 公钥、要装的包、要跑的命令)。
  • meta-data:实例元数据(实例 ID、主机名等)。

虚拟机 BIOS 里多一个光驱,cloud-init 启动时读到它,然后完成初始化。

#写 user-data

yaml
#cloud-config
hostname: web01
users:
  - name: deploy
    groups: [sudo]
    sudo: ["ALL=(ALL) NOPASSWD:ALL"]
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...your-public-key
    lock_passwd: true
package_update: true
packages:
  - curl
  - vim
  - qemu-guest-agent
runcmd:
  - [systemctl, enable, --now, qemu-guest-agent]
  - [timedatectl, set-timezone, Asia/Shanghai]

几个值得注意的细节:

  • lock_passwd: true:锁定密码登录,只允许密钥——这是「无密码初始状态」的标准做法。
  • sudo: ["ALL=(ALL) NOPASSWD:ALL"]:给 deploy 用户免密 sudo。测试机可以,生产环境建议改成有密码或按需提权,别把 NOPASSWD ALL 变成默认配置。
  • runcmd 里的命令只在首次启动执行,之后不再运行——这是 cloud-init 的幂等边界,别指望它做日常运维。

#生成 seed ISO 并挂载

bash
mkdir -p /tmp/seed
cat > /tmp/seed/meta-data <<'EOF'
instance-id: web01-001
local-hostname: web01
EOF
# user-data 内容见上

sudo genisoimage -output seed.iso -volid cidata -joliet -rock /tmp/seed

sudo virt-install \
  --name web01 \
  --memory 4096 --vcpus 4 \
  --disk path=/var/lib/libvirt/images/web01.qcow2,size=40,format=qcow2 \
  --disk path=/data/seed/seed.iso,device=cdrom \
  --os-variant rhel9 \
  --network bridge=br0,model=virtio \
  --import

注意 -volid cidata:NoCloud 数据源靠这个卷标识别光盘,写错了 cloud-init 会忽略它。这是最常见的失败原因之一。

#验证:初始化到底成没成

等虚拟机启动完成,在虚拟机里(或用串口连进去):

bash
sudo cloud-init status --wait        # status: done
sudo cloud-init status --long
sudo cloud-init analyze show         # 看各阶段耗时

然后验证初始化结果:

bash
id deploy                       # 用户存在
sudo -u deploy ssh-keygen -y -f /dev/stdin <<< "$(cat /home/deploy/.ssh/authorized_keys)"   # 公钥可读
hostname                        # 主机名正确
systemctl is-active qemu-guest-agent
timedatectl                     # 时区正确

日志在 /var/log/cloud-init.log,出错先看它。最常见的失败:seed ISO 卷标不对、user-data 是 UTF-8 带 BOM(cloud-init 直接不认)、公钥粘贴时换行被破坏。

#模板化:克隆前必须清理

cloud-init 的默认行为是「只在首次启动初始化」。如果你把装好的虚拟机直接克隆,新机器会带着旧的 machine-id、旧的 SSH host key、旧的主机名。正确的模板流程:

  1. 虚拟机装好、cloud-init 跑完(status: done)。
  2. 关机前清理:
    bash
    sudo cloud-init clean
    sudo rm -f /etc/machine-id /var/lib/dbus/machine-id
    sudo rm -f /etc/ssh/ssh_host_*
    sudo truncate -s 0 /etc/hostname
  3. 关机,virt-clone 克隆,新机器首次启动时 cloud-init 会重新初始化(因为 instance-id 变了)。

#经验

cloud-init 只负责「首次启动」,别用它做日常配置漂移管理——那是 Ansible 等配置管理工具的活。组合拳是:cloud-init 做最小初始化(身份、密钥、基础包),配置管理工具做持续运维。另外,user-data 里永远不要放密码和私钥:seed ISO 是个明文文件,谁拿到 ISO 谁就拿到里面的全部凭据。

写于 2026 年 5 月 19 日

栏目
技术文章
约
3.1 分钟
字数
2.3K
阅读
168

本文为原创记录,转载请注明出处。如果这篇替你省了时间,欢迎留言说说你踩到的坑。

同题 · related

留言 · remarks

00 条

还没有留言,来说点什么吧。

KVM 虚拟机初始化:cloud-init 自动配置主机 · LXH·BLOG