openstack nova后端使用ceph rbd(增加在线迁移live_migrate和快照snapshot功能)

感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!

如有转载,请保留源作者博客信息。

Better Me的博客blog.youkuaiyun.com/tantexian

如需交流,欢迎大家博客留言。



声明:本文记录的都是一些开发调试的过程,由于时间关系没有整理成文。

附上报错信息:



附上代码:判断冷迁移还是热迁移逻辑:
        if live and not rebuild and not flavor:
            self._live_migrate(context, instance, scheduler_hint,
                               block_migration, disk_over_commit)
        elif not live and not rebuild and flavor:
            instance_uuid = instance['uuid']
            with compute_utils.EventReporter(context, self.db,
                                         'cold_migrate', instance_uuid):
                self._cold_migrate(context, instance, flavor,
                                   scheduler_hint['filter_properties'],
                                   reservations)



conductor调用conpute的代码:



virsh -c qemu+tcp://node32/system

virsh使用qemu+tcp访问远程libvirtd











拿fule代码直接替换:




注:在测试过程中,假若没有成功,下次则会提示目录已经存在,则再目的迁移地址将
/var/lib/nova/instances下面相应文件夹删除掉
ntpdate 192.168.18.85
openvswich开启:/etc/init.d/openvswitch restart

配置libvirt的监听端口:

修改文件

vim /usr/local/libvirt/etc/sysconfig/libvirtd

