2、k8s pod原理详解

Kubernetes Pod 介绍

Pod 直译是豆荚,可以把容器想像成豆荚里的豆子,把一个或多个关系紧密的豆子包在一起就是豆荚(一个 Pod)。在 k8s 中我们不会直接操作容器,而是把容器包装成 Pod 再进行管理。

Pod 介绍与原理

是 Kubernetes 项目中最小的 API 对象。如果换一个更专业的说法,我们可以这样描述:
Pod,是 Kubernetes 项目的原子调度单位。
Pod 是运行服务的基础.
基础容器是 pause.
每启动一个 Pod 都会附加启动这样一个容器,它的作用就只是简单的等待,设置 Pod 的网络。
一个 Pod 中的应用容器共享同一组资源:
(1)PID 命名空间:Pod 中的不同应用程序可以看见其他应用程序的进程 ID
(2)网络命名空间:Pod 中的多个容器能访问同一个 IP 和端口范围
(3)IPC 命名空间:Pod 中的多个容器能够使用 SystemV IPC 或 POSIX 消息队列进行通信。
(4)UTS 命名空间:Pod 中的多个容器共享一个主机名
(5)Volumes(共享存储卷):Pod 中的各个容器可以访问在 Pod 级别定义的 Volumes
每个 pod 中容器的镜像应该不同(不同的应用),避免端口重复
在这里插入图片描述

POD操作实战

POD的创建和删除

创建一个nginx pod

kubectl run ng --image=nginx:1.9 --port=80

状态检查

kubectl  get pod name
kubectl  describe pod name
kubectl  logs name

通过yaml创建pod,通过–dry-run来生成一个yaml文件

kubectl run ng --image=nginx:1.9 --port=80 --dry-run=client -o yaml >ng.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
  - image: nginx:1.9
    name: ng
    ports:
    - containerPort: 80
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

一个pod运行多个容器

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng01
  name: multi-pod
spec:
  containers:
    - image: nginx:1.9
      name: ng01
      ports:
        - containerPort: 80
      resources: { }
    - image: tomcat:8.0.41-jre8-alpine
      name: tomcat8-1
      ports:
        - containerPort: 8080
      resources: { }
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: { }

这个容器运行了两个容器nginx和tomcat,当执行了kubectl apply -f multi.yaml过后,查看pod如下:
在这里插入图片描述删除pod

k delete pod ng

pod的生命周期管理

Pod 生命周期的变化,主要体现在 Pod API 对象的 Status 部分,这是它除了
Metadata 和 Spec 之外的第三个重要字段。其中, pod.status.phase,就是 Pod 的当前状
态,它有如下几种可能的情况:

Pending:这个状态意味着, Pod 的 YAML 文件已经提交给了 Kubernetes, API 对象已经被创建并保存在 Etcd 当中。但是,这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
Running:这个状态下, Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
Succeeded:这个状态意味着, Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。
Failed:这个状态下, Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。
Unknown:这是一个异常状态,意味着 Pod 的状态不能持续地被 kubelet 汇报给kube-apiserver,这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题。

更进一步地, Pod 对象的 Status 字段,还可以再细分出一组 Conditions。这些细分状
态的值包括: PodScheduled、 Ready、 Initialized,以及 Unschedulable。它们主要用于描述造成当前 Status 的具体原因是什么。

Restart policy
Restart policy for all containers within the pod. One of Always, OnFailure, Never.
Default to Always

如果是deployment的话,那么它的状态是Always,当pod消失挂掉以后,会自动重启

资源的配额和限制

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
  - image: nginx:1.9
    name: ng
    ports:
    - containerPort: 80
    resources:
     limits:
      memory: "200Mi"
      cpu: "700m"
     requests:
      memory: "200Mi"
      cpu: "700m"
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

这个我们加了limits和requests,但是内存都是200Mi,cpu都是700m(cpu使用为1024,表示占700m)
当应用了这个yaml文件以后,我们通过describe看下:
在这里插入图片描述
QoS Class为:Guaranteed
当requests < limits的时候获取没有requests的时候如下:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
  - image: nginx:1.9
    name: ng
    ports:
    - containerPort: 80
    resources:
     limits:
      memory: "200Mi"
      cpu: "700m"
     requests:
      memory: "100Mi"
      cpu: "600m"
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

