标签归档:KVM

检查当前机器是不是虚拟机并确认底层虚拟化技术

要确定当前运行的系统是否在虚拟机(VM)中,并了解底层虚拟化技术,可以使用一些命令和工具进行检查。以下是几种方法:

使用 dmidecode 命令: dmidecode 是一个用于获取 BIOS、系统、主板和处理器信息的工具。要使用 dmidecode,请确保您已安装它。然后,可以运行以下命令:

sudo dmidecode -s system-manufacturer

这将输出系统制造商的信息。如果输出包含虚拟化提供商的名称(如 VMware、VirtualBox、KVM 等),则表示您正在虚拟机中运行。例如,输出可能类似于:

VMware, Inc.

使用 lscpu 命令: lscpu 是一个显示 CPU 架构信息的命令。您可以运行以下命令查看虚拟化技术:

lscpu

在输出中,找到 Hypervisor vendor 这一行。如果它显示虚拟化提供商的名称(如 KVM、VMware、Microsoft 等),则表示您正在虚拟机中运行。

使用 virt-what 工具: virt-what 是一个用于确定当前系统是否在虚拟机中以及虚拟化技术的工具。首先,您需要安装 virt-what:

对于基于 Debian 的系统(如 Ubuntu):

sudo apt-get install virt-what

对于基于 RHEL 的系统(如 CentOS、Fedora):

sudo yum install virt-what

安装完成后,运行以下命令:

sudo virt-what

如果您正在虚拟机中运行,输出将显示虚拟化技术的名称(如 kvm、qemu、vmware 等)。如果您不在虚拟机中运行,输出将为空。

以上方法可以帮助您检查当前系统是否在虚拟机中运行,以及了解底层虚拟化技术。

QEMU 编译报错 undefined reference to g_app_info_launch_default_for_uri_finish 解决过程

编译 QEMU 时报如下错误:

