操作系统部署方案

4175 字
21 分钟
操作系统部署方案
操作系统部署方案

操作系统部署方案#

[TOC]


🔄 VMware 用户视角对比#

概念映射#

VMwareKVM原理
完整克隆 (Full Clone)⚠️ virt-clone / 完整拷贝完全独立,占用完整空间
链接克隆 (Linked Clone)qemu-img create -b 增量盘COW,共享 base,只存差异
快照 (Snapshot)virsh snapshot保存状态,可回滚
模板 (Template)base 镜像 / virt-sysprep不可变基础镜像
Tip

VMware 链接克隆 = KVM COW 增量盘

🧱 存储目录规划#

在 Virt02 中我们聊过 COW(Copy-On-Write)的概念——多台 VM 共享同一个后端盘(base image),每台只存自己的差异数据

三层目录结构
三层目录结构

✅ 三层目录设计

目录用途特点
base/存放官方原始镜像只读,不直接运行 VM
作为所有 VM 的后端盘
images/VM 运行时使用的磁盘差异盘,通过 qemu-img create -b 链接到 base
每台 VM 一个,只存差异数据
seed/cloud-init 配置光盘每个 VM 一个 iso,挂载后首次启动自动配置
设置主机名、IP、SSH 公钥等
Terminal window
mkdir -p /home/jiuzhao/storage/{base,images,seed}
Terminal window
/home/jiuzhao/storage/
├── base/
│ ├── rocky10.qcow2 # 官方下载的原始镜像(只读)
│ └── ubuntu2404.qcow2
│ └── Rocky.img # 传统ISO镜像制作的后端盘
├── images/
│ ├── web01.qcow2 # 差异盘,仅存差异数据
│ ├── web02.qcow2
└── seed/
├── web01-seed.iso # cloud-init 配置光盘
└── web02-seed.iso
Note

📌 关键原则:base 不动、images 是差异层、seed 配置每台 VM 的独有属性 不管创建 10 台还是 100 台 VM,base 只需一份,镜像管理清晰

和默认路径的关系#

路径说明
/var/lib/libvirt/images/libvirt default 存储池路径
/home/jiuzhao/storage/我们自己的镜像仓库,统一管理 base/images/seed

💡 用 virsh pool-define-as 注册为 libvirt 的存储池(详见 Virt03 存储池章节)


路线 A:传统 ISO 安装#

流程:创建空白后端盘 → virt-install 装系统 (ISO/FTP) → 系统内优化配置 → virt-sysprep 清理镜像 → 导出XML模板并清理 → 创建前端盘 → 批量部署

Note

这条路线在 Virt02 模版机制作 中有完整记录 包括 base_config.sh 优化脚本、qemu-guest-agent、cloud-init、console 串口、开机自动扩容、virt-sysprep 清理、template.xml 制作与批量创建

✨操作命令速查#