在这里插入图片描述
QoS Class:Burstable

当都没有配置资源limits和requests的时候如下:
在这里插入图片描述
QoS Class:BestEffort
总结如下:

limits=requests -->Guaranteed
limits > requests -->Burstable
limitis ,requets 为空–>BestEffort
那这三个分别代表什么意思呢?就是说当k8s服务器上运行了很多的pod,那么这些pod都有Qos Class这个属性,当其中的某一个服务器资源不够用的时候,那么这个时候就要驱离pod了,也就是删除这个pod,在k8s的集群中找负载最小的服务器进行重启,所以QosClass这个参数就是用来判断那些先被驱离,通过上面的例子可以看出,当你limits和requests都没有配置的情况下是最先被驱离的,其次就是limits < requests的被驱离,而配置了limits和requests相等的pod是保留的。所以驱离的顺序:

BestEffort > Burstable > Guaranteed

静态pod

静态pod是什么呢?我们联想下java中的静态变量,静态变量在整个系统中是只存在一份的,所以静态变量是被所有的对象共享的;而k8s中的静态pod就表示在指定的目录中是存放静态pod的,当你将一个yaml文件放在静态pod所在目录,那么k8s会自动应用这个yaml文件,也就是说不需要你手动应用;
静态 Pod 是由 kubectl 进行管理的仅存于特定 Node 上的 Pod。他们不能通过 API Server 进行管理,无法与 ReplicationController、 Deployment 或者 DaemonSet 进行关联,并且 kubelet也无法对他们进行健康检查。静态 Pod 总是由 kubectl 进行创建,并且总是在 kubelet 所在的 Node 上运行。创建 Pod 有两种方式: 配置文件或 HTTP 方式,这里只说常用的配置文件方式:
配置文件方式
在目录 /etc/kubernetes/manifests 写入ng.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
  - image: nginx:1.9
    name: ng
    ports:
    - containerPort: 80
    resources:
     limits:
      memory: "200Mi"
      cpu: "700m"
     requests:
      memory: "100Mi"
      cpu: "600m"
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

首先我们将pod监控起来,如下:
在这里插入图片描述
然后我们把ng放入 /etc/kubernetes/manifests过后再看观看这个pod的变化
在这里插入图片描述
可以看到马上放过去,这边监控就在开始创建了,所以这就是静态pod的作用
k8s中有很多的pod,这些pod都是kube-system命名空间的,如下:
在这里插入图片描述
其实比如调度器这些都是静态pod,都在指定目录创建的,我们看这个目录:
在这里插入图片描述
现在已经正常的
在这里插入图片描述
当我们从这个目录中将这个文件删除过后,这个pod也将被移除
在这里插入图片描述

Init Containers

初始化容器,顾名思义容器启动的时候,会先启动可一个或多个容器,如果有多个,那么这几个 Init Container 按照定义的顺序依次执行,只有所有的 Init Container 执行完后,主容器才会启动。由于一个 Pod 里的存储卷是共享的,所以 Init Container 里产生的数据可以被主容器使用到。

Init Container 可以在多种 K8S 资源里被使用到如 Deployment、Daemon Set, Pet Set, Job 等,但归根结底都是在 Pod 启动时,在主容器启动前执行,做初始化工作。
应用场景:

第一种场景:等待其它模块 Ready, 比如我们有一个应用里面有两个容器化的服务,一个是Web Server,另一个是数据库。其中 Web Server 需要访问数据库。但是当我们启动这个应用的时候,并不能保证数据库服务先启动起来,所以可能出现在一段时间内 Web Server 有数据库连接错误。为了解决这个问题,我们可以在运行 Web Server 服务的 Pod 里使用一个InitContainer,去检查数据库是否准备好,直到数据库可以连接, Init Container 才结束退出,然后 Web Server 容器被启动,发起正式的数据库连接请求。

第二种场景:初始化配置, 比如集群里检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能用这个配置信息加入集群。其它使用场景: 如将 pod 注册到一个中央数据库、下载应用依赖等

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: initcontainer
  name: initcontainer
