利用搬瓦工 VPS 定制 Debian 镜像 支持 Cloud-Init 快速部署虚拟机【模板篇】
前言

经常买 VPS 的肯定都有这种体会:在商家的面板里装系统往往只需要几分钟,而且商家提供的系统模板中还预装了很多常用的工具。例如,搬瓦工提供的系统中,就有自带 BBR 的 CentOS 的系统镜像。

但自己安装系统时,先不说安装完成后还得手动更换软件源、调整时区、安装常用工具,就连安装过程各种设置选下来,整个过程起码也得花费几十分钟。
实际上,在虚拟机或者 VPS 中安装 Debian 并不需要下载 3.7G 的 ISO 镜像,在官方的下载页面就可以看到,完整安装镜像包含了所有桌面环境的套件,但虚拟机和 VPS 并不需要这些套件,右下角的“云镜像”反而更加合适,直接将下载好的 qcow2 镜像导入,就可以开机,没有复杂的安装过程。

定制镜像
使用 virt-customize 自动化定制参考了 Sukka 大佬的文章,如果想要深入了解的可以去看看原文
在“云镜像”的介绍界面,可以看到云镜像分为了 azure、ec2、generic、genericcloud、nocloud,这么多镜像我们到底应该选择哪个呢?
首先排除前两个 Amazon EC2 和 Microsoft Azure 的镜像,然后就是最后的 nocloud 不支持 Cloud-Init 也可以排除了。
那么最后就剩下 generic 和 genericcloud,在 Sukka 大佬的文章中推荐的是更加适合虚拟机的 genericcloud 镜像,但我实际测试下来发现这个镜像有些过于精简了,不包含 USB 相关内核模块,如果你有直通 USB 连接打印机、移动硬盘的需求,建议使用 generic 镜像,更加适合家庭用户。

选择好了合适的镜像后,我们接着用 libguestfs-tools 来自动化定制镜像,这一过程需要一台国外的 VPS,网络比较流畅,定制不容易出问题,这里用到的是搬瓦工的 E-commerce 系列,支持切换 15 个机房,具体配置如下:
👉查看所有机房的详细测评 (优惠码 BWHCGLUKKB,享受 6.77% 的循环优惠)
| 套餐名 | 机房 | 线路 | 配置 (核心/内存/硬盘/流量) | 价格 | 链接 |
|---|---|---|---|---|---|
| SPECIAL 20G KVM PROMO V5 - CN2 GIA ECOMMERCE | 可选美国、加拿大、日本、荷兰等 15 个机房 | 优化线路 | 2C / 1G / 20G / 1T | $49.99 / 季 | 立即购买 |
| SPECIAL 40G KVM PROMO V5 - CN2 GIA ECOMMERCE | 可选美国、加拿大、日本、荷兰等 15 个机房 | 优化线路 | 3C / 2G / 40G / 2T | $89.99 / 季 | 立即购买 |
| SPECIAL 80G KVM PROMO V5 - CN2 GIA ECOMMERCE | 可选美国、加拿大、日本、荷兰等 15 个机房 | 优化线路 | 4C / 4G / 80G / 3T | $56.99 / 月 | 立即购买 |
如果只是进行简单的镜像定制,对配置要求并不高,建议选择 SPECIAL 20G KVM PROMO V5 - CN2 GIA ECOMMERCE,1G 内存就够用了,性价比比较高。
若还需要对定制完成的镜像进行压缩处理,则对内存的要求会更高。此时建议选择 SPECIAL 80G KVM PROMO V5 - CN2 GIA ECOMMERCE 套餐,4G 内存更为合适,避免压缩过程中的性能瓶颈。

