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
#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 并挂载
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 会忽略它。这是最常见的失败原因之一。
#验证:初始化到底成没成
等虚拟机启动完成,在虚拟机里(或用串口连进去):
sudo cloud-init status --wait # status: done
sudo cloud-init status --long
sudo cloud-init analyze show # 看各阶段耗时然后验证初始化结果:
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、旧的主机名。正确的模板流程:
- 虚拟机装好、cloud-init 跑完(status: done)。
- 关机前清理: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 - 关机,
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 条还没有留言,来说点什么吧。