spec:
  containers:
  - image: nginx:1.9
    name: nginx
    volumeMounts:
    - name: task-pv-storage
      mountPath: "/usr/share/nginx/html"
    resources: {}
  initContainers:
  - image: centos
    name: centos
    imagePullPolicy: IfNotPresent
    command:
    - bin/sh
    - -c
    - echo "this is one initContainers test">>/pod-data/index.html
    volumeMounts:
    - name: task-pv-storage
      mountPath: "/pod-data"
    resources: {}
  volumes:
  - name: task-pv-storage
    emptyDir: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Never
status: {}


k8s健康检查

探针是由 kubelet 对容器执行的定期诊断。要执行诊断,kubelet 调用由容器实现的 Handler。有三种类型的探针: Exec探针:执行进程的地方,容器的状态由进程的退出状态代码确认; Http get探针:向容器发送HTTP GET请求,通过响应的HTTP状态代码判断容器是否准备好;如果响应的状态码大于等于200 且小于 400,则诊断被认为是成功的 Tcp socket探针:它打开一个TCP连接到容器的指定端口,如果连接己建立,则认为容器己准备就绪。

kubernetes会周期性地调用探针,并根据就绪探针的结果采取行动。如果某个pod报告它尚未准备就绪,则会从该服务中删除该pod。如果pod再次准备就绪,则重新添加pod; 每次探测都将获得以下三种结果之一:
成功:容器通过了诊断。
失败:容器未通过诊断。
未知:诊断失败,因此不会采取任何行动

livenessProbe:指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略 的影响。如果容器不提供存活探针,则默认状态为 Success

readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为Failure。如果容器不提供就绪探针,则默认状态为 Success 只要pod的标签和服务的pod选择器匹配,pod就可以作为服务的后端,但是如果pod没有准备好,是不能处理请求的,这时候就需要就绪探针了,用来检查pod是否已经准备好了,如果检查成功就可以作为服务的后端处理消息了;
1、向pod添加检测探针 - 存活检测

#检测/tmp/live,每隔10秒就会被删除,liveness检测,如果被删除,就会返回失败,重启pod。陷入无限循环。
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-pod
  namespace: default
spec:
  containers:
    - name: liveness-exec-container
      image: centos:7
      imagePullPolicy: IfNotPresent
      command: [ "/bin/sh","-c","touch /tmp/live ; sleep 10; rm -rf /tmp/live; sleep 10" ]
      livenessProbe:
        exec:
          command: [ "test","-e","/tmp/live" ]
        initialDelaySeconds: 1
        periodSeconds: 3

#检测/tmp/live,每隔10秒就会被删除,liveness检测,如果被删除,就会返回失败,重启pod。陷入无限循环,如图:在这里插入图片描述
基本上没10s就重启一次,所以这个探针是生效了的
2.向pod添加准备就绪探针

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
    - image: nginx:1.9
      name: ng
      ports:
        - containerPort: 80
      resources: { }
      readinessProbe:
        exec:
          command:
            - ls
            - /var/ready
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: { }

当应用了这个yaml文件以后,在容器中会创建ng这个pod,但是这个pod的状态为running,但是ready状态返回的始终是0,因为在容器里面的/var/下没有ready这个文件,因为我写的是就绪探针是要在ls /var/ready成功,没有这个文件就不会成功
在这里插入图片描述
这个时候我们进入容器,在var下添加一个ready文件,那么pod的ready状态就会变成1

kubectl exec it ng -- sh or
kubectl exec -it ng /bin/bash

在这里插入图片描述
在这里插入图片描述
可以看到确实好了,这是一种就绪探针,但是一般这种探测我们使用http好一点,不可能每次进入容器去添加文件,所以修改下这个ng的yaml文件

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
    - image: nginx:1.9
      name: ng
      ports:
        - containerPort: 80
      resources: { }
      readinessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 3
        periodSeconds: 3
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: { }

代表的意思就是说这个就绪探测是在容器启动过后延长3s执行,每隔3s执行一次,执行http请求,请求的路径是/,请求的端口是80,就是请求当前路径的nginx是否正常,如果请求正常,返回给k8s 的pod代表正常,就将ready修改为1,表示正常,我们看下监控的pod的状态变化:
在这里插入图片描述
可以看到是按照我们的预期执行的,pod从ready=0的状态变成了1的状态是根据配置的调度时间去执行的。

POD镜像升级

三种方式:
K edit pod name
K set image pod pod-name container-name=new image
K apply -f yaml file
Checking upgrading status
K describe pod pod-name # check image name
K get events
K get pod # check restart times.

