2024-07-05

一、认识YAML

1.什么是YAML

YAML语言创建于2001年,模仿了XML,但不同于XML,更适合人类阅读,便于计算机解析,JSON是YMAL子集,任何JSON文档都是YAML文档。

YAML支持证书、浮点数、布尔、字符串、数组和对象等数据类型。

YAML语法更简单,形式也更清晰紧凑,其主要规则如下:

使用缩进表示层次缩进不允许使用tab,只能使用空格,缩进数不要求,要保证同一层级缩进保持一致

使用“#”书写注释

数组(列表)是使用 “-”开头的清单形式

对象(字典)的格式与JSON基本相同,但Key不需要使用双引号。

表示对象的“:”和表示数组的“-”后面都必须有空格。

可以使用“---”在一个文件里分隔多个YAML对象。

2.YAML实例

YAML数组(列表)

ProgrammingLauguages:

 - Python

 - Java

 - JavaScript

对应json是这样的:

{

  "ProgrammingLanguages":[

   "Python",

   "Java",

   "JavaScript"

  ]

}

YAML对象(字典)

CloudResources:

  virtualMachines: 5

  storageAccounts: 2

对应json是这样:

{

  “CloudResources”:{

    "virtualMachines": 5,

    "storageAccounts":2

  }  

}

复杂例子,组合数组和对象

DataCenter:

  primary:

    - database: active                                         ######使用横杠的是列表

    - cache: active

  secondary:

    - webserver:active

    - load-balancer:inactive

    - storage-drvers: [s3,cepth,glusterfs]

对应的json为:

{

    “DataCenter”: {

        "primary":[

            {

                "database":"active"

            },

            {

                "cache":"active"

            }

    ],

    "secondary": [

        {   

              "webserver": "active"

        },

        {

              "load-balanver": "iactive"

        },

        {

              "storage-drivers": ["s3","ceph","glusterfs"]

        }

    ]

  }

}

 3.总结YAML

二、API资源对象POD

在K8s里,YAML用来声明API对象的,那么API对象都有哪些?可以这样查看资源对象:
kubectl api-resources
Pod为K8s里最小、最简单的资源对象
运行一个pod
kubectl run pod-demo --image=registry.cn-hangzhou.aliyuncs.com/daliyused/lucky:2.8.3
[root@aminglinux01 ~]# kubectl run pod-demo --image=registry.cn-hangzhou.aliyuncs.com/daliyused/lucky:2.8.3
pod/pod-demo created
从已知Pod导出YAML文件:
kubectl get pod pod-demo -o yaml  > pod-demo.yaml
[root@aminglinux01 ~]# kubectl get pod pod-demo -o yaml > pod-demo.yaml
[root@aminglinux01 ~]# ls
 anaconda-ks.cfg
 calico-cni.tar
 calico-kube-controllers.tar
 calico-node.tar
 calico-pod2daemon.tar
 calico.yaml
 pod-demo.yaml

Pod YAML示例:

四个核心部分:apiVersion、Kind、metadata、spec
apiVersion: v1             ###版本
kind: Pod                  ###资源类型
metadata:                  ###元数据,用来定义名称,命令空间,便签
  labels:                                     ## labels字段非常关键,它可以添加任意数量的Key-Value,目的是为了让pod的信息更加详细
    run: pod-demo
  name: pod-demo           ####pod的名称
  namespace: default       ####可以自定义命令空间“kubectl create name [命令空间的名字]”