Terminal window
1)创建空白后端盘
qemu-img create -f qcow2 /home/jiuzhao/storage/base/Rocky.img 15G
virsh pool-refresh base
# 刷新base存储池
2)virt-install 安装系统
virt-install --name Rocky --memory 2048 --vcpus 2 \
--osinfo rocky9 \
--disk path=/home/jiuzhao/storage/base/Rocky.img \
--cdrom /home/jiuzhao/img/Rocky-10.2-x86_64-minimal.iso \
--network network=default --graphics vnc,listen=0.0.0.0 --wait -1
3)进入系统 → 优化配置
# 换 yum/dnf 源、装工具包、时间同步、关 SELinux/Firewalld、改 PS1
# dnf install -y qemu-guest-agent cloud-init cloud-utils-growpart
# grubby 配置 console 串口(详见 Virt02)
# 编写 /etc/rc.d/rc.local 开机自动扩容
# growpart → pvresize → lvextend → xfs_growfs
⚠️ 注意核对实际 LV 名,通过 lsblk 查看
poweroff
4)virt-sysprep 清理模板
sudo virt-sysprep -d Rocky
⚠️ Ubuntu 上 virt-sysprep 需要 sudo
5)导出并精简 template.xml
virsh dumpxml Rocky > ./storage/Rocky-template.xml
✅️ 精简原则:删 UUID、MAC、PCIe root port、audio/watchdog/tablet
📌 只保留核心 7 件:磁盘 + 网络 + console + Guest Agent + VNC + memballoon + rng
# 名称<name>Rocky-template</name>
# 前端盘<source file="/home/jiuzhao/storage/images/Rocky-template.qcow2"/>
6)创建前端盘
virsh vol-create-as \
--pool "数据盘" \
--name node1.qcow2 \
--capacity 30G \
--format qcow2 \
--backing-vol /home/jiuzhao/storage/base/Rocky.img \
--backing-vol-format qcow2
# node1.qcow2 卷会被自动放到该存储池(数据盘)对应的目录下
# 后端盘(指定路径)
jiuzhao@Ubuntu ~$ virsh vol-list "数据盘"
Name Path
---------------------------------------------------------
node1.qcow2 /home/jiuzhao/storage/images/node1.qcow2
7)从模板生成 XML 并定义 VM
sudo bash -c "sed 's@Rocky-template@node1@' /home/jiuzhao/storage/Rocky-template.xml > /etc/libvirt/qemu/node1.xml"
# libvirt 自动生成 UUID 和 MAC,不会冲突
jiuzhao@Ubuntu images$ cd /etc/libvirt/qemu/
jiuzhao@Ubuntu qemu$ ls
networks node1.xml
# 这里我取消定义了模板机的xml文件,只保留了后端磁盘
virsh define /etc/libvirt/qemu/node1.xml
8)启动验证
virsh start --console node1
# 验证磁盘扩容 `lsblk`
# 分区撑满指定大小,LV 和文件系统已扩容

🌐 常用云镜像下载#

Ubuntu 官方为云环境提供预配置的 cloud 镜像,基于 qcow2 格式,即下即用

官方下载:https://cloud-images.ubuntu.com/releases/

Ubuntu 每个版本除了版本号,还有一个动物代号(codenames):

版本代号类型
24.04 LTSNoble Numbat长期支持 ✅️
22.04 LTSJammy Jellyfish长期支持 ✅️
20.04 LTSFocal Fossa长期支持

当前我们使用的是 Ubuntu 24.04 LTS (Noble Numbat)

https://cloud-images.ubuntu.com/releases/noble/release/

Terminal window
# 下载 Ubuntu 24.04 云镜像
wget https://cloud-images.ubuntu.com/releases/noble/release/ubuntu-24.04-server-cloudimg-amd64.img \
-O /home/jiuzhao/ubuntu2404.qcow2
# -O 重命名为易识别的文件名
Note

Ubuntu 的 .img 和 .qcow2 是同一格式,直接下载后重命名即可 qemu-img info 查看会显示 file format: qcow2


Rocky Linux 官方也提供 GenericCloud 镜像:

官方下载:https://download.rockylinux.org/pub/rocky/10.2/images/x86_64/

Terminal window
# 下载 Rocky Linux 10 云镜像
wget https://download.rockylinux.org/pub/rocky/10.2/images/x86_64/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2 \
-O /home/jiuzhao/storage/base/rocky10.qcow2

其他常用 Linux 云镜像

发行版下载地址
Debianhttps://cloud.debian.org/images/cloud/
CentOS Streamhttps://cloud.centos.org/centos/
Fedorahttps://dl.fedoraproject.org/pub/fedora/linux/releases/

路线 B:☁️ 云镜像#

官方预装好的 Cloud Image,cloud-init 自动入职——不进系统、不跑脚本、不 virt-sysprep

和路线 A 的本质区别#

