使用 libguestfs 和 qemu 工具更改 VM 磁盘镜像密码和扩容

libguestfs 是一个 C 语言库和一套用于访问和修改虚拟机(VM)磁盘镜像的工具集。它能让管理员能够在不启动虚拟机的情况下直接操作磁盘内容,极大简化了虚拟化环境的管理工作。

本文以 Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2 这个镜像为例,简单演示添加用户、修改密码、添加密钥、修改硬盘大小等常用功能。

  • 运行 useradd 添加用户并设置密码

    注:默认情况下 virt-customize 不会显示 --run-command 命令的标准输出,如果需要查看输出可以添加-v--verbose 标志

    # 添加一个叫 rocky 的用户并设置密码为 MyPassword123
    $ virt-customize -a ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2 \
      --run-command "useradd -m -s /bin/bash rocky" \
      --password rocky:password:MyPassword123
    # 修改 root 密码为 MyPassword123
    $ virt-customize -a ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2 \
      --password root:password:MyPassword123
    [   0.0] Examining the guest ...
    [   9.4] Setting a random seed
    [   9.4] Setting passwords
    [  11.1] Finishing off
  • 扩容虚拟磁盘

    # 查看当前可用的分区大小和使用情况
    $ virt-filesystems -a ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2 --all --long -h
    Name       Type        VFS      Label  MBR  Size   Parent
    /dev/sda1  filesystem  unknown  -      -    2.0M   -
    /dev/sda2  filesystem  vfat     EFI    -    100M   -
    /dev/sda3  filesystem  xfs      BOOT   -    1000M  -
    /dev/sda4  filesystem  xfs      rocky  -    8.9G   -
    /dev/sda1  partition   -        -      -    2.0M   /dev/sda
    /dev/sda2  partition   -        -      -    100M   /dev/sda
    /dev/sda3  partition   -        -      -    1000M  /dev/sda
    /dev/sda4  partition   -        -      -    8.9G   /dev/sda
    /dev/sda   device      -        -      -    20G    -
    
    $ virt-df -h -a ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2
    Filesystem                                Size       Used  Available  Use%
    Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2:/dev/sda2
                                              100M        10M        90M   11%
    Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2:/dev/sda3
                                              936M       188M       748M   21%
    Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2:/dev/sda4
                                              8.9G       955M       7.9G   11%
    
    $ qemu-img info ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2
    image: ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2
    file format: qcow2
    virtual size: 10 GiB (10737418240 bytes)
    disk size: 609 MiB
    cluster_size: 65536
    Format specific information:
        compat: 1.1
        compression type: zlib
        lazy refcounts: false
        refcount bits: 16
        corrupt: false
        extended l2: false
    # 创建最终扩容好的镜像
    $ qemu-img create -f qcow2 ./Rocky-9-expanded.qcow2 20G
    Formatting './Rocky-9-expanded.qcow2', fmt=qcow2 cluster_size=2097152 extended_l2=off compression_type=zlib size=21474836480 lazy_refcounts=off refcount_bits=16 cache=writeback
    
    # 会自动检测 --expand 的分区fs类型
    $ virt-resize --expand /dev/sda4 \
    >   ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2 \
    >   ./Rocky-9-expanded.qcow2
    [   0.0] Examining ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2
    **********
    
    Summary of changes:
    
    /dev/sda1: This partition will be left alone.
    
    /dev/sda2: This partition will be left alone.
    
    /dev/sda3: This partition will be left alone.
    
    /dev/sda4: This partition will be resized from 8.9G to 18.9G.  The
    filesystem xfs on /dev/sda4 will be expanded using the ‘xfs_growfs’
    method.
    
    **********
    [   8.3] Setting up initial partition table on ./Rocky-9-expanded.qcow2
    [  23.3] Copying /dev/sda1
    [  23.3] Copying /dev/sda2
    [  23.5] Copying /dev/sda3
    [  24.6] Copying /dev/sda4
    ...进度条
    [  39.4] Expanding /dev/sda4 using the ‘xfs_growfs’ method
    
    Resize operation completed with no errors.  Before deleting the old disk,
    carefully check that the resized disk boots and works correctly.
    
    $ virt-filesystems -a ./Rocky-9-expanded.qcow2 --all --long -h
    Name       Type        VFS      Label  MBR  Size   Parent
    /dev/sda1  filesystem  unknown  -      -    2.0M   -
    /dev/sda2  filesystem  vfat     EFI    -    100M   -
    /dev/sda3  filesystem  xfs      BOOT   -    1000M  -
    /dev/sda4  filesystem  xfs      rocky  -    19G    -
    /dev/sda1  partition   -        -      -    2.0M   /dev/sda
    /dev/sda2  partition   -        -      -    100M   /dev/sda
    /dev/sda3  partition   -        -      -    1000M  /dev/sda
    /dev/sda4  partition   -        -      -    19G    /dev/sda
    /dev/sda   device      -        -      -    20G    -
  • 添加 ssh 登录密钥

    # 最好关闭SELinux,--ssh-inject 不会自动给 authorized-keys 文件打标签
    $ virt-customize -a ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2\  
      --run-command "sed -i s#SELINUX=enforcing#SELINUX=disabled# /etc/selinux/config"
    
    # 启动虚拟机进行简单测试,test 用户的 .ssh/authorized_keys 为手动创建
    root@VM $ ls -Z /root/.ssh/authorized_keys
    system_u:object_r:unlabeled_t:s0 /root/.ssh/authorized_keys
    root@VM $ ls -Z /home/test/.ssh/authorized_keys
    unconfined_u:object_r:ssh_home_t:s0 /home/test/.ssh/authorized_keys
    
    # 在 sshd_config 中取消允许公钥登录的注释
    # 此时 PermitRootLogin 默认应为 prohibit-password 即为不允许密码登录 root 用户
    $ virt-customize -a ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2 \
      --run-command "sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' /etc/ssh/sshd_config"
    # 添加公钥, 格式支持文件和字符串 
    # 参考通用说明 https://libguestfs.org/virt-builder.1.html#ssh-keys
    $ virt-customize -a ./Rocky-9-GenericCloud-Base-9.6-20250531.0.x86_64.qcow2 \ 
        --ssh-inject root:string:'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFCdvnUa450M4W5o2Ty85H9RKwM8FZbG6ZiKtGEuO+jI shao'