惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
Last Week in AI
Last Week in AI
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
H
Help Net Security
F
Fortinet All Blogs
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 司徒正美
量子位
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Recorded Future
Recorded Future
博客园 - 三生石上(FineUI控件)
Vercel News
Vercel News
aimingoo的专栏
aimingoo的专栏
I
InfoQ
Microsoft Security Blog
Microsoft Security Blog
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
AI
AI
WordPress大学
WordPress大学
Security Archives - TechRepublic
Security Archives - TechRepublic
Google Online Security Blog
Google Online Security Blog
U
Unit 42
V2EX - 技术
V2EX - 技术
MongoDB | Blog
MongoDB | Blog
Schneier on Security
Schneier on Security
博客园 - Franky
H
Heimdal Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
W
WeLiveSecurity
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
B
Blog RSS Feed
N
News | PayPal Newsroom
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
Hacker News - Newest:
Hacker News - Newest: "LLM"
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园_首页
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
雷峰网
雷峰网

QEMU - 标签 - This Cute World

Proxmox Virtual Environment 使用指南
QEMU/KVM 虚拟化环境的搭建与使用
於清樂 · 2021-01-17 · via QEMU - 标签 - This Cute World

QEMU/KVM 有一定的使用门槛,本文假设你已经拥有基础的虚拟化相关知识,最好是已经有 virtualbox 或 vmware workstation 的使用经验。

虚拟机(Virtual Machine)是指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。它的主要用途有:

  1. 测试、尝鲜新的操作系统。
  2. 快速创建完全隔离的沙箱环境,用于运行某些不安全的或者敏感的文件/程序。
  3. 云服务商或企业会通过服务器虚拟化,提升服务器的利用率。
  4. 虚拟机可以创建快照跟备份,系统环境可以随时还原到旧的快照,也能方便地拷贝给他人。

而 QEMU/KVM 则是目前最流行的企业级虚拟化技术,它基于 Linux 内核提供的 KVM 模块,结构精简, 性能损失小,而且开源免费,因此成了大部分企业的首选虚拟化方案。

目前各大云厂商的虚拟化方案,新的服务器实例基本都是用的 KVM 技术。即使是起步最早,一直重度使用 Xen 的 AWS,从 EC2 C5 开始就改用了基于 KVM 定制的 Nitro 虚拟化技术。

但是 KVM 作为一个企业级的底层虚拟化技术,却没有对桌面使用做深入的优化,因此如果想把它当成桌面虚拟化软件来使用,替代掉VirtualBox/VMware Workstation, 有一定难度。

本文是我个人学习 KVM 的一个总结性文档,其目标是使用 KVM 作为桌面虚拟化软件。

QEMU/KVM 环境需要安装很多的组件,它们各司其职:

  1. qemu: 模拟各类输入输出设备(网卡、磁盘、USB端口等)
    • qemu 底层使用 kvm 模拟 CPU 和 RAM,比软件模拟的方式快很多。
  2. libvirt: 提供简单且统一的工具和 API,用于管理虚拟机,屏蔽了底层的复杂结构。(支持 qemu-kvm/virtualbox/vmware)
  3. ovmf: 为虚拟机启用 UEFI 支持
  4. virt-manager: 用于管理虚拟机的 GUI 界面(可以管理远程 kvm 主机)。
  5. virt-viewer: 通过 GUI 界面直接与虚拟机交互(可以管理远程 kvm 主机)。
  6. dnsmasq vde2 bridge-utils openbsd-netcat: 网络相关组件,提供了以太网虚拟化、网络桥接、NAT网络等虚拟网络功能。
    • dnsmasq 提供了 NAT 虚拟网络的 DHCP 及 DNS 解析功能。
    • vde2: 以太网虚拟化
    • bridge-utils: 顾名思义,提供网络桥接相关的工具。
    • openbsd-netcat: TCP/IP 的瑞士军刀,详见socat & netcat,这里不清楚是哪个网络组件会用到它。

安装命令:

# archlinux/manjaro
sudo pacman -S qemu virt-manager virt-viewer dnsmasq vde2 bridge-utils openbsd-netcat

# ubuntu,参考了官方文档,但未测试
sudo apt install qemu-kvm libvirt-daemon-system virt-manager virt-viewer virtinst bridge-utils

# centos,参考了官方文档,但未测试
sudo yum groupinstall "Virtualization Host"
sudo yum install virt-manager virt-viewer virt-install

# opensuse
# see: https://doc.opensuse.org/documentation/leap/virtualization/html/book-virt/cha-vt-installation.html
sudo yast2 virtualization
# enter to terminal ui, select kvm + kvm tools, and then install it.

