Kubernetes之资源控制器

1、控制器分类

  • Kubernetes中内建了很多controller(控制器),这些相当于一个状态机,用来控制Pod的具体状态和行为。 控制器大致分为如下介绍六种:
    • ReplicationController和ReplicaSet
    • Deployment
    • DaemonSet
    • Job/CronJob
    • StateFulSet
    • Horizontal Pod Autoscaling

1.1 ReplicationController和ReplicaSet

  • ReplicationController(RC)用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收。

  • 在新版本的Kubernetes中建议使用ReplicaSet来取代RC。ReplicaSet跟RC没有本质的不同,只是名字不一样,并且ReplicaSet支持集合式的selector。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    [root@master ~]# vim rs.yaml
    [root@master ~]# cat rs.yaml
    apiVersion: extensions/v1beta1
    kind: ReplicaSet
    metadata:
    name: frontend
    spec:
    replicas: 3
    selector:
    matchLabels:
    tier: frontend
    template:
    metadata:
    labels:
    tier: frontend
    spec:
    containers:
    - name: mynginx
    image: nginx:latest
    env:
    - name: GET_HOSTS_FROM
    value: dns
    ports:
    - containerPort: 80
    [root@master ~]# kubectl create -f rs.yaml
    replicaset.extensions/frontend created
    [root@master ~]# kubectl get pod
    NAME READY STATUS RESTARTS AGE
    frontend-2gcxt 1/1 Running 0 61s
    frontend-f6mft 1/1 Running 0 61s
    frontend-rvdwb 1/1 Running 0 61s
    [root@master ~]# kubectl get rs
    NAME DESIRED CURRENT READY AGE
    frontend 3 3 3 70s
    # 手动删除其中一个pod,验证副本功能
    [root@master ~]# kubectl delete pod frontend-2gcxt
    pod "frontend-2gcxt" deleted
    #根据副本数会自动创建新的pod
    [root@master ~]# kubectl get pod
    NAME READY STATUS RESTARTS AGE
    frontend-f6mft 1/1 Running 0 95s
    frontend-hnsn8 1/1 Running 0 9s
    frontend-rvdwb 1/1 Running 0 95s
    [root@master ~]# kubectl get pod --show-labels
    NAME READY STATUS RESTARTS AGE LABELS
    frontend-f6mft 1/1 Running 0 4m11s tier=frontend
    frontend-hnsn8 1/1 Running 0 2m45s tier=frontend
    frontend-rvdwb 1/1 Running 0 4m11s tier=frontend
    # 修改标签名后发现多了一个副本,因为上方配置了标签名为frontend的pod才属于该rs,而修改成frontend1后不满足副本数为3,于是多创建了一个pod
    [root@master ~]# kubectl label pod frontend-f6mft --overwrite=true tier=frontend1
    pod/frontend-f6mft labeled
    [root@master ~]# kubectl get pod --show-labels
    NAME READY STATUS RESTARTS AGE LABELS
    frontend-dwfqg 1/1 Running 0 14s tier=frontend
    frontend-f6mft 1/1 Running 0 5m40s tier=frontend1
    frontend-hnsn8 1/1 Running 0 4m14s tier=frontend
    frontend-rvdwb 1/1 Running 0 5m40s tier=frontend
    [root@master ~]# kubectl delete rs --all
    replicaset.extensions "frontend" deleted
    # 删除掉所有rs后,发现只有label被更改后的pod在运行,因为其已经不被识别属于该rs
    [root@master ~]# kubectl get pod --show-labels
    NAME READY STATUS RESTARTS AGE LABELS
    frontend-dwfqg 0/1 Terminating 0 3m21s tier=frontend
    frontend-f6mft 1/1 Running 0 8m47s tier=frontend1
    frontend-hnsn8 0/1 Terminating 0 7m21s tier=frontend
    frontend-rvdwb 0/1 Terminating 0 8m47s tier=frontend
    [root@master ~]# kubectl get pod --show-labels
    NAME READY STATUS RESTARTS AGE LABELS
    frontend-f6mft 1/1 Running 0 10m tier=frontend1

