Kubernetes 漫游:kube-scheduler

这篇具有很好参考价值的文章主要介绍了Kubernetes 漫游:kube-scheduler。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述

什么是 kube-scheduler ?

Kubernetes 集群的核心组件之一,它负责为新创建的 Pods 分配节点。它根据多种因素进行决策,包括:

  1. 资源需求和限制:考虑每个 Pod 请求的资源量(如 CPU 和内存)以及节点上可用的资源。
  2. 亲和性和反亲和性规则:根据 Pod 的亲和性设置选择最适合的节点。
  3. 健康检查:确保选择的节点健康且能够运行 Pod。
  4. 负载均衡:尽量平衡集群中各个节点的负载。

使用

limits 和 reuqests

在部署对象中的 spec 中常常会见到关于 limitsrequests 的声明 ,例如:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          resources:
            limits:
              memory: 1Gi
              cpu: 1
            requests:
              memory: 256Mi
              cpu: 100m

这里的 limits 和 requests 是与 Pod 容器资源管理相关的两个关键概念:

  • Limits:指定容器运行时能够使用的最大资源量
  • Requests:指定容器启动时最低需要的资源量

limits 和 requests 跟 scheduler 有什么关系 ?

在集群中 kube-scheduler 一直是默默无闻的幕后工作者,它主要工作内容如下:

  1. 当你创建一个 Deployment,如这个 nginx,是由 kube-scheduler 决策将其调度到哪个 Node 上运行的
  2. kube-scheduler 会监听 apiserver 获取集群全局视图,然后根据 Pod 的资源请求(requests 和 limits)分析
  3. 最终 kube-scheduler 会结合资源请求和集群的实际情况来调度 Pod

总之,kube-scheduler 会保证 Pod 会调度到满足其运行资源需求的 Node 节点上。

LimitRange

描述

LimitRange 是资源描述对象,主要用于限制命名空间内资源的使用。它可以设置默认的资源请求和限制,以及资源使用的最大和最小值。它可以确保每个 Pod 或容器在资源使用上遵循特定的策略,从而避免单个 Pod 或容器占用过多资源。使用示例如下:

创建一个 YAML 文件保存 LimitRange 内容,例如:mem-limit-range.yaml

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-limit-range
spec:
  limits:
    - default:
        memory: 512Mi
      defaultRequest:
        memory: 256Mi
      type: Container

应用到集群:

$ kubectl apply -f mem-limit-range.yaml

查看创建的 LimitRange 对象:

$ kubectl describe limitrange mem-limit-range

输出:

Name:       mem-limit-range
Namespace:  default
Type        Resource  Min  Max  Default Request  Default Limit  Max Limit/Request Ratio
----        --------  ---  ---  ---------------  -------------  -----------------------
Container   memory    -    -    256Mi            512Mi          -

说明:

  • Kind:设置为 LimitRange,用于限制命名空间内资源的使用。
  • Metadata:设置资源的名称
  • Spec:
    • Limits:
    • default:指定没有明确资源限制的容器的默认内存限制为 512Mi
    • defaultRequest:指定没有明确资源请求的容器的默认内存请求。这里设置为 256Mi
    • type:应用这些限制的资源类型,在这里是 Container

验证

定义一个没有声明资源请求的部署对象,文件命名为: nginx-without-resource.yaml ,如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx

应用部署到集群:

$ kubectl apply -f nginx-without-resource.yaml

等 Pod 创建后,可以通过检查它们的配置来确认 LimitRange 是否生效。

$ kubectl describe pod [POD_NAME]

输出:

Containers:
	#.. ignore
		Limits:
      memory:  512Mi
    Requests:
      memory:     256Mi

initContainers

initContainers 用于在主应用容器启动之前执行一些预备任务。常见于以下场景:

  1. 准备工作:设置需要的配置文件、数据库迁移、等待其他服务就绪等。
  2. 安全性:权限提升操作,如改变文件权限或者执行特定的安全检查。
  3. 服务依赖性:等待其他服务或数据库可用。

initContainers 在执行完其任务后会停止,且必须成功完成才能启动主容器。非常适合用于启动前的初始化任务。

示例:

在部署对象中声明 initContainers 属性:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      initContainers:
        - name: init-myservice
          image: busybox:1.28
          command: ['sh', '-c', 'echo The app is running! && sleep 10']
      containers:
        - name: nginx
          image: nginx

将部署对象应用到集群:

$ kubectl apply -f init-container.yaml

当 Pod 启动后,可以通过查看事件日志验证容器的加载顺序:

Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  2m20s  default-scheduler  Successfully assigned default/nginx-deployment-6445f86ddc-fmmzw to docker-desktop
  Normal  Pulling    2m20s  kubelet            Pulling image "busybox:1.28"
  Normal  Pulled     116s   kubelet            Successfully pulled image "busybox:1.28" in 23.099396719s (23.099404677s including waiting)
  Normal  Created    116s   kubelet            Created container init-myservice
  Normal  Started    116s   kubelet            Started container init-myservice
  Normal  Pulling    106s   kubelet            Pulling image "nginx"
  Normal  Pulled     88s    kubelet            Successfully pulled image "nginx" in 18.382000675s (18.382006008s including waiting)
  Normal  Created    88s    kubelet            Created container nginx
  Normal  Started    88s    kubelet            Started container nginx

可以看到 initContainers 声明的容器已经加载,然后查看特定的日志,来检查 Pod 日志输出:

$ kubectl logs [POD_NAME] -c init-myservice

输出:

The app is running!

验证完成。

initContainers 和 kube-scheduler 的关系 ?

如果 initContainers 没有声明资源需求,默认也会使用 LimitRange 声明的默认资源,这也意味着,initContainers 也是由 kube-scheduler 来调度创建的。所以在 initContainers 中加上资源需求也会影响着 kube-scheduler 的调度决策。

nodeSelector

在部署对象中,nodeSelector 属性的作用是用于把指定 Pod 调度到具有特定标签的节点上。如果没有满足要求的 Node 节点,则 Pod 会持续等待,示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
      nodeSelector:
        disktype: ssd

在这个例子中 nodeSelector 属性值为:disktype: ssd 。这表明这个 Pod 应该被调度到标签为 disktype=ssd 的 Node 节点上。kube-scheduler 在调度时,会选择合适的节点以运行这个 Pod 时。

先将部署对象应用到集群中:

$ kubectl apply -f node-selector.yaml

然后查看 Pod 状态:

$ kubectl get pod

输出:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-f5bc98d57-pmq9v    0/1     Pending   0          2m17s

可以看到创建的 Pod 一直保持在 "Pending" 状态。通过事件日志查看具体原因:

$ kubectl describe pod [POD_NAME]

输出:

Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  4m38s  default-scheduler  0/1 nodes are available: 1 node(s) didn't match Pod's node affinity/selector. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..

从事件日志可以看出,这个 Pod 不能被调度,因为没有节点满足其设定的节点选择条件。因为我的集群中确实没有任何标记为 disktype: ssd 的节点在运行。

Affinity 亲和性

NodeSelector 的演进版本,提供了更复杂的选择规则。除了简单的匹配,它们还支持更丰富的条件表达式,如 "存在"、"不等于"、"在集合中" 等,并且支持对 Pod 之间(Pod Affinity/Anti-Affinity)以及 Pod 与节点之间(Node Affinity)的亲和性/反亲和性设置。在 Kubernetes 后续版本中 Affinity 也逐渐替代了 NodeSelector。

podAffinity

podAffinity 用于定义 Pods 之间的亲和性。使得某个 Pod 被调度到与其他特定标签的 Pod 相同的节点上。

使用场景:当希望一组服务紧密地协同工作时,比如一个应用的不同组件需要低延迟通讯。

示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-anti
spec:
  replicas: 2
  selector:
    matchLabels:
      app: anti-nginx
  template:
    metadata:
      labels:
        app: anti-nginx
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
                - key: a
                  operator: In
                  values:
                    - b
            topologyKey: kubernetes.io/hostname
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
                - key: app
                  operator: In
                  values:
                    - anti-nginx
            topologyKey: kubernetes.io/hostname
      containers:
        - name: with-pod-affinity
          image: nginx

部署文件展示亲和性(Affinity)设置:

  • PodAffinity:要求调度的 Pod 必须与具有特定标签(键 a,值 b)的 Pod 在相同的节点上。
  • PodAntiAffinity:要求调度的 Pod 不能与具有相同标签(键 app,值 anti-nginx)的 Pod 在相同的节点上。

将上面部署文件应用到集群后,查看 Pods 的分布情况:

NAME                          READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
nginx-anti-5656fcbb98-62mds   0/1     Pending   0          5s    <none>   <none>   <none>           <none>
nginx-anti-5656fcbb98-wxphs   0/1     Pending   0          5s    <none>   <none>   <none>           <none>

可以 Pod 因为亲和性规则无法调度一直处于等待状态,查看特定 Pod 的事件日志可以验证:

Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  27s   default-scheduler  0/1 nodes are available: 1 node(s) didn't match pod affinity rules. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..

利用 Pod 亲和性和反亲和性规则来控制 Pod 的调度位置,以实现特定的调度需求和负载分布。

nodeAffinity

用于定义 Pod 与节点之间的亲和性。控制 Pod 被调度到具有特定标签或属性的节点上。

适用场景:当您需要根据硬件特性(如 GPU、高性能存储)或其他自定义标签(如环境标签)调度 Pod 时。

示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: disktype
                    operator: In
                    values:
                      - ssd
      containers:
        - name: nginx
          image: nginx

部署文件的亲和性(Affinity)设置:

  • nodeAffinity 被设置为要求 Pod 被调度到具有 disktype: ssd 标签的节点上。

将上面部署文件应用到集群后,查看 Pod 的运行情况:

NAME                                READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
nginx-deployment-565d7797dc-jf5nk   0/1     Pending   0          14s   <none>   <none>   <none>           <none>

可以 Pod 因为亲和性规则无法调度一直处于等待状态,查看特定 Pod 的事件日志可以验证:

Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  89s   default-scheduler  0/1 nodes are available: 1 node(s) didn't match Pod's node affinity/selector. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..

preferredDuringSchedulingIgnoredDuringExecution

和之前的 requiredDuringScheduling 调度类型不同,preferredDuringScheduling 表明其是一个偏好性的调度,调度器会根据偏好优先选择满足对应规则的节点来调度Pod。但如果找不到满足规则的节点,调度器则会选择其他节点来调度Pod。

示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
                - key: disktype
                  operator: In
                  values:
                    - ssd
      containers:
        - name: nginx
          image: nginx

配置说明:这里使用的是 preferredDuringSchedulingIgnoredDuringExecution 类型,这意味着调度器会尽量但不强制将 Pod 调度到具有 disktype: ssd 标签的节点上。

将上面部署文件应用到集群后,查看 Pod 的运行情况:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-69c654d896-7qh8t   1/1     Running   0          28s

可以看到虽然我本地没有满足亲和性规则的 Node 节点,但是 Pod 依然可以调度起来了。

总结:

  • podAffinity 关注的是 Pod 之间的关系不同
  • nodeAffinity 更关注 Pod 与节点特性之间的关系
    • requiredDuringScheduling:硬亲和,强制型调度规则,必须满足亲和性设置,否则不能调度
    • preferredDuringScheduling:软亲和,偏好型调度规则,首先找到满足设置的节点,没有则会调度到其他节点

Taints 污点

Taints 和 Tolerations 是 Kubernetes 中用于控制 Pod 调度到特定节点的一种机制,相比 Affinity 亲和性 **相似性 **的机制,Taints 的规则是属于 排斥性 的机制,用来“排斥”不满足特定条件的 Pod。

Taints 有三种效果:

  • NoSchedule(不会调度新 Pod)
  • PreferNoSchedule(尽量避免调度新 Pod)
  • NoExecute(新 Pod 不会调度且已存在 Pod 可能会被迁移)

Taints 常见的应用场景:

  • 对于集群中不想共享的 Node,可以加上 Taints 标签表示独享
  • 用于多租户 Kubernetes 计算资源隔离
  • Kubernetes 本身使用 Taints 机制驱除不可用的 Node

使用示例:

给节点添加 Taint,防止所有 Pod 自动调度到该节点,除非它们具有匹配的 Tolerations:

$ kubectl taint nodes docker-desktop for-special-user=cadmin:NoSchedule

先定义一个没有任何 Tolerations 的 Pod 来验证:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx

将它应用到集群,查看 Pod 状态会一直处于 Pending:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-77b4fdf86c-wm5f9   0/1     Pending   0          23s

从事件日志可以看到是 Taints 在发挥作用:

Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  56s   default-scheduler  0/1 nodes are available: 1 node(s) had untolerated taint {for-special-user: cadmin}. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..

然后再 Pod 定义中添加 Tolerations,允许它被调度到带有特定 Taint 的节点上:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
      tolerations:
        - key: "for-special-user"
          operator: "Equal"
          value: "docker-desktop"
          effect: "NoSchedule"

这个部署文件设置了一个 容忍度 (Tolerations) 规则:允许 Pod 被调度到标记为 for-special-user=docker-desktop 并且具有 NoSchedule 效果的节点上。

将它应用到集群,查看 Pod 状态:

NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-dd7d69c9c-77qlf   1/1     Running   0          31s

Pod 已经正常调度,这也是 Taints 发挥作用。

如果节点不在需要 Tanints 作为排除,可以移除 :

$ kubectl taint nodes docker-desktop for-special-user=cadmin:NoSchedule-

输出:

node/docker-desktop untainted

PriorityClass

PriorityClass 用于定义 Pod 的调度优先级。常见的场景包括:

  1. 确保关键服务优先调度:对于关键组件,如数据库、核心应用服务,可以设置更高的优先级。
  2. 管理资源争用:在资源有限的环境中,通过设置不同的优先级,管理不同 Pod 的调度顺序。

使用 PriorityClass 的步骤:

  1. 创建 PriorityClass

    apiVersion: scheduling.k8s.io/v1
    kind: PriorityClass
    metadata:
      name: high-priority
    value: 1000000
    globalDefault: false
    description: "This priority class should be used for XYZ service pods only."
    

说明:

  • value:这是一个整数,表示该 PriorityClass 的优先级。较高的数值表示更高的优先级。
  • globalDefault:表示为集群中所有没有指定优先级的 Pod 的默认优先级。
  1. 在 Pod 中指定 PriorityClass
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  priorityClassName: high-priority
  containers:
  - name: mycontainer
    image: myimage

通过 priorityClassName 应用刚才创建的 PriorityClass,从而确保该 Pod 具有更高的调度优先级。

自定义 scheduler

默认的调度器是面向通用的使用场景设计的,如果默认的 Kubernetes 调度器无法满足需求,也可以通过自定义的调度器来满足更加个性化的需求,示例:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  schedulerName: my-custom-scheduler
  containers:
  - name: mycontainer
    image: myimage

社区也有很多成熟开源的自定义调度器,例如:

  • 腾讯 TKE 的调度器
  • 华为 volcano 调度器

另外也可以参考 kube-scheduler 源码实现一个自己的调度器。文章来源地址https://www.toymoban.com/news/detail-747585.html

到了这里,关于Kubernetes 漫游:kube-scheduler的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Kubernetes核心组件之kube-proxy实现原理

    kube-proxy,负责为Service提供集群内部的服务发现和负载均衡。 了解不同网络组件的工作原理有助于正确设计和配置它们,以满足你的应用程序需求。 在Kubernetes网络的背后,有一个在幕后工作的组件。它将你的服务(Services)转化为一些可用的网络规则。这个组件被称为 Kube

    2024年02月03日
    浏览(40)
  • kubernetes CIS 安全基准 Kube-bench 安全工具

    开头语 写在前面:如有问题,以你为准, 目前24年应届生,各位大佬轻喷,部分资料与图片来自网络 内容较长,页面右上角目录方便跳转 问题:下载pdf后,根据里面的基准来检查K8s集群配置,但内容量太大,一般会采用相关工具来完成这项工作。 Kube-bench 是容器安全厂商

    2024年02月02日
    浏览(45)
  • 【云原生 | 从零开始学Kubernetes】二十、Service代理kube-proxy组件详解

    该篇文章已经被专栏《从零开始学k8s》收录 上一篇文章:Kubernetes核心技术Service实战 点击跳转 Kubernetes service 只是把应用对外提供服务的方式做了抽象,真正的应用跑在 Pod 中的 container 里,我们的请求转到 kubernetes nodes 对应的 nodePort 上,那么 nodePort 上的请求是如何进一步转

    2024年02月03日
    浏览(39)
  • Kubernetes高可用集群二进制部署(五)kubelet、kube-proxy、Calico、CoreDNS

    Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署(一)主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署(二)ETCD集群部署 Kubernetes高可用集群二进制部署(三)部署api-server Kubernetes高可用集群二进制部署(四)部署kubectl和kube-controller-man

    2024年02月14日
    浏览(36)
  • 【云原生-K8s】Kubernetes安全组件CIS基准kube-beach安装及使用

    为了保证集群以及容器应用的安全,Kubernetes 提供了多种安全机制,限制容器的行为,减少容器和集群的攻击面,保证整个系统的安全性。 互联网安全中心(CIS,Center for Internet Security),是一个非盈利组织,致力为互联网提供免费的安全防御解决方案 官网 :https://www.cisecu

    2024年02月06日
    浏览(74)
  • 云原生之深入解析使用Kube-capacity CLI查看Kubernetes资源请求、限制和利用率

    Kube-capacity 是一个简单而强大的 CLI,它提供了 Kubernetes 集群中资源请求、限制和利用率的概览。它将输出的最佳部分结合 kubectl top 到 kubectl describe 一个易于使用的集中于集群资源的 CLI 中。

    2024年02月08日
    浏览(60)
  • Kubernetes实战(二十三)-k8s event监控利器kube-eventer对接企微告警

    监控是保障系统稳定性的重要组成部分,在Kubernetes开源生态中,资源类的监控工具与组件监控比较多。 cAdvisor:kubelet内置的cAdvisor,监控容器资源,如容器cpu、内存; Kube-state-metrics:kube-state-metrics通过监听 API Server 生成有关资源对象的状态指标,主要关注元数据,比如 Dep

    2024年02月21日
    浏览(35)
  • rancher和k8s接口地址,Kubernetes监控体系,cAdvisor和kube-state-metrics 与 metrics-server

    为了能够提前发现kubernetes集群的问题以及方便快捷的查询容器的各类参数,比如,某个pod的内存使用异常高企 等等这样的异常状态(虽然kubernetes有自动重启或者驱逐等等保护措施,但万一没有配置或者失效了呢),容器的内存使用量限制,过去10秒容器CPU的平均负载等等容

    2024年01月23日
    浏览(43)
  • 802.11kvr无线漫游原理及漫游测试工具推荐

    常见支持漫游的无线组网场景主要有AC+AP组网、mesh组网两大类: (1)AC+AP 组网   网络中所有AP被AC集中管理,发射统一的无线SSID,终端连入该SSID网络中可在不同的AP点之间实现无缝漫游。 (2)mesh 组网   母子路由无线mesh组网,子节点配置全部受控于主路由以扩展无线信号

    2024年02月04日
    浏览(51)
  • 【JavaScript 漫游】【053】Reflect

    文章简介 本篇文章为【JavaScript 漫游】专栏的第 053 篇文章,记录了 ES6 规范中关于 Reflect 的知识点。 概述 Reflect 对象与 Proxy 对象一样,也是 ES6 为了操作对象而提供的新 API。 Reflect 对象的设计目的有这样几个。 (1)将 Object 对象的一些明显属于语言内部的方法(比如 Obje

    2024年04月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包