路线 A(传统 ISO)路线 B(云镜像)
获取系统qemu-img create + virt-install 手动装wget 一行下载
growpart / cloud-init / qemu-ga / console全部手动安装出厂自带,一个都不需要装
优化配置进系统敲命令 / 注入 bash 脚本 / virt-sysprep 清洗全写在 seed.iso 的 user-data 里,首次启动自动执行
模板手动做镜像 → virt-sysprep → 导出 XMLbase 镜像永不变,改配置 = 改 user-data
加新 VM创建增量盘 + XML + sed一模一样,但 cloud-init 帮你把脏活全干了

cloud-init 就是每台新虚拟机的”自动入职流程”——首次启动时读取 seed.iso,执行 user-data 中的所有配置,下次启动不再重复

流程总览#

Terminal window
下载官方 qcow2 → base/
│
├─ virt-customize 最小预设(仅设 root 密码,1 行)
│
├─ 编写 seed.iso(user-data + meta-data)
│ ├─ 换阿里云源 / DNS 223.5.5.5
│ ├─ 装包 / 禁用 IPv6 / 关 SELinux / 时钟同步 / PS1
│ ├─ SSH 公钥 / 时区 / 主机名
│ └─ 一切定制化写在这里,不进系统
│
├─ genisoimage 生成 seed.iso
├─ virsh vol-create-as 创建 COW 增量盘
└─ 模板 XML → sed 替换 → define → start → 启动即用

① 下载云镜像#

Terminal window
# Rocky Linux 10
wget https://download.rockylinux.org/pub/rocky/10.2/images/x86_64/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2 \
-O /home/jiuzhao/storage/base/rocky10.qcow2
# Ubuntu 24.04
wget https://cloud-images.ubuntu.com/releases/noble/release/ubuntu-24.04-server-cloudimg-amd64.img \
-O /home/jiuzhao/storage/base/ubuntu2404.qcow2

② virt-customize 最小预设#

Terminal window
sudo virt-customize -a /home/jiuzhao/storage/base/rocky10.qcow2 \
--root-password password:1
# 云镜像没有预设密码,用一行命令设好
# 其他所有配置(换源、装包、DNS)都交给 cloud-init,不要在这里做
Note

为什么只设密码,不注入脚本? 路线 A 需要 virt-customize 注入 bash 脚本,是因为传统 ISO 装机后没有自动化机制。 云镜像出厂自带 cloud-init——把脚本逻辑全部迁移到 user-data,享受声明式配置的幂等性和可重复性。

③ 编写 seed.iso 配置#

新建两个文件:/home/jiuzhao/storage/seed/user-data 和 meta-data

⚠️ 文件名必须是 user-data 和 meta-data,cloud-init 源码写死的

meta-data:

Terminal window
# 实例唯一标识符
instance-id: cloud-test
# 主机名(优先级更高)
local-hostname: cloud-test

user-data:

#cloud-config
timezone: Asia/Shanghai
# root 密码 + SSH 公钥
ssh_pwauth: true
chpasswd:
list: |
root:1
expire: false
users:
- name: root
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHce3fbO8q7VgnnzqgNzBoJrrP/nTl69ACyH5GTe/7b3 jiuzhao@Ubuntu
# 装包 — 全部走 runcmd(等换源后再装,避免默认源超时)
# 写入配置文件
write_files:
# DNS 直通(NetworkManager 全局 DNS,重启不丢)
- path: /etc/NetworkManager/conf.d/99-dns.conf
permissions: '0644'
content: |
[global-dns-domain-*]
servers=223.6.6.6,114.114.114.114
# /etc/resolv.conf 兜底(cloud-init 阶段用)
- path: /etc/resolv.conf
permissions: '0644'
content: |
nameserver 223.6.6.6
nameserver 114.114.114.114
# VM 内 TCP 缓冲区 + 禁用 IPv6
- path: /etc/sysctl.d/99-fast-network.conf
permissions: '0644'
content: |
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# chrony 阿里云时钟同步
- path: /etc/chrony.conf
permissions: '0644'
content: |
server ntp.aliyun.com iburst
sourcedir /run/chrony-dhcp
driftfile /var/lib/chrony/drift
makestep 1.0 3
rtcsync
ntsdumpdir /var/lib/chrony
logdir /var/log/chrony
# PS1 配色
- path: /etc/profile.d/ps1.sh
permissions: '0644'
content: |
PS1="[\[\e[34;1m\]\u@\[\e[0m\]\[\e[32;1m\]\H\[\e[0m\]\[\e[31;1m\] \W\[\e[0m\]]\\$ "
# ASCII 动态欢迎(登录时 figlet 显示主机名)
- path: /etc/profile.d/color-welcome.sh
permissions: '0755'
content: |
#!/bin/bash
hostname | figlet -f big | lolcat
# 首次启动按顺序执行
runcmd:
# 换阿里云 dnf 源
- sed -e 's|^mirrorlist=|#mirrorlist=|g' -e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' -i.bak /etc/yum.repos.d/[Rr]ocky*.repo
- dnf clean all
- dnf makecache
# 系统更新 + 基础包(baseos 自带)
- dnf update -y
- dnf install -y vim-enhanced net-tools bash-completion tree wget lrzsz tar unzip sysstat bc telnet
# 装 EPEL + EPEL 包(htop / figlet 在 EPEL)
- dnf install -y epel-release
- dnf install -y htop figlet sl cowsay lolcat
# 关闭 SELinux
- sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
# SSH 优化
- sed -i '/^#UseDNS/c\UseDNS no' /etc/ssh/sshd_config
- sed -i '/^#GSSAPIAuthentication/c\GSSAPIAuthentication no' /etc/ssh/sshd_config
# 文件描述符
- echo "* - nofile 65535" >> /etc/security/limits.conf
# 应用内核参数 + 重启 chronyd
- sysctl -p /etc/sysctl.d/99-fast-network.conf
- systemctl restart chronyd

④ 生成 seed.iso#

Terminal window
cd /home/jiuzhao/storage/seed/
genisoimage -input-charset utf-8 -output cloud-test-seed.iso \
-volid cidata -joliet -rock user-data meta-data
# -volid cidata:卷标必须叫 cidata,cloud-init 靠这个识别
# 验证内容
isoinfo -R -f -i cloud-test-seed.iso

⑤ 创建 COW 增量盘#

Terminal window
virsh vol-create-as \
--pool images \
--name cloud-test.qcow2 \
--capacity 30G \
--format qcow2 \
--backing-vol /home/jiuzhao/storage/base/rocky10.qcow2 \
--backing-vol-format qcow2

⑥ XML 模板#

模板已放在 ~/storage/Cloud-template.xml,包含 seed cdrom:

<domain type='kvm'>
<name>Cloud-template</name>
<memory unit='KiB'>2097152</memory>
<currentMemory unit='KiB'>2097152</currentMemory>
<vcpu placement='static'>2</vcpu>
<os>
<type arch='x86_64' machine='pc-q35-noble'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
</features>
<cpu mode='host-passthrough' check='none' migratable='on'/>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<!-- 磁盘:占位符,sed 替换 -->
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/home/jiuzhao/storage/images/Cloud-template.qcow2'/>
<target dev='vda' bus='virtio'/>
</disk>
<!-- cloud-init seed -->
<disk type="file" device="cdrom">
<driver name="qemu" type="raw"/>
<source file="/home/jiuzhao/storage/seed/Cloud-template-seed.iso"/>
<target dev="sda" bus="sata"/>
<readonly/>
</disk>
<!-- 网络 -->
<interface type='network'>
<source network='default'/>
<model type='virtio'/>
</interface>
<!-- console 串口 -->
<serial type='pty'>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
</serial>
<console type='pty'>
<target type='serial' port='0'/>
</console>
<!-- Guest Agent -->
<channel type='unix'>
<target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>
<!-- VNC -->
<graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'>
<listen type='address' address='0.0.0.0'/>
</graphics>
<video>
<model type='virtio' heads='1' primary='yes'/>
</video>
<memballoon model='virtio'/>
<rng model='virtio'>
<backend model='random'>/dev/urandom</backend>
</rng>
</devices>
</domain>