1.2 Deployment

  • Deployment为Pod和ReplicaSet提供了一个声明式定义 (declarative) 方法,用来替代以前的RC来方便的管理应用。

  • 典型的应用场景包括:

    • 定义Deployment来创建Pod和ReplicaSet。

    • 滚动升级和回滚应用。

    • 扩容和缩容。

    • 暂停和继续Deployment。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      [root@master ~]# vim deployment.yaml
      [root@master ~]# cat deployment.yaml
      apiVersion: extensions/v1beta1
      kind: Deployment
      metadata:
      name: nginx-deployment
      spec:
      replicas: 3
      template:
      metadata:
      labels:
      app: nginx
      spec:
      containers:
      - name: mynginx
      image: nginx:latest
      ports:
      - containerPort: 80
      # --record参数可以记录命令,我们可以很方便的查看每次revision的变化
      [root@master ~]# kubectl apply -f deployment.yaml --record
      deployment.extensions/nginx-deployment created
      [root@master ~]# kubectl get deployment
      NAME READY UP-TO-DATE AVAILABLE AGE
      nginx-deployment 3/3 3 3 15s
      [root@master ~]# kubectl get rs
      NAME DESIRED CURRENT READY AGE
      nginx-deployment-7f57b5c565 3 3 3 36s
      [root@master ~]# kubectl get pod
      NAME READY STATUS RESTARTS AGE
      nginx-deployment-7f57b5c565-pshpl 1/1 Running 0 42s
      nginx-deployment-7f57b5c565-wxlbn 1/1 Running 0 42s
      nginx-deployment-7f57b5c565-zcckg 1/1 Running 0 42s
      [root@master ~]# kubectl get pod -o wide
      NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
      nginx-deployment-7f57b5c565-pshpl 1/1 Running 0 2m48s 10.244.1.13 node1 <none> <none>
      nginx-deployment-7f57b5c565-wxlbn 1/1 Running 0 2m48s 10.244.2.18 node2 <none> <none>
      nginx-deployment-7f57b5c565-zcckg 1/1 Running 0 2m48s 10.244.1.14 node1 <none> <none>
      # 动态扩容副本为5
      [root@master ~]# kubectl scale deployment nginx-deployment --replicas=5
      deployment.extensions/nginx-deployment scaled
      [root@master ~]# kubectl get deployment
      NAME READY UP-TO-DATE AVAILABLE AGE
      nginx-deployment 5/5 5 5 5m20s
      [root@master ~]# kubectl get rs
      NAME DESIRED CURRENT READY AGE
      nginx-deployment-7f57b5c565 5 5 5 5m27s
      [root@master ~]# kubectl get pod
      NAME READY STATUS RESTARTS AGE
      nginx-deployment-7f57b5c565-9x2fc 1/1 Running 0 68s
      nginx-deployment-7f57b5c565-d9t68 1/1 Running 0 68s
      nginx-deployment-7f57b5c565-pshpl 1/1 Running 0 5m34s
      nginx-deployment-7f57b5c565-wxlbn 1/1 Running 0 5m34s
      nginx-deployment-7f57b5c565-zcckg 1/1 Running 0 5m34
      # 更改创建容器使用的镜像
      [root@master ~]# kubectl set image deployment/nginx-deployment mynginx=nginx:1.21.3
      deployment.extensions/nginx-deployment image updated
      # 会创建新的rs
      [root@master ~]# kubectl get rs
      NAME DESIRED CURRENT READY AGE
      nginx-deployment-59fbddf78c 5 5 5 43s
      nginx-deployment-7f57b5c565 0 0 0 93s
      [root@master ~]# kubectl get pod
      NAME READY STATUS RESTARTS AGE
      nginx-deployment-59fbddf78c-9vwv9 1/1 Running 0 112s
      nginx-deployment-59fbddf78c-bmjfp 1/1 Running 0 109s
      nginx-deployment-59fbddf78c-pkrcp 1/1 Running 0 116s
      nginx-deployment-59fbddf78c-prlsw 1/1 Running 0 107s
      nginx-deployment-59fbddf78c-zjq4w 1/1 Running 0 116s
      # 进行回滚操作
      [root@master ~]# kubectl rollout undo deployment/nginx-deployment
      deployment.extensions/nginx-deployment rolled back
      [root@master ~]# kubectl get rs
      NAME DESIRED CURRENT READY AGE
      nginx-deployment-59fbddf78c 0 0 0 4m46s
      nginx-deployment-7f57b5c565 5 5 5 5m36s
      [root@master ~]# kubectl get pod
      NAME READY STATUS RESTARTS AGE
      nginx-deployment-7f57b5c565-7nn4z 1/1 Running 0 18s
      nginx-deployment-7f57b5c565-dhv46 1/1 Running 0 30s
      nginx-deployment-7f57b5c565-gq6mj 1/1 Running 0 29s
      nginx-deployment-7f57b5c565-m26lf 1/1 Running 0 24s
      nginx-deployment-7f57b5c565-qmd7x 1/1 Running 0 24s
  • Deployment更新策略:

    • Deployment可以保证在升级时只有一定数量的Pod是down的。默认的,它会确保至少有比期望的Pod数量少一个是up状态(最多一个不可用)。
    • Deployment同时也可以确保只创建出超过期望数量的一定数量的Pod。默认的,它会确保最多比期望的Pod数量多一个的Pod是up的(最多1个surge)。
    • 未来的Kuberentes版本中,将从1-1变成25%-25% 。
  • Deployment回滚策略(多个rollout并行):

    • 假如创建了一个有5个niginx:1.7.9 replica的Deployment,但是当还只有3个nginx:1.7.9的replica创建出来的时候您就开始更新含有5个nginx:1.9.1 replica的 Deployment。在这种情况下,Deployment会立即杀掉已创建的3个nginx:1.7.9的Pod,并开始创建nginx:1.9.1的Pod。它不会等到所有的5个nginx:1.7.9的Pod都创建完成后才开始改变航道。

      1
      2
      3
      4
      5
      6
      7
      8
      # 查看版本历史
      $ kubectl rollout history deployment/nginx-deployment

      # 可以使用--revision参数指定某个历史版本
      $ kubectl rollout undo deployment/nginx-deployment --to-revision=2

      # 暂停 deployment 的更新
      $ kubectl rollout pause deployment/nginx-deployment
  • 清理Policy:

    • 可以通过设置spec.revisonHistoryLimit项来指定Deployment最多保留多少revision历史记录。默认的会保留所有的revision;如果将该项设置为0,Deployment就不允许回退了。