spec:                   ##用来定义该pod更多的资源信息,比如containers, volume, storage
  containers:              ##定义容器属性
  - image: registry.cn-hangzhou.aliyuncs.com/*/lucky:2.8.3    ###镜像地址,一个元素的列表
    imagePullPolicy: IfNotPresent    IfNotPresent  ##镜像拉取策略,三种:Always/Never/IfNotPresent,一般默认是IfNotPresent,也就是说只有本地不存在才会远程拉取镜像,可以减少网络消耗。
    name: pod-demo          #####容器名称
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File

vi ngx-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: ngx-pod
  namespace: aming
  labels:  ## labels字段非常关键,它可以添加任意数量的Key-Value,目的是为了让pod的信息更加详细
    env: dev                                                ####自定义信息
spec:  ##用来定义该pod更多的资源信息,比如containers, volume, storage
  containers:  ##定义容器属性
  - image: nginx:1.23.2
    imagePullPolicy: IfNotPresent  ##镜像拉取策略,三种:Always/Never/IfNotPresent,一般默认是IfNotPresent,也就是说只有本地不存在才会远程拉取镜像,可以减少网络消耗。
    name: ngx
    env:  ##定义变量,类似于Dockerfile里面的ENV指令
      - name: os
        value: "Rocky Linux"
    ports:
    - containerPort: 80
创建命名空间
“kubectl create name [命令空间的名字]
使用YAML创建pod:
kubectl apply -f ngx-pod.yaml
[root@aminglinux01 ~]# kubectl apply -f ng-pod.yaml 
pod/ngnix created
[root@aminglinux01 ~]# kubectl get pods
NAME                      READY   STATUS             RESTARTS   AGE
lucky-6cdcf8b9d4-qslbj    1/1     Running            0          16h
ngnix                     1/1     Running            0          76s
[root@aminglinux01 ~]# 

创建过程;

[root@aminglinux01 ~]# kubectl describe pod ngnix
Name:             ngnix
Namespace:        default
Priority:         0
Service Account:  default
Node:             aminglinux02/192.168.100.152
Start Time:       Fri, 05 Jul 2024 11:40:40 -0400
Labels:           run=ngnix
Annotations:      cni.projectcalico.org/containerID: 8d4288f1decd87f5006219dd67d042e92cd4dcef962230924e5fdf0191bbb410
                  cni.projectcalico.org/podIP: 10.18.206.198/32
                  cni.projectcalico.org/podIPs: 10.18.206.198/32
Status:           Running
IP:               10.18.206.198
IPs:
  IP:  10.18.206.198
Containers:
  pod-demo1:
    Container ID:   containerd://c85b8ed0c6429b8b60e94ae96ef0dcc52e00df366bae00ffaf9a8b9fe45ba819
    Image:          registry.cn-hangzhou.aliyuncs.com/*/nginx
    Image ID:       registry.cn-hangzhou.aliyuncs.com/*/nginx@sha256:c94f3436f3bfcb467e9723bdb4957e2e86c00cc5f21e38a40d668c1a4c324696
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Fri, 05 Jul 2024 11:40:42 -0400
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-kvcw9 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-kvcw9:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  26s   default-scheduler  Successfully assigned default/ngnix to aminglinux02
  Normal  Pulling    26s   kubelet            Pulling image "registry.cn-hangzhou.aliyuncs.com/daliyused/nginx"
  Normal  Pulled     25s   kubelet            Successfully pulled image "registry.cn-hangzhou.aliyuncs.com/daliyused/nginx" in 530.576182ms (530.583435ms including waiting)
  Normal  Created    25s   kubelet            Created container pod-demo1
  Normal  Started    25s   kubelet            Started container pod-demo1
[root@aminglinux01 ~]# 

删除pod:

kubectl delete -f ngx-pod.yaml
[root@aminglinux01 ~]# kubectl delete -f ng-pod.yaml 
pod "ngnix" deleted
[root@aminglinux01 ~]# kubectl get pods
NAME                      READY   STATUS             RESTARTS   AGE
lucky-6cdcf8b9d4-qslbj    1/1     Running            0          16h
[root@aminglinux01 ~]# 

三、Pod原理和生命周期

1.Pod原理
Pod 是在 K8s集群中运行部署应用或服务最小单元 pod由一个或者多个容器组成 这些容器能够共享网络、存储以及 CPU/内存等资源。
每个Pod都有一个特殊的被称为 “根容器” 的Pause容器 Pause容器的主要作用是为Pod中的其他容器提供一些基本的功能,比如网络和PID命名空间的共享、 负责管理Pod内容器的生命周期
  • 网络命名空间共享: pause容器为整个Pod创建一个网络命名空间 ,Pod内的其他容器都将加入这个网络命名空间。这样,Pod中的所有容器都可以共享同一个IP地址和端口空间,从而实现容器间的紧密通信。
  • PID命名空间共享:pause容器充当Pod内其他容器的父容器,它们 共享同一个PID命名空间。这使得Pod内的容器可以通过进程ID直接发现和相互通信,同时也使得Pod具有一个统一的生命周期。
  • 生命周期管理: pause容器作为Pod中其他容器的父容器,负责协调和管理它们的生命周期。当pause容器启动时,它会成为Pod中其他容器的根容器。当 pause容器终止时,所有 其他容器也会被自动终止,确保了整个Pod的生命周期的一致性。
  • 保持Pod状态:pause容器保持运行状态,即使Pod中的其他容器暂时停止或崩溃,也可以确保Pod保持活跃。这有助于Kubernetes更准确地监视和管理Pod的状态。
2.Pod生命周期
Pod生命周期包括以下几个阶段:
  • Pending:在此阶段, Pod被创建,但尚 未调度到运行节点上。此时,Pod可能还在等待被调度, 或者因为某些限制(如资源不足)而无法立即调度
  • Running:在此阶段 Pod已被调度到一个节点,并创建了所有的容器。至少有一个容器正在运行,或者正在启动或重启。
  • Succeeded:在此阶段, Pod中的所有容器都已成功终止,并且不会再次重启
  • Failed:在此阶段,Pod中的 至少一个容器已经失败(退出码非零)。这意味着容器已经崩溃或以其他方式出错。
  • Unknown:在此阶段,Pod的状态无法由Kubernetes确定。这 通常是因为与Pod所在节点的通信出现问题
除了这些基本的生命周期阶段之外,还有一些更详细的容器状态,用于描述容器在Pod生命周期中的不同阶段:
  • ContainerCreating:容器正在创建,但尚未启动。
  • Terminating:容器正在终止,但尚未完成。
  • Terminated:容器已终止。
  • Waiting:容器处于等待状态,可能是因为它正在等待其他容器启动,或者因为它正在等待资源可用。
  • Completed:有一种Pod是一次性的,不需要一直运行,只要执行完就会是此状态。
3.创建Pod流程
1. 用户通过kubectl或其他API客户端提交Pod对象给API server
2. API server尝试将Pod对象的相关信息存入etcd中,写入操作完成API server会返回确认信息至客户端。
3. API server开始反映etcd中的状态变化
4. 所有的Kubernetes组件使用watch机制来跟踪检查API server上的相关变化
5. scheduler通过其watcher观察API server创建新的Pod对象尚未绑定至任何节点
6. scheduler为Pod对象挑选一个工作节点并将结果更新至API server
7. 调度结果由API server更新至etcd,而且API server也开始反映此Pod对象的调度结果。
8. Pod 被调度的目标工作节点上的kubelet尝试在当前节点上调用Containerd启动容器,并将容器的结果状态返回至API server。
9. API server将Pod 状态信息存入etcd
10. 在etcd确认写操作完成后,API server将确认信息发送至相关的kubelet。
4.删除Pod流程
1. 请求删除Pod。
2. API server将Pod标记为Terminating状态
3. (与第 2 步同时进行)kubelet监控Pod对象转为Terminating状态同时启动Pod关闭过程。
4. (与第 2 步同时进行)Service将Endpoint摘除
5. 如果当前Pod对象定义了preStop hook,则在其标记为Terminating后会以同步的方式执行宽限期开始计时
6. Pod中的容器进程收到TERM信号
7. 宽限期结束后,若进程仍在运行,会收到SIGKILL信号
8. kubelet请求API server将此Pod对象的宽限期设置为0,从而完成删除操作

四、Pod资源限制

1.Resource Quota
资源配额Resource Quotas (简称quota)是对namespace进行资源配额,限制资源使用的一种策略。 
K8S是一个多用户架构,当多用户或者团队共享一个K8S系统时,SA使用quota防止用户(基于namespace的)的资源抢占,定义好资源分配策略
Quota应用在Namespace 默认 情况下, 没有Resource Quota 的,需要另外创建Quota,并且每个Namespace最多 只能有一个Quota对象
可限定资源类型:
计算资源:limits.cpu、requests.cpu、limits.memory、requests.memory
存储资源,包括存储资源的总量以及指定storage class的总量
    requests.storage:存储资源总量,如500Gi
    persistentvolumeclaims:pvc的个数
对象数,即可创建的对象的个数
    pods, replicationcontrollers, configmaps, secrets,persistentvolumeclaims,services, services.loadbalancers,services.nodeports
注意:
Quota依赖于资源管理器 ,可以使用资源对象limits或者在创建资源对象时为pod设置资源限制(resources),如果不设置,资源对象无法创建。
当该namespace中的任意个额度达到预设Quota时,将无法创建资源对象。
 
Resource Quota示例:
cat > quota.yaml <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
  namespace: aming
  name: aming-quota

spec:
  hard:
    pods: 50  ## 该命名空间里最多支持启动50个Pods
    requests.cpu: 0.5 ##最低保证0.5个CPU资源
    requests.memory: 512Mi ##最低保证512M内存
    limits.cpu: 5  ##最多使用5核CPU
    limits.memory: 16Gi ##最多使用16G内存
    configmaps: 20 ##最多支持20个configMaps
    persistentvolumeclaims: 20 ##最多支持20个pvc
    replicationcontrollers: 20 ##最多支持20个replicationControllers
    secrets: 20 ##最多支持20个secrets
    services: 50 ##最多支持50个services
EOF
apiVersion: v1
kind: ResourceQuota
metadata:
  name: yeyunyi-quota
  namespace: yeyunyi

spec:
  hard:
    pods: 50              ####该命名空间里最多支持启动50个pods
    requests.cpu: 0.5       #####最低保证0.5个资源,这里的0.5=500m,1=1000m
    requests.memory: 512Mi    ####限最低保证512M内存
    limits.cpu: 2           ####限制内存资源最多使用2核
    limits.memory: 2Gi      ####限制内存资源最多使用2G
    configmaps: 20          ####最多支持20个configMaps
    persistentvolumeclaims: 10        ####最多支持10个pvc
    replicationcontrollers: 10        ####最多支持10个replicationcontrollers:
    secrets: 10               ####最多支持10个secrets
    services: 50              ####最多支持50个services
生效
kubectl apply -f quota.yaml
[root@aminglinux01 ~]# kubectl apply -f  quota.yaml 
resourcequota/yeyunyi-quota created

查看

kubectl get quota -n yeyunyi
[root@aminglinux01 ~]# kubectl get quota -n yeyunyi
NAME            AGE   REQUEST                                                                                                                                                                   LIMIT
yeyunyi-quota   19s   configmaps: 1/20, persistentvolumeclaims: 0/10, pods: 0/10, replicationcontrollers: 0/10, requests.cpu: 0/500m, requests.memory: 0/512Mi, secrets: 0/10, services: 0/50   limits.cpu: 0/2, limits.memory: 0/2Gi
[root@aminglinux01 ~]# 
测试:
为了显示限额效果,修改quota.yaml,将pod限制数改为10,其它先删除掉
命令行创建deployment,指定Pod副本为5
 kubectl create deployment lucky --image=registry.cn-hangzhou.aliyuncs.com/daliyused/lucky:2.8.3 -n yeyunyi --replicas=5
[root@aminglinux01 ~]# kubectl create deployment lucky --image=registry.cn-hangzhou.aliyuncs.com/*/lucky:2.8.3 -n yeyunyi --replicas=5
deployment.apps/lucky created