安装完成后,还不能直接使用,需要做些额外的工作。请继续往下走。

libguestfs 是一个虚拟机磁盘映像处理工具,可用于直接修改/查看/虚拟机映像、转换映像格式等。

它提供的命令列表如下:

  1. virt-df centos.img: 查看硬盘使用情况
  2. virt-ls centos.img /: 列出目录文件
  3. virt-copy-out -d domain /etc/passwd /tmp:在虚拟映像中执行文件复制
  4. virt-list-filesystems /file/xx.img:查看文件系统信息
  5. virt-list-partitions /file/xx.img:查看分区信息
  6. guestmount -a /file/xx.qcow2(raw/qcow2都支持) -m /dev/VolGroup/lv_root --rw /mnt:直接将分区挂载到宿主机
  7. guestfish: 交互式 shell,可运行上述所有命令。
  8. virt-v2v: 将其他格式的虚拟机(比如 ova) 转换成 kvm 虚拟机。
  9. virt-p2v: 将一台物理机转换成虚拟机。

学习过程中可能会使用到上述命令,提前安装好总不会有错,安装命令如下:

# opensuse
sudo zypper install libguestfs

# archlinux/manjaro,目前缺少 virt-v2v/virt-p2v 组件
sudo pacman -S libguestfs

# ubuntu
sudo apt install libguestfs-tools

# centos
sudo yum install libguestfs-tools

通过 systemd 启动 libvirtd 后台服务:

sudo systemctl enable libvirtd.service
sudo systemctl start libvirtd.service

qumu/kvm 装好后,默认情况下需要 root 权限才能正常使用它。为了方便使用,首先编辑文件/etc/libvirt/libvirtd.conf:

  1. unix_sock_group = "libvirt",取消这一行的注释,使 libvirt 用户组能使用 unix 套接字。
  2. unix_sock_rw_perms = "0770",取消这一行的注释,使用户能读写 unix 套接字。

然后新建 libvirt 用户组,将当前用户加入该组:

newgrp libvirt
sudo usermod -aG libvirt $USER

最后重启 libvirtd 服务,应该就能正常使用了:

sudo systemctl restart libvirtd.service

如果你需要在虚拟机中运行虚拟机(比如在虚拟机里测试 katacontainers 等安全容器技术),那就需要启用内核模块 kvm_intel 或 kvm_amd 实现嵌套虚拟化。

首先通过如下指令验证下是否已经启用了嵌套虚拟化(一般的发行版默认都不会启用):

# intel 用这个命令,输出 Y 则表示启用了嵌套虚拟化
cat /sys/module/kvm_intel/parameters/nested
# amd 用如下指令,输出 1 则表示启用了嵌套虚拟化
cat /sys/module/kvm_amd/parameters/nested

如果输出不是 Y/1,说明默认未启用嵌套虚拟化,需要手动启用,步骤如下。

如果是 intel cpu,需要使用如下命令启用嵌套虚拟化功能:

## 1. 关闭所有虚拟机,并卸载 kvm_intel 内核模块
sudo modprobe -r kvm_intel
## 2. 启用嵌套虚拟化功能
sudo modprobe kvm_intel nested=1
## 3. 保存配置,使嵌套虚拟化功能在重启后自动启用
cat <<EOF | sudo tee /etc/modprobe.d/kvm.conf
options kvm_intel nested=1
EOF

如果是 amd cpu,则应使用如下命令启用嵌套虚拟化功能:

## 1. 关闭所有虚拟机,并卸载 kvm_intel 内核模块
sudo modprobe -r kvm_amd
## 2. 启用嵌套虚拟化功能
sudo modprobe kvm_amd nested=1
## 3. 保存配置,使嵌套虚拟化功能在重启后自动启用
cat <<EOF | sudo tee /etc/modprobe.d/kvm.conf
options kvm_amd nested=1
EOF

改完后再利用前面提到的命令验证下是否启用成功。

至此,KVM 的安装就大功告成啦,现在应该可以在系统中找到 virt-manager 的图标,进去就可以使用了。virt-manager 的使用方法和 virtualbox/vmware workstation 大同小异,这里就不详细介绍了, 自己摸索摸索应该就会了。


如下内容是进阶篇,主要介绍如何通过命令行来管理虚拟机磁盘,以及 KVM。如果你还是 kvm 新手,建议先通过图形界面 virt-manager 熟悉熟悉,再往下继续读。