⑦ sed 替换 + 启动#

Terminal window
sudo bash -c "sed 's#Cloud-template#cloud-test#g' /home/jiuzhao/storage/Cloud-template.xml > /etc/libvirt/qemu/cloud-test.xml"
sudo virsh define /etc/libvirt/qemu/cloud-test.xml
# console 观察 cloud-init 输出
virsh start --console cloud-test

⑧ 验证#

Terminal window
[root@cloud-test ~]# hostname
cloud-test
[root@cloud-test ~]# cat /etc/resolv.conf
nameserver 223.5.5.5
nameserver 223.6.6.6
[root@cloud-test ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sr0 11:0 1 366K 0 rom # ← seed.iso 光盘
vda 252:0 0 30G 0 disk
├─vda1 252:1 0 2M 0 part
├─vda2 252:2 0 200M 0 part /boot/efi
├─vda3 252:3 0 1000M 0 part /boot
└─vda4 252:4 0 28.8G 0 part / # ← 分区自动撑满 30G
[root@cloud-test ~]# cat ~/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMAt2bYeCYLeKJ7m3IJfKSQkUHeql9HUPQKUIYjV4MHo jiuzhao@Ubuntu

批量创建新 VM#

新加一台 VM:复制一份 seed 配置(改 hostname)→ 增量盘 → sed → done

Terminal window
cd /home/jiuzhao/storage/seed/
# 1. 改主机名
sed -i 's/cloud-test/web01/' meta-data
genisoimage -input-charset utf-8 -output web01-seed.iso -volid cidata -joliet -rock user-data meta-data
# 2. 增量盘
virsh vol-create-as --pool "数据盘" --name web01.qcow2 --capacity 30G \
--format qcow2 --backing-vol /home/jiuzhao/storage/base/rocky10.qcow2 --backing-vol-format qcow2
# 3. XML + define
sudo bash -c "sed 's#Cloud-template#web01#g' /home/jiuzhao/storage/Cloud-template.xml > /etc/libvirt/qemu/web01.xml"
sudo virsh define /etc/libvirt/qemu/web01.xml
virsh start web01
Tip

base 镜像永远不动。 想加软件?改 user-data 的 packages 列表。想换源?改 runcmd 里的 sed 命令。 10 台 VM 全装 nginx?一行 yaml 搞定,重新生成 seed.iso 即可


和路线 A 的最终对比#

操作路线 A(传统 ISO)路线 B(云镜像)
获取基础系统qemu-img create + virt-install 装半小时wget 一分钟
系统优化进系统敲命令 / virt-customize 注入脚本user-data 声明式,首次启动自动跑
模板制作virt-sysprep 清洗 ±15 分钟base 镜像不动,干净永久
新增 VM增量盘 + XML + sed同样操作,但 cloud-init 接管所有初始化
修改全局配置重做模板(virt-sysprep 再来一遍)改 user-data,重建 seed.iso

ASCII艺术字符#

Terminal window
sudo yum -y install figlet lolcat
2)编写动态脚本
vim /etc/profile.d/color-welcome.sh
#!/bin/bash
# 核心命令:figlet 生成大字符 + lolcat 上彩虹色
hostname | figlet -f big | lolcat -fs
# Ubuntu系统:hostname | figlet -f big | /usr/games/lolcat -sf
# 附加信息(可省略)
# echo -e "\033[1;33m登录时间: $(date '+%Y-%m-%d %H:%M:%S')\033[0m"
# echo -e "\033[1;32m当前在线用户: $(who | wc -l)\033[0m"
Terminal window
chmod +x /etc/profile.d/color-welcome.sh