查看deployment和pod

kubectl get  deploy,po -n yeyunyi
[root@aminglinux01 ~]# kubectl get  deploy,po -n yeyunyi
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/lucky   0/5     0            0           2s

[root@aminglinux01 ~]# kubectl describe deployment lucky -n yeyunyi
Name:                   lucky
Namespace:              yeyunyi
CreationTimestamp:      Fri, 05 Jul 2024 15:05:18 -0400
Labels:                 app=lucky
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=lucky
Replicas:               5 desired | 0 updated | 0 total | 0 available | 5 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=lucky
  Containers:
   lucky:
    Image:        registry.cn-hangzhou.aliyuncs.com/*/lucky:2.8.3
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type             Status  Reason
  ----             ------  ------
  Progressing      True    NewReplicaSetCreated
  Available        False   MinimumReplicasUnavailable
  ReplicaFailure   True    FailedCreate
OldReplicaSets:    <none>
NewReplicaSet:     lucky-6cdcf8b9d4 (0/5 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  46s   deployment-controller  Scaled up replica set lucky-6cdcf8b9d4 to 5

删除Quota限制

[root@aminglinux01 ~]# kubectl delete -f  quota.yaml 
resourcequota "yeyunyi-quota" deleted
[root@aminglinux01 ~]# kubectl get quota -n yeyunyi
No resources found in yeyunyi namespace.
[root@aminglinux01 ~]# kubectl get  deploy,po -n yeyunyi
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/lucky   0/5     0            0           9m28s

重新创建

[root@aminglinux01 ~]# kubectl create deployment lucky1 --image=registry.cn-hangzhou.aliyuncs.com/*/lucky:2.8.3 -n yeyunyi --replicas=5
deployment.apps/lucky1 created
[root@aminglinux01 ~]# kubectl get quota -n yeyunyi
No resources found in yeyunyi namespace.
[root@aminglinux01 ~]# kubectl get  deploy,po -n yeyunyi
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/lucky    0/5     0            0           10m
deployment.apps/lucky1   5/5     5            5           6s

