libvirt 问题解决记录集

本篇记录在使用libvirt的时候遇到的一些情况,即解决方法.

出现Permission denied

[root@localhost vm]# virsh start centos
error: Failed to start domain centos
error: internal error process exited while connecting to monitor: qemu-system-x86_64: -drive file=/home/d/shixun/vm/vdisk.img,if=none,id=drive-ide0-0-0,format=qcow2: could not open disk image /home/d/shixun/vm/vdisk.img: Permission denied

解决:/etc/libvirt/qemu.conf

# Some examples of valid values are:
#
# user = "qemu" # A user named "qemu"
# user = "+0" # Super user (uid=0)
# user = "100" # A user named "100" or a user with uid=100
#
user = "root"
 
# The group for QEMU processes run by the system instance. It can be
# specified in a similar way to user.
group = "root"
 
# Whether libvirt should dynamically change file ownership
# to match the configured user/group above. Defaults to 1.
# Set to 0 to disable file ownership changes.
dynamic_ownership = 0

我的这个没能解决问题

/etc/init.d/libvirtd restart 不管用

libvirt遇到死锁

在使用virsh对虚拟机进行动态迁移时,虚拟机没有开启.于是就去开启虚拟机,但是却发现无法运行.

Error starting domain: Timed out during operation: cannot acquire state change lock
 
libvirtError: Timed out during operation: cannot acquire state change lock

我进行了以下的步骤

virsh undefine centos
 
####Login as a root user and kill the libvirtd.
 
killall -9 libvirtd
 
rm /var/run/libvirtd.pid   ##### Remove the libvirtd pid file.
 
/etc/init.d/libvirtd  restart   #### Restart libvirtd.

在尝试上面的操作后,不起作用.

修改一下的domain的xml文件中的name标签,给虚拟机改个名字,期间我还删除了domain的xml文件里的qemu:commandline里面信息,重新define后就可以运行了.

迁移需要FQDN

在进行迁移的时候使用命令 virsh migrate –live ubuntu qemu+ssh://dest_ip/system

1
error: internal hostname on destination resolved to localhost, but migration requires an FQDN

解决:ssh进入到dest host机器,运行hostname domain 就好了.其中domian是一个域名.将接下来就可以进行迁移了.

启动nfs出现问题

启动NFS服务,出现问题

Failed to issue method call: Unit nfsserver.service failed to load: No such file or directory. See system logs and 'systemctl status nfsserver.service' for details.

解决:按照教程来就好了

# vim /etc/exports
/var/lib/libvirt/images *.example.com(rw,no_root_squash,sync)
 
# service nfs start
 
# mount -t nfs storage_host:/var/lib/libvirt/images /var/lib/libvirt/images

挂载nfs出现问题

当去mount远程的nfs服务器,出现问题了.

mount.nfs: access denied by server while mounting 211.87.***.53:/mnt/nfs

解决:原来发现,在/etc/exports中,没有将/mnt/nfs添加进去,只能将里面有的文件夹才能mount到本机来.

可以先umoutn /mnt/nfs ,然后在mount看看能否成功

virsh关闭虚拟机

virsh shutdown 关闭虚拟机没起作用

解决:需要在虚拟机里安装一些东西

yum install acpid
chkconfig acpid on
service acpid start

virsh无法使用ssh协议访问远程机器

当我去远程查询信息的时候,virsh -c qemu+ssh://211.87.***.88/system list 的时候出现了下面的问题.

error: failed to connect to the hypervisor
error: no valid connection
error: End of file while reading data: : Input/output error

查看日志 cat /var/log/secure | grep sshd 发现是我这里主动发出断开的.难道是检测到libvirtd有些问题导致的?

当时使用virt-manage可以查询到远程的信息.估计是sshd出现的问题把.

解决:我没有重新安装sshd,我是通过tcp协议进行迁移的,因为是做测试,所以没有考虑安全的问题.具体使用tcp进行迁移,查看virsh使用qemu+tcp访问远程libvirtd .

virsh对kvm虚拟机进行动态迁移

迁移的类型:静态迁移和动态迁移

迁移虚拟机的方式有两种:一种是动态迁移,一种是静态迁移。

静态迁移

静态迁移:也叫做常规迁移、离线迁移(Offline Migration)。就是在虚拟机关机暂停的情况下从一台物理机迁移到另一台物理机。因为虚拟机的文件系统建立在虚拟机镜像上面,所以在虚拟机关机的情况下,只需要简单的迁移虚拟机镜像和相应的配置文件到另外一台物理主机上;如果需要保存虚拟机迁移之前的状态,在迁移之前将虚拟机暂停,然后拷贝状态至目的主机,最后在目的主机重建虚拟机状态,恢复执行。这种方式的迁移过程需要显式的停止虚拟机的运行。从用户角度看,有明确的一段停机时间,虚拟机上的服务不可用。这种迁移方式简单易行,适用于对服务可用性要求不严格的场合。

动态迁移

动态迁移(Live Migration):也叫在线迁移(Online Migration)。就是在保证虚拟机上服务正常运行的同时,将一个虚拟机系统从一个物理主机移动到另一个物理主机的过程。该过程不会对最终用户造成明显的影响,从而使得管理员能够在不影响用户正常使用的情况下,对物理服务器进行离线维修或者升级。与静态迁移不同的是,为了保证迁移过程中虚拟机服务的可用,迁移过程仅有非常短暂的停机时间。迁移的前面阶段,服务在源主机的虚拟机上运行,当迁移进行到一定阶段,目的主机已经具备了运行虚拟机系统的必须资源,经过一个非常短暂的切换,源主机将控制权转移到目的主机,虚拟机系统在目的主机上继续运行。对于虚拟机服务本身而言,由于切换的时间非常短暂,用户感觉不到服务的中断,因而迁移过程对用户是透明的。动态迁移适用于对虚拟机服务可用性要求很高的场合。

动态迁移需要将原有的虚拟机镜像放在采用 SAN(storage area network)或 NAS(network-attached storage)之类的集中式共享外存设备,这样迁移的时候,不是迁移整个硬盘镜象,而是迁移内存的信息.所以迁移起来,速度比较快,停顿时间少。