☁️ 从ISO传统镜像制作Ubuntu云镜像#

Terminal window
1)制作后端盘
virsh vol-create-as \
--pool base \
--name Ubuntu.img\
--capacity 10GB \
--format qcow2
# 由 libvirtd (root) 代理创建
2)安装镜像
virt-install --name Ubuntu --memory 2048 --vcpus 2 \
--osinfo ubuntu24.04 \
--disk path=/home/jiuzhao/storage/base/Ubuntu.img \
--cdrom /home/jiuzhao/img/ubuntu-24.04.4-live-server-amd64.iso \
--network network=default --graphics vnc,listen=0.0.0.0 --wait -1
3)系统优化
# 执行之前的脚本即可
cloud-init ✅ 已装,Ubuntu Server 默认带
growpart ✅ 已装,来自 cloud-guest-utils(cloud-init 的自动依赖)
# 不需要单独装 cloud-utils-growpart
cloud-init (Ubuntu Server 自带)
└── Depends: cloud-guest-utils ← 自动装上
└── Depends: cloud-utils
⚠️ 手动装:apt install -y qemu-guest-agent
4)console 串口
# 编辑 GRUB 加入 console 参数
sed -i 's/^GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="console=ttyS0,115200n8"/' /etc/default/grub
update-grub
# 启用串口登录
systemctl enable --now serial-getty@ttyS0.service
# 重启后验证
reboot
virsh console Ubuntu # 应该看到登录提示符

Ubuntu 自动扩容#

Rocky 和 Ubuntu 传统 ISO 都走 LVM,扩容链完全一样——4 步,只差最后一步的文件系统命令:

发行版扩容链差异
Rockygrowpart → pvresize → lvextend → xfs_growfsXFS,xfs_growfs
Ubuntugrowpart → pvresize → lvextend → resize2fsext4,resize2fs

Rocky 用 rc.local,Ubuntu 用 systemd oneshot——因为 /etc/rc.local 在 Ubuntu 24.04 默认不存在且需要额外 chmod +x,systemd 更干净

/usr/local/sbin/growpart-lvm.sh
#!/bin/bash
set -e
[ -f /var/lib/growpart-done ] && exit 0
# 判断标记文件是否存在(由services服务成功运行后创建)
# 一旦执行过,这个文件被创建出来,脚本直接成功退出(exit 0)
DISK=/dev/vda
PARTNUM=3
PART=${DISK}${PARTNUM}
VG=ubuntu-vg
LV=ubuntu-lv
growpart $DISK $PARTNUM || true
partprobe $DISK
# 强制内核重读分区表
pvresize $PART
lvextend -l +100%FREE /dev/$VG/$LV || true
resize2fs /dev/$VG/$LV || true
Note

⚠️ 注意核对 VG/LV 名:vgs / lvs 查看当前值。Ubuntu 24.04 默认 VG=ubuntu-vg,LV=ubuntu-lv

[Unit]
Description=Auto-expand LVM root partition on first boot
After=local-fs.target
# 确保本地文件系统已经挂载好(分区能正常读写)
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/growpart-lvm.sh
ExecStartPost=/usr/bin/touch /var/lib/growpart-done
# 主进程启动成功后,才会创建这个空文件,作为“已完成”的标记,之后开机跳过
# 脚本执行失败,是不会创建这个文件的
# 标记文件绑脚本成功,能知道脚本到底跑通没
RemainAfterExit=yes
# 即使服务进程退出了,systemd 仍把它视为“已激活”状态
# 方便依赖它的后续服务查询状态
[Install]
WantedBy=multi-user.target
参数触发时机执行条件失败影响典型用途
ExecStartPost主进程启动成功后主进程退出码为 0影响服务状态,失败则服务标记为 failed成功后必须完成的收尾(如写标记文件)
ExecStartPost=-主进程启动成功后主进程退出码为 0不影响服务状态,失败只记日志成功后非关键的辅助操作(如发通知)
ExecStopPost主进程停止后(含成功/失败/被kill)无条件执行不影响服务状态,失败只记日志无论成败都必须做的清理(如解锁、删临时文件)
Terminal window
# 1. 写入脚本并赋权
vim /usr/local/sbin/growpart-lvm.sh
chmod +x /usr/local/sbin/growpart-lvm.sh
# 2. 写入服务
vim /etc/systemd/system/growpart-lvm.service
# 3. 启用
systemctl daemon-reload
systemctl enable growpart-lvm.service