NAME                          READY   STATUS    RESTARTS   AGE
pod/lucky1-5cf7f459cf-6p9xz   1/1     Running   0          6s
pod/lucky1-5cf7f459cf-7t2gj   1/1     Running   0          6s
pod/lucky1-5cf7f459cf-bcsl5   1/1     Running   0          6s
pod/lucky1-5cf7f459cf-dv5fz   1/1     Running   0          6s
pod/lucky1-5cf7f459cf-jmvnr   1/1     Running   0          6s
[root@aminglinux01 ~]# kubectl get  deploy,po -n yeyunyi
2.Pod的limits和requests
Resource Quota是针对namespace下面所有的Pod的限制,而Pod自身也有限制。
示例:

 

apiVersion: v1
kind: Pod
metadata:
  name: quota-pod
  namespace: yeyunyi

spec:
  containers:
  - image: nginx:latest              ####该命名空间里最多支持启动50个pods
    name: nginx       #####最低保证0.5个资源,这里的0.5=500m,1=1000m
    imagePullPolicy: IfNotPresent    ####限最低保证512M内存
    ports:
    - containerPort: 80           ####限制内存资源最多使用2核
    resources:
      limits:
        cpu: 0.5        ####限制pod,cpu资源最多使用500m,这里的0.5=500m,1=1000m
        memory: 2Gi     ####限制内存资源最多使用2G
      requests:
        cpu: 200m        ####k8s要保证pod使用最小内存为200m,如果node上资源满足不了则不会调度到该node上。
        memory: 512Mi     ####k8s要保证pod使用最小内存为512M,如果node上资源满足不了则不会调度到该node上。
