办公室之前使用的虚拟化方案是 XenServer,虚拟机镜像是我自己手搓的。前段时间为了与生产环境的虚拟机镜像统一,试图使用发行版官方的预制 Cloud Image,然后发现 XenServer 无法优雅地使用 cloud-init。考虑到 Xen 的确已经是个夕阳技术了,AWS 前几年也开始抛弃 Xen 了,是时候换一波虚拟机方案了。调查了一番,我选择了 Proxmox VE(简称 PVE)。

一、Proxmox VE 简介

Proxmox Virtual Environment,或 Proxmox VE,是来自德国的开源虚拟化方案。软件和社区支持都是免费的,企业用户则可以通过订阅制获得付费商业支持。

前几年我曾了解过 Proxmox VE,当时 PVE 的重心还在容器化(OpenVZ 和 LXC)上,因此没多做考虑。后来 PVE 的重心渐渐转移到虚拟机上,现在已经是相当成熟的 VM 虚拟化方案了。PVE 的虚拟化核心是 QEMU/KVM,因此可以说是「站在巨人的肩膀上」。QEMU 虽然成熟而强大,但是使用与管理却不够用户友好,PVE 则是补上了这缺失的一环,通过直观的网页管理界面和高效的命令行工具,让各种用户都能愉快地管理虚拟机。

贯彻「不重复造轮子」的原则,当前版本的 PVE 基于成熟稳定的 Debian 9 "Stretch" 构建。在熟悉和使用 PVE 的过程中,我越发喜欢它「不重复造轮子」的特性。相较之前用过的其他虚拟化方案,PVE 的内部构造和工作原理对我来说不再是一个黑盒,我可以清晰地观测到它在干什么——比如要迁移一台虚拟机到另一个节点,我就可以通过 ps 观察到它启动了一个 dd 进程,对接 ssh 管道,将磁盘数据通过网络复制到目标机器——这种仿佛透明手表一样能看到内部工作原理的感觉真是太棒了!

二、Proxmox VE 安装

用户可以直接在现有的 Debian 上安装 PVE 相关的软件包,将之改造成 PVE 节点,但更推荐的方法是直接用官方提供的 ISO 文件,完成全系统的安装。安装过程中除了问装到哪儿之类的常规问题,没有别的复杂情况。安装完之后会重启,重启完成后即可使用 SSH 登录,或是用 https://<ipaddress>:8006/ 访问网页管理页面(注意是 https://),这一地址也会打印在屏幕上(如果你忘了的话)。

网页或是命令行管理真是方便啊,再也不用在 Windows 虚拟机里运行 Citrix XenCenter 或是 VMware vSphere Client 了。

三、Proxmox VE 配置

存储

默认安装下,安装程序会将目标硬盘分出一个专供存储 VM 镜像的 LVM Thin Pool,与系统分区一起放在名为 pve 的 LVM Volume Group 里——相比 XenServer 和 ESXi 需要额外配置诡异、专有的 SR / Datastore,直接使用 LVM 实在是太方便了。

当然,PVE 也支持 NFS, iSCSI, Ceph, GlusterFS, ZFS 等其他 VM 存储。对于小规模使用来说,本地 LVM 存储已经足够好用了,高效、透明、稳定。当你的 VM 出现问题的时候(比如 GRUB 或 initramfs 坏了),你可以直接在宿主机上用 losetupmount 挂载其分区,然后 chroot 进去修。对于中型及以上规模的集群使用场景的话,网络存储将会是个更好的选择。

在 VM 需要直接使用宿主机磁盘的场景下(比如后来我把我的 Gen8 也换成 PVE 了),PVE 也可以很方便地做直通

网络

网络方面,PVE 支持桥接和路由两种方式,默认是桥接。这个桥接就是标准的 Linux Bridge(路由也是标准的 iptables),甚至配置就是直接写在 Debian 的网络配置文件 /etc/network/interfaces 里的,你可以直接编辑它添加更多的网桥,或是修改已有的网桥。更推荐的方式是使用其网页管理界面进行管理,以避免语法错误。在网页管理界面进行修改后,PVE 会将新的配置写在 /etc/network/interfaces.new 里,并给出一个 diff,供再次检查。确认无误后,重启机器,即可让新的配置替换掉旧的配置。这一点可以说是非常贴心了(回想起了早年远程改一台没有 LOM 的服务器网络配置导致其断网,最后不得不派小伙伴人肉前往机房的惨痛经历)。

集群

