K8s网络插件Flannel,Calico

一、K8s网络插件flannel与calico

Kubernetes跨主机容器之间的通信组件,目前主流的是flannel和calico。
我们将集中探索与对比目前最流行的CNI插件:Flannel、Calico

1. k8s网络解决方案

我们说k8s的网络解决方案,其实指代的是k8s的pod网络解决方案。

容器网络规范容器网络发展到现在,形成了两大阵营:

  • docker的cnm
    cnm(container network model):CNM 的优势在于原生,容器网络和 Docker 容器生命周期结合紧密;缺点是被 Docker “绑架”。支持 CNM 网络规范的容器网络实现包括:Docker Swarm overlay、Macvlan & IP networkdrivers、Calico、Contiv、Weave等。

  • google、coreos、kubernetes主导的cni
    CNI(Container Netork Interface):CNI 的优势是兼容其他容器技术(rkt)以及上层的编排系统(Kubernetes&Mesos)、而且社区活跃势头迅猛;缺点是非 Docker 原生。支持 CNI 的网络规范的容器网络实现包括:Kubernetes、Weave、Macvlan、Calico、Flannel、Contiv、Mesos CNI,因为它们都实现了 CNI 规范,用户无论选择哪种方案,得到的网络模型都一样,即每个 Pod 都有独立的 IP,可以直接通信。区别在于不同方案的底层实现不同,有的采用基于 VxLAN 的 Overlay 实现,有的则是 Underlay,性能上有区别。再有就是是否支持 Network Policy。

CNM 和 CNI 是网络规范或者网络体系,并不是网络实现,因此不关心容器的网络实现方式,CNM 和 CNI 关心的只是网络管理。

为了保证网络方案的标准化、扩展性和灵活性,Kubernetes 采用了 Container Networking Interface(CNI)规范。

CNI(容器网络接口):
  这是K8s中提供的一种通用网络标准规范,因为k8s本身不提供网络解决方案。
  目前比较知名的网络解决方案有:
    flannel
    calico
    canel
    kube-router

容器虚拟化网络方案

容器虚拟化网络方案,总体分为2种截然不同的发展路线:

基于隧道

1、隧道方案最具普适性,在任何网络环境下都可以正常工作,这与它的原理密不可分。
2、最常见的隧道方案是flannel vxlan模式,以及calico的ipip模式,其核心原理包含了2个部分。

优势/劣势
优势就是对物理网络环境没有特殊要求,只要宿主机IP层可以路由互通即可。

劣势就是性能差,这需要从以下方面看:
封包和解包耗费CPU性能;
额外的封装导致带宽浪费,大约有30%左右的带宽损耗;

基于路由

1、路由方案性能最好,原因是该方案不需要封包和解包,所以没有隧道方案的劣势,网络性能很好。
2、常见的路由方案包括了flannel的host-gw模式,以及calico的bgp模式。

优势/劣势
优势就是没有封包和解包过程,完全基于两端宿主机的路由表进行转发。

劣势包含2方面:
要求宿主机处于同一个2层网络下,也就是连在一台交换机上,这样才能基于MAC通讯,而不需要在IP上动封包/解包的手脚。
路由表膨胀导致性能降低,因为宿主机上每个容器需要在本机添加一条路由规则,而不同宿主机之间需要广播自己的网段路由规则。

2. CNI(容器网络接口) flannel与calico 选型比较

争议 | Flannel vs Calico,k8s 网络方案该选谁?
参考URL: https://blog.51cto.com/u_15127582/2750450

目前比较常用的时flannel和calico,flannel的功能比较简单,不具备复杂网络的配置能力,calico是比较出色的网络管理插件,单具备复杂网络配置能力的同时,往往意味着本身的配置比较复杂,所以相对而言,比较小而简单的集群使用flannel,考虑到日后扩容,未来网络可能需要加入更多设备,配置更多策略,则使用calico更好

总的来说,Flannel是大多数用户的不错选择。从管理角度来看,它提供了一个简单的网络模型,用户只需要一些基础知识,就可以设置适合大多数用例的环境。一般来说,在初期使用Flannel是一个稳妥安全的选择,直到你开始需要一些它无法提供的东西。

Flannel默认和推荐的方法是使用VXLAN,因为VXLAN性能更良好并且需要的手动干预更少。

Calico 作为老牌网络解决方案,可圈可点,已被 github 等公司用于生产。

  • flannel实现的是网络通信,calico的特性是在pod之间的隔离。
  • 纯三层的转发,中间没有任何的NAT和overlay,转发效率最好。

总结:
Flannel:
优势:部署简单,性能一般。