用来启用tcp的端口(本环境中由于libvirt升级过,配置文件目录不一致,默认为/etc/sysconfig/libvirtd

1
2
3
LIBVIRTD_CONFIG=/usr/local/libvirt/etc/libvirt/libvirtd.conf
 
LIBVIRTD_ARGS="--listen"

修改文件

vim /usr/local/libvirt/etc/libvirt/libvirtd.conf

1
2
3
4
5
6
7
8
9
listen_tls = 0
 
listen_tcp = 1
 
tcp_port = "16509"
 
listen_addr = "0.0.0.0"
 
auth_tcp = "none"

运行 libvirtd

1
service libvirtd restart



报secret错误,则将每个计算节点的uuid设置为一样:
rbd_secret_uuid=c245e1ef-d340-4d02-9dcf-fd091cd1fe47

配置nova。conf

Table 4.6. Description of live migration configuration options
Configuration option = Default valueDescription
[DEFAULT]
live_migration_retry_count = 30(IntOpt) Number of 1 second retries needed in live_migration
[libvirt]
live_migration_bandwidth = 0(IntOpt) Maximum bandwidth to be used during migration, in Mbps
live_migration_flag = VIR_MIGRATE_UNDEFINE_SOURCE, VIR_MIGRATE_PEER2PEER, VIR_MIGRATE_LIVE, VIR_MIGRATE_TUNNELLED(StrOpt) Migration flags to be set for live migration
live_migration_uri = qemu+tcp://%s/system(StrOpt) Migration target URI (any included "%s" is replaced with the migration target hostname)



发现两个key不一致,因此ceph的key配置有问题


重新配置正确的key值,保证所有计算节点key值相同。重新配置后,需要将之前创建的虚拟机删除,重新生成



错误报在migrateToURI函数,打印输出参数:
vim /usr/lib/python2.6/site-packages/nova/virt/libvirt/driver.py
 def _live_migration(self, context, instance, dest, post_method,
                        recover_method, block_migration=False,
                        migrate_data=None):
        """Do live migration.
        :param context: security context
        :param instance:
            nova.db.sqlalchemy.models.Instance object
            instance object that is migrated.
        :param dest: destination host
        :param post_method:
            post operation method.
            expected nova.compute.manager.post_live_migration.
        :param recover_method:
            recovery method when any exception occurs.
            expected nova.compute.manager.recover_live_migration.
        :param block_migration: if true, do block migration.
        :param migrate_data: implementation specific params
        """
        # Do live migration.
        try:
            if block_migration:
                flaglist = CONF.libvirt.block_migration_flag.split(',')
            else:
                flaglist = CONF.libvirt.live_migration_flag.split(',')
            flagvals = [getattr(libvirt, x.strip()) for x in flaglist]
            logical_sum = reduce(lambda x, y: x | y, flagvals)
            dom = self._lookup_by_name(instance["name"])
            LOG.error('1-start------------------------------------------')
            LOG.error('dom==%s' % dom)
            LOG.error('live_migration_uri==%s' % CONF.libvirt.live_migration_uri)
            LOG.error('dest==%s' %dest)
            LOG.error('logical_sum==%s' % logical_sum)
            LOG.error('live_migration_bandwidth==%s' % CONF.libvirt.live_migration_bandwidth)
            LOG.error('1-end------------------------------------------')
            dom.migrateToURI(CONF.libvirt.live_migration_uri % dest,
                             logical_sum,
                             None,
                             CONF.libvirt.live_migration_bandwidth)
        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.error(_("Live Migration failure: %s"), e,
                          instance=instance)
                recover_method(context, instance, dest, block_migration)


查看下虚拟机实例在node31情况:
从上述打印日志可以看出dom值为libvirt.virtDomain对象:

/usr/lib64/python2.6/site-packages/libvirt.py



在能正常迁移的fule节点上面打印上述日志:



再迁移目标主机上建立文件夹:
mkdir /var/lib/nova/instances/022d1291-3967-4363-ab48-fb9713802e42/

libvirt debug模式:
LIBVIRT_DEBUG=1 virsh list --all
调试方法汇总:


命令测试:
在node31上面:
virsh -c qemu+tcp://node32/system


说到虚拟机的迁移,其实就是数据的转移,数据的转移就涉及数据的传输,数据的传输需要通过网络。libvirt提供了两种方案。

hypervisor native transport:
“本地”数据传输,相当于一种手动方式做的迁移。数据是否可以加密取决于hypervisor自身是否已实现。
libvirt tunnelled transport
隧道化的(tunnelled)数据传输支持很强的加密功能,这要归结于libvirt的RPC协议。不好的一方面是数据会在hypervisor和libvirtd之间的传输。

虚拟机的迁移需要两个主机之间的密切协调,同时应用程序(虚拟机管理平台)也将参与进来,此应用可以是在源主机、目的主机、甚至是第三台主机。

受管理的直接迁移(Managed direct migration):
直接管理迁移,libvirt客户端进程控制迁移的不同阶段。客户端应用程序必须连接以及获得在源主机和目的主机的libvirtd进程的认证。无需两个libvirtd进程间相互通信。如果客户端应用崩溃了,或者在迁移过程中失去了链接,一种办法就是强制终止迁移,重启源主机的guest CPU。
Migration direct, managed
管理的点对点迁移:(Managed peer to peer migrate)
对于点对点,libvirt客户端程序只是与在源主机的libvirtd进程通信,源主机libvirtd进程自己控制整个迁移的过程,直接连接到目的主机的libvirtd。如果客户端应用崩溃了或者与libvirtd失去连接,迁移过程无需中断直至迁移完成。需要注意的是源主机认证(通常是root)链接到目的主机,而不是通过客户端应用链接到源主机。

Migration peer-to-peer
不受管理的直接迁移:(Unmanaged direct migrate)
此迁移方式既不受libvirt客户端控制,也不受libvirtd控制,迁移的控制过程委托给基于hypervisor之上的管理服务来处理。libvirt 客户只需要通过hypervisor的管理层做一下初始化。不管是libvirt 客户端还是libvirtd崩溃了,迁移还是会照样进行直至完成。

Migration direct, unmanaged

迁移URI:
虚拟机迁移时,客户端应用程序需要准备的URI可达三个,具体取决于控制流如何选择或者API如何调用。第一个URI就是应用程序到运行着虚拟机的源主机的连接,第二个URI就是目的主机到应用程序之间的连接(在点对点的迁移中,这个连接是来自源主机,而不是客户端应用程序)。第三个URI就是hypervisor指定的用来控制以何种方式迁移的连接。在任何一种受管理的迁移中,前两个URI是必不可少的,第三个是可选的。而在不受管理的直接迁移中,第一个和第三个是必须的,第二个并不会使用。


通常管理应用程序只需关心前两个URI。二者都是普通的libvirt 连接URI格式。libvirt会通过查找目的主机的配置文件中的hostname自动确定hypervisor指定的URI(第三个URI)。

应用程序获取第三个URI时可能会出现的如下几种情况:
1、hostname的配置不正确,或者DNS不正确,如果主机的hostname不能正确的解析到IP地址的化就会生成一个错误的URI,这刚好时文章开头出现的问题。此时需要明确指定IP地址或者使用正确的hostname。
2、主机有多个网络接口,此时需要用IP明确指定来关联某个具体的网卡。
3、防火墙限制端口的使用,当libvirt自动生扯功能的端口需要防火墙对其端口是开放的。




virsh migrate --live --verbose --direct --p2p 5 qemu+tcp://node32/system 

virsh migrate source_vm qemu+ssh://ip:port/system --live --storage-all?


vim /usr/local/libvirt/etc/libvirt/qemu.conf 

service libvirtd restart


问题木有解决,继续,重新编译libvirt:
./configure --prefix=/usr/local/libvirt/ --with-selinux=no

selinux问题解决了。again:

这次成功了,什么都不改变,后面不能成功了???


重新生成虚拟机测试:
重启libvirt报错:
 yum install pm-utils 
重启libvirt没有出现错误:




编译升级libvirt到1.2.3:
./configure   --with-storage-rbd=yes --with-selinux=no
make && make install

启动virsh list all
error: Failed to connect socket to '/usr/local/var/run/libvirt/libvirt-sock': No such file or directory

ln -s /usr/local/libvirt/var/run/libvirt/libvirt-sock /usr/local/var/run/libvirt/libvirt-sock


[ root@node31 ~]# virsh migrate 7 qemu+tcp://node32/system --live --verbose 
error: Cannot get interface MTU on 'qbrbc05fd4a-43': No such device

在目标主机上:brctl addbr qbrbc05fd4a-43


[ root@node31 86da6dff-d943-4065-889a-4ed281390be9]# virsh migrate 7 qemu+tcp://node32/system --live --verbose 
error: Timed out during operation: cannot acquire state change lock
kill掉libvirt重启(两个节点都重启)

virsh migrate 7 qemu+tcp://node32/system --live --verbose --p2p --direct






上图中的volume-backed是指的xml等文件也共享呢?但是nfs也是共享了的。。。疑惑ing~~~~




如果是nfs那么/var/lib/nova/instance文件夹是共享的,里面的xml文件也共享,
那么如果是nfs的迁移则不需要迁移xml文件(因为各个节点共享)。
但如果是ceph-backed,则需要将libvirt的xml等文件迁移复制过去。

上述问题通过将vnc_listen设置为0.0.0.0即可以全部节点访问








用1.1.2版本热迁移和快照都成功。(直接无语)
但是有个bug是,迁移完虚拟机后,/var/lib/nova/instance的配置文件继续保存着,
这样就会导致下次再迁移回来时候,则不能继续迁移了。

继续测试:


63compute日志:

找到文件:
/usr/lib/python2.6/site-packages/nova/virt/libvirt/driver.py", line 4255, in check_can_live_migrate_source

由上述可知:live_migrate的执行流程为:dashboard->novaclient->nova-api->nova-conductor->nova-compute->virt.libvirt.driver.py.

从上图打印的值,可以看出is_volume_backed:False,似乎有bug,继续跟踪下去。
查看fule对应处的输出:

上述两处的差别在于,fuel代码中传递了image_type==rbd参数。(其实,在nova.conf配置文件中会配置,images_type=rbd项,因此不需要传递参数,自己根据配置文件修改即可。)
原始代码:

修改后代码:

再次实验:
迁移成功。

继续将该虚拟机从63->64:

图2:虚拟机配置文件及相关metedate:


出现上述错误的原因是:
因为迁移走的是rbd,不是共享存储(共享存储不需要拷贝上图2的文件,因此各个node都共享)。rbd的话需要将上图2中的文件拷贝过去。所以每次迁移时候就得先copy该文件夹过去。然后再拷贝内存,然后调用libvirt接口(例如virsh define XXX/libvirt.xml)再目标主机启动虚拟机,迁移即成功。但是有个bug在里面,就是迁移成功后,没有将目标主机的相应文件夹给删除,这样导致再次迁移回来时候报上述错误。

解决办法:
在迁移成功后,删除该文件夹。
解决办法很简单,但是由于迁移的复杂性,难免会出现迁移不成功的情况,因此删除文件夹必须在100%确认已经迁移成功之后。(注:假若迁移没有成功,openstack自身提供了回滚机制,请自行参考相关代码(在conductor模块有rollback函数))

所以还得继续跟踪源码:
根进到driver.live_migration()

步骤10:修复bug
重点关注此处代码:
        def wait_for_live_migration():
            """waiting for live migration completion."""
            try:
                self.get_info(instance)['state']  #此处一直获取实例状态,当实例被迁移走了,就会出现异常,说明迁移成功
            except exception.InstanceNotFound:
                timer.stop()
                post_method(context, instance, dest, block_migration,
                            migrate_data)

因此删除文件夹,比较好的地方在此处。



原生代码:
日志说明是在 dom.migrateToURI函数执行后异常错误(即调用libvirt出异常)。

打印fuel代码:
日志打印一致,则说明在之前步骤中,原生nova没有pre-create文件夹。

用fule代码:
说明在 dom.migrateToURI()之前自动创建了文件夹。


再来看原生代码:

说明原生代码没有pre-create实例相应的文件夹。
查看代码driver.py代码:pre_live_migration()

这里if语句说明只有当is_shared_instance_path没有共享时候,才回去目的路径建立文件夹。


测试:

63的compute.log日志:可以看出为True,因此63不会自己创建保存实例的文件夹:

修复bug:
修改前:

修复后:

if (not is_shared_storage) or (CONF.libvirt.images_type == 'rbd') :

再次测试:
此时可以看到e95d7c8b-e8b7-44a0-8880-323e6720b435文件夹被创建了。


测试:

迁移成功:

看后台目录:


从上面可以看出虚拟机虽然从64迁移到63了,但是64上面虚拟机的文件还存在。这样就会导致虚拟机迁移回来时候,回报文件以及存在错误。(fuel的bug)
修复bug:
回到上述: 步骤10:修复bug分析:
        def wait_for_live_migration():
            """waiting for live migration completion."""
            try:
                self.get_info(instance)['state']  #此处一直获取实例状态,当实例被迁移走了,就会出现异常,说明迁移成功
            except exception.InstanceNotFound:
                timer.stop()
                post_method(context, instance, dest, block_migration,
                            migrate_data)

调试:
继续测试从64将vm迁移到63:,看打印日志:
64日志:
63日志(没有输出日志):

结论和“ 步骤10:修复bug分析”分析一致
修复bug:


 def wait_for_live_migration():
            """waiting for live migration completion."""
            try:
                self.get_info(instance)['state']
            except exception.InstanceNotFound:
                #add by ttx 2014-10-30
                if (CONF.libvirt.images_type == 'rbd' and os.path.exists(inst_base)):
                    utils.execute('rm', '-rf', inst_base)
                timer.stop()
                post_method(context, instance, dest, block_migration,
                            migrate_data)
        timer.f = wait_for_live_migration
        timer.start(interval=0.5).wait()

继续测试:


继续迁移回来:


成功。
所有问题全部解决完成。


最后附上基础配置:

1       安装libvirt1.1.2

  1. openstack安装脚本openstack_init_env.sh中去掉libvirt的安装,当openstack_init_env.sh执行完之后,手动安装libvirt-1.1.2,安装完之后再执行后续的自动部署脚本。
  2. 安装libvirt-1.1.2

解压libvirt-1.1.2源码安装包,进入源码目录

yum install -y libpciaccess-devel device-mapper-devel libnl-devel yajl-devel yajl

./configure --prefix=/usr/local/libvirt/

make

make install

mkdir -p /var/run/libvirt

mkdir -p /usr/local/libvirt/var/lock/subsys

cp -rf /usr/local/libvirt/bin/* /usr/bin/

cp -rf /usr/local/libvirt/sbin/* /usr/sbin/

cp -rf /usr/local/libvirt/libexec/* /usr/libexec/

cp -rf /usr/local/libvirt/etc/rc.d/init.d/* /etc/rc.d/init.d/

cp -rf /etc/init.d/functions /usr/local/libvirt/etc/rc.d/init.d/

cp -rf /usr/local/libvirt/lib64/python2.6/site-packages/* /usr/lib64/python2.6/site-packages/

cp -rf /usr/local/libvirt/etc/sysconfig/* /etc/sysconfig/

cp -rf /usr/local/libvirt/var/run/libvirt/* /var/run/libvirt

check_file_exist "/usr/local/libvirt/etc/libvirt/libvirtd.conf"

sed -i "/^[[:blank:]]*#[[:blank:]]*unix_sock_group/s/#unix_sock_group/unix_sock_group/g" /usr/local/libvirt/etc/libvirt/libvirtd.conf

sed -i "/^[[:blank:]]*#[[:blank:]]*unix_sock_ro_perms/s/#unix_sock_ro_perms/unix_sock_ro_perms/g" /usr/local/libvirt/etc/libvirt/libvirtd.conf

sed -i "/^[[:blank:]]*#[[:blank:]]*unix_sock_rw_perms/s/#unix_sock_rw_perms/unix_sock_rw_perms/g" /usr/local/libvirt/etc/libvirt/libvirtd.conf

sed -i "/^[[:blank:]]*#[[:blank:]]*auth_unix_ro/s/#auth_unix_ro/auth_unix_ro/g" /usr/local/libvirt/etc/libvirt/libvirtd.conf

sed -i "/^[[:blank:]]*#[[:blank:]]*auth_unix_rw/s/#auth_unix_rw/auth_unix_rw/g" /usr/local/libvirt/etc/libvirt/libvirtd.conf

groupadd libvirt

service libvirtd start


  1. 继续执行剩余的自动部署脚本。


2、  配置libvirt1.1.2

修改文件

vim /usr/local/libvirt/etc/sysconfig/libvirtd

用来启用tcp的端口(本环境中由于libvirt升级过,配置文件目录不一致,默认为/etc/sysconfig/libvirtd

1
2
3
LIBVIRTD_CONFIG=/usr/local/libvirt/etc/libvirt/libvirtd.conf
 
LIBVIRTD_ARGS="--listen"

修改文件

vim /usr/local/libvirt/etc/libvirt/libvirtd.conf

1
2
3
4
5
6
7
8
9
listen_tls = 0
 
listen_tcp = 1
 
tcp_port = "16509"
 
listen_addr = "0.0.0.0"
 
auth_tcp = "none"

运行 libvirtd

1
service libvirtd restart



<think>好的,我现在需要帮用户解决将OpenStack中Cinder卷从Ceph后端迁移到其他主机的问题。首先,我得确认用户的需求。他们可能是在进行存储迁移,或者重新规划存储资源,或者是故障转移。需要了解用户的具体场景,但用户没有提供更多细节,所以我得保持通用。 接下来,我要回忆OpenStackCeph的整合机制。Cinder的Ceph后端通常使用RBDRBD驱动),卷在Ceph中存储为RBD镜像。迁移可能涉及数据复制或者调整挂载点。用户提到迁移到“其他主机”,这里的主机可能是指计算节点(Nova)或者存储节点?需要明确,但可能是指迁移到不同存储节点上的Ceph池,或者迁移到另一个Ceph集群? 假设用户是指将卷从一个Ceph迁移到另一个池,或者同一池的不同位置,同时保持Cinder管理的卷信息。这时候需要考虑步骤:数据复制、更新Cinder元数据、确保卷可用性。 关键步骤可能包括:1. 确认源目标Ceph环境是否可达;2. 使用rbd命令复制卷;3. 在Cinder中更新卷的位置信息;4. 确保Nova实例能访问新位置。 需要注意的问题:权限问题(Ceph的keyring是否正确)、卷状态是否可用(需要确保卷未被挂载)、Cinder数据库更新的风险(直接修改数据库可能导致不一致)。 可能的命令步骤:首先,停止相关实例,卸载卷;然后用rbd export/import或者rbd clone/migration;接着在Cinder中更新volume的provider_location字段,或者用Cinder命令重新注册卷。 另外,是否有Cinder本身的迁移命令或API?比如cinder migrate,但可能不支持不同后端。需要查阅文档,可能用户需要手动操作。 还要考虑是否需要更新存储池的配置,比如cinder.conf中的rbd_pool参数,但如果是迁移到同一集群的不同池,可能需要修改后端配置,并重新创建卷类型。 还有可能用户想将卷迁移到另一个Ceph集群,这时需要跨集群复制,可能更复杂,涉及网络带宽认证问题。 需要提醒用户备份数据,并在测试环境验证步骤,避免生产环境数据丢失。 另外,如果Cinder使用的是多后端配置,可能需要指定目标后端进行迁移,但常规的cinder migrate可能有限制。 总结步骤:1. 确认环境;2. 停止实例并卸载卷;3. 使用Ceph工具复制RBD镜像;4. 在Cinder中删除原卷并注册新卷,或者更新数据库中的卷信息;5. 重新挂载卷到实例。 需要注意的细节:UUID一致性问题,卷的元数据如ID、名称是否需要保留,以及如何确保Cinder正确识别新位置。可能还需要使用cinder-manage命令来更新数据库条目。 此外,可能存在的替代方案:使用Cinder的备份功能,创建备份并恢复到新后端,但需要确保备份功能配置正确,并且支持跨后端恢复。 最后,提醒用户操作风险,确保有回滚计划,并参考官方文档进行操作。</think>在OpenStack使用Ceph作为Cinder后端存储时,迁移卷需结合Ceph RBD特性与Cinder元数据管理。以下是分步指南: --- ### 一、迁移原理 Cinder卷在Ceph中存储为RBD镜像(默认格式为`volume-<UUID>`)。迁移核心步骤: 1. **数据层**:通过Ceph RBD命令复制镜像。 2. **元数据层**:更新Cinder数据库中的卷路径。 --- ### 二、操作步骤 #### 1. 环境检查 ```bash # 确认源卷状态(必须为可用状态) cinder list | grep <卷名称或ID> # 确认卷未被挂载 nova volume-attachments <实例ID> ``` #### 2. 停止相关实例(若卷已挂载) ```bash nova stop <实例ID> cinder detach <卷ID> ``` #### 3. 使用Ceph RBD复制卷 ```bash # 导出卷UUID VOL_UUID=$(cinder show <卷ID> | grep ' os-vol-host-attr:host ' | awk '{print $4}') # 查找RBD镜像路径 RBD_IMAGE="volume-${VOL_UUID}" # 复制数据到目标Ceph池(假设目标池为`new_pool`) rbd export ${RBD_POOL}/${RBD_IMAGE} - | rbd import - new_pool/${RBD_IMAGE} ``` #### 4. 更新Cinder元数据 ```sql # 连接Cinder数据库(如MySQL) mysql -u root -p cinder -- 更新provider_location字段 UPDATE volumes SET provider_location='rbd:new_pool/volume-<UUID>' WHERE id='<卷UUID>'; ``` #### 5. 验证并恢复服务 ```bash # 重新挂载卷 cinder attach <实例ID> <卷ID> # 启动实例 nova start <实例ID> ``` --- ### 三、注意事项 1. **权限问题**:确保执行命令的用户有Ceph `rbd`权限Cinder数据库写入权限。 2. **跨集群迁移**:若目标Ceph集群不同,需配置集群间复制或使用`rbd mirror`。 3. **备份方案**:可通过Cinder备份恢复实现迁移: ```bash cinder backup-create <卷ID> --name <备份名> cinder backup-restore <备份ID> --target-backend new_ceph_backend ``` 4. **风险控制**:操作前备份Cinder数据库及Ceph数据。 --- ### 四、替代方案(推荐) 使用`cinder retype`命令迁移到新后端(需提前配置多存储后端): ```bash # 创建目标存储类型 cinder type-create new_ceph_type cinder type-key new_ceph_type set volume_backend_name=new_ceph_backend # 执行迁移 cinder retype <卷ID> new_ceph_type --migration-policy on-demand ``` --- ### 五、故障排查 - **问题**:卷状态错误 **解决**:检查`cinder-volume`日志`/var/log/cinder/cinder-volume.log` - **问题**:RBD镜像复制失败 **解决**:确认Ceph集群间网络连通性认证文件(`ceph.conf``keyring`) 建议在测试环境验证后实施生产迁移
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值