其他优化#

Terminal window
① 禁用 IPv6 + 扩充 TCP buffer
cat > /etc/sysctl.d/99-network.conf << 'EOF'
# 禁用 IPv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# TCP 缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 262144
net.core.wmem_default = 262144
EOF
sysctl -p /etc/sysctl.d/99-network.conf
② DNS 直通配 systemd-resolved:
cat > /etc/systemd/resolved.conf << 'EOF'
[Resolve]
DNS=223.5.5.5 223.6.6.6
FallbackDNS=114.114.114.114
DNSOverTLS=no
EOF
systemctl restart systemd-resolved
Terminal window
# 只改 /etc/systemd/resolved.conf,不要动 /etc/resolv.conf
# 原因很简单——Ubuntu 的 DNS 架构不是直写文件,而是代理模式:
/etc/resolv.conf → symlink → /run/systemd/resolve/stub-resolv.conf
↑
nameserver 127.0.0.53
│
systemd-resolved 本地缓存代理
│
/etc/systemd/resolved.conf ← 这里决定上游 DNS
# 直接改 /etc/resolv.conf 是没用的——它是 link,systemd-resolved 下次启动就会覆盖
Caution
  • Ubuntu 安装器(subiquity)的默认行为
    • 装完后写了一个 /etc/cloud/cloud-init.disabled,并且把 datasource 锁成 None
  • cloud-init 状态显示 status: disabled
    • 后果:就算你挂 seed.iso,cloud-init 也不会读——它在启动时就跳过了