劣势:没办法实现固定 IP 的容器漂移,没法做子网隔离,对上层设计依赖程度高,没有 IPAM , IP 地址浪费,对 Docker 启动方法有绑定。

Calico还以其先进的网络功能而闻名。网络策略是其最受追捧的功能之一。此外,Calico还可以与服务网格Istio集成,以便在服务网格层和网络基础架构层中解释和实施集群内工作负载的策略。这意味着用户可以配置强大的规则,描述Pod应如何发送和接受流量,提高安全性并控制网络环境。

Calico是Kubernetes生态系统中另一种流行的网络选择。虽然Flannel被公认为是最简单的选择,但Calico以其性能、灵活性而闻名。Calico的功能更为全面,不仅提供主机和pod之间的网络连接,还涉及网络安全和管理。Calico CNI插件在CNI框架内封装了Calico的功能。

生产环境使用Calico 多一些!

3. 基础:k8s集群三种IP

  • Node IP:Node节点的IP地址
    Node IP是Kubernetes集群中节点的物理网卡IP地址(一般为内网),所有属于这个网络的服务器之间都可以直接通信,所以Kubernetes集群外要想访问Kubernetes集群内部的某个节点或者服务,肯定得通过Node IP进行通信(这个时候一般是通过外网IP了)

  • Pod IP:Pod的IP地址
    然后Pod IP是每个Pod的IP地址,它是Docker Engine根据docker0网桥的IP地址段进行分配的(我们这里使用的是flannel这种网络插件保证所有节点的Pod IP不会冲突)

  • Cluster IP:Service的IP地址
    Cluster IP是一个虚拟的IP,仅仅作用于Kubernetes Service这个对象,由Kubernetes自己来进行管理和分配地址,当然我们也无法ping这个地址,他没有一个真正的实体对象来响应,他只能结合Service Port来组成一个可以通信的服务。

k8s集群初始化时的service网段,pod网段,网络插件的网段,以及真实服务器的网段,都不能相同,如果相同就会出各种各样奇怪的问题,而且这些问题在集群做好之后是不方便改的,改会导致更多的问题,所以,就在搭建前将其规划好。

二、CNI 容器网络规范

[推荐-关于K8s网络模型,写的比较清晰简单]K8s网络模型
参考URL: https://www.jianshu.com/p/3f2401d14c78

CNI 是由 CoreOS 提出的容器网络规范,它使用了插件(Plugin)模型创建容器的网络栈。
在这里插入图片描述CNI 的优点是支持多种容器 runtime,不仅仅是 Docker。CNI 的插件模型支持不同组织和公司开发的第三方插件,这对运维人员来说很有吸引力,可以灵活选择适合的网络方案。

目前已有多种支持 Kubernetes 的网络方案,比如 Flannel、Calico、Canal、Weave Net 等。因为它们都实现了 CNI 规范,用户无论选择哪种方案,得到的网络模型都一样,即每个 Pod 都有独立的 IP,可以直接通信。 区别在于不同方案的底层实现不同,有的采用基于 VxLAN 的 Overlay 实现,有的则是 Underlay,性能上有区别。再有就是是否支持 Network Policy。

k8s网络模型设计基础原则:每个Pod都拥有一个独立的 IP地址,而且 假定所有 Pod 都在一个可以直接连通的、扁平的网络空间中 。 所以不管它们是否运行在同 一 个 Node (宿主机)中,都要求它们可以直接通过对方的 IP 进行访问。 设计这个原则的原因 是,用户不需要额外考虑如何建立 Pod 之间的连接,也不需要考虑将容器端口映射到主机端口等问题

由于 Kubemetes 的网络模型假设 Pod 之间访问时使用的是对方 Pod 的实际地址,所以一个
Pod 内部的应用程序看到的自己的 IP 地址和端口与集群内其他 Pod 看到的一样。它们都是 Pod 实际分配的IP地址 (从dockerO上分配的)。将IP地址和端口在Pod内部和外部都保持一致, 我们可以不使用 NAT 来进行转换,地址空间也自然是平的。

鉴于上面这些要求,我们需要解决四个不同的网络问题::

  • Docker容器和Docker容器之间的网络
  • Pod与Pod之间的网络
  • Pod与Service之间的网络
  • Internet与Service之间的网络

使用kubectl get nodes查看集群节点,看到master节点处于NotReady状态,这是因为集群网络组建flannel并未安装

容器和容器之间的网络

在这里插入图片描述

  1. 在k8s中每个Pod中管理着一组Docker容器,这些Docker容器共享同一个网络命名空间。
  2. Pod中的每个Docker容器拥有与Pod相同的IP和port地址空间,并且由于他们在同一个网络命名空间,他们之间可以通过localhost相互访问。

