我的基础设施环境主要靠我家局域网内一台 7x24 小时运行的工作站支撑,我平时使用的基础设施,比如:gitlab、gitlab-runner、jira 甚至是现在你正在访问的博客站点,都是运行在这台工作站上的。这台工作站基于 Arch Linux,部署了 libvirt 虚拟化,不同的基础设施运行在彼此独立的虚拟机内。

下面简单介绍一下我平时是如何使用这个虚拟化的。

关于网络

我的虚拟化使用的是 bridge 网络模式,所以任何一台虚拟机都可以直接拿到局域网 IP,如何配置可参考:archwiki systemd-networkd#bridge-interface

关于系统和镜像

我的所有虚拟机也是 Arch Linux 系统。在虚拟化的宿主机上准备了一个 Arch Linux 虚拟机作为模板(模板镜像的准备使用了 virt-sysprep 这个工具),其他的虚拟机的镜像都是从模板镜像克隆出来的。虚拟机的镜像文件来自 Arch 官方,下载地址如下:Arch-Linux-x86_64-basic.qcow2

如何基于模板机创建实例

列出所有的虚拟机

virsh list --all

模板机关机备用

virsh shutdown template

基于模板机(我这里的模板机名字是:template)克隆出所需的主机

virt-clone --auto-clone --original template --name demo1
virt-clone --auto-clone --original template --name demo2
virt-clone --auto-clone --original template --name demo3

镜像准备,避免 machine-id、ssh-host-key 等冲突问题(这步是必须的)

virt-sysprep -d demo1
virt-sysprep -d demo2
virt-sysprep -d demo3

[可选] 为了后面能够通过 SSH 免密登录,所以在准备镜像的时候,我会顺便注入我的公钥,命令如下:

virt-sysprep \
    --ssh-inject root:string:'ssh-rsa AAAAB... jinmiaoluo@gmail.com' \
    -d demo1

virt-sysprep \
    --ssh-inject root:string:'ssh-rsa AAAAB... jinmiaoluo@gmail.com' \
    -d demo2

virt-sysprep \
    --ssh-inject root:string:'ssh-rsa AAAAB... jinmiaoluo@gmail.com' \
    -d demo3

启动虚拟机操作如下:

virsh start demo1
virsh start demo2
virsh start demo3

[可选] 启动虚拟机并通过 console 方式管理虚拟机,操作如下(ctrl+] 可退出 console):

virsh start demo1 --console
virsh start demo2 --console
virsh start demo3 --console

配置虚拟机在物理机开机后自动启动:

virsh autostart demo1
virsh autostart demo2
virsh autostart demo3

管理虚拟机我使用的是 serial console 模式,这样的好处是:即使网络没有就绪,也能管理主机。刚创建的虚拟机往往不知道分配的局域网的 IP 是多少,所以使用 serial 模式是非常方便的,操作如下(ctrl+] 可退出 console):

virsh console demo1
virsh console demo2
virsh console demo3

采用 serial 模式管理,需要更新 grub 配置,如果你也是用 Arch Linux 系统,只需更新:/etc/default/grub 文件,将:GRUB_CMDLINE_LINUX 的值更新为如下:

GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200n8 net.ifnames=0"

然后执行:grub-mkconfig -o /boot/grub/grub.cfg 即可

演示视频如下:

其他常见的操作

制作快照操作如下:

virsh snapshot-create demo1
virsh snapshot-create demo2
virsh snapshot-create demo3

恢复快照操作如下:

virsh snapshot-revert demo1 --current
virsh snapshot-revert demo2 --current
virsh snapshot-revert demo3 --current

删除快照操作如下:

virsh snapshot-delete --domain demo1 --current
virsh snapshot-delete --domain demo2 --current
virsh snapshot-delete --domain demo3 --current

关闭虚拟机操作如下:

# 删除虚拟机
virsh destroy demo1
virsh destroy demo2
virsh destroy demo3
# 清理镜像文件
virsh undefine --domain demo1 --remove-all-storage
virsh undefine --domain demo2 --remove-all-storage
virsh undefine --domain demo3 --remove-all-storage

演示视频如下: 快照制作、恢复、删除和主机的销毁

系统分区扩容

在宿主机上,对 demo 虚拟机的磁盘文件(QCOW2)进行扩容(增加 20G)

# 登录宿主机
ssh root@m4.jinmiaoluo.com

# 在宿主机上通过 virsh 关闭虚拟机
virsh shutdown demo

# 在宿主机上执行扩容操作(我打算扩容 20G,这里分成两遍,从而更好的演示后面查询命令的作用)
qemu-img resize /var/lib/libvirt/images/demo.img +10G

# 在宿主机上查看扩容后的大小
qemu-img info /var/lib/libvirt/images/demo.img

启动虚拟机,在虚拟机内操作。

# 启动虚拟机
virsh start demo

# 通过串口的方式登录虚拟机(视频中使用的方式)
virsh console demo

# 通过网络方式登录虚拟机
ssh root@demo.jinmiaoluo.com

查看根目录对应的磁盘分区,然后调整分区大小,调整文件系统大小。

# 查看分区
lsblk

# 调整分区大小。我这里根目录对应 /dev/vda2(所以第二个参数是 2,根据实际情况调整)
growpart /dev/vda 2

# 调整文件系统大小。如果是:btrfs
btrfs filesystem resize max /

# 调整文件系统大小。如果是:ext4
resize2fs /dev/vda2

# 调整文件系统大小。如果是 xfs
xfs_growfs /dev/vda1

操作见视频:

虚拟机救援

有些时候我们会因为一些特殊的原因导致虚拟机完全无法开机(即:console 都无法连接),如果是在普通的服务器,我们可以用 Live USB 启动后再回收数据,虚拟机怎么做呢?

我们可以用 qemu-nbd(QEMU Disk Network Block Device Server),这是 qemu 提供的一个工具,可以直接将虚拟机的镜像,直接以一个物理设备的形式,挂载到虚拟机镜像所在的物理机上。然后就可以像访问一个 U 盘的方式,访问和更改虚拟机中的数据(比如更换了磁盘,磁盘的 UUID 变了导致无法开机) 操作如下:

在 root 用户,启动对应的内核模块:

modprobe nbd

加载镜像为 nbd 设备:

qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2

如果虚拟机有多个分区,那么就会有多个 nbd 设备,类似:nbd0p1 nbd0p2,挂载根分区 对应的 nbd 设备(我这里是 nbd0p1):

mount /dev/nbd0p1 /mnt

然后进行读取和更改即可,完成后取消文件系统的挂载:

cd /mnt
# do something
umount /mnt

取消镜像文件到 nbd 设备的连接:

qemu-nbd -d /dev/nbd0

虚拟化的远程管理

我的虚拟化和开发环境是在两台不同的工作站上,这里用 A(虚拟化)和 B(开发环境) 来替代。

虚拟化远程管理指的是在 B 上,通过 virsh 实现虚拟机的克隆、启动、管理、删除。

做法很简单,将 B 的 ssh 公钥添加到 A 的 root 用户下(也可以添加到 A 的普通用户下,但要配置 A 上的 libvirt 允许该普通用户管理虚拟机),然后将下面的内容添加到 ~/.bashrc 即可:

# 这里的 m4.jinmiaoluo.com 是 A 的内部域名,你替换成自己虚拟化的 IP 即可
export LIBVIRT_DEFAULT_URI="qemu+ssh://root@m4.jinmiaoluo.com/system"

录屏中,m4 是虚拟化(A),m7 是开发环境(B),演示视频如下:

演示视频如下:

最后附上一张设备的丑图:workstation