这需要用到两个工具:

  1. libguestfs: 虚拟机磁盘映像管理工具,前面介绍过了
  2. qemu-img: qemu 的磁盘映像管理工具,用于创建磁盘、扩缩容磁盘、生成磁盘快照、查看磁盘信息、转换磁盘格式等等。
# 创建磁盘
qemu-img create -f qcow2 -o cluster_size=128K virt_disk.qcow2 20G

# 扩容磁盘
qemu-img resize ubuntu-server-cloudimg-amd64.img 30G

# 查看磁盘信息
qemu-img info ubuntu-server-cloudimg-amd64.img

# 转换磁盘格式
qemu-img convert -f raw -O qcow2 vm01.img vm01.qcow2  # raw => qcow2
qemu-img convert -f qcow2 -O raw vm01.qcow2 vm01.img  # qcow2 => raw

直接从 vmware ova 文件导入 kvm,这种方式转换得到的镜像应该能直接用(网卡需要重新配置):

virt-v2v -i ova centos7-test01.ova -o local -os /vmhost/centos7-01  -of qcow2

也可以先从 ova 中解压出 vmdk 磁盘映像,将 vmware 的 vmdk 文件转换成 qcow2 格式,然后再导入 kvm(网卡需要重新配置):

# 转换映像格式
qemu-img convert -p -f vmdk -O qcow2 centos7-test01-disk1.vmdk centos7-test01.qcow2
# 查看转换后的映像信息
qemu-img info centos7-test01.qcow2

直接转换 vmdk 文件得到的 qcow2 镜像,启会报错,比如「磁盘无法挂载」。根据Importing Virtual Machines and disk images - ProxmoxVE Docs 文档所言,需要在网上下载安装 MergeIDE.zip 组件,另外启动虚拟机前,需要将硬盘类型改为 IDE, 才能解决这个问题。

img 镜像文件,就是所谓的 raw 格式镜像,也被称为裸镜像,IO 速度比 qcow2 快,但是体积大,而且不支持快照等高级特性。如果不追求 IO 性能的话,建议将它转换成 qcow2 再使用。

qemu-img convert -f raw -O qcow2 vm01.img vm01.qcow2

虚拟机管理可以使用命令行工具 virsh/virt-install,也可以使用 GUI 工具 virt-manager.

GUI 很傻瓜式,就不介绍了,这里主要介绍命令行工具 virsh/virt-install

先介绍下 libvirt 中的几个概念:

  1. Domain: 指代运行在虚拟机器上的操作系统的实例 - 一个虚拟机,或者用于启动虚拟机的配置。
  2. Guest OS: 运行在 domain 中的虚拟操作系统。

大部分情况下,你都可以把下面命令中涉及到的 domain 理解成虚拟机。

virsh/virt-install/virt-viewer 等一系列 libvirt 命令,sudo virsh net-list –all 默认情况下会使用 qemu:///session 作为 URI 去连接 QEMU/KVM,只有 root 账号才会默认使用qemu:///system.

另一方面 virt-manager 这个 GUI 工具,默认也会使用 qemu:///system 去连接 QEMU/KVM(和 root 账号一致)

qemu:///system 是系统全局的 qemu 环境,而 qemu:///session 的环境是按用户隔离的。另外qemu:///session 没有默认的 network,创建虚拟机时会出毛病。。。

因此,你需要将默认的 URI 改为 qemu:///system,否则绝对会被坑:

echo 'export LIBVIRT_DEFAULT_URI="qemu:///system"' >> ~/.bashrc

qemu-kvm 安装完成后,qemu:///system 环境中默认会创建一个 default 网络,而qemu:///session 不提供默认的网络,需要手动创建。

我们通常使用 qemu:///system 环境就好,可以使用如下方法查看并启动 default 网络,这样后面创建虚拟机时才有网络可用。

# 列出所有虚拟机网络
$ sudo virsh net-list --all
 Name      State      Autostart   Persistent
----------------------------------------------
 default   inactive   no          yes

# 启动默认网络
$ virsh net-start default
Network default started

# 将 default 网络设为自启动
$ virsh net-autostart --network default
Network default marked as autostarted

# 再次检查网络状况,已经是 active 了
$ sudo virsh net-list --all
 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   yes         yes

也可以创建新的虚拟机网络,这需要手动编写网络的 xml 配置,然后通过virsh net-define --file my-network.xml 创建,这里就不详细介绍了,因为暂时用不到…

# 使用 iso 镜像创建全新的 proxmox 虚拟机,自动创建一个 60G 的磁盘。
virt-install --virt-type kvm \
--name pve-1 \
--vcpus 4 --memory 8096 \
--disk size=60 \
--network network=default,model=virtio \
--os-type linux \
--os-variant generic \
--graphics vnc \
--cdrom proxmox-ve_6.3-1.iso