apt install libguestfs-tools下载官方原版的 qcow2 镜像
wget -c https://cdimage.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2下面是我定制 Debian 13 镜像用到的命令,主要是为了让这个镜像更加适合国内宝宝体质。
危险
安装软件包时要注意避免冲突。我一开始不小心装了 ifupdown,结果和 systemd-networkd 冲突,导致虚拟机开机一直获取不到 DNS。
通过 resolvectl status 可以查看当前实际生效的 DNS,正常情况下如果 IPv4 是 DHCP 的会获取到 DHCP 分配的 DNS,如果 IPv4 是静态 IP,则可以在 Cloud-Init 中设置 DNS。
提示
在PVE 中使用的虚拟机,强烈建议安装上 qemu-guest-agent
virt-customize -a debian-13-generic-amd64.qcow2 \
--smp 2 --verbose \
--timezone "Asia/Shanghai" \
--append-line "/etc/default/grub:# disables OS prober to avoid loopback detection which breaks booting" \
--append-line "/etc/default/grub:GRUB_DISABLE_OS_PROBER=true" \
--run-command "update-grub" \
--run-command "sed -i 's|Types: deb deb-src|Types: deb|g' /etc/apt/sources.list.d/debian.sources" \
--run-command "sed -i 's|generate_mirrorlists: true|generate_mirrorlists: false|g' /etc/cloud/cloud.cfg.d/01_debian_cloud.cfg" \
--update --install "wget,curl,nano,vim,sudo,unzip,mtr-tiny,iputils-ping,bind9-host,dnsutils,net-tools,lsb-release,ca-certificates,bash-completion,fail2ban,dialog,netbase,iproute2,whois,ssh,dbus,systemd,systemd-sysv,locales,apt-utils,gnupg2,apt-transport-https,rsyslog,logrotate,less,rsync,qemu-guest-agent,haveged,systemd-timesyncd" \
--run-command "apt-get -y autoremove --purge && apt-get -y clean" \
--run-command "sed -i 's/^#\?\s*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config && sed -i 's/^#\?\s*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config" \
--append-line "/etc/systemd/timesyncd.conf:NTP=ntp.aliyun.com" \
--delete "/var/log/*.log" \
--delete "/var/lib/apt/lists/*" \
--delete "/var/cache/apt/*" \
--truncate "/etc/apt/mirrors/debian.list" \
--append-line "/etc/apt/mirrors/debian.list:https://mirrors.tuna.tsinghua.edu.cn/debian" \
--truncate "/etc/apt/mirrors/debian-security.list" \
--append-line "/etc/apt/mirrors/debian-security.list:https://mirrors.tuna.tsinghua.edu.cn/debian-security" \
--truncate "/etc/machine-id"具体做了这些操作:
| 类别 | 功能 |
|---|---|
| 基础 | 指定镜像、多核执行 |
| 时区 | 设置为上海 |
| 引导 | 禁止 OS prober |
| 系统 | 更新包、安装常用工具 |
| SSH | 启用 root 登录和密码认证 |
| 网络 | 设置阿里云 NTP |
| APT | 改用清华源、禁用源码仓库 |
| Cloud-Init | 禁止自动生成镜像源 |
| 清理 | 删除日志和缓存 |
| 唯一性 | 清空 machine-id 以防冲突 |
最后再把刚才定制好的镜像压缩一下就大功告成了。
virt-sparsify --compress debian-13-generic-amd64.qcow2 debian-13-generic-amd64-zc.qcow2制作模板
这一步是将刚才定制好的 qcow2 导入 PVE 中并创建成虚拟机模板,其中大部分操作都可以在 PVE 的网页中完成
创建虚拟机
由于创建的虚拟机后面是转换为模板使用,因此 VM ID 可以填的大一些,与正在运行的虚拟机区分开。

操作系统后面会直接导入 qcow2,所以这里选择“不使用任何介质”

机型可以选择 “q35”,BIOS 选择 “OVMF (UEFI)”,最重要的是勾选 “Qemu 代理”,可以和虚拟机内安装的 qemu-guest-agent 交互。

刚才定制的 qcow2 本质上就是一个磁盘文件,后面直接导入就是系统盘,因此这里不需要单独添加磁盘,将其删除就好。

