• k8s 中 Pod 的控制器


    k8s 中 Pod 的控制器

    前言

    Pod 是 Kubernetes 集群中能够被创建和管理的最小部署单元。所以需要有工具去操作和管理它们的生命周期,这里就需要用到控制器了。

    Pod 控制器由 master 的 kube-controller-manager 组件提供,常见的此类控制器有 Replication Controller、ReplicaSet、Deployment、DaemonSet、StatefulSet、Job 和 CronJob 等,它们分别以不同的方式管理 Pod 资源对象。

    Replication Controller

    RC 是 k8s 集群中最早的保证 Pod 高可用的 API 对象。它的作用就是保证集群中有指定数目的 pod 运行。

    当前运行的 pod 数目少于指定的数目,RC 就会启动新的 pod 副本,保证运行 pod 数量等于指定数目。

    当前运行的 pod 数目大于指定的数目,RC 就会杀死多余的 pod 副本。

    直接上栗子

    cat < >./pod-rc.yaml
    apiVersion: v1
    kind: ReplicationController
    metadata:
      name: nginx
    spec:
      replicas: 3
      selector:
        app: nginx
      template:
        metadata:
          name: nginx
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80
    EOF
    

    在新版的 Kubernetes 中建议使用 ReplicaSet (RS)来取代 ReplicationController。

    ReplicaSet 跟 ReplicationController 没有本质的不同,只是名字不一样,但 ReplicaSet 支持集合式 selector。

    关于 ReplicationController 这里也不展开讨论了,主要看下 ReplicaSet。

    ReplicaSet

    RS 是新一代 RC,提供同样的高可用能力,区别主要在于 RS 后来居上,能支持支持集合式 selector。

    副本集对象一般不单独使用,而是作为 Deployment 的理想状态参数使用。

    下面看下 Deployment 中是如何使用 ReplicaSet 的。

    Deployment

    一个 Deployment 为 Pod 和 ReplicaSet 提供声明式的更新能力,每一个 Deployment 都对应集群中的一次部署。

    一般使用 Deployment 来管理 RS,可以用来创建一个新的服务,更新一个新的服务,也可以用来滚动升级一个服务。

    滚动升级一个服务,滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新 RS 中副本数增加到理想状态,将旧 RS 中的副本数减小到 0 的复合操作;这样一个复合操作用一个 RS 是不太好描述的,所以用一个更通用的 Deployment 来描述。

    举个栗子

    cat < >./nginx-deployment.yaml
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
      labels:
        app: nginx
    spec:
      replicas: 3
      selector:
        matchLabels: # 这里定义需要管理的 pod,通过 Pod 的标签进行匹配
          app: nginx
      template:
        metadata:
          labels: # 运行的 pod 的标签
            app: nginx
        spec:
          containers: # pod 中运行的容器
          - name: nginx
            image: nginx:1.14.2 
            ports:
            - containerPort: 80
    EOF
    

    部署

    $ kubectl apply -f nginx-deployment.yaml -n study-k8s
    deployment.apps/nginx-deployment created
    
    $ study-k8s kubectl describe deployment nginx-deployment -n study-k8s
    Name:                   nginx-deployment
    Namespace:              study-k8s
    CreationTimestamp:      Mon, 26 Sep 2022 09:06:16 +0800
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision: 1
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 1 available | 2 unavailable
    StrategyType:           RollingUpdate # 默认是滚动更新  
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge  # 滚动更新的策略,最大 25% 不可用,最大 25% 增加
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.14.2
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  
        Mounts:       
      Volumes:        
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      False   MinimumReplicasUnavailable
      Progressing    True    ReplicaSetUpdated
    OldReplicaSets:  
    NewReplicaSet:   nginx-deployment-66b6c48dd5 (3/3 replicas created)
    Events:
      Type    Reason             Age   From                   Message
      ----    ------             ----  ----                   -------
      Normal  ScalingReplicaSet  3s    deployment-controller  Scaled up replica set nginx-deployment-66b6c48dd5 to 3
    

    可以看到 Deployment 中默认的更新方式滚动更新,并且默认的滚动更新的策略是 最大 25% 不可用,最大 25% 增加。

    更新 Deployment

    1、直接更新

    $ kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.20.1 -n study-k8s
    
    # 或者  
    
    $ kubectl set image deployment/nginx-deployment nginx=nginx:1.20.1 -n study-k8s
    

    2、对 Deployment 执行 edit 操作

    $ kubectl get deployment -n study-k8s
    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           18m
    
    $ kubectl edit deployment -n study-k8s
    # 修改对应的镜像版本,保存即可  
    
        spec:
          containers:
          - image: nginx:1.20.3
            imagePullPolicy: IfNotPresent
            name: nginx
    

    3、直接编辑部署的 YAML 文件,然后重新 Apply

    $ kubectl apply -f nginx-deployment.yaml -n study-k8s
    deployment.apps/nginx-deployment configured
    
    $ kubectl get pods -n study-k8s
    NAME                                READY   STATUS              RESTARTS   AGE
    nginx-deployment-6bcf8f4884-dxxxg   1/1     Running             0          38s
    nginx-deployment-6bcf8f4884-plbkh   0/1     ContainerCreating   0          13s
    nginx-deployment-cc4b758d6-lzlxl    1/1     Running             0          7m25s
    nginx-deployment-cc4b758d6-r5rkb    1/1     Running             0          7m11s
    

    回滚 deployment

    了解了如何更新 deployment,那么当部署出现问题,如果回滚呢,下面来详细介绍下

    查看之前部署的版本

    $ kubectl rollout history deployment/nginx-deployment -n study-k8s
    deployment.apps/nginx-deployment
    REVISION  CHANGE-CAUSE
    1         <none>
    2         <none>
    3         <none>
    4         <none>
    5         <none>
    6         <none>
    7         image updated to 1.21.1
    

    REVISION:就是之前部署的版本信息;

    CHANGE-CAUSE:变动的备注信息。

    $ kubectl apply -f nginx-deployment.yaml -n study-k8s --record
    

    加上 --record,或者每次部署完之后,使用

    $ kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="image updated to 1.21.1" -n study-k8s
    

    CHANGE-CAUSE 信息就会被记录

    查看历史版本的详细详细

    $ kubectl rollout history deployment/nginx-deployment --revision=6 -n study-k8s
    deployment.apps/nginx-deployment with revision #6
    Pod Template:
      Labels:	app=nginx
    	pod-template-hash=d985dd8bf
      Containers:
       nginx:
        Image:	nginx:1.20.3
        Port:	80/TCP
        Host Port:	0/TCP
        Environment:	
        Mounts:	
      Volumes:	
    

    回滚

    # 回滚到上一个版本
    $ kubectl rollout undo deployment/nginx-deployment -n study-k8s
    
    # 回滚到指定的版本
    $ kubectl rollout undo deployment/nginx-deployment --to-revision=5 -n study-k8s
    

    缩放 Deployment

    $ kubectl scale deployment/nginx-deployment --replicas=10 -n study-k8s
    
    $  kubectl get pods -n study-k8s
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-deployment-66b6c48dd5-5scbj   1/1     Running   0          44s
    nginx-deployment-66b6c48dd5-7nmtp   1/1     Running   0          44s
    nginx-deployment-66b6c48dd5-f5xsg   1/1     Running   0          12m
    nginx-deployment-66b6c48dd5-fnpnb   1/1     Running   0          12m
    nginx-deployment-66b6c48dd5-gg4ng   1/1     Running   0          44s
    nginx-deployment-66b6c48dd5-qd5z7   1/1     Running   0          44s
    nginx-deployment-66b6c48dd5-qqh4m   1/1     Running   0          44s
    nginx-deployment-66b6c48dd5-xww49   1/1     Running   0          44s
    nginx-deployment-66b6c48dd5-zndlh   1/1     Running   0          44s
    nginx-deployment-66b6c48dd5-zp45g   1/1     Running   0          12m
    

    关于使用 deployment 实现 k8s 中的几种部署策略,可参见Kubernetes 部署策略详解

    StatefulSet

    StatefulSet 用来管理有状态的应用。

    在 Pods 管理的基础上,保证 Pods 的顺序和一致性。与 Deployment一样,StatefulSet 也是使用容器的 Spec 来创建Pod,与之不同 StatefulSet 创建的 Pods 在生命周期中会保持持久的标记(例如Pod Name)。

    StatefulSet 适用于具有以下特点的应用:

    1、稳定的、唯一的网络标识符;

    2、稳定的、持久的存储;

    3、有序的、优雅的部署和扩缩;

    4、有序的、自动的滚动更新。

    那么什么是有状态服务什么是无状态服务呢?

    无状态服务

    无状态服务不会在本地存储持久化数据。多个服务实例对于同一个用户请求的响应结果是完全一致的。这种多服务实例之间是没有依赖关系,比如web应用,在k8s控制器 中动态启停无状态服务的pod并不会对其它的pod产生影响。

    有状态服务

    有状态服务需要在本地存储持久化数据,典型的是分布式数据库的应用,分布式节点实例之间有依赖的拓扑关系。

    比如,主从关系。 如果 K8S 停止分布式集群中任 一实例 pod,就可能会导致数据丢失或者集群的 crash。

    来个栗子

    cat < >./pod-statefulSet.yaml
    
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      ports:
      - port: 80
        name: web
      clusterIP: None
      selector:
        app: nginx
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      selector:
        matchLabels:
          app: nginx # 必须匹配 .spec.template.metadata.labels
      serviceName: "nginx"
      replicas: 3 # 默认值是 1
      template:
        metadata:
          labels:
            app: nginx # 必须匹配 .spec.selector.matchLabels
        spec:
          terminationGracePeriodSeconds: 10
          containers:
          - name: nginx
            image: nginx:1.14.2 
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: [ "ReadWriteOnce" ]
          resources:
            requests:
              storage: 1Gi
    EOF
    

    DaemonSet

    DaemonSet:主要是用来保证集群中的每个节点只运行一个 Pod,且保证只有一个 Pod,这非常适合一些系统层面的应用,例如日志收集、资源监控等,这类应用需要每个节点都运行,且不需要太多实例,一个比较好的例子就是 Kubernetes 的 kube-proxy。

    当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。

    DaemonSet 的应用场景:

    1、在每个节点上运行集群守护进程;

    2、在每个节点上运行日志收集守护进程;

    3、在每个节点上运行监控守护进程。

    来个栗子

    cat < >./nginx-daemonset.yaml
    
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: nginx-daemonset
      labels:
        app: nginx-daemonset
    spec:
      selector:
        matchLabels:
          app: nginx-daemonset
      template:
        metadata:
          labels:
            app: nginx-daemonset
        spec:
          containers:
          - name: nginx-daemonset
            image: nginx:alpine
            resources:
              limits:
                cpu: 250m
                memory: 512Mi
              requests:
                cpu: 250m
                memory: 512Mi
          imagePullSecrets:
          - name: default-secret
    EOF
    

    这样就能在每个节点中部署一个 Pod 了,不过 DaemonSet 也支持通过 label 来选择部署的目标节点

    cat < >./nginx-daemonset.yaml
    
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: nginx-daemonset
      labels:
        app: nginx-daemonset
    spec:
      selector:
        matchLabels:
          app: nginx-daemonset
      template:
        metadata:
          labels:
            app: nginx-daemonset
        spec:
          nodeSelector: # 节点选择,当节点拥有nodeName=node7时才在节点上创建Pod
            nodeName: node7
          containers:
          - name: nginx-daemonset
            image: nginx:alpine
            resources:
              limits:
                cpu: 250m
                memory: 512Mi
              requests:
                cpu: 250m
                memory: 512Mi
          imagePullSecrets:
          - name: default-secret
    EOF
    

    Job 和 CronJob

    Job 和 CronJob 是负责处理定时任务的。

    两者的区别主要在于:

    Job 负责处理一次性的定时任务,即仅需执行一次的任务;

    CronJob 是基于时间的 Job,就类似于 Linux 系统的 crontab 文件中的一行,在指定的时间周期运行指定的 Job。

    首先来创建一个 Job

    cat < >./job-demo.yaml
    
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-demo
    spec:
      template:
        metadata:
          name: job-demo
        spec:
          restartPolicy: Never
          containers:
          - name: counter
            image: busybox
            command:
            - "bin/sh"
            - "-c"
            - "echo hello"  
    EOF
    

    运行

    $ kubectl apply -f job-demo.yaml
    
    $ kubectl get pods
    NAME             READY   STATUS      RESTARTS   AGE
    job-demo-8qrd9   0/1     Completed   0          40s
    $ study-k8s kubectl get job
    NAME       COMPLETIONS   DURATION   AGE
    job-demo   1/1           36s        45s
    $ kubectl logs -f job-demo-8qrd9
    hello
    

    再来看下 CronJob

    cat < >./cronJob-demo.yaml
    
    apiVersion: batch/v1beta1
    kind: CronJob
    metadata:
      name: hello
    spec:
      schedule: "* * * * *"
      jobTemplate:
        spec:
          template:
            spec:
              containers:
              - name: hello
                image: busybox:1.28
                imagePullPolicy: IfNotPresent
                command:
                - /bin/sh
                - -c
                - date; echo Hello
              restartPolicy: OnFailure
    EOF
    

    通过 kubectl get jobs --watch 就能查看 CronJob 的调度

    $ kubectl get jobs --watch
    NAME               COMPLETIONS   DURATION   AGE
    hello-1664449860   1/1           2s         4s
    hello-1664449920   0/1                      0s
    hello-1664449920   0/1           0s         0s
    hello-1664449920   1/1           2s         2s
    

    总结

    关于 pod 中的几个控制器最常用的就是 Deployment 和 ReplicaSet;

    使用 Deployment 来管理 RS,来实现服务的部署,更新和滚动升级;

    StatefulSet 主要用来管理无状态应用;

    DaemonSet:主要是用来保证集群中的每个节点只运行一个 Pod,且保证只有一个 Pod,这非常适合一些系统层面的应用,例如日志收集、资源监控等,这类应用需要每个节点都运行,且不需要太多实例,一个比较好的例子就是 Kubernetes 的 kube-proxy;

    Job 和 CronJob 是负责处理定时任务的;

    Job 负责处理一次性的定时任务,即仅需执行一次的任务;

    CronJob 是基于时间的 Job,就类似于 Linux 系统的 crontab 文件中的一行,在指定的时间周期运行指定的 Job。

    参考

    【Deployments】https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/deployment/
    【Kubernetes 部署策略详解】https://www.qikqiak.com/post/k8s-deployment-strategies/
    【StatefulSet 和 Deployment 区别及选择方式】https://blog.csdn.net/nickDaDa/article/details/90401635
    【K8S: 有状态 vs 无状态服务】https://zhuanlan.zhihu.com/p/390440336
    【StatefulSet】https://support.huaweicloud.com/basics-cce/kubernetes_0015.html
    【k8s 中 Pod 的控制器】https://boilingfrog.github.io/2022/09/30/k8s中Pod控制器/

  • 相关阅读:
    React18原理: React核心对象之ReactElement对象和Fiber对象
    Hadoop HA 高可用集群详解
    TypeScript 实践中的 Equals 类型的实现原理是什么?
    Java基础之继承、多态、抽象类
    【手写数据库toadb】SQL解析器的实现架构,create table/insert 多values语句的解析树生成流程和输出结构分析
    写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明
    毕业后成为大厂职业游戏建模师?门槛高不高,一看就知道
    Stratasys F170 3D打印教程
    文字处理工具 word 2019 mac中文版改进功能
    Java-单列集合Collection详解
  • 原文地址:https://www.cnblogs.com/ricklz/p/16743702.html