POD 死掉后就不可以了。只能删除后重新创建。

pod 无法启动可能原因

a) 已有 pod 的名字,在相同命名空间。容器名字可以相同,但是在一个 POD 中的容
器名字不能相同。
b) 镜像名字是否正确,是否在本地存在
c) 是否有 1 号进程在运行。
d) 相应的 PVC, Configmap, secret 是否已经建立。
e) 是否有足够资源
f) 调度到相应的节点
g) 探针配置错误。
h) 镜像本身的错误。

POD调度过程

在这里插入图片描述

  1. 用户提交 pod, APIServer 记录到 etcd 中;
  2. scheduler 周期性查询 APIServer,以获取未绑定的 pod,尝试为 pod 分配节点;
  3. scheduler 调度:首先过滤不符合 pod 资源要求的主机。然后考虑整体优化策略对
    主机打分,比如使用最低负载,使用分散主机等。最后选择最高分的主机存储绑
    定信息到 etcd 中;
  4. kubelet 周期查询绑定对象,获取需要在本机启动的 pod 并通过 docker 启动。

调度例子

nodeSelector

nodeSelector 是节点选择约束的最简单推荐形式。 nodeSelector 是 PodSpec 的一个字段。 它包含键值对的映射。为了使 pod 可以在某个节点上运行,该节点的标签中 必须包含这里的每个键值对(它也可以具有其他标签)。 最常见的用法的是一对键值对。

apiVersion: v1
kind: Pod
metadata:
  name: nginx8
  labels:
    env: test
spec:
  containers:
    - name: nginx
      image: nginx:1.7.9
      imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssy

这个时候应用这个pod以后,看下状态是:
在这里插入图片描述
是异常的,所以我们看下详细:
在这里插入图片描述
意思就是说3个节点中没有找到符合seelctor的可用节点,因为在pod中我们设置了nodeSelector中的disktype=ssy的,但是目前这三个节点都没有设置,现在我们设置一下:

kubectl label node k8s-node2 disktype=ssy

意思就是将node2节点设置一个label为disktype=ssy
在这里插入图片描述
这个时候这个nginx8就可以了,从pending变为running,而ready也变为1了,但是这个nginx8肯定是在k8s-node2这个节点上的,看下:
在这里插入图片描述
所以这就是制定节点部署,就不是k8s来给我自动计算到那个节点。

nodeName

nodeName 是节点选择约束的最简单方法,但是由于其自身限制,通常不使用它nodeName 是 PodSpec 的一个字段。 如果它不为空,调度器将忽略 Pod,并且给定点上运行的 kubelet 进程尝试执行该 Pod。 因此,如果 nodeName 在 PodSpec 中指了,则它优先于上面的节点选择方法
使用 nodeName 来选择节点的一些限制: 如果指定的节点不存在, 如果指定的节点没有资源来容纳 Pod,Pod 将会调度失败并且其原因将显示为, 比如 OutOfmemory 或 OutOfcpu。 云环境中的节点名称并非总是可预测或稳定的。
nodeName的优先级是最高的,就算你在指定的nodeName上打上了污点,那么污点也会失效,也会在上面部署启动;

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
  - image: nginx:1.9
    name: ng
    ports:
    - containerPort: 80
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  nodeName: k8s-node1
status: {}

我这里指定了ng这个pod部署到k8s-node1上
在这里插入图片描述

taint and Tolerance

节点亲和性 是 Pod 的一种属性,它使 Pod 被吸引到一类特定的节点 (这可能出于一种偏好,也可能是硬性要求)。 污点(Taint)则相反——它使节点能够排斥一类特定的 Pod。 容忍度(Toleration)是应用于 Pod 上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。 污点和容忍度(Toleration)相互配合,可以用来避免 Pod 被分配到不合适的节点上。 每个节点上都可以应用一个或多个污点,这表示对于那些不能容忍这些污点的 Pod,是不会被该节点接受的。
简单来说就是如果节点上打了污点,那么如果你不是指定了nodeName的话是不会安装到这个节点上的,如果说节点上打了污点,但是你又指定了nodeName,那么也会在上面安装,而我们的k8s的master在使用kubadm安装的时候,是自动给打上污点了的,也就是如果没有指定nodename的话,那么默认的调度室不会调度到k8s的master上的,我们看下k8s的master上自动打的污点:
在这里插入图片描述
这样,我们把刚刚的那个节点指定nodeName放到k8s-master上看下


apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
  - image: nginx:1.9
    name: ng
    ports:
    - containerPort: 80
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  nodeName: k8s-master
status: {}

在执行看下:
在这里插入图片描述
所以当你指定了nodeName以后,就算该节点上有污点也会在上面部署,也就是nodeName的优先级非常高,我们看下节点的信息:
在这里插入图片描述
是成功的部署到了master上了
在node节点上打污点:

kubectl taint node k8s-node1 key1=value1:NoSchedule

在这里插入图片描述
现在k8s-node1就有了一个污点了,我们将刚刚的那个ng部署到上面看行不行

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
  - image: nginx:1.9
    name: ng
    ports:
    - containerPort: 80
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  nodeSelector:
   disktype: ssy
status: {}

我们将ng部署到label有disktype=ssy的节点上,这个时候我们在k8s-node1上创建一个label为disktype=ssy
在这里插入图片描述创建label为:

kubectl label node k8s-node1 disktype=ssy

删除label为

kubectl label node k8s-node1 disktype-

node1上加了一个label为disktype=ssy的,而nodeSelector又选择的是disktype=ssy的节点,所以ng会部署到node1上,但是node1上打了污点,我们看下是否能成功?
在这里插入图片描述

状态是Pending,我们看下信息:
在这里插入图片描述
是不 成功的,信息如下:
default-scheduler 0/3 nodes are available: 1 node(s) didn’t match Pod’s node affinity/selector, 1 node(s) had taint {key1: value1}, that the pod didn’t tolerate, 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn’t tolerate.
翻译过来的就是说:3个node节点都不可用
1个节点不匹配pod,因为ng这个pod指定了selector;
1个节点有一个污点key1:value1,然后pod这个ng没有指定容忍tolerate配置,也不满足;
1个节点配置了污点role.kubernetes.io/master:(k8s-master)
大概就这么个意思,从上面可以知道打了污点要可以的话就需要在污点上打上一个容忍tolerate,所以我们修改下ng这个yaml文件

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ng
  name: ng
spec:
  containers:
  - image: nginx:1.9
    name: ng
    ports:
    - containerPort: 80
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  tolerations:
   - key: "key1"
     operator: "Equal"
     value: "value1"
     effect: "NoSchedule"
  nodeSelector:
   disktype: ssy
status: {}

加上了tolerations容忍度,也就是说当节点上有配置了容忍就是可以的,容忍的配置:

tolerations:
   - key: "key1"
     operator: "Equal"
     value: "value1"
     effect: "NoSchedule"

容忍的是污点,也就是说配置了容忍污点的信息,也就是可以的,对应的污点是:
在这里插入图片描述

  • key:配置的是污点的key
    opeator:配置的是污点的操作,Equal就是“=”,Exists代表存在,像k8smaster就是配置的Exists
    value:就是“=”后面的value
    errect:效果,就是“:”后面的值NoSchedule
    配置了以后看下还行不行:
    在这里插入图片描述
    很显然是可以的,我们看下ng这个pod的信息:
    在这里插入图片描述
    表示nodeSelector是带有标签为disktype=ssy的节点和
    配置了容忍的污点是key1=value1:NoSchedule

删除污点为:

kubectl taint nodes k8s-node1 key1=value1:NoSchedule-

标签常用命令

查看标签:

查看所有
kubectl get 资源(pod/node/rs/rc/deploy/svc  --shwo-labels)
查看具体的资源:
kubectl get 资源(pod/node/rs/rc/deploy/svc 资源名称  --shwo-labels)
如
kubectl get pod --show-labels(pod的所有标签)
kubectl get pod ng --show-labels(pod为ng的标签)

设置标签

kubectl label 资源(pod/node/rs/deploy/svc 资源名称  标签key=标签值
如
kubectl label pod ng  testlabel=1111

删除标签:

kubectl label 资源(pod/node/rs/deploy/svc 资源名称  标签key-
如
kubectl label pod ng  testlabel-

根据label筛选资源:

kubectl get 资源(pod/node/rs/rc/deploy/svc -l key=value
比如筛选rs
kubectl get rs -l tier=nginx-rs
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值