创建的这个虚拟机会转换为模板使用,接下来的 CPU 和内存设置选择低配即可,从模板克隆虚拟机的时候再根据实际需求修改。


在内网使用 PVE,可以去掉勾选“防火墙”,方便操作。

这样就创建好了虚拟机,但此时还无法使用,还得导入镜像并且添加上必要的硬件。
导入镜像
网页上不支持导入镜像,因此这一步必须使用命令行操作。
将 qcow2 镜像上传到 PVE 中,通过以下命令导入:
9000是刚才创建的虚拟机 VM ID/tmp/debian-13-generic-amd64-zc.qcow2是镜像存放的路径local-lvm是存储--format=qcow2将其指定为 qcow2 格式
qm importdisk 9000 /tmp/debian-13-generic-amd64-zc.qcow2 local-lvm --format=qcow2返回网页,会看到“未使用的硬盘 0”,双击将其添加进来。

接着来到“选项”,找到引导顺序,勾选刚才添加的系统盘,并把它移动到第一位。

添加硬件
除了系统盘以外,我们还需要添加 CloudInit 设备以及串行端口。
其中 CloudInit 设备可以用于配置虚拟机的用户名、密码、DNS、SSH 公钥以及 IP,而串行端口可以让我们使用控制台中的 xterm.js。
CloudInit
添加 CloudInit 设备,添加时建议选择 SCSI 设备。

接着来到 Cloud-Init 选项卡中,添加完 CloudInit 设备之后里面的选项就能修改了,可以设置一下用户、密码以及 IP。

串行端口
默认情况下,控制台只能选择 noVNC,但它使用起来非常的不方便,清晰度不够也不支持复制粘贴,因此我们需要添加一个“串行端口”来使用其他终端。

此时,控制台就多了个 xterm.js 可以选择了,使用起来更加的方便。

转换为模板
提示
为了保持系统的纯净度,如果你导入的是定制后的镜像,那么转换成模板前不要启动这台虚拟机。
如果你没有条件自动化定制镜像,那么可以在导入镜像之后,直接启动虚拟机,按照你自己的需求换源、安装常用的软件包等等。
不过这一系列操作会留下一堆日志以及缓存,好在除了 machine-id 外基本上都不会影响克隆后的虚拟机正常使用。
但不移除 machine-id 会使得后续克隆的虚拟机 machine-id 也相同,从而导致各种冲突,因此建议在所有操作完成后、关机前,执行以下命令将 machine-id 移除,最后再关闭虚拟机。
truncate -s /etc/machine-id /var/lib/dbus/machine-id完成以上所有步骤后,就可以将其转换成模板了,后续的创建虚拟机都以它为基础,几秒钟就可以新开一台虚拟机。

克隆虚拟机
创建一台新的虚拟机,只需要在刚才的模板上右键,选择“克隆”,模式建议选择“完整克隆”,稍等片刻左边的列表中就会有一台新的虚拟机出现了。

由于我们创建的模板选择配置都是比较低的,这时候可以适当调整一下配置,分配更大的内存、硬盘以及 CPU 核心数。
其中硬盘的扩容稍复杂些,选中硬盘后,找到上方的调整大小,初始硬盘为 3G,输入的数字为增量,例如扩容到 10G,则需要在弹出的窗口中填写 7G。

一键脚本
对于不适合直接定制到系统镜像中的应用,为此我写了一个脚本,集成了一些常用的软件和测试脚本。
wget -O one-click.sh https://gist.githubusercontent.com/izhichao/60906715e2c59ff92be281b1ba58f482/raw/one-click.sh && chmod +x one-click.sh && ./one-click.sh后续可以通过 ./one-click.sh 执行脚本,一共分成了三个板块,分别是测试脚本、常用软件以及常用设置,其中测试脚本以及常用软件调用的都是官方的下载链接,可以放心使用。



脚本完全开源,并且代码结构非常的简单,如果我的脚本不满足你的需求,完全可以在此基础上加上你自己需要的软件。