动态迁移实际上是把虚拟机的配置封装在一个文件中,然后通过高速网络,把虚拟机配置和内存运行状态从一台物理机迅速传送到另外一台物理机上,期间虚拟机一直保持运行状态。现有技术条件下,大多虚拟机软件如 VMware、Hyper-V、Xen 进行动态迁移都需要共享存储的支持。典型的共享存储包括 NFS 和 SMB/CIFS 协议的网络文件系统,或者通过 iSCSI 连接到 SAN 网络。选用哪一种网络文件系统,需要根据具体情况而定。本文的实验采用了 NFS 文件系统作为源主机和目的主机之间的共享存储。

使用nfs网络文件系统

source host和dest host都通过网卡和NFS server进行连接,所有的VM镜像都在NFS Server上,然后将VM1从source host迁移到dest host上,通过网络来进行迁移,所以网络的速度决定这迁移的快慢.

nfs配置

配置NFS清单
修改文件vi /etc/exports
/home/images *(rw,sync,no_root_squash)
/home/images 为要共享的文件夹
  rw:可读写的权限;
  ro:只读的权限;
  no_root_squash:登入到 NFS 主机的用户如果是 ROOT 用户,他就拥有 ROOT 权限,此参数很不安全,建议不要使用。
  sync:资料同步写入存储器中。
  async:资料会先暂时存放在内存中,不会直接写入硬盘。
 
重新启动 nfsserver 服务
  # service nfsserver restart 或者 systemctl restart nfs-server.service
 
查看状态
systemctl status nfs-server.service   ####查看nfs-server状态
exportfs -av   #####如果显示出共享的文件夹的话,说明设置已经生效
 
挂载NFS-server共享网络存储到本机
mount -t  nfs  nfs-server-host:/home/images /mnt/nfs
将NFS-server上的存储器/home/images/ 挂载到本地的/mnt/nfs 文件夹下。

nfs可能遇到的问题

如果在挂载nfs的时候,按tab键无法补全路径.

那么是防火墙的原因,把它添加到允许的规则里。设置selinux

1
setsebool -P virt_use_nfs 1

权限拒绝

error: Failed to start domain vdisk
error: internal error Process exited while reading console log output: char device redirected to /dev/pts/1
qemu-system-x86_64: -drive file=/mnt/nfs/vdisk.img,if=none,id=drive-ide0-0-0,format=qcow2: could not open disk image /mnt/nfs/vdisk.img: Permission denied

设置/mnt/nfs/vdisk.img的权限

注意问题

一定要注意所要共享的目录的权限问题,包括它的父目录,这些目录的权限要一样,否则mount的时候会出错

另外这个镜像最好都方在一个位置下。比如将nfs挂载到source host在/mnt/nfs,那么最好也在dest host的/mnt/nfs,路径一样,这样将来迁移就比较方便了。增加迁移的成功率。而且使用virt-manage必须路径一直,virsh可以要求不一致。只好还是source host和dest host在相同的路径挂载NFS网络共享系统

进行迁移

启动虚拟机

在 source host主机启动虚拟机

virsh start vdisk    ####vdisk 为虚拟机的名字

查看虚拟机状态

# virsh list --all
  Id    名称                            状态
----------------------------------------------------
  1     centos                         running
  2     vdisk                          running

进行迁移

1
virsh migrate vdisk --live qemu+ssh://des_ip:/system --unsafe

稍微过一会儿,我们就可以在dest host目标主机上看到虚拟机正在运行了。

参考资料

使用virsh创建虚拟机

第一步,先新建一块虚拟硬盘,这个不多说了,看之前的资料有

第二步,新建一个xml文件,里面存放虚拟机的配置信息,有内存、cpu、硬盘位置、光驱、VNC等配置,我们先贴出一个demo,下载该文件

< domain type = "kvm" >
     < name >centos</ name <!--虚拟机名称-->
     < memory unit = "MiB" >1024</ memory >   <!--最大内存,单位k-->
     < currentMemory unit = "MiB" >1024</ currentMemory <!--可用内存,单位k-->
     < vcpu >2</ vcpu >   <!--//虚拟cpu个数-->
     < os >
         < type arch = "x86_64" machine = "pc" >hvm</ type >
         < boot dev = "hd" /> <!-- 硬盘启动 -->
         < boot dev = "cdrom" />     <!--//光盘启动-->
     </ os >
     < features >
         < acpi />
         < apic />
         < pae />
     </ features >
     < clock offset = "localtime" />
     < on_poweroff >destroy</ on_poweroff >
     < on_reboot >restart</ on_reboot >
     < on_crash >destroy</ on_crash >
     < devices >
         < emulator >/usr/libexec/qemu-kvm</ emulator >
         < disk type = "file" device = "disk" >
             < driver name = "qemu" type = "qcow2" />
             < source file = "/var/lib/libvirt/images/test.qcow2" />        <!--目的镜像路径-->
             < target dev = "hda" bus = "ide" />
         </ disk >
         < disk type = "file" device = "cdrom" >
             < source file = "/var/lib/libvirt/images/ubuntu.iso" />        <!--光盘镜像路径 -->
             < target dev = "hdb" bus = "ide" />
         </ disk >
         < interface type = "bridge" >       <!--虚拟机网络连接方式-->
             < source bridge = "br0" />      <!--当前主机网桥的名称-->
         </ interface >
         < input type = "mouse" bus = "ps2" />
         <!--vnc方式登录,端口号自动分配,自动加1,可以通过virsh vncdisplay来查询-->
         < graphics type = "vnc" port = "-1" autoport = "yes" listen = "0.0.0.0" keymap = "en-us" />
     </ devices >
</ domain >

创建虚拟机

virsh define centos.xml   ###将配置导入到虚拟机
virsh start centos    #### 启动虚拟机

qemu和libvirt在pci pass through下动态迁移失败

最近的项目是虚拟机在PCI pass-through下做动态迁移,目前还没有一个好的解决方案,需要我们小组去讨论并提出一个解决方案来.既然是要做PCI pass through下做动态迁移的解决方案,那就得要先在这样的情况下做个迁移,看看到底会出现什么样的问题.

方案一:使用virsh进行动态迁移

在前面的文章中,我们进行的虚拟机的pci pass through,如果对pci pass through有不会的地方,参考之前的文章来进行设置.

