Redhat 虚拟化小记

@vrqq  February 1, 2025

在裸机安装redhat

正好我有一块盘上面有GRUB, 于是 新建一个分区, 下载minimum ISO (注意 此处由于GRUB限制, 完整版尺寸过大 无法在GRUB中挂载)
然后按照这个教程完成配置 https://blog.vrqq.org/archives/20/

RHEL的安装程序也是分两段, 先加载一个临时环境, 再从临时环境加载完整安装程序, 其中通过inst.stage2=指定第二段完整安装程序的位置.

Reference: https://docs.redhat.com/pt-br/documentation/red_hat_enterprise_linux/9/html-single/interactively_installing_rhel_from_installation_media/index#installation-source-boot-options_custom-boot-options

Podman梯子

https://www.redhat.com/en/blog/container-systemd-persist-reboot
没有发现啥好用的, 暂用V2RayA.

在安装时指定了HTTP Proxy 之后想换找不到

起初在网上搜索的是 yum http proxy, 然后在/etc/yum.repos.d/redhat.repo 这个里面找到了proxy=字样, 尝试删除后重新dnf update 发现依然报代理错误, 好像没改一样.
最后发现在这里改: /etc/rhsm/rhsm.conf proxy_hostname =

安装cockpit

我在局域网中使用的一个证书是基于ED25519的, 然后我基于此签发了一对 certification给cockpit用, 发现用不了.
一通搜索 发现似乎其内部的库不支持EdDSA, 于是新生成了一对RSA4906的根证书给局域网内机器分发, 然后给cockpit-web-server生成了RSA2048的服务器证书, 并签发之.

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_primes:2 -out rhost.key
openssl req -new -key rhost.key -out rhost.csr -config rhost.ini
openssl x509 -req -days 720 -CA ca.crt -CAkey ca.key -CAcreateserial -in rhost.csr -out rhost.crt -extensions req_ext -extfile rhost.ini
openssl x509 -noout -text -in rhost.crt

其中rhost.ini内容如下

req]
default_bits       = 4096
distinguished_name = req_distinguished_name
req_extensions     = req_ext
 
[req_distinguished_name]
countryName                 = Country Name (2 letter code)
countryName_default                 = CN
stateOrProvinceName         = stateOrProvince Name
stateOrProvinceName_default         = ShangHai
#localityName                = Locality Name (eg, city)
#localityName_default                = ShangHai
organizationName            = Organization Name (eg, company)
organizationName_default            = vrqq
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_default                  = vrqq rhost
 
[req_ext]
subjectAltName = @alt_names
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
basicConstraints = CA:false
 
[alt_names]
DNS.1   = rhost
DNS.2   = rhost.local.vrqq.org
IP      = 192.168.6.50

此处 DNS.1 是hostname, IP是内网约定的固定IP.
把生成的证书放进/etc/cockpit/ws-certs.d/1-rhost.cer 并配置好权限, 运行/usr/libexec/cockpit-certificate-ensure --check检查成果.

安装个 windows server 2025 并把显卡通进去

Reference : https://docs.redhat.com/zh-cn/documentation/red_hat_enterprise_linux/9/html/configuring_and_managing_virtualization/assembly_managing-gpu-devices-in-virtual-machines_configuring-and-managing-virtualization#proc_assigning-a-gpu-to-a-virtual-machine_assembly_managing-gpu-devices-in-virtual-machines

先装个工具dnf install driverctl, 然后lspci找到显卡并按教程把它自带的驱动ban了, 用直通驱动.

[root@rhost ~]# lspci -v |grep NVIDIA
81:00.0 VGA compatible controller: NVIDIA Corporation GP107 [GeForce GTX 1050] (rev a1) (prog-if 00 [VGA controller])
81:00.1 Audio device: NVIDIA Corporation GP107GL High Definition Audio Controller (rev a1)

[root@rhost ~]# driverctl set-override 0000:81:00.0 vfio-pci
[root@rhost ~]# driverctl set-override 0000:81:00.1 vfio-pci

检查驱动挂载: lspci -s 81:00.0 -v 以及 lspci -s 81:00.0 -v, 发现驱动已成功更换.
然后在cockpit控制台中直接选择该设备挂载即可. (cockpit自带了vnc可以直接操作桌面)

高级应用
参考: Linux解锁NVIDIA消费级显卡vGPU功能的入坑记录 https://bytehorizon.net/archives/crystalast/74/
Github: https://github.com/DualCoder/vgpu_unlock

生成mstsc证书

参照上述生成cockpit证书内容, 按照ms文档, 修改ini文件如下字段

keyUsage = critical, digitalSignature, keyEncipherment, dataEncipherment

然后如法炮制生成一对证书后 打包成pfx格式
openssl pkcs12 -export -out prism.pfx -inkey prism.key -in prism.crt -certfile ca.crt

然后在宿主机挂载mount -t cifs //192.168.122.223/Users ./prism_shared -ouser=Administrator 把文件粘进去. (windows server自动共享了这个目录, 参见Server Manager-> File and Storage Service)

注意 windows server 2025 没有WMIC了
mmc -> Add Snap-in -> Certificates -> Add 然后选 "Computer Account", 然后把刚才打包的pfx格式, 导入到Persional中. 双击刚才导入的证书, Details -> Thumbprint 把这串值复制下来.
接下来新建mstsc_cert.reg直接修改注册表, 内容如下.

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp]
"SSLCertificateSHA1Hash"=hex:62,9f,bb,68,32,1e,3c,70,5e,47,10,51,3f,f7,c4,e5,11,3f,40,ed

运行导入, 不用重启, 直接连接发现证书已更新.

Reference: https://vircloud.net/exp/rdp-ssl.html

小插曲 宿主机EFI分区挂了

Screen Shot 2025-02-02 at 10.11.27 AM.png
好在Boot分区没挂, 于是找个GRUB2环境, 按c进入grub command prompt

set root=(hd0,gpt2)   # Replace with actual RHEL boot partition
linuxefi /vmlinuz-<version> root=/dev/sdX ro quiet   # 'root=' could be ignored if unknown
initrdefi /initramfs-<version>.img
boot

我当时不知道root=怎么填 就先空着了, 然后进入了initramfs环境.
使用mount 查看当前挂载了哪些分区, 接下来先将我们真正的系统分区挂载道'/root' 然后再依次准备/dev等

## show mounted
mount

## mount OS partition to /root
mount /dev/mapper/luks-xxxx /root

## mount all others from shown above
mount --bind /dev /root/dev
mount --bind /... /root/...

## chroot
chroot /root

## mount /boot/EFI and /boot from disk
mount /dev/nvme2n1p2 /boot
mount /dev/nvme2n1p1 /boot/EFI

接下来准备联网

ifconfig eno2 192.168.6.50 netmask 255.255.255.0 up
ping bing.com

接下来按照下方教程重新生成EFI分区
Ref: https://access.redhat.com/solutions/3486741

yum reinstall grub2-efi-x64 shim-x64
yum reinstall grub2-common

efibootmgr -c -d /dev/<disk> -p 1  -l \\EFI\\redhat\\shimx64.efi -L "Red Hat Enterprise Linux <insert  7 or 8 or 9 here depending on system version>"

如果格式化了分区 一定记得修改/etc/fstab里面的分区UUID, 避免开机失败

ls -la /dev/disk/by-uuid
vim /etc/fstab

## Then replace UUID to right one

如果提示Crash recovery kernel arming.
他会自动进 rescue mode 然后上文没注意的地方可以看一下..

小插曲2: 内存报了ECC Corrected error

journctl -k 查询具体的问题

Feb 04 17:01:29 kernel: mce: [Hardware Error]: Machine check events logged
Feb 04 17:01:29 kernel: [Hardware Error]: Corrected error, no action required.
Feb 04 17:01:29 kernel: [Hardware Error]: CPU:3 (17:31:0) MC17_STATUS[Over|CE|MiscV|AddrV|-|-|SyndV|CECC|-|-|-]: 0xdc2040000000011b
Feb 04 17:01:29 kernel: [Hardware Error]: Error Addr: 0x0000000733418100
Feb 04 17:01:29 kernel: [Hardware Error]: IPID: 0x0000009600650f00, Syndrome: 0x636044440a800900
Feb 04 17:01:29 kernel: [Hardware Error]: Unified Memory Controller Ext. Error Code: 0
Feb 04 17:01:29 kernel: EDAC MC0: 1 CE on mc#0csrow#0channel#6 (csrow:0 channel:6 page:0xf83418 offset:0x100 grain:64 syndrome:0x4444)
Feb 04 17:01:29 kernel: [Hardware Error]: cache level: L3/GEN, tx: GEN, mem-tx: RD

显示日志如下, 这里的 Error Addr: 0x0000000733418100 是kernel访问的内存, 真实的内存地址通过page:0xf83418 offset:0x100换算, 每个page是4kb, 那么当前page的真实地址范围为 0xf83418000 -> 0xf83419000.

下载memtest86跑一下测试, 官网下载压缩包, 解压后将 EFI/BOOT 文件夹改名复制到EFI分区, 例如我的EFI分区如下:
Screen Shot 2025-02-12 at 5.58.42 PM.png

然后配置grub文件, 新增 /etc/grub.d/10_memtest86 内容如下
5FCC-1122 是 EFI分区的 UUID, 可以用ls -la ls /dev/disk/by-uuid/确定

cat << EOF
menuentry 'Memtest86' {
search --fs-uuid --no-floppy --set=root 5FCC-1122
chainloader /EFI/memtest86/BOOTX64.efi
}
EOF

然后给权限 chmod +x /etc/grub.d/10_memtest86.
运行 grub2-mkconfig > /boot/grub2/grub.cfg 更新 (注意不同的系统这个文件位置不同, 我是 redhat 9)

起初用免费的memtest+发现测不出, 因为ECC的错误汇报给cpu, 他没有解析. 故建议关闭ECC 或用 memtest86开启ECC reporting. 测试范围填上述以page换算的真实内存地址, 例如我的是0xf83418000 -> 0xf83419000.

将Linux虚拟机从 HyperV 迁移至 KVM

先来复习一下Linux的启动步骤:

(Motherboard) 
-> (EFI partition) EFI {load driver.efi}
-> (boot partition) Grub 
-> (boot partition) initramfs.img {
  load luks-encrypt
  load platform-drivers
  iterator all drivers for full_kernel & OS requirement
  load the real OS
}
-> (root partition)

直接在KVM中配置虚拟机后, 启动直接卡在dracut, 并且报找不到设备, 然后ls /dev确实没有block设备.
尝试在kvm配置中更换磁盘为scsi, sata, virtio 均不行, 然后lsmod发现没有virtio相关驱动载入.

问题出在 initramfs.img 上, Linux为了精简, 不会携带大部分驱动写入initramfs, 一般只会在安装或更新系统时 写入当前硬件平台 能挂载'/'的 所需要的驱动程序, 可以认为initramfs属于kernel的一部分. (注: initramfs和真正的系统 都不能从EFI阶段获取硬件驱动).
例如当前硬件平台从PCIE-NVME启动, 系统在制作initramfs.img时, 就会包含nvme.ko, 否则可能不会包含.

从 HyperV 迁移到 KVM时, 在initramfs阶段缺少了 virtio virtio_pci virtio_blk virtio_net virtio_scsi 这几个驱动, 导致找不到KVM下映射的硬盘.
于是参考 https://kyle.pericak.com/rhel-virtio-initrd.html 我们重新生成一个initramfs

先引导至rescue系统, 可以是ISO带的rescue, 也可以是 GRUB里面的rescue条目, 系统版本大差不差就可以.
如果KVM宿主机是Linux 也可以尝试使用宿主机系统.

# 如果已经成功mount和chroot, 省略此步
mount /dev/mapper/the-target-os-root /target
mount /dev/the-target-os-boot /target/boot
mount --bind /dev /target/dev
chroot /target

## -- 此时我们位于 VM-GUEST的root文件夹 --

# 检查之前的initramfs是哪个 并备份
ls /boot
cp /boot/initramfs-5.14.0-503.19.1.el9_5.x86_64.img /boot/initramfs-5.14.0-503.19.1.el9_5.x86_64.img.hyperV

# 确认系统含有当前版本的kernel-module
ls /lib/modules

# 重新生成initramfs.img
dracut -f \
  /boot/initramfs-5.14.0-503.19.1.el9_5.x86_64.img \
  5.14.0-503.19.1.el9_5.x86_64 \
  --add-drivers "virtio virtio_pci virtio_blk virtio_net virtio_scsi"

重启后正常.

额外注意点

  • 检查 /etc/fstab 是否有非UUID挂载的分区
  • 如有桥接网卡, 保持mac地址一致, 可以在上游DHCP获得相同的IP地址.

一些idea: 我认为initramfs对于桌面版Linux, 应当出一个 尽可能保持大版本兼容 小版本迭代的版本 类似portable version, 或提供跨平台的 initramfs定制工具 / install ISO内提供定制工具, 这或许需要发行商作出努力. 这样便可以支持直接以默认方式把系统装进U盘, 然后再简单的定制就能用了.

在宿主机关机时暂停虚拟机

Ref: https://www.reddit.com/r/kvm/comments/1as5x04/rhel9_how_to_perform_vm_graceful_shutdown_with/
Ref: https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/7/html/virtualization_deployment_and_administration_guide/sect-shutting_down_rebooting_and_force_shutdown_of_a_guest_virtual_machine-manipulating_the_libvirt_guests_configuration_settings

参照上述文档, 新建 /etc/sysconfig/libvirt-guests 填入如下内容(具体可参考man libvirt-guests)

ON_SHUTDOWN=suspend

添加新评论