# 使用已存在的 opensuse cloud 磁盘创建虚拟机
virt-install --virt-type kvm \
  --name opensuse15-2 \
  --vcpus 2 --memory 2048 \
  --disk opensuse15.2-openstack.qcow2,device=disk,bus=virtio \
  --disk seed.iso,device=cdrom \
  --os-type linux \
  --os-variant opensuse15.2 \
  --network network=default,model=virtio \
  --graphics vnc \
  --import

其中的 --os-variant 用于设定 OS 相关的优化配置,官方文档强烈推荐设定,其可选参数可以通过 osinfo-query os 查看。

虚拟机创建好后,可使用 virsh 管理虚拟机。

首先介绍万能的帮助命令:

除了官方的 help 之外,我也总结了下 virsh 的常用命令,如下。

查看虚拟机列表:

# 查看正在运行的虚拟机
virsh list

# 查看所有虚拟机,包括 inactive 的虚拟机
virsh list --all

使用 virt-viewer 以 vnc 协议登入虚拟机终端:

# 使用虚拟机 ID 连接
virt-viewer 8
# 使用虚拟机名称连接,并且等待虚拟机启动
virt-viewer --wait opensuse15

启动、关闭、暂停(休眠)、重启虚拟机:

virsh start opensuse15
virsh suspend opensuse15
virsh resume opensuse15
virsh reboot opensuse15
# 优雅关机
virsh shutdown opensuse15
# 强制关机
virsh destroy opensuse15

# 启用自动开机
virsh autostart opensuse15
# 禁用自动开机
virsh autostart --disable opensuse15

虚拟机快照管理:

# 列出一个虚拟机的所有快照
virsh snapshot-list --domain opensuse15
# 给某个虚拟机生成一个新快照
virsh snapshot-create <domain>
# 使用快照将虚拟机还原
virsh snapshot-restore <domain> <snapshotname>
# 删除快照
virsh snapshot-delete <domain> <snapshotname>

删除虚拟机:

virsh undefine opensuse15

迁移虚拟机:

# 使用默认参数进行离线迁移,将已关机的服务器迁移到另一个 qemu 实例
virsh migrate 37 qemu+ssh://[email protected]/system
# 还支持在线实时迁移,待续

cpu/内存修改:

# 改成 4 核
virsh setvcpus opensuse15 4
# 改成 4G
virsh setmem opensuse15 4096

虚拟机监控:

修改磁盘、网络及其他设备:

# 添加新设备
virsh attach-device
virsh attach-disk
virsh attach-interface
# 删除设备
virsh detach-disk
virsh detach-device
virsh detach-interface

在本机的 KVM 环境中,也可以使用 cloud-init 来初始化虚拟机。好处是创建虚拟机的时候,就能设置好虚拟机的 hostname/network/user-pass/disk-size 等一系列参数,不需要每次启动后再手动登录到机器中配置。

注意:下面的几种镜像都分别有自己的坑点,仅 Ubuntu/OpenSUSE 测试通过,其他发行版的 Cloud 镜像都有各种毛病…

首先下载 Cloud 版本的系统镜像:

  1. Ubuntu Cloud Images (RELEASED): 提供 img 格式的裸镜像(PVE 也支持此格式)
    • 请下载带有 .img 结尾的镜像,其中 kvm.img 结尾的镜像会更精简一点。
  2. OpenSUSE Cloud Images
    • 请下载带有 NoCloud 或者 OpenStack 字样的镜像。
  3. 对于其他镜像,可以考虑手动通过 iso 来制作一个 cloudinit 镜像,参考openstack - create ubuntu cloud images from iso

上述镜像和我们普通虚拟机使用的 ISO 镜像的区别,一是镜像格式不同,二是都自带了cloud-init/qemu-guest-agent/cloud-utils-growpart 等 cloud 相关软件。

其中 NoCloud 表示支持 cloudinit NoCloud 数据源——即使用 seed.iso 提供 user-data/meta-data/network-config 配置,PVE 就是使用的这种模式。而 Openstack 镜像通常也都支持 NoCloud 模式,所以一般也是可以使用的。

cloud image 基本都没有默认密码,并且禁用了 SSH 密码登录,必须通过 cloud-init 设置私钥方式进行 ssh 登录。

这需要用到一个工具:cloud-utils

# manjaro
sudo pacman -S cloud-utils

# ubuntu
sudo apt install cloud-utils