1.3 DaemonSet

  • DaemonSet确保全部(或者一些)Node上运行一个Pod的副本。当有Node加入集群时,也会为他们新增一个Pod。当有Node从集群移除时,这些Pod也会被回收。删除DaemonSet将会删除它创建的所有Pod。

  • 使用DaemonSet的一些典型用法:

    • 运行集群存储daemon,例如在每个Node上运行glusterd、ceph。
    • 在每个Node上运行日志收集daemon,例如fluentd、logstash。
    • 在每个Node上运行监控daemon,例如Prometheus Node Exporter、collectd、Datadog代理、New Relic代理,或Ganglia gmond。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    [root@master ~]# vim daemonset.yaml
    [root@master ~]# cat daemonset.yaml
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
    name: deamonset-example
    labels:
    app: daemonset
    spec:
    selector:
    matchLabels:
    name: deamonset-example
    template:
    metadata:
    labels:
    name: deamonset-example
    spec:
    containers:
    - name: daemonset-example
    image: nginx:latest
    [root@master ~]# kubectl create -f daemonset.yaml
    daemonset.apps/deamonset-example created
    [root@master ~]# kubectl get pod
    NAME READY STATUS RESTARTS AGE
    deamonset-example-9vr5w 1/1 Running 0 10s
    deamonset-example-ldw29 1/1 Running 0 10s
    # 每个node都将创建一个DaemonSet副本
    [root@master ~]# kubectl get pod -o wide
    NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
    deamonset-example-9vr5w 1/1 Running 0 2m16s 10.244.1.31 node1 <none> <none>
    deamonset-example-ldw29 1/1 Running 0 2m16s 10.244.2.35 node2 <none> <none>