/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_app_info_launch_default_for_uri_finish'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_type_check_instance_is_fundamentally_a'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_app_info_launch_default_for_uri_async'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_strv_contains'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_list_model_get_type'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_drive_is_removable'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_application_get_resource_base_path'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_log_structured_standard'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_type_get_instance_count'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_list_model_get_n_items'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_file_enumerator_iterate'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_param_spec_get_name_quark'
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/libgtk-3.so: undefined reference to `g_list_model_get_item'
collect2: error: ld returned 1 exit status
make[1]: *** [qemu-system-x86_64] Error 1
make: *** [subdir-x86_64-softmmu] Error 2

先看一下报错的动态链接库依赖了哪些库:

$ ldd /lib64/libgtk-3.so       
        linux-vdso.so.1 =>  (0x00007ffc89762000)
        libgdk-3.so.0 => /lib64/libgdk-3.so.0 (0x00007f19938ab000)
        libgmodule-2.0.so.0 => /lib64/libgmodule-2.0.so.0 (0x00007f19936a7000)
        libpangocairo-1.0.so.0 => /lib64/libpangocairo-1.0.so.0 (0x00007f1993499000)
        libX11.so.6 => /lib64/libX11.so.6 (0x00007f199315b000)
        libXi.so.6 => /lib64/libXi.so.6 (0x00007f1992f4b000)
        libXfixes.so.3 => /lib64/libXfixes.so.3 (0x00007f1992d45000)
        libcairo-gobject.so.2 => /lib64/libcairo-gobject.so.2 (0x00007f1992b3c000)
        libcairo.so.2 => /lib64/libcairo.so.2 (0x00007f1992805000)
        libgdk_pixbuf-2.0.so.0 => /lib64/libgdk_pixbuf-2.0.so.0 (0x00007f19925dd000)
        libatk-1.0.so.0 => /lib64/libatk-1.0.so.0 (0x00007f19923b7000)
        libatk-bridge-2.0.so.0 => /lib64/libatk-bridge-2.0.so.0 (0x00007f1992188000)
        libwayland-client.so.0 => /lib64/libwayland-client.so.0 (0x00007f1991f79000)
        libepoxy.so.0 => /lib64/libepoxy.so.0 (0x00007f1991c4d000)
        libpangoft2-1.0.so.0 => /lib64/libpangoft2-1.0.so.0 (0x00007f1991a37000)
        libpango-1.0.so.0 => /lib64/libpango-1.0.so.0 (0x00007f19917f1000)
        libfontconfig.so.1 => /lib64/libfontconfig.so.1 (0x00007f19915af000)
        libgio-2.0.so.0 => /lib64/libgio-2.0.so.0 (0x00007f199120f000)
        libgobject-2.0.so.0 => /lib64/libgobject-2.0.so.0 (0x00007f1990fbe000)
        libglib-2.0.so.0 => /lib64/libglib-2.0.so.0 (0x00007f1990ca8000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f19909a6000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f199078a000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f19903bc000)
        libXinerama.so.1 => /lib64/libXinerama.so.1 (0x00007f19901b9000)
        libXrandr.so.2 => /lib64/libXrandr.so.2 (0x00007f198ffae000)
        libXcursor.so.1 => /lib64/libXcursor.so.1 (0x00007f198fda3000)
        libXcomposite.so.1 => /lib64/libXcomposite.so.1 (0x00007f198fba0000)
        libXdamage.so.1 => /lib64/libXdamage.so.1 (0x00007f198f99d000)
        libxkbcommon.so.0 => /lib64/libxkbcommon.so.0 (0x00007f198f75d000)
        libwayland-cursor.so.0 => /lib64/libwayland-cursor.so.0 (0x00007f198f555000)
        libwayland-egl.so.1 => /lib64/libwayland-egl.so.1 (0x00007f198f353000)
        libXext.so.6 => /lib64/libXext.so.6 (0x00007f198f141000)
        librt.so.1 => /lib64/librt.so.1 (0x00007f198ef39000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f198ed35000)
        libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f198ead3000)
        libfreetype.so.6 => /lib64/libfreetype.so.6 (0x00007f198e814000)
        libxcb.so.1 => /lib64/libxcb.so.1 (0x00007f198e5ec000)
        libpixman-1.so.0 => /lib64/libpixman-1.so.0 (0x00007f198e343000)
        libEGL.so.1 => /lib64/libEGL.so.1 (0x00007f198e12f000)
        libpng15.so.15 => /lib64/libpng15.so.15 (0x00007f198df04000)
        libxcb-shm.so.0 => /lib64/libxcb-shm.so.0 (0x00007f198dd00000)
        libxcb-render.so.0 => /lib64/libxcb-render.so.0 (0x00007f198daf2000)
        libXrender.so.1 => /lib64/libXrender.so.1 (0x00007f198d8e7000)
        libz.so.1 => /lib64/libz.so.1 (0x00007f198d6d1000)
        libGL.so.1 => /lib64/libGL.so.1 (0x00007f198d445000)
        libatspi.so.0 => /lib64/libatspi.so.0 (0x00007f198d214000)
        libdbus-1.so.3 => /lib64/libdbus-1.so.3 (0x00007f198cfc4000)
        libffi.so.6 => /lib64/libffi.so.6 (0x00007f198cdbc000)
        libharfbuzz.so.0 => /lib64/libharfbuzz.so.0 (0x00007f198cb1f000)
        libthai.so.0 => /lib64/libthai.so.0 (0x00007f198c913000)
        libfribidi.so.0 => /lib64/libfribidi.so.0 (0x00007f198c6f7000)
        libexpat.so.1 => /lib64/libexpat.so.1 (0x00007f198c4cd000)
        libuuid.so.1 => /lib64/libuuid.so.1 (0x00007f198c2c8000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f198c0a1000)
        libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f198be87000)
        libmount.so.1 => /lib64/libmount.so.1 (0x00007f198bc44000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f198ba2e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f199449d000)
        libbz2.so.1 => /lib64/libbz2.so.1 (0x00007f198b81e000)
        libXau.so.6 => /lib64/libXau.so.6 (0x00007f198b61a000)
        libGLdispatch.so.0 => /lib64/libGLdispatch.so.0 (0x00007f198b364000)
        libGLX.so.0 => /lib64/libGLX.so.0 (0x00007f198b132000)
        libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007f198af01000)
        libgraphite2.so.3 => /lib64/libgraphite2.so.3 (0x00007f198acd3000)
        libblkid.so.1 => /lib64/libblkid.so.1 (0x00007f198aa93000)
        libcap.so.2 => /lib64/libcap.so.2 (0x00007f198a88e000)
        liblzma.so.5 => /lib64/liblzma.so.5 (0x00007f198a668000)
        liblz4.so.1 => /lib64/liblz4.so.1 (0x00007f198a459000)
        libgcrypt.so.11 => /lib64/libgcrypt.so.11 (0x00007f198a1d8000)
        libgpg-error.so.0 => /lib64/libgpg-error.so.0 (0x00007f1989fd3000)
        libdw.so.1 => /lib64/libdw.so.1 (0x00007f1989d82000)
        libattr.so.1 => /lib64/libattr.so.1 (0x00007f1989b7d000)
        libelf.so.1 => /lib64/libelf.so.1 (0x00007f1989965000)

观察输出,所有依赖的动态链接库都有指向一个内存地址,说明所依赖的链接库都已经被加载入内存,排除了链接库不存在情况,下面就有可能是某个链接库有问题了,接下来做两件事:

  • 使用 objdump -T <lib name and path> |grep <funcname> 命令检索报错函数属于哪一个链接库;
  • 使用 find / -name <lib name> 命令查找是否有哪一个报错链接库在系统的动态链接库搜索目录中有多个;

经过一番检索,发现 [libgio-2.0.so](http://libgio-2.0.so) 在系统so检索目录有两个,分别是 /usr/local/lib/libgio-2.0.so/lib64/libgio-2.0.so ,其中 /lib64 为系统默认链接库存放位置,而 /usr/local 为编译安装库的默认安装位置,移除/usr/local/lib/libgio-2.0.so 之后再次尝试编译发现报错减少了。

此时发现系统曾编译安装了 glib ,可能是那时引入了一些错误的 so 库,因此进入编译目录 make uninstall 移除此前安装的错误的库,再次尝试编译发现编译通过。

总结

本次编译错误排查了很久,最后在大佬的协助下终于解决,此类缺少依赖错误排查错误思路可以总结为 检查链接库是否存在 -> 检查是否存在重复链接库 -> 移除错误链接库

参考文献

virt-manager 启动提示 virt qemu-sock no such file or directory

检查后应该是 virtqemud 服务没起来导致的,编译安装默认生成的服务路径在 /usr/local/* 下,而我们指定了安装在默认位置 /usr ,参考 systemd 无法启动的解决方法即可解决该问题。

首先看一下 /usr/sbin/virtqemud 这个文件是存在的,下面继续操作,首先使能该服务。

$ systemctl enable virtqemud
Created symlink from /etc/systemd/system/multi-user.target.wants/virtqemud.service to /usr/local/lib/systemd/system/virtqemud.service.
Created symlink from /etc/systemd/system/sockets.target.wants/virtqemud.socket to /usr/local/lib/systemd/system/virtqemud.socket.
Created symlink from /etc/systemd/system/sockets.target.wants/virtqemud-ro.socket to /usr/local/lib/systemd/system/virtqemud-ro.socket.
Created symlink from /etc/systemd/system/sockets.target.wants/virtqemud-admin.socket to /usr/local/lib/systemd

创建了改服务的软连接,宣告自启,此时服务配置有问题,直接修改该软连接指定的文件

vim /usr/local/lib/systemd/system/virtqemud.service

# 类比 libvirtd 的修改这两行,指定正确服务位置

[Service]
...
EnvironmentFile=-/etc/sysconfig/virtqemud
ExecStart=/usr/sbin/virtqemud $VIRTQEMUD_ARGS

之后重新加载服务,再启动

systemctl daemon-reload
systemctl reload virtqemud
systemctl start virtqemud

不出意外的话就能启动了,若有问题请留言。

本文截取自,前一篇文章,详细内容及上下文请查看原文

Linux虚拟化入门(五)KVM 管理命令指南

  • qemu-img是QEMU的磁盘管理工具
格式: qemu-img command [command options]
command:
        check                          对磁盘镜像文件进行一致性检查,查找镜像文件中的错误
        create                         创建一个可以指定格式、大小、名称的镜像文件
        convert                        镜像文件格式转换
        info                           展示镜像文件的信息
  • virsh kvm虚拟机管理工具
[root@node1 ~]# virsh help
command:

 Domain Management (help keyword 'domain'):
    attach-device                  从一个XML文件附加装置
    attach-disk                    附加磁盘设备
    attach-interface               获得网络界面
    autostart                      自动开始一个域
    blkdeviotune                   设定或者查询块设备 I/O 调节参数。
    blkiotune                      获取或者数值 blkio 参数
    blockcommit                    启动块提交操作。
    blockcopy                      启动块复制操作。
    blockjob                       管理活跃块操作
    blockpull                      使用其后端映像填充磁盘。
    blockresize                    创新定义域块设备大小
    change-media                   更改 CD 介质或者软盘驱动器
    console                        连接到客户会话
    cpu-stats                      显示域 cpu 统计数据
    create                         从一个 XML 文件创建一个域
    define                         从一个 XML 文件定义(但不开始)一个域
    desc                           显示或者设定域描述或者标题
    destroy                        销毁(停止)域
    detach-device                  从一个 XML 文件分离设备
    detach-device-alias            detach device from an alias
    detach-disk                    分离磁盘设备
    detach-interface               分离网络界面
    domdisplay                     域显示连接 URI
    domfsfreeze                    Freeze domain's mounted filesystems.
    domfsthaw                      Thaw domain's mounted filesystems.
    domfsinfo                      Get information of domain's mounted filesystems.
    domfstrim                      在域挂载的文件系统中调用 fstrim。
    domhostname                    输出域主机名
    domid                          把一个域名或 UUID 转换为域 id
    domif-setlink                  设定虚拟接口的链接状态
    domiftune                      获取/设定虚拟接口参数
    domjobabort                    忽略活跃域任务
    domjobinfo                     域任务信息
    domname                        将域 id 或 UUID 转换为域名
    domrename                      rename a domain
    dompmsuspend                   使用电源管理功能挂起域
    dompmwakeup                    从 pmsuspended 状态唤醒域
    domuuid                        把一个域名或 id 转换为域 UUID
    domxml-from-native             将原始配置转换为域 XML
    domxml-to-native               将域 XML 转换为原始配置
    dump                           把一个域的内核 dump 到一个文件中以方便分析
    dumpxml                        XML 中的域信息
    edit                           编辑某个域的 XML 配置
    event                          Domain Events
    inject-nmi                     在虚拟机中输入 NMI
    iothreadinfo                   view domain IOThreads
    iothreadpin                    control domain IOThread affinity
    iothreadadd                    add an IOThread to the guest domain
    iothreaddel                    delete an IOThread from the guest domain
    send-key                       向虚拟机发送序列号
    send-process-signal            向进程发送信号
    lxc-enter-namespace            LXC 虚拟机进入名称空间
    managedsave                    管理域状态的保存
    managedsave-remove             删除域的管理保存
    managedsave-edit               edit XML for a domain's managed save state file
    managedsave-dumpxml            Domain information of managed save state file in XML
    managedsave-define             redefine the XML for a domain's managed save state file
    memtune                        获取或者数值内存参数
    perf                           Get or set perf event
    metadata                       show or set domain's custom XML metadata
    migrate                        将域迁移到另一个主机中
    migrate-setmaxdowntime         设定最大可耐受故障时间
    migrate-getmaxdowntime         get maximum tolerable downtime
    migrate-compcache              获取/设定压缩缓存大小
    migrate-setspeed               设定迁移带宽的最大值
    migrate-getspeed               获取最长迁移带宽
    migrate-postcopy               Switch running migration from pre-copy to post-copy
    numatune                       获取或者数值 numa 参数
    qemu-attach                    QEMU 附加
    qemu-monitor-command           QEMU 监控程序命令
    qemu-monitor-event             QEMU Monitor Events
    qemu-agent-command             QEMU 虚拟机代理命令
    reboot                         重新启动一个域
    reset                          重新设定域
    restore                        从一个存在一个文件中的状态恢复一个域
    resume                         重新恢复一个域
    save                           把一个域的状态保存到一个文件
    save-image-define              为域的保存状态文件重新定义 XML
    save-image-dumpxml             在 XML 中保存状态域信息
    save-image-edit                为域保存状态文件编辑 XML
    schedinfo                      显示/设置日程安排变量
    screenshot                     提取当前域控制台快照并保存到文件中
    set-lifecycle-action           change lifecycle actions
    set-user-password              set the user password inside the domain
    setmaxmem                      改变最大内存限制值
    setmem                         改变内存的分配
    setvcpus                       改变虚拟 CPU 的号
    shutdown                       关闭一个域
    start                          开始一个(以前定义的)非活跃的域
    suspend                        挂起一个域
    ttyconsole                     tty 控制台
    undefine                       取消定义一个域
    update-device                  从 XML 文件中关系设备
    vcpucount                      域 vcpu 计数
    vcpuinfo                       详细的域 vcpu 信息
    vcpupin                        控制或者查询域 vcpu 亲和性
    emulatorpin                    控制火车查询域模拟器亲和性
    vncdisplay                     vnc 显示
    guestvcpus                     query or modify state of vcpu in the guest (via agent)
    setvcpu                        attach/detach vcpu or groups of threads
    domblkthreshold                set the threshold for block-threshold event for a given block device or it's backing chain element

 Domain Monitoring (help keyword 'monitor'):
    domblkerror                    在块设备中显示错误
    domblkinfo                     域块设备大小信息
    domblklist                     列出所有域块
    domblkstat                     获得域设备块状态
    domcontrol                     域控制接口状态
    domif-getlink                  获取虚拟接口链接状态
    domifaddr                      Get network interfaces' addresses for a running domain
    domiflist                      列出所有域虚拟接口
    domifstat                      获得域网络接口状态
    dominfo                        域信息
    dommemstat                     获取域的内存统计
    domstate                       域状态
    domstats                       get statistics about one or multiple domains
    domtime                        domain time
    list                           列出域

 Host and Hypervisor (help keyword 'host'):
    allocpages                     Manipulate pages pool size
    capabilities                   性能
    cpu-baseline                   计算基线 CPU
    cpu-compare                    使用 XML 文件中描述的 CPU 与主机 CPU 进行对比
    cpu-models                     CPU models
    domcapabilities                domain capabilities
    freecell                       NUMA可用内存
    freepages                      NUMA free pages
    hostname                       打印管理程序主机名
    hypervisor-cpu-baseline        compute baseline CPU usable by a specific hypervisor
    hypervisor-cpu-compare         compare a CPU with the CPU created by a hypervisor on the host
    maxvcpus                       连接 vcpu 最大值
    node-memory-tune               获取或者设定节点内存参数
    nodecpumap                     节点 cpu 映射
    nodecpustats                   输出节点的 cpu 状统计数据。
    nodeinfo                       节点信息
    nodememstats                   输出节点的内存状统计数据。
    nodesuspend                    在给定时间段挂起主机节点
    sysinfo                        输出 hypervisor sysinfo
    uri                            打印管理程序典型的URI
    version                        显示版本

 Interface (help keyword 'interface'):
    iface-begin                    生成当前接口设置快照,可在今后用于提交 (iface-commit) 或者恢复 (iface-rollback)
    iface-bridge                   生成桥接设备并为其附加一个现有网络设备
    iface-commit                   提交 iface-begin 后的更改并释放恢复点
    iface-define                   define an inactive persistent physical host interface or modify an existing persistent one from an XML file
    iface-destroy                  删除物理主机接口(启用它请执行 "if-down")
    iface-dumpxml                  XML 中的接口信息
    iface-edit                     为物理主机界面编辑 XML 配置
    iface-list                     物理主机接口列表
    iface-mac                      将接口名称转换为接口 MAC 地址
    iface-name                     将接口 MAC 地址转换为接口名称
    iface-rollback                 恢复到之前保存的使用 iface-begin 生成的更改
    iface-start                    启动物理主机接口(启用它请执行 "if-up")
    iface-unbridge                 分离其辅助设备后取消定义桥接设备
    iface-undefine                 取消定义物理主机接口(从配置中删除)

 Network Filter (help keyword 'filter'):
    nwfilter-define                使用 XML 文件定义或者更新网络过滤器
    nwfilter-dumpxml               XML 中的网络过滤器信息
    nwfilter-edit                  为网络过滤器编辑 XML 配置
    nwfilter-list                  列出网络过滤器
    nwfilter-undefine              取消定义网络过滤器
    nwfilter-binding-create        create a network filter binding from an XML file
    nwfilter-binding-delete        delete a network filter binding
    nwfilter-binding-dumpxml       XML 中的网络过滤器信息
    nwfilter-binding-list          list network filter bindings

 Networking (help keyword 'network'):
    net-autostart                  自动开始网络
    net-create                     从一个 XML 文件创建一个网络
    net-define                     define an inactive persistent virtual network or modify an existing persistent one from an XML file
    net-destroy                    销毁(停止)网络
    net-dhcp-leases                print lease info for a given network
    net-dumpxml                    XML 中的网络信息
    net-edit                       为网络编辑 XML 配置
    net-event                      Network Events
    net-info                       网络信息
    net-list                       列出网络
    net-name                       把一个网络UUID 转换为网络名
    net-start                      开始一个(以前定义的)不活跃的网络
    net-undefine                   undefine a persistent network
    net-update                     更新现有网络配置的部分
    net-uuid                       把一个网络名转换为网络UUID

 Node Device (help keyword 'nodedev'):
    nodedev-create                 根据节点中的 XML 文件定义生成设备
    nodedev-destroy                销毁(停止)节点中的设备
    nodedev-detach                 将节点设备与其设备驱动程序分离
    nodedev-dumpxml                XML 中的节点设备详情
    nodedev-list                   这台主机中中的枚举设备
    nodedev-reattach               重新将节点设备附加到他的设备驱动程序中
    nodedev-reset                  重置节点设备
    nodedev-event                  Node Device Events

 Secret (help keyword 'secret'):
    secret-define                  定义或者修改 XML 中的 secret
    secret-dumpxml                 XML 中的 secret 属性
    secret-event                   Secret Events
    secret-get-value               secret 值输出
    secret-list                    列出 secret
    secret-set-value               设定 secret 值
    secret-undefine                取消定义 secret

 Snapshot (help keyword 'snapshot'):
    snapshot-create                使用 XML 生成快照
    snapshot-create-as             使用一组参数生成快照
    snapshot-current               获取或者设定当前快照
    snapshot-delete                删除域快照
    snapshot-dumpxml               为域快照转储 XML
    snapshot-edit                  编辑快照 XML
    snapshot-info                  快照信息
    snapshot-list                  为域列出快照
    snapshot-parent                获取快照的上级快照名称
    snapshot-revert                将域转换为快照

 Storage Pool (help keyword 'pool'):
    find-storage-pool-sources-as   找到潜在存储池源
    find-storage-pool-sources      发现潜在存储池源
    pool-autostart                 自动启动某个池
    pool-build                     建立池
    pool-create-as                 从一组变量中创建一个池
    pool-create                    从一个 XML 文件中创建一个池
    pool-define-as                 在一组变量中定义池
    pool-define                    define an inactive persistent storage pool or modify an existing persistent one from an XML file
    pool-delete                    删除池
    pool-destroy                   销毁(删除)池
    pool-dumpxml                   XML 中的池信息
    pool-edit                      为存储池编辑 XML 配置
    pool-info                      存储池信息
    pool-list                      列出池
    pool-name                      将池 UUID 转换为池名称
    pool-refresh                   刷新池
    pool-start                     启动一个(以前定义的)非活跃的池
    pool-undefine                  取消定义一个不活跃的池
    pool-uuid                      把一个池名称转换为池 UUID
    pool-event                     Storage Pool Events

 Storage Volume (help keyword 'volume'):
    vol-clone                      克隆卷。
    vol-create-as                  从一组变量中创建卷
    vol-create                     从一个 XML 文件创建一个卷
    vol-create-from                生成卷,使用另一个卷作为输入。
    vol-delete                     删除卷
    vol-download                   将卷内容下载到文件中
    vol-dumpxml                    XML 中的卷信息
    vol-info                       存储卷信息
    vol-key                        为给定密钥或者路径返回卷密钥
    vol-list                       列出卷
    vol-name                       为给定密钥或者路径返回卷名
    vol-path                       为给定密钥或者路径返回卷路径
    vol-pool                       为给定密钥或者路径返回存储池
    vol-resize                     创新定义卷大小
    vol-upload                     将文件内容上传到卷中
    vol-wipe                       擦除卷

 Virsh itself (help keyword 'virsh'):
    cd                             更改当前目录
    echo                           echo 参数
    exit                           退出这个非交互式终端
    help                           打印帮助
    pwd                            输出当前目录
    quit                           退出这个非交互式终端
    connect                        连接(重新连接)到 hypervisor

参考文献

Linux虚拟化入门(四)KVM 创建一个 Centos 实例

前提条件

  • 完整 KVM 环境
  • Centos 镜像

步骤

  • 第一步、创建磁盘
qemu-img create -f qcow2 centos_kvm1.qcow2 16G
  • 第二步、安装虚拟机
virt-install \
--virt-type=kvm \
--name=centos-kvm \
--hvm \
--vcpus=1 \
--memory=1024 \
--cdrom=/srv/kvm/CentOS-7-x86_64-Minimal-1810.iso \
--disk path=/srv/kvm/centos_kvm1.qcow2,size=16,format=qcow2 \
--graphics vnc,password=kvm,listen=::,port=5911 \
--network bridge=virbr0 \
--autostart \
--force

安装成功后使用任意一个可以访问KVM宿主机的带有桌面的设备上的 VNC viewer 进入 YourIp:5911 输入密码 kvm 就可以进入虚拟机,然后继续安装了。

  • 第三步、基础操作
virsh list --all           # 查看所有运行和没有运行的虚拟机
virsh list                 # 查看在运行的虚拟机
virsh dumpxml vm-name      # 查看kvm虚拟机配置文件
virsh start vm-name        # 启动kvm虚拟机
virsh shutdown vm-name     # 正常关机

virsh destroy vm-name      # 非正常关机,强制关闭虚拟机(相当于物理机直接拔掉电源)
virsh undefine vm-name     # 删除vm的配置文件

virsh define file-name.xml # 根据配置文件定义虚拟机
virsh suspend vm-name      # 挂起,终止
virsh resumed vm-name      # 恢复被挂起的虚拟机
virsh autostart vm-name    # 开机自启动vm
virsh console <虚拟机名称>   # 连接虚拟机

install 常用参数说明展开目录

–name指定虚拟机名称
–memory分配内存大小。
–vcpus分配CPU核心数,最大与实体机CPU核心数相同
–disk指定虚拟机镜像,size指定分配大小单位为G。
–network网络类型,此处用的是默认,一般用的应该是bridge桥接。
–accelerate加速
–cdrom指定安装镜像iso
–vnc启用VNC远程管理,一般安装系统都要启用。
–vncport指定VNC监控端口,默认端口为5900,端口不能重复。
–vnclisten指定VNC绑定IP,默认绑定127.0.0.1,这里改为0.0.0.0。
–os-type=linux,windows
–os-variant=rhel6

--name      指定虚拟机名称
--ram       虚拟机内存大小,以 MB 为单位
--vcpus     分配CPU核心数,最大与实体机CPU核心数相同
–-vnc       启用VNC远程管理,一般安装系统都要启用。
–-vncport   指定VNC监控端口,默认端口为5900,端口不能重复。
–-vnclisten  指定VNC绑定IP,默认绑定127.0.0.1,这里改为0.0.0.0。
--network   虚拟机网络配置
  # 其中子选项,bridge=br0 指定桥接网卡的名称。

–os-type=linux,windows
–os-variant=rhel7.2

--disk 指定虚拟机的磁盘存储位置
  # size,初始磁盘大小,以 GB 为单位。

--location 指定安装介质路径,如光盘镜像的文件路径。
--graphics 图形化显示配置
  # 全新安装虚拟机过程中可能会有很多交互操作,比如设置语言,初始化 root 密码等等。
  # graphics 选项的作用就是配置图形化的交互方式,可以使用 vnc(一种远程桌面软件)进行链接。
  # 我们这列使用命令行的方式安装,所以这里要设置为 none,但要通过 --extra-args 选项指定终端信息,
  # 这样才能将安装过程中的交互信息输出到当前控制台。
--extra-args 根据不同的安装方式设置不同的额外选项

参考文献

Linux虚拟化入门(三)Fedora 安装 KVM 管理环境

部署步骤

  • 第一步、检查环境要求

使用如下命令检查您的 CPU 是否支持虚拟化:

$ egrep '^flags.*(vmx|svm)' /proc/cpuinfo

如果没有任何输出,则说明您的系统不支持相关扩展功能。您仍然可以使用 QEMU/KVM ,但是虚拟将只能使用软件虚拟化(想当慢)。

  • 第二步、安装虚拟化软件包

当安装 Fedora 时,可以通过勾选安装基本组中的虚拟化组以安装虚拟化软件包。

在已经完成 Fedora 安装的系统中, QEMU、KVM和其他一些虚拟化工具的安装可以通过运行如下命令安装虚拟化组:

su -c "yum install @virtualization"

该命令将安装 qemu-kvm、 python-virtinst、 qemu、 virt-manager、 virt-viewer 以及所有需要的依赖软件包。

su -c "systemctl start libvirtd"

确认所有 kvm 内核模块已正常加载:

$ lsmod | grep kvm
kvm_amd                55563  0
kvm                   419458  1 kvm_amd

如果该命令没有列出 kvm_intel 或 kvm_amd, 则说明 KVM 没有正常配置。参看 确保系统正常使用 KVM 以获得解决问题的建议。

  • 第三步、使用虚拟机

您可以使用命令行工具 virsh 管理虚拟机。 你可以在命令行下使用 virsh 工具管理guest 。 virsh 工具是基于 libvirt 管理 API 实现的:

  • virsh 有一套稳定的命令,其语法与虚拟化平台无关。
  • virsh 可以作为仅有只读权限的工具使用(如:列出所有主机及其统计信息)。
  • virsh 可以管理 Xen,Qemu/KVM,esx 及其他一些类具有相同贵发后端下的主机。

一个有效地址可以使用 “-c” 参数传递给 virsh 来连接到远程 libvirtd 实例。详情请参看 http://libvirt.org/uri.html

如下命令启动虚拟机:

su -c "virsh create <name of virtual machine>"

要列出当前运行的虚拟机,执行:

su -c "virsh list"

列出所有虚拟机(不管是否运行):

su -c "virsh list --all"

正常关闭 guest :

su -c "virsh shutdown <virtual machine (name | id | uuid)>"

强制关闭 guest :

su -c "virsh destroy <virtual machine (name | id | uuid)>"

保存虚拟机快照到文件:

su -c "virsh save <virtual machine (name | id | uuid)> <filename>"

从快照恢复虚拟机:

su -c "virsh restore 
<filename>"

导出虚拟机配置文件:

su -c "virsh dumpxml <virtual machine (name | id | uuid)"

列出全部 virsh 可用命令:

su -c "virsh help"

也可以查看手册: man 1 virsh

参考文献

Linux虚拟化入门(一)Qemu,KVM,Virsh 概念指南

当你安装了一台Linux,想启动一个KVM虚拟机的时候,你会发现需要安装不同的软件,启动虚拟机的时候,有多种方法:

  • virsh start
  • kvm命令
  • qemu命令
  • qemu-kvm命令
  • qemu-system-x86_64命令

QEMU

首先看qemu,其中关键字emu,全称emulator,模拟器,所以单纯使用qemu是采用的完全虚拟化的模式。

Qemu向Guest OS模拟CPU,也模拟其他的硬件,GuestOS认为自己和硬件直接打交道,其实是同Qemu模拟出来的硬件打交道,Qemu将这些指令转译给真正的硬件。由于所有的指令都要从Qemu里面过一手,因而性能比较差。

完全虚拟化是非常慢的,所以要使用硬件辅助虚拟化技术Intel-VT,AMD-V,所以需要CPU硬件开启这个标志位,一般在BIOS里面设置。查看是否开启

# 对于Intel CPU 可用命令判断
grep "vmx" /proc/cpuinfo 

# 对于AMD CPU 可用命令判断
grep "svm" /proc/cpuinfo 

当确认开始了标志位之后,通过KVM,GuestOS的CPU指令不用经过Qemu转译,直接运行,大大提高了速度。

所以KVM在内核里面需要有一个模块,来设置当前CPU是Guest OS在用,还是Host OS在用。

KVM

基于内核的虚拟机(英语:Kernel-based Virtual Machine,缩写为KVM)是一种用于Linux内核中的虚拟化基础设施,可将Linux内核转化为一个虚拟机监视器。

KVM提供抽象的设备,但不模拟处理器。它开放了/dev/kvm接口,供用户模式的主机使用。

https://imagehost-cdn.frytea.com/images/2021/07/05/2021070511463525bc131220fa136e.png

qemu-kvm

Qemu将KVM整合进来,通过ioctl调用/dev/kvm接口,将有关CPU指令的部分交由内核模块来做,就是qemu-kvm (qemu-system-XXX)

qemu和kvm整合之后,CPU的性能问题解决了,另外Qemu还会模拟其他的硬件,如Network, Disk,同样全虚拟化的方式也会影响这些设备的性能。

于是qemu采取半虚拟化或者类虚拟化的方式,让Guest OS加载特殊的驱动来做这件事情。

例如网络需要加载virtio_net,存储需要加载virtio_blk,Guest需要安装这些半虚拟化驱动,GuestOS知道自己是虚拟机,所以数据直接发送给半虚拟化设备,经过特殊处理,例如排队,缓存,批量处理等性能优化方式,最终发送给真正的硬件,一定程度上提高了性能。

https://imagehost-cdn.frytea.com/images/2021/07/05/202107051150145c91ed5fd0d33987.png

virsh

然而直接用 qemu 或者 qemu-kvm 或者 qemu-system-xxx 的少,大多数还是通过 virsh 启动, virsh 属于 libvirt 工具, libvirt 是目前使用最为广泛的对 KVM 虚拟机进行管理的工具和 API ,可不止管理KVM。

Libvirt 分服务端和客户端, Libvirtd 是一个daemon进程,是服务端,可以被本地的virsh调用,也可以被远程的virsh调用,virsh相当于客户端。

Libvirtd调用 qemu-kvm 操作虚拟机,有关CPU虚拟化的部分,qemu-kvm调用 kvm 的内核模块来实现

https://imagehost-cdn.frytea.com/images/2021/07/05/202107051152022b49de7970473781.png

这下子,整个相互关系才搞清楚了。

参考文献

Linux虚拟化入门(二)Hyper-V 开启 KVM 嵌套虚拟化

日常办公使用 Windows 平台,需要研究 KVM 的使用,此时就需要在 Windows 提供的 Hyper-V 工具运行 Linux 虚拟机来测试 KVM 相关的使用,但是在 Hyper-V 虚拟机中再次运行 KVM 虚拟化属于嵌套虚拟化,需要开启相关功能。

下面给出 Hyper-V 开启嵌套虚拟化的方法,默认您已经创建出一个虚拟机实例,下面的操作在虚拟实例中进行。

  • 查看 Hyper-V 虚拟机是否支持虚拟化
egrep -o 'vmx|svm' /proc/cpuinfo

没有输出说明不支持,下面进行设置,在 Windows 宿主机进行:

  • 查看虚拟机参数

关闭虚拟机,管理员权限打开 Powershell

Get-VM  ##列出虚拟机
Get-VMProcessor -VMName [KVM主机] | fl
#查看虚拟化选项参数

# 示例,ExposeVirtualizationExtensions 为 false 说明不支持虚拟化
PS C:\Users\lenovo> Get-VMProcessor -VMName Fedora-Dev | fl
ResourcePoolName                             : Primordial
Count                                        : 2
CompatibilityForMigrationEnabled             : False
CompatibilityForOlderOperatingSystemsEnabled : False
HwThreadCountPerCore                         : 0
ExposeVirtualizationExtensions               : False
EnablePerfmonPmu                             : False
EnablePerfmonLbr                             : False
EnablePerfmonPebs                            : False
EnablePerfmonIpt                             : False
EnableLegacyApicMode                         : False
AllowACountMCount                            : False
Maximum                                      : 100
Reserve                                      : 0
RelativeWeight                               : 100
MaximumCountPerNumaNode                      : 12
MaximumCountPerNumaSocket                    : 1
EnableHostResourceProtection                 : False
OperationalStatus                            : {Ok, HostResourceProtectionDisabled}
StatusDescription                            : {确定, 主机资源保护已禁用。}
Name                                         : 处理器
Id                                           : Microsoft:369F6873-EDEE-4FCB-B154-E09A3095C743\b637f346-6a0e-4dec-af52-b
                                               d70cb80a21d\0
VMId                                         : 369f6873-edee-4fcb-b154-e09a3095c743
VMName                                       : Fedora-Dev
VMSnapshotId                                 : 00000000-0000-0000-0000-000000000000
VMSnapshotName                               :
CimSession                                   : CimSession: .
ComputerName                                 : MYIEUCD_DP
IsDeleted                                    : False
VMCheckpointId                               : 00000000-0000-0000-0000-000000000000
VMCheckpointName                             :
  • 开启嵌套虚拟化
Set-VMProcessor -ExposeVirtualizationExtensions $true -VMName [KVM主机]
##将其设置为True
# 重启虚拟机,查看已支持虚拟化
# 示例,ExposeVirtualizationExtensions 已经被设置为 true

PS C:\Users\lenovo> Get-VMProcessor -VMName Fedora-Dev | fl

ResourcePoolName                             : Primordial
Count                                        : 2
CompatibilityForMigrationEnabled             : False
CompatibilityForOlderOperatingSystemsEnabled : False
HwThreadCountPerCore                         : 0
ExposeVirtualizationExtensions               : True
EnablePerfmonPmu                             : False
EnablePerfmonLbr                             : False
EnablePerfmonPebs                            : False
EnablePerfmonIpt                             : False
EnableLegacyApicMode                         : False
AllowACountMCount                            : False
Maximum                                      : 100
Reserve                                      : 0
RelativeWeight                               : 100
MaximumCountPerNumaNode                      : 12
MaximumCountPerNumaSocket                    : 1
EnableHostResourceProtection                 : False
OperationalStatus                            : {}
StatusDescription                            : {}
Name                                         : 处理器
Id                                           : Microsoft:369F6873-EDEE-4FCB-B154-E09A3095C743\b637f346-6a0e-4dec-af52-b
                                               d70cb80a21d\0
VMId                                         : 369f6873-edee-4fcb-b154-e09a3095c743
VMName                                       : Fedora-Dev
VMSnapshotId                                 : 00000000-0000-0000-0000-000000000000
VMSnapshotName                               :
CimSession                                   : CimSession: .
ComputerName                                 : MYIEUCD_DP
IsDeleted                                    : False
VMCheckpointId                               : 00000000-0000-0000-0000-000000000000
VMCheckpointName                             :
# 虚拟机上查看,已经有多个VMX,有几个就意味着有几个CPU
$ egrep -o 'vmx|svm' /proc/cpuinfo
vmx
vmx
vmx
vmx

参考文献

Kvm 虚拟机安装 OnlyOffice

OnlyOffice

OnlyOffice 是一个开源的办公套件,由 Ascensio System SIA 公司开发,提供了在线文档编辑、文档管理、文档协作、邮件和项目管理等功能。(WikiPedia)

打开官网可以看到 OnlyOffice 提供企业版、高校版等多个收费版本,在这里我们安装开源免费的 社区版。社区版提供多种安装方式,在这里我们选择使用官方ONLYOFFICE Docker容器安装ONLYOFFICE社区版及其正常运行所需的全部依赖项。旨在最大程度的重现官方建议的环境节约时间。请注意,官方建议安装环境为 2C/6G/40GB

环境要求:

  • CPU dual core 2 GHz or better
  • RAM 6 GB or more
  • HDD at least 40 GB of free space
  • Additional requirements at least 6 GB of swap
  • OS amd64 Linux distribution with kernel version 3.10 or later

安装步骤

1、生成虚拟机并安装 ubuntu 18.04 server

虚拟磁盘:qemu-img create -f qcow2 ubuntu_onlyoffice.qcow2 50G

新建虚拟机:


virt-install \
--virt-type=kvm \
--name=ubuntu_onlyoffice \
--hvm \
--vcpus=4 \
--memory=6144 \
--cdrom=/srv/kvm/iso/ubuntu-18.04.3-live-server-amd64.iso \
--disk path=/srv/kvm/ubuntu_onlyoffice.qcow2,size=50,format=qcow2 \
--network network=default \
--graphics vnc,password=tianlun666,listen=::,port=5916 \
--autostart \
--force

2、配置 Docker 镜像

基于 Docker 的一键安装脚本需要拉取许多镜像,国内访问速度很慢,在这里配置一下阿里的 Docker 镜像加速

在阿里的容器服务控制台可以申请一个镜像地址,同时可以在控制台看到形如下列的命令:


sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://*******.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

运行安装即可。

3、运行官方提供的一键安装脚本(基于 Docker 安装)

首先下载脚本:

$ wget https://download.onlyoffice.com/install/opensource-install.sh

接下来二选一,如果需要配置邮件服务,可以是用自己为邮件准备好的域名替换命令中的域名并运行,如果不配置邮件服务就运行第二行命令。

$ bash opensource-install.sh -md "yourdomain.com"

$ bash opensource-install.sh -ims false

结束

参考文献

KVM虚拟机安装 GitLab EE

GitLab 是一个基于网页的软件开发生命周期管理工具,提供了 git 代码仓库、知识库、问题追踪、CI/CD 流水线功能,使用开源许可证,由 GitLab 公司开发(来自 WikiPedia)。

自建 GitLab 服务器基于以下原因:

  1. GitHub 到国内的访问速度实在堪忧
  2. 随着某种关系的变化发展,作者并不指望这种状况有什么改善
  3. Git 仓库速度缓慢会拖慢开发效率
  4. GitHub Pages 的访问速度慢,托管在此的网页对于国内用户实在太不友好
  5. GitLab 提供的 CI/CD 流水线功能比较完善
  6. GitLab 的文档很全面,英文说明还不错,值得探索
  7. 借此机会提高英文

自建方法还是基于 KVM 虚拟化一台 Ubuntu 虚拟机,并由这台虚拟机提供服务,外网由宿主机反向代理到内网实现外网访问。本文记录基于此上描述的环境及日后遇到的解决方案,本文带有一个持续更新区。

虚拟化

  1. 创建虚拟磁盘

    qemu-img create -f qcow2 ubuntu_gitlab.qcow2 100G
  2. 新建虚拟机

    
    virt-install \
    --virt-type=kvm \
    --name=ubuntu_gitlab \
    --hvm \
    --vcpus=4 \
    --memory=6144 \
    --cdrom=/srv/kvm/iso/ubuntu-18.04.3-live-server-amd64.iso \
    --disk path=/srv/kvm/ubuntu_gitlab.qcow2,size=100,format=qcow2 \
    --network network=default \
    --graphics vnc,password=tianlun666,listen=::,port=5914 \
    --autostart \
    --force

3. 安装

在安装的过程中,为虚拟机配置网络如下:

 - statics ip : `192.168.122.4`
 - GatWay: `192.168.122.1`
 - name server: `192.168.122.1`
 - Subnet: `192.168.122.0/24`

## 安装 GitLab

GitLab 官方提供的[安装说明](https://about.gitlab.com/install/)很全面,本文提取其中关键步骤,具体安装请以官网文档为准。

1. 安装并配置相关依赖

$ sudo apt-get update $ sudo apt-get install -y curl openssh-server ca-certificates


[tip type="info" title="安装Postfix (选装)"]
GitLab 支持 Postfix, SMTP 等方式实现邮件通知,使用SMTP请跳过此步,如果需要安装Postfix邮件请执行以下的命令:

sudo apt-get install -y postfix

安装过程中可能会出现一个全屏显示窗口,此时选择'Internet Site'并回车,使用你服务器的外部 DNS 作为'mail name'并回车,不出意外的话安装会继续进行。
[/tip]

2. 添加 GitLab 软件源

curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash


3. 运行 GitLab 安装

将代码中的网址替换为您为 GitLab 准备的地址,GitLab 会使用 Let's Encrypt 验证您的域名并自动为其配置 SSL。

sudo EXTERNAL_URL=”https://gitlab.example.com” apt-get install gitlab-ee


4. 设置默认 root 密码。

使用浏览器进入您上一步设置的地址,使用 `root` 账户登录,系统会提示您为其设置密码。

## 案例汇总(持续更新区)

### 为 GitLab 配置 SMTP 邮箱

1. 依次修改配置文件

vim /etc/gitlab/gitlab.rb

依次修改

gitlab_rails[‘smtp_enable’] = true gitlab_rails[‘smtp_address’] = “smtp.mxhichina.com” gitlab_rails[‘smtp_port’] = 25 gitlab_rails[‘smtp_user_name’] = “gitlab@yinnote.com” gitlab_rails[‘smtp_password’] = “xxxxxx” gitlab_rails[‘smtp_domain’] = “yinnote.com” gitlab_rails[‘smtp_authentication’] = “login” gitlab_rails[‘smtp_enable_starttls_auto’] = true gitlab_rails[‘smtp_tls’] = false

gitlab_rails[‘gitlab_email_from’] = “gitlab@yinnote.com” user[“git_user_email”] = “gitlab@yinnote.com”


2. 生效配置并重启 GitLab

$ gitlab-ctl reconfigure $ gitlab-ctl restart


3. 监听日志打印

如果出现问题,可以使用该命令在控制台监听日志。

$ gitlab-ctl tail


4. 可能用到的 VIM 命令

整页翻页 ctrl-f ctrl-b

f就是forword b就是backward

翻半页

ctrl-d ctlr-u d=down u=up

滚一行

ctrl-e ctrl-y

zz 让光标所在的行居屏幕中央 zt 让光标所在的行居屏幕最上一行 t=top zb 让光标所在的行居屏幕最下一行 b=bottom

查找

/string

向前(Forward)查找字符串string

按下回车后,光标就能跳到正确的地方


### 手动刷新 Let’s Encrypt 证书

sudo gitlab-ctl reconfigure sudo gitlab-ctl renew-le-certs



## 参考文献

 - [GitLab Installation](https://about.gitlab.com/install/)
 - [WikiPedia/GitLab](https://en.wikipedia.org/wiki/GitLab)
 - [VIM中的翻页命令](https://blog.csdn.net/nyist327/article/details/48625385)
 - [VIM 查找文本](https://blog.csdn.net/hitustc/article/details/5585101)
 - [GitLab邮件配置](https://www.jianshu.com/p/b91d2e676cba)
 - [Git:gitlab修改域名配置](https://blog.csdn.net/qq_36937234/article/details/89381857)
 - [SSL Configuration](https://docs.gitlab.com/omnibus/settings/ssl.html)
 - [使用SSL证书保护GitLab服务器的方法](https://ywnz.com/linuxyffq/3961.html)