接下来,我们要进行虚拟机的动态迁移.使用virsh启动虚拟机,运行以下命令进行迁移

virsh migrate --live vdisk qemu+ssh://211.87.***.45/system --unsafe

运行完后会出现问题.报了一个这样的错误.

error: Requested operation is not valid: domain has assigned non-USB host devices

上面的错误说明,现在的请求操作的非法的,因为虚拟机使用了非USB设备.

可以看到libvirt的源码 src/qemu/qemu_migration.c

/* Validate whether the domain is safe to migrate.  If vm is NULL,
  * then this is being run in the v2 Prepare stage on the destination
  * (where we only have the target xml); if vm is provided, then this
  * is being run in either v2 Perform or v3 Begin (where we also have
  * access to all of the domain's metadata, such as whether it is
  * marked autodestroy or has snapshots).  While it would be nice to
  * assume that checking on source is sufficient to prevent ever
  * talking to the destination in the first place, we are stuck with
  * the fact that older servers did not do checks on the source. */
bool
qemuMigrationIsAllowed(virQEMUDriverPtr driver, virDomainObjPtr vm,
                        virDomainDefPtr def, bool remote, bool abort_on_error)
{
     int nsnapshots;
     int pauseReason;
     bool forbid;
     size_t i;
 
     if (vm) {
         if (qemuProcessAutoDestroyActive(driver, vm)) {
             virReportError(VIR_ERR_OPERATION_INVALID,
                            &amp;quot;%s&amp;quot;, _(&amp;quot;domain is marked for auto destroy&amp;quot;));
             return false ;
         }
 
         /* perform these checks only when migrating to remote hosts */
         if (remote) {
             nsnapshots = virDomainSnapshotObjListNum(vm-&amp;gt;snapshots, NULL, 0);
             if (nsnapshots &amp;lt; 0)
                 return false ;
 
             if (nsnapshots &amp;gt; 0) {
                 virReportError(VIR_ERR_OPERATION_INVALID,
                                _(&amp;quot;cannot migrate domain with %d snapshots&amp;quot;),
                                nsnapshots);
                 return false ;
             }
 
             /* cancel migration if disk I/O error is emitted while migrating */
             if (abort_on_error &amp;amp;&amp;amp;
                 virDomainObjGetState(vm, &amp;amp;pauseReason) == VIR_DOMAIN_PAUSED &amp;amp;&amp;amp;
                 pauseReason == VIR_DOMAIN_PAUSED_IOERROR) {
                 virReportError(VIR_ERR_OPERATION_INVALID, &amp;quot;%s&amp;quot;,
                                _(&amp;quot;cannot migrate domain with I/O error&amp;quot;));
                 return false ;
             }
 
         }
 
         if (virDomainHasDiskMirror(vm)) {
             virReportError(VIR_ERR_OPERATION_INVALID, &amp;quot;%s&amp;quot;,
                            _(&amp;quot;domain has an active block job&amp;quot;));
             return false ;
         }
 
         def = vm-&amp;gt;def;
     }
 
     /* Migration with USB host devices is allowed, all other devices are
      * forbidden.
      */
     forbid = false ;
     for (i = 0; i &amp;lt; def-&amp;gt;nhostdevs; i++) {
         virDomainHostdevDefPtr hostdev = def-&amp;gt;hostdevs[i];
         if (hostdev-&amp;gt;mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
             hostdev-&amp;gt;source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
             forbid = true ;
             break ;
         }
     }
     if (forbid) {
         virReportError(VIR_ERR_OPERATION_INVALID, &amp;quot;%s&amp;quot;,
                        _(&amp;quot;domain has assigned non-USB host devices&amp;quot;));
         return false ;
     }
 
     return true ;
}

可以看到源码上显示,只要检测到虚拟机上使用了非USB的PCI设备,直接将borbid设置为true,禁止虚拟机的迁移.

如果要添加可以迁移的PCI设备,那么需要在这里进行相应的修改.

方案二:使用qemu进行动态迁移

在source host运行以下命令以启动虚拟机

/usr/bin/qemu-kvm -name vdisk - enable -kvm -m 512 -smp 2
-hda /mnt/nfs/vdisk .img
-monitor stdio
-vnc 0.0.0.0:0
-device pci-assign,host=00:1b.0

在dest host上运行以下命令,等待虚拟机的迁移:

/usr/bin/qemu-kvm -name vdisk -enable-kvm -m 512 -smp 2
-hda /mnt/nfs/vdisk.img
-monitor stdio
-vnc 0.0.0.0:0
-incoming tcp:0.0.0.0:8888
  • 参数-incoming tcp:0.0.0.0:8888说明的是,虚拟机在监听tcp的8888端口,等待源虚拟机被迁移过来.

此时虚拟机的状态是inmigration状态,等待被迁移过来.

在source host上输入以下命令,进行虚拟机的迁移.

(qemu) migrate -d -b tcp:dest_ip:8888
 
  -d 可以在迁移的过程中查询迁移状态,否则只能在迁移结束后查询。
  -b 迁移虚拟机存储文件
  tcp:dest_ip:8888 数据迁移的协议、目的主机和端口。协议和端口必须和目的主机上虚拟机的 -incoming 参数一致。

运行这个命令后,出报这样的错误

1
2
(qemu) migrate -d tcp:dest_ip:8888
migrate: State blocked by non-migratable device '0000:00:04.0/pci-assign'

应该也是进行了检查,使用了一个不可迁移设备即pci pass through的设备.

通过libvirt和qemu的动态迁移出先的问题,基本可以定位到相应的代码,那么只要提供一种相应的解决方案,那么就可以迁移其他的设备了.

查看virsh管理qemu虚拟机日志信息和libvirtd日志信息

在软件的开发和运行过程中,程序的运行日志log是不能少的。我们可以从log中发现程序的运行状态,以及可能出先的一些error.通常运行日志log上有时间、信息级别(level)、消息输出(message)。通过分析日志,我们可以从中获得程序的运行信息,方便我们的调试、恢复等.

在libvirt程序管理VM虚拟机时,也会有日志的记录.既有libvirtd的后台程序的日志,也有vm日志.接下来,我们设置一些日志的信息.