多台 PVE 节点可以组成一个集群(cluster)。PVE 实现的集群的方式也有意思:把配置目录 /etc/pve 用 FUSE 挂载,通过 Corosync 跨机器同步,任何对配置文件的修改都会实时出现在节点中的任何一台机器上,从而实现集群之间的配置文件同步。

四、创建及启动 VM

cloud-init 支持

我为什么要从 XenServer 换到 Proxmox VE?最主要的原因是为了能用 cloud-init。「如何得知新启动的 VM 的 IP 地址」一直是个困扰 VM 使用者的问题。大厂(公有云)和小作坊(自建)的解决方法从 random shell script 到 DHCP hook 各不相同。之前我在 XenServer 里一直用的方法手搓镜像,往里面加塞 random shell script,这实在太不优雅了。总算随着以 AWS EC2 为首各种「云」的广泛普及,cloud-init 逐渐成为这一问题的标准答案,而各大发行版们也纷纷推出自带 cloud-init 的预制镜像(例:CentOS, Ubuntu, Debian)。这些镜像可以在支持 cloud-init 的 hypervisor 环境里直接启动,并自动从外部数据源获取 IP 地址、SSH 公钥,甚至是 random shell script 等信息,彻底将 DevOps 从手搓 VM 镜像的痛苦过程中解放出来。

Proxmox VE 是支持 cloud-init 的,用的是 NoCloud 数据源。用户将发行版的预制 Cloud Image 导入 PVE 并设置为模板之后,就可以从这个模板克隆出新的 VM。配置好新 VM 的主机名、IP 地址、SSH 公钥等信息,PVE 会生成一个 8 MiB 大小的 ISO 镜像,以虚拟光驱形式挂载在 VM 上。VM 启动后,内置的 cloud-init 会根据虚拟光驱里的 cloud-init 信息自己配置自己。关于导入 Cloud Image 的详细步骤,请参阅 Proxmox VE Wiki

目前 PVE 的 cloud-init 支持会强制启用 package_ugprade: true 选项,导致 cloud-init 启动时一定会做一次全系统升级。如果不需要这个行为,可以修改 /usr/share/perl5/PVE/QemuServer/Cloudinit.pm 文件注释掉相关设置,并重启 pvedaemon.service。这个选项在下个版本的 PVE 中有望变成可配置的,详见 Proxmox Bugzilla

Ansible 支持

Ansible 模块 proxmox_kvm 可以用来管理 PVE 里虚拟机。然而我觉得这个模块并不好用。在可以直接登录 PVE 机器的情况下,直接用命令行创建也是个不错的选择。以下是我写的 Ansible Playbook 的部分片段,仅供参考:

- name: Get a list of existing VMs
  command: pvesh get /cluster/resources -type vm --output-format json
  register: pvesh_vms
  changed_when: False

- set_fact:
    pve_vm_names: "{{ pvesh_vms.stdout | from_json | json_query('[*].name') | sort }}"

- debug:
    var: pve_vm_names

- name: Check if VM already exists
  assert:
    that:
      - pve_hostname_fqdn not in pve_vm_names

- name: Get next VMID
  command: pvesh get /cluster/nextid
  register: pvesh_nextid
  changed_when: False

- set_fact:
    pve_vmid: "{{ pvesh_nextid.stdout }}"

- name: Clone a new PVE instance
  command: "qm clone {{ pve_template_id }} {{ pve_vmid }} --name {{ pve_hostname_fqdn }}"

- name: Configure the new instance
  command: "qm set {{ pve_vmid }} --cores {{ pve_vcpus }} --memory {{ pve_memory }} --ipconfig0 gw={{ pve_gw }},ip={{ pve_ip }} --sshkey {{ pve_sshkey_path }}"

- name: Start the new instance
  command: "qm start {{ pve_vmid }}"

五、迁移到 Proxmox VE

从别的虚拟化方案(XenServer, ESXi 等)迁移到 PVE 也不难。总体思路就是获取到虚拟磁盘(需要是 qemu-img 支持的格式,如 raw, vhd, qcow2, vmdk 等),然后像上文导入 Cloud Image 那样导入 PVE 即可。由于半虚拟化驱动的不同,部分情况下 VM 可能会无法启动(找不到磁盘),为了避免不必要的麻烦,建议在迁移前先在旧系统里将 initramfs 的生成配置改为 generic / fallback 或其他类似设置,使其包含所有的存储驱动,以便在各种虚拟化方案里都能成功启动。

本文地址: https://wzyboy.im/post/1293.html


欢迎留下评论。评论前,请先阅读《隐私声明》。