什么机制让同一个Pod内的多个docker容器相互通信那?其实是使用Docker的一种网络模型:–net=container

container模式指定新创建的Docker容器和已经存在的一个容器共享一个网络命名空间,而不是和宿主机共享。新创建的Docker容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等

每个Pod容器有有一个pause容器其有独立的网络命名空间,在Pod内启动Docker容器时候使用 –net=container就可以让当前Docker容器加入到Pod容器拥有的网络命名空间(pause容器)
在这里插入图片描述

Pod与Pod之间的网络

  1. k8s中,每个Pod拥有一个ip地址,不同的Pod之间可以直接使用该ip与彼此进行通讯。
  2. 在同一个Node上,从Pod的视角看,它存在于自己的网络命名空间中,并且需要与该Node上的其他网络命名空间上的Pod进行通信。

Linux以太网桥(Linux Ethernet bridge)是一个虚拟的2层网络设备,目的是把多个以太网段链接起来, 网桥维护了一个转发表,通过检查转发表通过它传输的数据包的目的地并决定是否将数据包传递到连接到网桥的其他网段,网桥代码通过查看网络中每个以太网设备特有的MAC地址来决定是传输数据还是丢弃数据。

网桥实现了ARP协议用来根据给定的ip地址找到对应机器的数据链路层的mac地址,一开始转发表为空,当一个数据帧被网桥接受后,网桥会广播该帧到所有的链接设备(除了发送方设备),并且把响应这个广播的设备记录到转发表;随后发往相同ip地址的流量会直接从转发表查找正确的mac地址,然后转发包到对应的设备。

在这里插入图片描述如上图显示了两个Pod通过veth对链接到root网络命名空间,并且通过网桥进行通信

同一个Node中的Pod之间的一次通信

鉴于每个Pod有自己独立的网络命名空间,我们使用虚拟以太网设备把多个Pod的命名空间链接到了root命名空间,并且使用网桥让多个Pod之间进行通信,下面我们看如何在两个pod之间进行通信:
在这里插入图片描述

  1. 通过网桥这里把veth0和veth1组成为一个以太网,他们直接是可以直接通信的,另外这里通过veth对让pod1的eth0和veth0、pod2的eth0和veth1关联起来,从而让pod1和pod2相互通信。
  2. Pod 1通过自己默认的以太网设备eth0发送一个数据包,eth0把数据传递给veth0,数据包到达网桥后,网桥通过转发表把数据传递给veth1,然后虚拟设备veth1直接把包传递给Pod2网络命名空间中的虚拟设备eth0.
不同Node中的Pod之间通信

k8s网络模型需要每个pod必须通过ip地址可以进行访问,每个pod的ip地址总是对网络中的其他pod可见,并且每个pod看待自己的ip与别的pod看待的是一样的(虽然他没规定如何实现),下面我们看不同Node间Pod如何交互

k8s中每个集群中的每个Node都会被分配了一个CIDR块(无类别域间路由选择,把网络前缀都相同的连续地址组成的地址组称为CIDR地址块)用来给该Node上的Pod分配IP地址。(保证pod的ip不会冲突)
另外还需要把pod的ip与所在的nodeip关联起来

在这里插入图片描述1. 如上图Node1(vm1)上的Pod1与Node2(vm2)上Pod4之间进行交互。
2. 首先pod1通过自己的以太网设备eth0把数据包发送到关联到root命名空间的veth0上,然后数据包被Node1上的网桥设备cbr0接受到,网桥查找转发表发现找不到pod4的Mac地址,则会把包转发到默认路由(root命名空间的eth0设备),然后数据包经过eth0就离开了Node1,被发送到网络。
3. 数据包到达Node2后,首先会被root命名空间的eth0设备,然后通过网桥cbr0把数据路由到虚拟设备veth1,最终数据表会被流转到与veth1配对的另外一端(pod4的eth0)

每个Node都知道如何把数据包转发到其内部运行的Pod,当一个数据包到达Node后,其内部数据流就和Node内Pod之间的流转类似了。

对于如何来配置网络,k8s在网络这块自身并没有实现网络规划的具体逻辑,而是制定了一套CNI(Container Network Interface)接口规范,开放给社区来实现。

CoreOS的Flannel是k8s中实现CNI规范较为出名的一种实现。

三、Kubernetes-基于flannel的集群网络

Kubernetes-基于flannel的集群网络
参考URL: https://www.kubernetes.org.cn/4105.html
Flannel 安装部署
参考URL: https://www.jianshu.com/p/e4c7f83a2a0b