# opensuse,包仓库里找不到 cloud-utils,只能源码安装
git clone https://github.com/canonical/cloud-utils
git checkout 0.32
cd cloud-utils && sudo make install
# 生成 iso 文件还需要 genisoimage,请使用一键安装:https://software.opensuse.org/package/genisoimage

cloud-utils 提供 cloud-init 相关的各种实用工具,其中有一个 cloud-localds 命令,可以通过 cloud 配置生成一个非 cloud 的 bootable 磁盘映像,供本地的虚拟机使用。

首先编写 user-data:

#cloud-config
hostname: opensuse15-2
fqdn: opensuse15-2.local
# 让 cloud-init 自动更新 /etc/hosts 中 localhost 相关的内容
manage_etc_hosts: localhost

package_upgrade: true

disable_root: false

# 设置 root 的 ssh 密钥
user: root
# 设置密码,仅用于控制台登录
password: xxxxx
# 使用密钥登录
ssh_authorized_keys:
  - "<ssh-key content>"
chpasswd:
  # expire 使密码用完即失效,用户每次登录都需要设置并使用密码!
  expire: false

# ssh 允许密码登录(不建议开启)
ssh_pwauth: false

注意 user-data 的第一行的 #cloud-config 绝对不能省略!它标识配置格式为text/cloud-config

再编写 network-config(其格式和 ubuntu 的 netplan 基本完全一致,但是我只测通了 v1 版本,v2 版没测通):

version: 1
config:
  - type: physical
    name: eth0
    subnets:
      - type: static
        address: 192.168.122.160
        netmask: 255.255.255.0
        gateway: 192.168.122.1
  - type: nameserver
    interface: eth0
    address:
      - 114.114.114.114
      - 8.8.8.8
    # search:  # search domain
    #   - xxx
cloud-localds seed.iso user-data --network-config network-config

每次都手动生成 seed.iso 太麻烦了,实际使用,建议用后面介绍的自动化功能 proxmox-libvirt 或者 terraform-libvirt-provider~

这样就生成出了一个 seed.iso,创建虚拟机时同时需要载入 seed.iso 和 cloud image,cloud-image 自身为启动盘,这样就大功告成了。示例命令如下:

virt-install --virt-type kvm \
  --name k8s-master-0 \
  --vcpus 2 --memory 3072 \
  --disk k8s-master-0.qcow2,device=disk,bus=virtio \
  --disk ../vm-seeds/160-seed-k8s-master-0.iso,device=cdrom \
  --os-type linux \
  --os-variant opensuse15.3 \
  --network network=default,model=virtio \
  --graphics vnc \
  --import

也可以使用 virt-viewer 的 GUI 界面进行操作。

这样设置完成后,cloud 虚拟机应该就可以启动了,可以检查下 hostname、网络、root 的密码和私钥、ssh 配置是否均正常。

一切正常后,还有个问题需要解决——初始磁盘应该很小。可以直接手动扩容 img 的大小,cloud-init 在虚拟机启动时就会自动扩容分区:

qemu-img resize ubuntu-server-cloudimg-amd64.img 30G
  • ubuntu 启动时会报错 no such device: root,但是过一会就会正常启动。
  • ubuntu 启动后很快就会进入登录界面,但是 root 密码可能还没改好,登录会报密码错误,等待一会再尝试登录就 OK 了
  • ubuntu 的默认网卡名称是 ens3,不是 eth0,注意修改 network_config 的网卡名称,否则网络配置不会生效
  • opensuse leap 15 只支持 network_config v1,对 v2 的支持有 bug,gateway4 不会生效

debian 的 cloud 镜像根本没法用,建议避免使用它。

  • debian 启动时会彻底卡住,或者直接报错 kernel panic
    • 原因是添加了 spice 图形卡,换成 vnc 就正常了
  • Debian Cloud Images 中的 nocloud 镜像不会在启动时运行 cloudinit,cloudinit 完全不生效
    • 不知道是啥坑,没解决

cloudinit 有三个参数与 hostname 相关。其中有两个,就是上面提到的 user-data 中的:

  1. hostname: 主机名称
  2. fqdn: 主机的完全限定域名,优先级比 hostname 更高

这两个参数的行为均受 preserve_hostname: true/false 这个参数的影响。

另一个是 meta-data 中,可以设置一个 local-hostname,此参数的地位好像和 user-data 中的 hostname 相同,不过可能优先级会高一些吧。没有找到相关文档。

可以使用 pulumi/terraform 自动化创建与管理 QEMU/KVM 虚拟机,相当方便: