K8s的五种控制器类型解析

前言

K8S的五种控制器

控制器类型介绍

控制器:又称之为工作负载,分别包含以下类型控制器
1:Deployment
2:StatefulSet
3:DaemonSet
4:Job
5:CronJob

Pod与控制器之间的关系

controllers:在集群上管理和运行容器的对象通过label-selector相关联
Pod通过控制器实现应用的运维,如伸缩,升级等

控制器又被称为工作负载,pod通过控制器实现应用的运维,比如伸缩、升级等
在这里插入图片描述

1. Deployment控制器

适合部署无状态的应用服务,用来管理pod和replicaset,具有上线部署、副本设定、滚动更新、回滚等功能,还可提供声明式更新,例如只更新一个新的Image

测试deployment控制器

编写yaml文件,并创建nginx服务pod资源

//Replicaset 是控制版本,副本数,回滚就是通过此来实现

[root@master demo]# vim nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3	'//指定副本数为3'
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx1
        image: nginx:1.15.4
        ports:
        - containerPort: 80
        

创建pod资源

[root@master demo]# kubectl create -f nginx-deployment.yaml
deployment.apps/nginx-deployment created

查看资源

[root@master demo]# kubectl get pods,deploy,rs
NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-78cdb5b557-8gxc2   1/1     Running   0          88s
pod/nginx-deployment-78cdb5b557-pnkmb   1/1     Running   0          88s
pod/nginx-deployment-78cdb5b557-v6tr9   1/1     Running   0          88s
pod/pod-example                         1/1     Running   0          29m

NAME                                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/nginx-deployment   3         3         3            3           88s

NAME                                                DESIRED   CURRENT   READY   AGE
replicaset.extensions/nginx-deployment-78cdb5b557   3         3         3       88s

查看控制器

查看控制器参数:可以使用describe或者edit两种方式
[root@localhost demo]# kubectl edit deployment/nginx-deployment

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: 2020-10-14T11:48:34Z
  generation: 1
  labels:
    app: nginx
  name: nginx-deployment
  namespace: default
  resourceVersion: "89774"
  selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/nginx-deployment
  uid: 304e5d7b-0e13-11eb-9770-000c2994a1fc
spec:
  progressDeadlineSeconds: 600
  replicas: 3
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: nginx
  strategy:                      //'此段解释的是滚动更新机制'
    rollingUpdate:
      maxSurge: 25%        // //25%指的是pod数量的百分比,最多可以扩容125%'
      maxUnavailable: 25%    //'//25%指的是pod数量的百分比,最多可以缩容75%'
"/tmp/kubectl-edit-0xo04.yaml" 70L, 1962C

在这里插入图片描述

更新机制:

会先增加25% ,在删除25% ,保持资源数量在 replicas 值范围 在设定值

查看历史版本

[root@master demo]# kubectl rollout history deployment/nginx-deployment
deployment.extensions/nginx-deployment
REVISION CHANGE-CAUSE
1

2. SatefulSet 控制器

部署有状态应用

解决Pod独立生命周期,保持Pod启动顺序和唯一性
稳定,唯一的网络标识符,持久存储(例如:etcd配置文件,节点地址发生变化,将无法使用)
有序,优雅的部署和扩展、删除和终止(例如:mysql主从关系,先启动主,再启动从)
有序,滚动更新
应用场景:数据库
https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/

无状态服务的特点:

1)deployment 认为所有的pod都是一样的
2)不用考虑顺序的要求
3)不用考虑在哪个node节点上运行
4)可以随意扩容和缩容

有状态服务的特点

1)实例之间有差别,每个实例都有自己的独特性,元数据不同,例如etcd,zookeeper
2)实例之间不对等的关系,以及依靠外部存储的应用。

常规service和无头服务区别
service:一组Pod访问策略,提供cluster-IP群集之间通讯,还提供负载均衡和服务发现。
Headless service 无头服务,不需要cluster-IP,直接绑定具体的Pod的IP

[root@master demo]# !vim
vim nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx

创建资源

[root@master demo]# kubectl create -f nginx-service.yaml
service/nginx-service created

查看资源

[root@master demo]# kubectl get pods,svc
NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-78cdb5b557-8gxc2   1/1     Running   0          24m
pod/nginx-deployment-78cdb5b557-pnkmb   1/1     Running   0          24m
pod/nginx-deployment-78cdb5b557-v6tr9   1/1     Running   0          24m
pod/pod-example                         1/1     Running   0          52m

NAME                    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
service/kubernetes      ClusterIP   10.0.0.1     <none>        443/TCP        4d10h
service/nginx-service   NodePort    10.0.0.65    <none>        80:49741/TCP   59s

//在node节点上操作
systemctl restart flanneld.service
systemctl restart docker

[root@node2 ~]# curl 10.0.0.65
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

headless方式(因为Pod动态IP地址,所以常用于绑定DNS访问)

[root@master demo]# vim headless.yaml
apiVersion: v1
kind: Service              '//创建一个service类型的资源'
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None         '//不使用clusterIP'
  selector:
    app: nginx

查看service 资源

[root@master demo]#  kubectl get svc
NAME            TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.0.0.1     <none>        443/TCP        4d11h
nginx           ClusterIP   None         <none>        80/TCP         27s     //'//刚刚创建的无头服务没有clusterIP'
nginx-service   NodePort    10.0.0.65    <none>        80:49741/TCP   90m
[root@master demo]#

复制coredns.yaml到master01的root家目录

官方文档地址:
https://www.kubernetes.org.cn/4694.html
下载 coredns.yaml 文件

[root@master opt]# ls
coredns.yaml demo dockersoft etcd kubernetes rh
创建DNS资源

、配置dns服务,使用yaml文件创建

[root@master test]# vim coredns.yaml

# Warning: This is a file generated from the base underscore template file: coredns.yaml.base

apiVersion: v1
kind: ServiceAccount	'//系统账户,为pod中的进程和外部用户提供身份信息'
metadata:
  name: coredns
  namespace: kube-system	'//指定名称空间'
  labels:
      kubernetes.io/cluster-service: "true"
      addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole	'//创建访问权限的角色'
metadata:
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
    addonmanager.kubernetes.io/mode: Reconcile
  name: system:coredns
rules:
- apiGroups:
  - ""
  resources:
  - endpoints
  - services
  - pods
  - namespaces
  verbs:
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding	'//创建集群角色绑定的用户'
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
    addonmanager.kubernetes.io/mode: EnsureExists
  name: system:coredns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:coredns
subjects:
- kind: ServiceAccount
  name: coredns
  namespace: kube-system
---
apiVersion: v1
kind: ConfigMap	'//通过此服务来更改服务发现的工作方式'
metadata:
  name: coredns
  namespace: kube-system
  labels:
      addonmanager.kubernetes.io/mode: EnsureExists
data:
  Corefile: |	'//是coreDNS的配置文件'
    .:53 {
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure	
            upstream
            fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        proxy . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: coredns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
    kubernetes.io/name: "CoreDNS"
spec:
  # replicas: not specified here:
  # 1. In order to make Addon Manager do not reconcile this replicas parameter.
  # 2. Default is 1.
  # 3. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  selector:
    matchLabels:
      k8s-app: kube-dns
  template:
    metadata:
      labels:
        k8s-app: kube-dns
      annotations:
        seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
    spec:
      serviceAccountName: coredns
      tolerations:
        - key: node-role.kubernetes.io/master
          effect: NoSchedule
        - key: "CriticalAddonsOnly"
          operator: "Exists"
      containers:
      - name: coredns
        image: coredns/coredns:1.2.2
        imagePullPolicy: IfNotPresent
        resources:
          limits:
            memory: 170Mi
          requests:
            cpu: 100m
            memory: 70Mi
        args: [ "-conf", "/etc/coredns/Corefile" ]
        volumeMounts:
        - name: config-volume
          mountPath: /etc/coredns
          readOnly: true
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
        - containerPort: 9153
          name: metrics
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 60
          timeoutSeconds: 5
          successThreshold: 1
          failureThreshold: 5
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            add:
            - NET_BIND_SERVICE
            drop:
            - all
          readOnlyRootFilesystem: true
      dnsPolicy: Default
      volumes:
        - name: config-volume
          configMap:
            name: coredns
            items:
            - key: Corefile
              path: Corefile
---
apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
  annotations:
    prometheus.io/port: "9153"
    prometheus.io/scrape: "true"
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
    kubernetes.io/name: "CoreDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP: 10.0.0.2 
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP

创建资源

[root@master opt]# kubectl create -f coredns.yaml
serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
configmap/coredns created
deployment.extensions/coredns created
service/kube-dns created

查看资源

[root@master opt]#  kubectl get pods -n kube-system
NAME                                    READY   STATUS              RESTARTS   AGE
coredns-56684f94d6-rsfqv                0/1     ContainerCreating   0          24s
kubernetes-dashboard-7dffbccd68-gzlkg   1/1     Running             1          4d10h
[root@master opt]#  kubectl get pods -n kube-system -w
NAME                                    READY   STATUS              RESTARTS   AGE
coredns-56684f94d6-rsfqv                0/1     ContainerCreating   0          31s
kubernetes-dashboard-7dffbccd68-gzlkg   1/1     Running             1          4d10h
coredns-56684f94d6-rsfqv   1/1   Running   0     41s

‘//创建用来测试dns的pod资源’

vim pod8.yaml
apiVersion: v1
kind: Pod
metadata:
  name: dns-test
spec:
  containers:
  - name: busybox
    image: busybox:1.28.4
    args:
    - /bin/sh
    - -c
    - sleep 36000
  restartPolicy: Never

创建资源

 kubectl  create -f   pod8.yaml

[root@master demo]# kubectl get all
NAME           READY   STATUS    RESTARTS   AGE
pod/dns-test   1/1     Running   0          5m5s

NAME                    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
service/kubernetes      ClusterIP   10.0.0.1     <none>        443/TCP        4d12h
service/nginx           ClusterIP   None         <none>        80/TCP         2m4s
service/nginx-service   NodePort    10.0.0.154   <none>        80:45247/TCP   3s

进入dns-test容器,查看DNS主机名解析

[root@master demo]#  kubectl exec -it dns-test sh
/ # nslookup kubernetes
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes
Address 1: 10.0.0.1 kubernetes.default.svc.cluster.local
/ # nslookup nginx-service
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      nginx-service
Address 1: 10.0.0.154 nginx-service.default.svc.cluster.local
/ #

创建YAML文件,

[root@master demo]# vim sts.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: nginx-statefulset
  namespace: default
spec:
  serviceName: nginx
  replicas: 3
  selector:
    matchLabels:
       app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

清理所有pods

[root@master demo]# kubectl delete -f .

创建资源

[root@master demo]# kubectl create -f  sts.yaml
service/nginx created
statefulset.apps/nginx-statefulset created

[root@master demo]# kubectl get pods
NAME                  READY   STATUS    RESTARTS   AGE
dns-test              1/1     Running   0          7m57s
nginx-statefulset-0   1/1     Running   0          2m1s
nginx-statefulset-1   1/1     Running   0          49s
nginx-statefulset-2   1/1     Running   0          46s


[root@master demo]#  kubectl get pods,svc
NAME                      READY   STATUS    RESTARTS   AGE
pod/dns-test              1/1     Running   0          9m1s
pod/nginx-statefulset-0   1/1     Running   0          3m5s
pod/nginx-statefulset-1   1/1     Running   0          113s
pod/nginx-statefulset-2   1/1     Running   0          110s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.0.0.1     <none>        443/TCP   4d12h
service/nginx        ClusterIP   None         <none>        80/TCP    3m5s

[root@master demo]#  kubectl get ep
NAME         ENDPOINTS                                      AGE
kubernetes   192.168.100.3:6443,192.168.100.8:6443          4d12h
nginx        172.17.16.4:80,172.17.18.3:80,172.17.18.4:80   5m35s

DNS 域名解析,解析成功

[root@master demo]# kubectl exec -it dns-test sh
/ # nslookup nginx-statefulset-0.nginx
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      nginx-statefulset-0.nginx
Address 1: 172.17.18.3 nginx-statefulset-0.nginx.default.svc.cluster.local
/ # nslookup nginx-statefulset-1.nginx
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      nginx-statefulset-1.nginx
Address 1: 172.17.16.4 nginx-statefulset-1.nginx.default.svc.cluster.local
/ # nslookup nginx-statefulset-2.nginx
Server:    10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local

Name:      nginx-statefulset-2.nginx
Address 1: 172.17.18.4 nginx-statefulset-2.nginx.default.svc.cluster.local
/ #

节点访问 172.17.18.3
在这里插入图片描述

总结

StatefulSet与Deployment区别:有身份的!
身份三要素:
域名 nginx-statefulset-0.nginx
主机名 nginx-statefulset-0
存储(PVC)

3 . DaemonSet 控制器

在每一个Node上运行一个Pod
新加入的Node也同样会自动运行一个Pod

应用场景:Agent (代理)
官方文档:
https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/

示例1

[root@master demo]# vim ds.yaml

apiVersion: apps/v1
kind: DaemonSet 
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.15.4
        ports:
        - containerPort: 80
    - 
[root@master demo]#  kubectl apply -f ds.yaml
daemonset.apps/nginx-deployment created

检查资源

因为yaml 文件并未设置副本集, 然而却自动每个节点都创造了一个deployment
可见,DaemonSet会在每个node节点都创建一个Pod

root@master demo]# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
dns-test                 1/1     Running   0          27m
nginx-deployment-7gvsx   1/1     Running   0          31s
nginx-deployment-qgzgm   1/1     Running   0          31s

4. Job 控制器

Job分为普通任务(Job)和定时任务(CronJob)

一次性执行

应用场景:离线数据处理,视频解码等业务
官方文档:
https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/

示例:
//示例中当遇到异常时Never状态不会重启。

[root@master demo]# vim job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never   //不重启
 # backoffLimit: 4    //重启次数

//在node节点下载perl镜像,因为镜像比较大所以提前下载好
[root@node1 ~]# docker pull perl

创建资源,用perl 语言去计算圆周率

[root@master demo]# kubectl apply -f job.yaml
job.batch/pi created

查看状态
因为是一次性任务,在执行完任务后,会自动结束

[root@master demo]#  kubectl get pods
NAME                     READY   STATUS      RESTARTS   AGE
dns-test                 1/1     Running     0          36m
nginx-deployment-7gvsx   1/1     Running     0          10m
nginx-deployment-qgzgm   1/1     Running     0          10m
pi-fd25w                 0/1     Completed   0          36s

查看执行的结果

[root@master demo]#  kubectl logs pi-fd25w
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094330572703657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566430860213949463952247371907021798609437027705392171762931767523846748184676694051320005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235420199561121290219608640344181598136297747713099605187072113499999983729780499510597317328160963185950244594553469083026425223082533446850352619311881710100031378387528865875332083814206171776691473035982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989380952572010654858632788659361533818279682303019520353018529689957736225994138912497217752834791315155748572424541506959508295331168617278558890750983817546374649393192550604009277016711390098488240128583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912933136770289891521047521620569660240580381501935112533824300355876402474964732639141992726042699227967823547816360093417216412199245863150302861829745557067498385054945885869269956909272107975093029553211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000816470600161452491921732172147723501414419735685481613611573525521334757418494684385233239073941433345477624168625189835694855620992192221842725502542568876717904946016534668049886272327917860857843838279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863067442786220391949450471237137869609563643719172874677646575739624138908658326459958133904780275901
[root@master demo]#  kubectl get job
NAME   COMPLETIONS   DURATION   AGE
pi     1/1           10s        5m8s

//清除job资源

[root@master demo]# kubectl delete -f job.yaml
job.batch "pi" deleted

5 . CronJob 控制器

周期性任务,像Linux的Crontab一样。

周期性任务

应用场景:通知,备份

https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/

示例:
//每分钟打印hello

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
[root@master demo]# kubectl create -f cronjob.yaml
cronjob.batch/hello created
[root@master demo]# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
dns-test                 1/1     Running   0          46m
nginx-deployment-7gvsx   1/1     Running   0          19m
nginx-deployment-qgzgm   1/1     Running   0          19m
[root@master demo]# kubectl get cronjob
NAME    SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hello   */1 * * * *   False     0        <none>          34s
[root@master demo]#

查看执行结果

[root@master demo]# kubectl logs hello-1602687420-l5qkh
Wed Oct 14 14:57:04 UTC 2020
Hello from the Kubernetes cluster
//等待一分钟后又会再执行一次,计划任务生效
[root@master demo]# kubectl get pods -w
NAME                     READY   STATUS      RESTARTS   AGE
dns-test                 1/1     Running     0          48m
hello-1602687360-dz8gg   0/1     Completed   0          99s
hello-1602687420-l5qkh   0/1     Completed   0          39s
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值