1.4 Job

  • Job负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。

  • 特殊说明:

    • RestartPolicy仅支持Never或OnFailure。
    • 单个Pod时,默认Pod成功运行后Job即结束。
    • .spec.completions标志Job结束需要成功运行的Pod个数,默认为1。
    • .spec.parallelism标志并行运行的Pod的个数,默认为1。
    • spec.activeDeadlineSeconds标志失败Pod的重试最大时间,超过这个时间不会继续重试。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    [root@master ~]# vim job.yaml
    [root@master ~]# cat job.yaml
    apiVersion: batch/v1
    kind: Job
    metadata:
    name: pi
    spec:
    template:
    metadata:
    name: pi
    spec:
    containers:
    - name: pi
    image: perl
    command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
    restartPolicy: Never
    [root@master ~]# kubectl create -f job.yaml
    job.batch/pi create
    [root@master ~]# kubectl get pod -o wide
    NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
    pi-8hl8j 0/1 Completed 0 10m 10.244.2.36 node2 <none> <none>
    [root@master ~]# kubectl get job
    NAME COMPLETIONS DURATION AGE
    pi 1/1 9m47s 11m
    [root@master ~]# kubectl log pi-8hl8j
    log is DEPRECATED and will be removed in a future version. Use logs instead.
    3.1415926535897932384626433832795028841971693993751058209749445923078164062862
    …………

1.5 CronJob

  • Cron Job管理基于时间的Job,即:

    • 在给定时间点只运行一次。
    • 周期性地在给定时间点运行。
  • 使用前提条件:当前使用的Kubernetes集群,版本 >= 1.8(对CronJob);对于先前版本的集群,版本 <1.8,启动API Server时,通过传递选项–runtime-config=batch/v2alpha1=true可以开启batch/v2alpha1API。

  • 典型的用法如下所示:

    • 在给定的时间点调度Job运行。
    • 创建周期性运行的Job,例如:数据库备份、发送邮件。
  • CronJob spec用法:

    • .spec.schedule:调度,必需字段,指定任务运行周期,格式同Cron。
    • .spec.jobTemplate:Job模板,必需字段,指定需要运行的任务,格式同Job。
    • .spec.startingDeadlineSeconds:启动Job的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的Job将被认为是失败的。如果没有指定,则没有期限。
    • .spec.concurrencyPolicy:并发策略,该字段也是可选的。它指定了如何处理被Cron Job创建的Job的并发执行。只允许指定下面策略中的一种:
    • Allow(默认):允许并发运行Job。
    • Forbid:禁止并发运行,如果前一个还没有完成,则直接跳过下一个。
    • Replace:取消当前正在运行的Job,用一个新的来替换注意,当前策略只能应用于同一个Cron Job创建的Job。如果存在多个Cron Job,它们创建的Job之间总是允许并发运行。
    • .spec.suspend:挂起,该字段也是可选的。如果设置为true,后续所有执行都会被挂起。它对已经开始执行的Job不起作用。默认值为false。
    • .spec.successfulJobsHistoryLimit和.spec.failedJobsHistoryLimit:历史限制,是可选的字段。它们指定了可以保留多少完成和失败的Job。默认情况下,它们分别设置为3和1。设置限制的值为0,相关类型的Job完成后将不会被保留。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    [root@master ~]# vim cronjob.yaml
    [root@master ~]# cat cronjob.yaml
    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 ~]# kubectl create -f cronjob.yaml
    cronjob.batch/hello created
    [root@master ~]# kubectl get cronjob
    NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
    hello */1 * * * * False 0 <none> 7s
    [root@master ~]# kubectl get job
    NAME COMPLETIONS DURATION AGE
    hello-1633877460 1/1 9s 24s
    # 1分钟后再创建一个job
    [root@master ~]# kubectl get job
    NAME COMPLETIONS DURATION AGE
    hello-1633877460 1/1 9s 89s
    hello-1633877520 1/1 9s 29s
    [root@master ~]# kubectl get pod
    NAME READY STATUS RESTARTS AGE
    hello-1633877460-d78qt 0/1 Completed 0 2m12s
    hello-1633877520-mnbq9 0/1 Completed 0 72s
    hello-1633877580-rz7np 0/1 Completed 0 12s
    [root@master ~]# kubectl log hello-1633877460-d78qt
    log is DEPRECATED and will be removed in a future version. Use logs instead.
    Sun Oct 10 14:51:13 UTC 2021
    Hello from the Kubernetes cluster

1.6 StateFulSet

  • StatefulSet作为Controller为Pod提供唯一的标识。它可以保证部署和scale的顺序。
  • StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括:
    • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现。
    • 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现。
    • 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现。
    • 有序收缩,有序删除(即从N-1到0)。

1.7 Horizontal Pod Autoscaling

  • 即HPA,本身不是控制器,而是上方列举的控制器的附属品,用来管理控制器。

  • 应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让service中的Pod个数自动调整呢?这就有赖于Horizontal Pod Autoscaling了,顾名思义,使Pod水平自动缩放。