关于Flannel

官网: https://github.com/coreos/flannel

由CoreOS开发的项目Flannel,可能是最直接和最受欢迎的CNI插件。它是容器编排系统中最成熟的网络结构示例之一,旨在实现更好的容器间和主机间网络。随着CNI概念的兴起,Flannel CNI插件算是早期的入门。

与其他方案相比,Flannel相对容易安装和配置。它被打包为单个二进制文件FlannelD,许多常见的Kubernetes集群部署 工具 和许多Kubernetes发行版都可以默认安装Flannel。Flannel可以使用Kubernetes集群的现有etcd集群来使用API存储其状态信息,因此不需要专用的数据存储。

flannel是一个专为kubernetes定制的三层网络解决方案,主要用于解决容器的跨主机通信问题。

Flannel是CoreOS团队针对Kubernetes设计的一个网络规划实现,简单来说,它的功能有以下几点:

  • 使集群中的不同Node主机创建的Docker容器都具有全集群唯一的虚拟IP地址。
  • 建立一个覆盖网络(overlay network),通过这个覆盖网络,将数据包原封不动的传递到目标容器。覆盖网络是建立在另一个网络之上并由其基础设施支持的虚拟网络。覆盖网络通过将一个分组封装在另一个分组内来将网络服务与底层基础设施分离。在将封装的数据包转发到端点后,将其解封装。
  • 创建一个新的虚拟网卡flannel0接收docker网桥的数据,通过维护路由表,对接收到的数据进行封包和转发(vxlan)。
  • 路由信息一般存放到etcd:多个node上的Flanneld依赖一个etcd cluster来做集中配置服务,etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化
  • Flannel首先会在Node上创建一个名为flannel0的网桥(vxlan类型的设备),并且在每个Node上运行一个名为flanneld的代理.每个node上的flannel代理会从etcd上为当前node申请一个CIDR地址块用来给该node上的pod分配地址。

Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让**集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。**但在默认的Docker配置中,每个Node的Docker服务会分别负责所在节点容器的IP分配。Node内部得容器之间可以相互访问,但是跨主机(Node)网络相互间是不能通信。Flannel设计目的就是为集群中所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得"同属一个内网"且"不重复的"IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。

不同Pod中容器之间的通信

对于此场景,情况现对比较复杂一些,这就需要解决Pod间的通信问题。在Kubernetes通过flannel、calic等网络插件解决Pod间的通信问题。本文以flannel为例说明在Kubernetes中网络模型,flannel是kubernetes默认提供网络插件。 Flannel是由CoreOs团队开发社交的网络工具,CoreOS团队采用L3 Overlay模式设计flannel, 规定宿主机下各个Pod属于同一个子网,不同宿主机下的Pod属于不同的子网。

**flannel会在每一个宿主机上运行名为flanneld代理,其负责为宿主机预先分配一个子网,并为Pod分配IP地址。Flannel使用Kubernetes或etcd来存储网络配置、分配的子网和主机公共IP等信息。**数据包则通过VXLAN、UDP或host-gw这些类型的后端机制进行转发。

  • Flannel致力于给k8s集群中的nodes提供一个3层网络,他并不控制node中的容器是如何进行组网的,仅仅关心流量如何在node之间流转。

在这里插入图片描述* 如上图ip为10.1.15.2的pod1与另外一个Node上的10.1.20.3的pod2进行通信。

  • 首先pod1通过veth对把数据包发送到docker0虚拟网桥,网桥通过查找转发表发现10.1.20.3不在自己管理的网段,就会把数据包
    转发给默认路由(这里为flannel0网桥)

  • flannel.0网桥是一个vxlan设备,flannel.0收到数据包后,由于自己不是目的地10.1.20.3,也要尝试将数据包重新发送出去。数据包沿着网络协议栈向下流动,在二层时需要封二层以太包,填写目的mac地址,这时一般应该发出arp:”who is 10.1.20.3″。但vxlan设备的特殊性就在于它并没有真正在二层发出这个arp包,而是由linux kernel引发一个”L3 MISS”事件并将arp请求发到用户空间的flanned程序。

  • flanned程序收到”L3 MISS”内核事件以及arp请求(who is 10.1.20.3)后,并不会向外网发送arp request,而是尝试从etcd查找该地址匹配的子网的vtep信息,也就是会找到node2上的flanel.0的mac地址信息,flanned将查询到的信息放入node1 host的arp cache表中,flanneel0完成这项工作后,linux kernel就可以在arp table中找到 10.1.20.3对应的mac地址并封装二层以太包了:

  • 由于是Vlanx设备,flannel0还会对上面的包进行二次封装,封装新的以太网mac帧:

  • node上2的eth0接收到上述vxlan包,kernel将识别出这是一个vxlan包,于是拆包后将packet转给node上2的flannel.0。flannel.0再将这个数据包转到docker0,继而由docker0传输到Pod2的某个容器里。