libvirtd的运行日志

在运行libvirtd的时候,我们需要获得lbivirtd的运行信息.所以我们需要找到他的日志文件.一般情况下,它是在/var/log/libvirt/libvirtd.log路径下.

可能在这个目录下没有发现这个的日志文件.那么就要配置一些libvitd的参数了.

编辑文件/etc/libvirt/libvirtd.conf

#将日志级别设置为 1(调试)
log_level = 1
#指定日志输出文件名称
log_outputs="1:file:/var/log/libvirt/libvirtd.log"
注意 :libvirtd. 日志文件可能会飞速增长。
用户应配置logrotate ,否则您的 /var 文件系统最后会装满内容。

以上的日志的输出级别为debug级别.这个级别的日志是最多的,一般只在开发以及测试的时候使用.而在生产运行环境中,日志的级别为info,warn,error,fatal. 以下前面的数字为他们的level.

  • 1 debug
  • 2 info
  • 3 warn
  • 4 error
  • 5 fatal

重启libvirtd 。虚拟机Guest操作系统正在运行时可重启 libvirtd。

# /etc/init.d/libvirtd restart

如果在目录下还是没发现日志文件,那么你可能需要使用一下命令来运行libvirtd.

libvirtd --daemon --listen --config /etc/libvirt/libvirtd .conf

查看virsh管理qemu虚拟机的信息

在使用virsh管理qemu的虚拟机时候,也是有日志的输出的.

具体的log在目录/var/log/livirt/qemu/ 下面,具体的log文件名与虚拟机名字关联.

virsh使用qemu+tcp访问远程libvirtd

因为ssh的不能访问 所以使用tcp进行对远程libvirtd进行连接访问,例如

virsh -c qemu+tcp://example.com/system

修改文件vim /etc/sysconfig/libvirtd,用来启用tcp的端口

LIBVIRTD_CONFIG=/etc/libvirt/libvirtd.conf
 
LIBVIRTD_ARGS="--listen"

修改文件vim /etc/libvirt/libvirtd.conf

listen_tls = 0
 
listen_tcp = 1
 
tcp_port = "16509"
 
listen_addr = "0.0.0.0"
 
auth_tcp = "none"

运行 libvirtd

1
service libvirtd restart