生效
kubectl apply -f quota-pod.yaml
[root@aminglinux01 ~]# kubectl apply -f quota-pod.yaml 
pod/quota-pod created

验证:

kubectl get pods -n yeyunyi

[root@aminglinux01 ~]# kubectl get pods -n yeyunyi
NAME                      READY   STATUS    RESTARTS   AGE
lucky-6cdcf8b9d4-4ws4c    1/1     Running   0          20m
lucky-6cdcf8b9d4-6vc5g    1/1     Running   0          20m
lucky-6cdcf8b9d4-8fql2    1/1     Running   0          20m
lucky-6cdcf8b9d4-g8p26    1/1     Running   0          20m
lucky-6cdcf8b9d4-mxm97    1/1     Running   0          20m
lucky1-5cf7f459cf-6p9xz   1/1     Running   0          21m
lucky1-5cf7f459cf-7t2gj   1/1     Running   0          21m
lucky1-5cf7f459cf-bcsl5   1/1     Running   0          21m
lucky1-5cf7f459cf-dv5fz   1/1     Running   0          21m
lucky1-5cf7f459cf-jmvnr   1/1     Running   0          21m
quota-pod                 1/1     Running   0          58s
[root@aminglinux01 ~]# 

 kubectl describe pod quota-pod -n yeyunyi 

[root@aminglinux01 ~]# kubectl describe pod quota-pod -n yeyunyi
Name:             quota-pod
Namespace:        yeyunyi
Priority:         0
Service Account:  default
Node:             aminglinux03/192.168.100.153
Start Time:       Fri, 05 Jul 2024 15:35:26 -0400
Labels:           <none>
Annotations:      cni.projectcalico.org/containerID: ef000cfa696eaeab022c4d4a0769c017b93ad097e41d3c4a6f85dcf7cd7f269c
                  cni.projectcalico.org/podIP: 10.18.68.136/32
                  cni.projectcalico.org/podIPs: 10.18.68.136/32
Status:           Running
IP:               10.18.68.136
IPs:
  IP:  10.18.68.136
Containers:
  nginx:
    Container ID:   containerd://ff86f3c223294ecb15a595efb989b6db179cba16013debaa19df5752197abfef
    Image:          nginx:latest
    Image ID:       docker.io/library/nginx@sha256:67682bda769fae1ccf5183192b8daf37b64cae99c6c3302650f6f8bf5f0f95df
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Fri, 05 Jul 2024 15:35:33 -0400
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     500m
      memory:  2Gi
    Requests:
      cpu:        200m
      memory:     512Mi
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-8wp57 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-8wp57:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   Burstable
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  2m51s  default-scheduler  Successfully assigned yeyunyi/quota-pod to aminglinux03
  Normal  Pulling    2m51s  kubelet            Pulling image "nginx:latest"
  Normal  Pulled     2m45s  kubelet            Successfully pulled image "nginx:latest" in 6.075546841s (6.075558002s including waiting)
  Normal  Created    2m45s  kubelet            Created container nginx
  Normal  Started    2m45s  kubelet            Started container nginx
[root@aminglinux01 ~]# 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值