Terminal window
'修复计划(virt-sysprep 之前)'
1)删除 disabled 标记——恢复 cloud-init 运行
rm -f /etc/cloud/cloud-init.disabled
2)修复 datasource——允许 NoCloud(读取 seed.iso)
sed -i '/datasource_list:/d' /etc/cloud/cloud.cfg.d/*.cfg
sed -i '/^datasource:/,/^[a-z]/d' /etc/cloud/cloud.cfg.d/99-installer.cfg
3)重置 cloud-init 状态(清除 instance-id,下次启动视为新机器)
cloud-init clean --machine-id
# ⚠️ 注:cloud-init clean 后会删除 SSH host keys,virt-sysprep 也会清
⭕ 日志里看到的 growpart: 和 resize_rootfs: false 保持不变
⭕ 因为扩容我们走 systemd 服务,不与 cloud-init 的 growpart 模块冲突
# 改完就能进 virt-sysprep
Terminal window
poweroff
# 关机
4)virt-sysprep 清理模板
sudo virt-sysprep -d Ubuntu

✨注意事项#

Terminal window
# 后面如果要修改模板机时,一些收尾工作💼
1)收尾(顺序不能错)
rm -f /var/lib/growpart-done # ①删标记
cloud-init clean --machine-id # ②重置 cloud-init
> /root/.bash_history # ③清命令历史
poweroff # ④关机
2)virt-sysprep(清理 machine-id、SSH host keys、日志等)
sudo virt-sysprep -d Ubuntu
# 这是 libguestfs 的老问题——virt-sysprep 无法正确挂载 LVM 根文件系统
# 在 Ubuntu LVM 安装上必炸 --> 替代方案:手动清洗(在 VM 里直接做)
# 删除标记 & 重置 cloud-init
rm -f /var/lib/growpart-done
cloud-init clean --machine-id
# 清 machine-id(下次启动 cloud-init 当作新机器)
rm -f /etc/machine-id
echo "" > /etc/machine-id 2>/dev/null || true
# 清日志
> /var/log/lastlog
> /var/log/wtmp
rm -rf /tmp/*
rm -rf /var/tmp/*
rm -f /var/lib/systemd/random-seed
# 清 SSH host keys(首次启动自动重新生成)
rm -f /etc/ssh/ssh_host_*_key* && ssh-keygen -A
# 清 cloud-init 缓存
rm -rf /var/lib/cloud/instances/*
# 清 shell 历史
> /root/.bash_history
# 关机
poweroff

批量创建#

Terminal window
1)创建 COW 增量盘
virsh vol-create-as --pool images --name Ceph01.qcow2 \
--capacity 30G --format qcow2 \
--backing-vol /home/jiuzhao/storage/base/Ubuntu.img \
--backing-vol-format qcow2
2)准备 seed.iso(最小化)
cd /home/jiuzhao/storage/seed/
# 只写 hostname + SSH,其他全在 base(基础盘里)
3)种子文件
cat > user-data <<'EOF'
#cloud-config
timezone: Asia/Shanghai
ssh_pwauth: true
chpasswd:
list: |
root:1
expire: false
write_files:
- path: /root/.ssh/authorized_keys
permissions: '0600'
owner: 'root:root'
content: |
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHce3fbO8q7VgnnzqgNzBoJrrP/nTl69ACyH5GTe/7b3 jiuzhao@Ubuntu
EOF
cat > meta-data <<'EOF'
instance-id: Ceph01
local-hostname: Ceph01
EOF
genisoimage -input-charset utf-8 -output Ceph01-seed.iso \
-volid cidata -joliet -rock user-data meta-data
4)XML配置清单——Cloud-template.xml 可以直接用
sudo bash -c "sed 's#Cloud-template#Ceph01#g' /home/jiuzhao/storage/Cloud-template.xml > /etc/libvirt/qemu/Ceph01.xml"
sudo virsh define /etc/libvirt/qemu/Ceph01.xml
5)启动验证
# console 观察 cloud-init 输出
virsh start --console Ceph01
检查项结果
主机名Ceph01 ✅
扩容vda 30G → LV 28.2G ✅
growpart 服务active (exited) SUCCESS ✅
cloud-initdone ✅
SSH 公钥注入成功 + 免密登录验证通过 ✅
DNSGlobal 223.5.5.5 ✅
IPv6 禁用1 ✅
TCP buffer16MB ✅
串口 / qemu-gaactive / active ✅
SSH host keys3 个 ✅
Terminal window
# 固定IP
# vim /etc/netplan/50-cloud-init.yaml
network:
version: 2
ethernets:
enp1s0:
match:
macaddress: "52:54:00:c6:b9:fa"
dhcp4: false
dhcp6: false
set-name: "enp1s0"
addresses:
- 10.0.0.3/24 # IP地址/子网掩码位数
routes:
- to: default
via: 10.0.0.1 # 网关,virbr0 的 IP
nameservers:
addresses:
- 223.6.6.6
- 114.114.114.114
netplan apply
# 清屏
sudo apt install kitty-terminfo
# 下载 Kitty 的 terminfo 文件并安装
wget https://raw.githubusercontent.com/kovidgoyal/kitty/master/terminfo/kitty.terminfo
tic -x kitty.terminfo
# 验证
echo $TERM # 应该显示 xterm-kitty
clear # 应该正常工作

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Profile Image of the Author
久棹
不是先学好了再干,而是先干起来再学习,干中学!
分类
站点统计
文章
115
分类
15
标签
272
总字数
306,562
运行时长
0 天
最后活动
0 天前
文章目录