如果没起效果(我的就没有生效 :( ),那么使用命令行:

libvirtd --daemon --listen --config /etc/libvirt/libvirtd .conf

查看运行进程

[root@ddd run]# ps aux | grep libvirtd
root 16563 1.5 0.1 925880 7056 ? Sl 16:01 0:28 libvirtd -d -l --config /etc/libvirt/libvirtd.conf

查看端口

[root@ddd run]# netstat -apn | grep tcp
tcp        0      0 0.0.0.0:16509           0.0.0.0:*               LISTEN      13971/libvirtd

在source host连接dest host远程libvirtd查看信息

virsh -c qemu+tcp://211.87.***.97/system
 
Welcome to virsh, the virtualization interactive terminal.
 
Type: 'help' for help with commands
'quit' to quit

成功使用tcp去访问libvirtd。

使用libvirt和qemu将pci pass through设备添加到虚拟机上

透传的优势

guest使用透传设备可以获得设备近乎原生的性能,

PCI pass-throught设备给动态迁移带来的问题,
dest host可能没有同样的硬件.

就算可以模拟一个设备,但是原始设备的内部状态不能获得.

VT-d support

In order to assign devices in KVM, you’ll need a system which supports VT-d. This has nothing to do with the VT-x support of your CPU, VT-d needs to be supported by both your chipset on your motherboard and by your CPU.

If you are in doubt whether your motherboard or CPU supports VT-d or not, the Xen VT-d wikipage has some pointers of VT-d enabled chipsets, motherboards and CPUs:

http://wiki.xensource.com/xenwiki/VTdHowTo

If your hardware doesn’t have an IOMMU (“Intel VT-d” support in case of Intel – “AMD I/O Virtualization Technology” support in case of AMD), you’ll not be able to assign devices in KVM. Some work towards allowing this were done, but the code never made it into KVM, due to various issues with the code. At the moment it doesn’t seem like device assignment without hardware support, will ever be integrated into KVM.

Assignment of graphics cards are not officially supported at the moment, but there has been some success passing through a secondary Radeon HD 5850 as a VM’s secondary display.

资料:http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM

在host选择PCI pass through设备

reboot and verify that your system has IOMMU support

AMD Machine

dmesg | grep AMD-Vi
  ...
  AMD-Vi: Enabling IOMMU at 0000:00:00.2 cap 0x40
  AMD-Vi: Lazy IO/TLB flushing enabled
  AMD-Vi: Initialized for Passthrough Mode
  ...

Intel Machine

dmesg | grep -e DMAR -e IOMMU
...
DMAR:DRHD base: 0x000000feb03000 flags: 0x0
IOMMU feb03000: ver 1:0 cap c9008020e30260 ecap 1000
...

If you get no output you’ll need to fix this before moving on. Check if your hardware supports VT-d and check that it has been enabled in BIOS.

NOTE: If you still get an error “No IOMMU found.” Check dmesg for errors suggesting your BIOS is broken. Another possible reason: CONFIG_DMAR_DEFAULT_ON is not set. In that case, pass “intel_iommu=on” as kernel parameter to enable it. AMD uses different kernel parameter than Intel, on AMD you need to pass “iommu=pt iommu=1″.

请看附录:No IOMMU found 解决

选择要使用的透传设备

# lspci -nn
00:00.0 Host bridge [0600]: Intel Corporation 2nd Generation Core Processor Family DRAM Controller [8086:0100] (rev 09)
......
00:1b.0 Audio device [0403]: Intel Corporation 6 Series/C200 Series Chipset Family High Definition Audio Controller [8086:1c20] (rev 04)
.....
00:1f.3 SMBus [0c05]: Intel Corporation 6 Series/C200 Series Chipset Family SMBus Controller [8086:1c22] (rev 04)

友情提示:使用透传设备时,拿USB控制器作实验,可能鼠标键盘不能使用.请谨慎.

将设备从宿主机上解除绑定

使用echo命令,将设备从host机器上解除绑定,将来用于guest机器. For example:

echo "8086 1c20" > /sys/bus/pci/drivers/pci-stub/new_id
echo 0000:00:1b.0 > /sys/bus/pci/devices/0000:00:1b.0/driver/unbind
echo 0000:00:1b.0 > /sys/bus/pci/drivers/pci-stub/bind

关闭虚拟机

关闭虚拟机,修改配置文件.

使用libvirt进行pci pass through

修改虚拟机配置文件

<devices>

    <hostdev mode=’subsystem’ type=’pci’ managed=’yes’>
      <source>
         <address domain=’0×000′ bus=’0×00′ slot=’0x1b’ function=’0×0′/>
      </source>
   </hostdev>
</devices>

在修改完虚拟机配置文件后,运行虚拟机.

使用qemu进行pci pass through

使用qemu进行pci pass through也是一样的简单的.我们需要一个已经创建好操作系统的虚拟机.

在qemu命令行运行以下命令以启动虚拟机

/usr/bin/qemu-kvm -name vdisk - enable -kvm -m 512 -smp 2 \
-hda /mnt/nfs/vdisk .img \
-monitor stdio \
-vnc 0.0.0.0:0 \
-device pci-assign,host=00:1b.0

这样就将设备挂载到虚拟机上了.

  • 参数-device pci-assign,host=00:1b.0说的是使用一个pci设备,并提供一个设备的地址.
  • 参数-monitor stdio是使用一个标准的控制台输出.在命令行中进行输入命令,等等迁移的时候也在这里输入命令.

附录1:No IOMMU found 解决

启动虚拟机的时候出现了iommu的问题.以供大家参考

1.查看错误日志说明

在配置好XML文件后,启动虚拟机,遇到一个问题.

error: Failed to start domain vdisk
error: Unable to read from monitor: Connection reset by peer

查看虚拟机日志( cat /var/log/libvirt/qemu/vdisk.log )信息.

char device redirected to /dev/pts/3
No IOMMU found. Unable to assign device "hostdev0"
qemu-system-x86_64: -device pci-assign,configfd=20,host=00:1b.0,id=hostdev0,bus=pci.0,addr=0x4: Device 'pci-assign' could not be initialized
2013-07-08 06:41:23.256+0000: shutting down

上网查阅资料,说是要在BIOS上设置虚拟化,然后在引导程序里也要设置iommu.可以查看一下自己的电脑信息是否开启了.

2.查看信息gurb的引导信息

# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.9.6-200.fc18.x86_64 root=/dev/mapper/fedora-home ro rd.lvm.lv=fedora/swap rd.md=0 rd.dm=0 rd.lvm.lv=fedora/home rd.luks=0 vconsole.keymap=us rhgb quiet LANG=en_US.UTF-8

可以发现,我的还未开启intel_iommu=on选项.所以接下来我们来激活它.

3.激活intel_iommu=on

Activate Intel VT-d in the kernel

Activate Intel VT-d in the kernel by appending the intel_iommu=on parameter to the kernel line of the kernel line in the/boot/grub/grub.conf file.
The example below is a modified grub.conf file with Intel VT-d activated.

对于intel的cpu和amd的cpu,在grub配置上是不同的,具体的配置请参考文章:http://pve.proxmox.com/wiki/Pci_passthrough

4.更新grub

在编辑完grub文件后,需要更新

grub2-mkconfig   # fedora arch centos
update-grub            # ubuntu debian

5.重启电脑,使其生效

# cat /proc/cmdline
BOOT_IMAGE=/vmlinuz-3.9.6-200.fc18.x86_64 root=/dev/mapper/fedora-home ro rd.lvm.lv=fedora/swap rd.md=0 rd.dm=0 rd.lvm.lv=fedora/home rd.luks=0 vconsole.keymap=us rhgb quiet intel_iommu=on LANG=en_US.UTF-8

发现开机已经启动了inte _iommu=on了.再次启动虚拟机已经就不会出现这个bug了.

附录2 PCI pass through 失败要关闭SELinux

我用的是Fedora 18 ,将SELinux给disalbed了,但是发现PCI pass through出先问题.上网看到文章PCI passthrough fails in qemu-kvm unless selinux is disabled 说,要将selinux设置成permissive模式,于是这个问题解决了.

参考资料

qemu命令行参数转libvirt的xml文件

运行虚拟机有多种方式,其中可以使用qemu-system-x86或者qemu-kvm来运行虚拟。另外还可以使用libvirt的virsh命令从xml文件定义来运行虚拟机。而网上许多的教程或者示例使用qemu命令参数来运行的,如果使用libvirt来运行可能会无从下手,不知道对应的参数。

libvirt从Domain的配置文件启动虚拟机

虚拟机(有时Domain,guest都表示虚拟机的含义。)可以通过Domain xml文件来进行配置虚拟机。以下是一个demo的配置文件。

< domain type = 'qemu' >
   < name >QEmu-fedora-i686</ name >
   < uuid >c7a5fdbd-cdaf-9455-926a-d65c16db1809</ uuid >
   < memory >219200</ memory >
   < currentMemory >219200</ currentMemory >
   < vcpu >2</ vcpu >
   < os >
     < type arch = 'i686' machine = 'pc' >hvm</ type >
     < boot dev = 'cdrom' />
   </ os >
   < devices >
     < emulator >/usr/bin/qemu-system-x86_64</ emulator >
     < disk type = 'file' device = 'cdrom' >
       < source file = '/home/user/boot.iso' />
       < target dev = 'hdc' />
       < readonly />
     </ disk >
     < disk type = 'file' device = 'disk' >
       < source file = '/home/user/fedora.img' />
 
< target dev = 'hda' />
     </ disk >
     < interface type = 'network' >
       < source network = 'default' />
     </ interface >
     < graphics type = 'vnc' port = '-1' />
   </ devices >
</ domain >

然后使用以下命令从xml文件导入到libvirt虚拟机管理软件中。

virsh define demo.xml

启动虚拟机。其中QEmu-fedora-i686为xml定义的虚拟机别名。

virsh start QEmu-fedora-i686

使用qemu命令行启动虚拟机

也可以使用qemu命令行启动虚拟机,但是每次都要输入那么长的参数会忘记,所以也可以写个shell脚本来保存参数。类似的,qemu启动虚拟机使用如下命令:

/usr/bin/qemu-system-x86_64 --enable-kvm \
   -m 1024 \
   -smp 1 \
   -name QEMUGuest1 \
   -nographic \
   -monitor pty -no-acpi -boot c \
   -drive  file=/dev/HostVG/QEMUGuest1,if=ide,index=0 \
   -net none \
   -serial none -parallel none -usb

看到这样长的参数,输入起来当然麻烦了。而且这样运行虚拟机libvirt还没办法直接查询虚拟机的状态。

在xml文件里使用任意qemu命令行参数

有些命令行的参数可以使用xml直接定义出来,但是有些并不能找到对应的xml的标签。但是libvrit为我们提供了一个qemu:commandline来配置xml文件。

例如:

< domain type = 'qemu' xmlns:qemu = 'http://libvirt.org/schemas/domain/qemu/1.0' >
   < name >QEmu-fedora-i686</ name >
   < memory >219200</ memory >
   < os >
     < type arch = 'i686' machine = 'pc' >hvm</ type >
   </ os >
   < devices >
     < emulator >/usr/bin/qemu-system-x86_64</ emulator >
   </ devices >
   < qemu:commandline >
     < qemu:arg value = '-newarg' />
     < qemu:env name = 'QEMU_ENV' value = 'VAL' />
   </ qemu:commandline >
</ domain >

Libvirt提供一个XML命名空间和可选类库libvirt-qemu.so来解决qemu的一些特殊情况。当被正确使用时,这些扩展允许测试还没被libvirt的XML和API接口支持qemu的特性。然而,他们是不被支持的,因为该类库不能保证有一个稳定的API接口,滥用这些类库或者xml文件会导致不一致的状态使libvirtd奔溃,升级qemu-kvmlibvirtd会对那么依赖使用qemu特性的虚拟机有破坏性行为。如果你确信需要获得的qemu的特性,请发送RFE给libvirt的mailing list以得到纳入该功能的稳定的libvirt XML和API接口。

这些类库提供两个API:virDomainQemuMonitorCommand,用来发送任意监控命令(monitor command, 包括HMP和QMP格式)给qemu虚拟机 (Since 0.8.3);virDomainQemuAttach用来注册一个人工启动的qemu虚拟机,这样可以使用libvirtd来管理(Since 0.9.4)。

当启动虚拟机的时候,以下的XML扩展允许给qemu命令行参数。为了使用XML扩展,需要增加一个XML namespacehttp://libvirt.org/schemas/domain/qemu/1.0。一般给这namespace取名为qemu,需要增加一个元素<qemu:commandline>,还有两个子元素:

qemu:arg 当启动虚拟机的时候,向qemu进行提供一个命令行参数,将参数放在XML的value属性里。

qemu:env当启动虚拟机的时候,向qemu进行提供一个环境变量, 将 name-value对 放在name属性和可选的vlaue属性里。

将QEMU参数转domain XML配置

命令virsh domxml-from-native提供一个方法将已存在的一组QEMU参数转成可以被libvirt使用Domain XML文件。这个命令的目的用于将先前已经能够从命令行运行的虚拟机转化为用libvirt去管理的XML配置文件。请不要用它来创建一个新虚拟机。新的虚拟机应该由调用libvirt API的程序去创建,或者手工编写XML文件让virsh运行。

需要特别注意的是,在demo.args文件中,不能有换行出现,整个命令要在同一行里。

$ cat > demo.args <<EOF
LC_ALL=C PATH=/bin HOME=/home/test USER=test \
LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 \
-nographic -monitor pty -no-acpi -boot c -hda \
/dev/HostVG/QEMUGuest1 -net none -serial none \
-parallel none -usb
EOF
 
$ virsh domxml-from-native qemu-argv demo.args
<domain type='qemu'>
   <uuid>00000000-0000-0000-0000-000000000000</uuid>
   <memory>219136</memory>
   <currentMemory>219136</currentMemory>
   <vcpu>1</vcpu>
   <os>
     <type arch='i686' machine='pc'>hvm</type>
     <boot dev='hd'/>
   </os>
   <clock offset='utc'/>
   <on_poweroff>destroy</on_poweroff>
   <on_reboot>restart</on_reboot>
   <on_crash>destroy</on_crash>
   <devices>
     <emulator>/usr/bin/qemu</emulator>
     <disk type='block' device='disk'>
       <source dev='/dev/HostVG/QEMUGuest1'/>
       <target dev='hda' bus='ide'/>
     </disk>
   </devices>
</domain>

转化domain XML为QEMU参数

命令virsh domain-to-natice可以将libvirt的Domian XML文件转化成一组QEMU参数。

$ cat > demo.xml <<EOF
<domain type='qemu'>
   <name>QEMUGuest1</name>
   <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
   <memory>219200</memory>
   <currentMemory>219200</currentMemory>
   <vcpu>1</vcpu>
   <os>
     <type arch='i686' machine='pc'>hvm</type>
     <boot dev='hd'/>
   </os>
   <clock offset='utc'/>
   <on_poweroff>destroy</on_poweroff>
   <on_reboot>restart</on_reboot>
   <on_crash>destroy</on_crash>
   <devices>
     <emulator>/usr/bin/qemu</emulator>
     <disk type='block' device='disk'>
       <source dev='/dev/HostVG/QEMUGuest1'/>
       <target dev='hda' bus='ide'/>
     </disk>
   </devices>
</domain>
EOF
 
$ virsh domxml-to-native qemu-argv demo.xml
   LC_ALL=C PATH=/usr/bin:/bin HOME=/home/test \
   USER=test LOGNAME=test /usr/bin/qemu -S -M pc \
   -no-kqemu -m 214 -smp 1 -name QEMUGuest1 -nographic \
   -monitor pty -no-acpi -boot c -drive \
   file=/dev/HostVG/QEMUGuest1,if=ide,index=0 -net none \
   -serial none -parallel none -usb

参考资料


libvirt kvm 虚拟机上网 – Bridge桥接

通过艰难的过程,终于使用libvirt安装完qemu kvm虚拟机了.但是发现虚拟机不能上网.虚拟机想要上网,有很多中方法.

先说两个定义:Guest机器为虚拟机,Host机器为开机运行的真实机器.

查看libvirt的教程,看到网络配置有许多中方式.详情可以访问http://libvirt.org/formatdomain.html#elementsNICS

  • NAT (默认上网) 虚拟机利用host机器的ip进行上网.对外显示一个ip
  • Bridge 将虚拟机桥接到host机器的网卡上,guest和host机器都通过bridge上网.对外不同的ip,

Bridge桥接原理

Bridge方式即虚拟网桥的网络连接方式,是客户机和子网里面的机器能够互相通信。可以使虚拟机成为网络中具有独立IP的主机。

桥接网络(也叫物理设备共享)被用作把一个物理设备复制到一台虚拟机。网桥多用作高级设置,特别是主机多个网络接口的情况。

Bridge桥接原理

如上图,网桥的基本原理就是创建一个桥接接口br0,在物理网卡和虚拟网络接口之间传递数据。

在host机器配置桥接网络

首先可以按照ibm 文章的虚拟机配置一下host机的网络情况,这个的目的就是让host生成一个不日多个桥接,大家都通过这个桥接上网.下面我们来看看具体的步骤.

查看网卡是否工作

确保你想使用bridge网桥的网卡能够为kvm虚拟机模块提供网络连接能力并且网卡是否可以工作.这个网卡应该要能够给guest的kvm虚拟机提供相同的网络支持.接下来例子说明了网卡的配置,并且可以被外部网络访问.这个例子中,使用eth0网卡.

# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:14:5E:C2:1E:40
           inet addr:10.10.1.152  Bcast:10.10.1.255  Mask:255.255.255.0
           inet6 addr: fe80::214:5eff:fec2:1e40/64 Scope:Link
           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
           RX packets:664 errors:0 dropped:526 overruns:0 frame:0
           TX packets:163 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:1000
           RX bytes:69635 (68.0 KiB)  TX bytes:25091 (24.5 KiB)
           Interrupt:74 Memory:da000000-da012800
................

备份network-scripts网络脚本文件

备份网络配置到不同的目录下,将来可以用来恢复网络配置,. 根据下面的命令将网络脚本ifcfg-eth0 网卡到 /root目录:

# cp /etc/sysconfig/network-scripts/ifcfg-eth0 /root/.
注意:不要把文件copy到 /etc/sysconfig/network-scripts/目录或者其子目录

进入network-scripts目录

进入/etc/sysconfig/network-scripts/目录.

cd /etc/sysconfig/network-scripts/

为桥接新建配置文件

为Linux的bridge创建一个新的配置文件为/etc/sysconfig/network-scripts/ifcfg-br0,这里br0是bridge网桥的名字,同eth0类似.使用以下命令

cp ifcfg-eth0 ifcfg-br0

具体的内容是基于已有的配置文件来进行的.

编辑bridge网桥配置文件

编辑配置文件,让数据包通过bridge(网桥)进行上网.你的网络配置很可能是通过静态ip(BOOTPROTO=static)上网或者是从DHCP服务器获得ip(BOOTPROTO=dhcp)上网的.

如果你的网络是通过静态ip地址来配置的,那么你的配置很可能是和下面一样的:

DEVICE=eth0
BOOTPROTO=static
HWADDR=00:14:5E:C2:1E:40
IPADDR=10.10.1.152
NETMASK=255.255.255.0
ONBOOT=yes

下面的表格显示了eth0和br0的静态ip配置内容,可以根据下面进行编辑

static ip配置
/etc/sysconfig/network-scripts/ifcfg-eth0/etc/sysconfig/network-scripts/ifcfg-br0
DEVICE=eth0
TYPE=Ethernet
HWADDR=00:14:5E:C2:1E:40
ONBOOT=yes
NM_CONTROLLED=no
BRIDGE=br0
DEVICE=br0
TYPE=Bridge
NM_CONTROLLED=no
BOOTPROTO=static
IPADDR=10.10.1.152
NETMASK=255.255.255.0
ONBOOT=yes

下面表格显示的eth0和br0的DHCP配置情况.

HDCP配置
/etc/sysconfig/network-scripts/ifcfg-eth0/etc/sysconfig/network-scripts/ifcfg-br0
DEVICE=eth0
TYPE=Ethernet
HWADDR=00:14:5E:C2:1E:40
ONBOOT=yes
NM_CONTROLLED=no
BRIDGE=br0
DEVICE=br0
TYPE=Bridge
NM_CONTROLLED=no
BOOTPROTO=dhcp
ONBOOT=yes

重启网络服务

重启网络以验证网络配置是否工作.

如果网络配置不正确,网络连接将会断开,你的电脑将失去访问能力.这样的话,检查以下配置文件,然后使用以下的命令重启以下网络服务:

# service network restart

禁用网络过滤器

向文件/etc/sysctl.conf添加以下代码:

net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0

重新加载kernel参数

1
2
3
4
5
6
# sysctl -p
net.ipv4.ip_forward = 0
...
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0

验证配置是否成功

通过运行ifconfig来验证以下配置是否成功.如果出现两个项,即网桥的br0和现在在工作的eth0:

br0       Link encap:Ethernet  HWaddr 00:14:5E:C2:1E:40
           inet addr:10.10.1.152  Bcast:10.10.1.255  Mask:255.255.255.0
           inet6 addr: fe80::214:5eff:fec2:1e40/64 Scope:Link
           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
           RX packets:125 errors:0 dropped:0 overruns:0 frame:0
           TX packets:81 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:0
           RX bytes:16078 (15.7 KiB)  TX bytes:18542 (18.1 KiB)
eth0      Link encap:Ethernet  HWaddr 00:14:5E:C2:1E:40
           inet6 addr: fe80::214:5eff:fec2:1e40/64 Scope:Link
           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
           RX packets:206 errors:0 dropped:0 overruns:0 frame:0
           TX packets:58 errors:0 dropped:0 overruns:0 carrier:0
           collisions:0 txqueuelen:1000
           RX bytes:27308 (26.6 KiB)  TX bytes:13881 (13.5 KiB)
           Interrupt:74 Memory:da000000-da012800
注意:你可能发现eth0的网络信息里面没有ipv4的地址了.或者你的GUI界面的网络显示你是断网的. 这不用担心.因为eth0的网络能力已经通过br0进行工作.你的host网络还是能正常工作的.

也可以使用以下的命令来查看网桥是否工作.

# brctl show
bridge name     bridge id               STP enabled     interfaces
virbr0          8000.000000000000       yes
br0             8000.000e0cb30550       no              eth0

如果是类似的输出,那么你的Linux的Bridge网桥就配置的差不多了.

为Guest虚拟机配置网络

配置Guest虚拟机的xml文件

在虚拟机的xml配置文件进行设置网络的连接方式为bridge桥接,并且桥接的设备为br0.其中mac地址要改变!不然MAC相同,局域网MAC地址相同的电脑不能上网.

< interface type = "bridge" > <!--虚拟机网络连接方式-->
     < source bridge = "br0" /> <!-- 当前主机网桥的名称-->
     < mac address = "00:16:e4:9a:b3:6a" /> <!--为虚拟机分配mac地址,务必唯一,否则dhcp获得同样ip,引起冲突-->
</ interface >

在虚拟机配置网络

我使用的是CentOS Minimal Installation的发行版.使用以下命令就可以进行上网了.
一开始只有lo显示.没有看到eth0,后来想到了eth0被禁用了,于是开启即可.

# ifconfig eth0 up
# dhclient eth0

现在还有个问题是,网络不随开机启动.
我打开/etc/sysconfig/network-scripts/ifcfg-eth0显示:

DEVICE=eth0
HWADDR=52:54:00:12:34:56
ONBOOT=yes
NM_CONTROLLED=yes
BOOTPROTO=dhcp

虽然设置了开机启动,但是每次开机都需要运行上面的两个命令.这样就没办法远程控制主机了.

后来发现是udev弄的.udev在/etc/udev/rules.d/下面搞了一个持久命名规则(persistent-net.rules),对于网卡的持久命令,就采取了MAC地址的匹配方式

原来虚拟机安装后自带MAC网卡地址和我xml定义的网卡不一样.于是我就将虚拟机的MAC网卡提取出来,替换xml文件的MAC网卡信息.然后undefine和define以下虚拟机.

重启虚拟机,发现这次网络能够自动开机获得了.

参考资料

Linux下编译安装qemu和libvirt


KVM虚拟机(英语:Kernel-based Virtual Machine),是一种用于Linux内核中的虚拟化基础设施。KVM目前支援Intel VT及AMD-V的原生虚拟技术。KVM在2007年2月被导入Linux 2.6.20核心中。它也被引入FreeBSD。在Mac OS X中,也可以见到KVM。

因为kvm已经集成到Linux中了,所有就不用安装kvm了。

安装qemu

qemu介绍

QEMU 是一个面向完整 PC 系统的开源仿真器。除了仿真处理器外,QEMU 还允许仿真所有必要的子系统,如连网硬件和视频硬件。它还允许实现高级概念上的仿真(如对称多处理系统(多达 255 个 CPU)和其他处理器架构(如 ARM 和 PowerPC)。

下载源文件

下面从git安装qemu最新版qemu官网的git地址:

http://git.qemu-project.org/qemu.git

使用以下命令下载qemu代码到本地来,将来可以用来自己编译和修改,然后发布自己新的qemu。

git clone git://git.qemu.org/qemu.git

编译安装

下载完后,进行编译安装.

### 如果你是从压缩包下载来的,先解压缩
cd /tmp
tar zxvf qemu-x.y.z. tar .gz
cd qemu-x.y.z
### Then you configure QEMU and build it (usually no options are needed):
 
###git下载及源码包下载都一样了
. /configure
  make
### Then type as root user:
 
make install
### to install QEMU in /usr/local.

你可能在./courfigure出现问题

注意:可能在安装的时候,许多的软件没有安装,需要提前安装,不过会提示你会少了什么

yum install gcc
yum install zlib-devel.x86_64
yum install glib2-devel.x86_64
yum install pixman-devel.x86_64
git submodule update --init dtc   ###会提示缺某个模块,在当前的git目录下运行

安装libvirt

libvirt介绍

libvirt是目前使用最为广泛的对KVM虚拟机进行管理的工具和应用程序接口(API),而且一些常用的虚拟机管理工具(如virsh、virt-install、virt-manager等)和云计算框架平台(如OpenStack、OpenNebula、Eucalyptus等)都在底层使用libvirt的应用程序接口。

下载libvirt

git项目的位置http://libvirt.org/git/?p=libvirt.git

新建一个文件夹,存放git的文件

1
git clone git://libvirt.org/libvirt.git

编译安装

这部分是参考http://libvirt.org/compiling.html文章

###个人不推荐这个方法,见下面
$ . /autogen .sh --prefix=$HOME /usr
$ make
$ sudo make install

默认状态下,配置和编译后安装的目录与Linux操作系统发行版提供的默认目录可能是不一致的,例如:RHEL 6.3中用系统的RPM包安装的libvirtd、virsh等可执行程序被安装 /usr/sbin/ 目录下,libvirt.so、libvirt-qemu.so等共享库文件被安装在 /usr/lib64/ 目录下,而从前面步骤中可知自己编译安装时,默认会将libvirtd、virsh等安装在 /usr/local/sbin 目录下,而libvirt.so、libvirt-qemu.so 等被安装在 /usr/local/lib/ 目录下。如果想保持对操作系统发型版中安装可执行程序和共享库的目录的一致性, autogen.sh 脚本提供了“–system”参数,带有这个参数来配置,就会尽可能保证安装目录与原生系统的一致性,其命令行操作如下.

###个人推荐这个方法##保持对操作系统发型版中安装可执行程序和共享库的目录的一致性
$ . /autogen .sh --system
### 或者 ./configure --prefix=/usr --localstatedir=/var  --sysconfdir=/etc
$ make
$ sudo make install

安装libvirt可能也会缺少一些软件,比如

yum install libtool.x86_64
autopint : yum install gettext-devel.x86_64
python-config : yum install python-devel.x86_64
pciaccess : yum install libpciaccess.devel.x86_64

检查已经安装的libvirt

# which libvirtd
/usr/sbin/libvirtd
 
# which virsh
/usr/bin/virsh

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值