如上图,总的来说就是建立VXLAN 隧道,通过UDP把IP封装一层直接送到对应的节点,实现了一个大的 VLAN。

Pod与Service之间的网络

上面展示了Pod之间如何通过他们自己的ip地址进行通信,但是**pod的ip地址是不持久的,当集群中pod的规模缩减或者pod故障或者node故障重启后,新的pod的ip就可能与之前的不一样的。**所以k8s中衍生出来Service来解决这个问题。

**k8s中 Service管理了一系列的Pods,每个Service有一个虚拟的ip,要访问service管理的Pod上的服务只需要访问你这个虚拟ip就可以了,这个虚拟ip是固定的 ** ,当service下的pod规模改变、故障重启、node重启时候,对使用service的用户来说是无感知的,因为他们使用的service的ip没有变。

当数据包到达Service虚拟ip后,数据包会被通过k8s给该servcie自动创建的负载均衡器路由到背后的pod容器。

Internet与Service之间的网络

Kubernetes Pod 是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结束。 通过 ReplicaSets 能够动态地创建和销毁 Pod(例如,需要进行扩缩容,或者执行 滚动升级)。 每个 Pod 都会获取它自己的 IP 地址,即使这些 IP 地址不总是稳定可依赖的。 这会导致一个问题:在 Kubernetes 集群中,如果一组 Pod(称为 backend)为其它 Pod (称为 frontend)提供服务,那么那些 frontend 该如何发现,并连接到这组 Pod 中的哪些 backend 呢?

Kubernetes Service 定义了这样一种抽象:逻辑上的一组 Pod,一种可以访问它们的策略 —— 通常称为微服务。

在k8s的集群中,我们都知道应用服务被打包成容器,而容器放在pod中运行,每个pod都会被分配到一个IP地址,当然我们还是可以按照上述的方式来访问这些pod里面的服务,但是,一个应用往往会部署多个pod(保证可靠性和健壮性),而不同的pod也会因为需求的不同或者节点宕机等原因被销毁或者新建(对应又是新的IP),所以k8s定义了service这一层抽象层,用来解决这个问题,**将同一应用下的一组pod进行打包,然后通过service来进行统一访问(主要通过label来实现),用户只需要访问service这一层,而不需要在意service之后的pod是怎么调度运行的。**也就是说,service会负责把用户的访问请求转发到pod对应的端口上,或者是说service会将pod对应的端口和service上暴露给用户访问的端口建立映射关系。

安装Pod网络插件(CNI) flannel

参考URL: http://www.360doc.com/document/19/0106/20/39911641_807063696.shtml

master节点使用 kubectl apply -f 配置网络插件

master节点使用 kubectl apply -f 配置网络插件
安装flannel

[root@master ~]# kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
unable to recognize "https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml": no matches for kind "DaemonSet" in version "extensions/v1beta1"
unable to recognize "https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml": no matches for kind "DaemonSet" in version "extensions/v1beta1"
unable to recognize "https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml": no matches for kind "DaemonSet" in version "extensions/v1beta1"
unable to recognize "https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml": no matches for kind "DaemonSet" in version "extensions/v1beta1"
unable to recognize "https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml": no matches for kind "DaemonSet" in version "extensions/v1beta1"
[root@master ~]# 

如上,官方链接flannel不行,换个链接
master节点使用 kubectl apply -f 配置网络插件

[root@master ~]# kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

node中安装flannel网络插件

经过测试,利用kubeadm搭建的环境,master执行kubectl apply -f 安装flannel后,
node节点只需要执行加入master 命令。

通过master 查看pod状态,可以看到node节点的flannel状态。

[root@master ~]# kubectl get pods -n kube-system 

如果node节点下载flannel失败,利用其它仓库下载,打tag的方式安装flannel镜像。

docker pull registry.cn-hangzhou.aliyuncs.com/huya_k8s_gcr_io/coreos_flannel:v0.11.0-amd64
docker tag registry.cn-hangzhou.aliyuncs.com/huya_k8s_gcr_io/coreos_flannel:v0.11.0-amd64 quay.io/coreos/flannel:v0.11.0-amd64

五、参考

带你认识Kubernetes 网络插件Flannel与Calico
参考URL: http://team.jiunile.com/blog/2020/10/k8s-cni.html

  • 7
    点赞
  • 111
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西京刀客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值