kubelet是k8s集群节点代理程序,它在每个工作节点上都运行着一个实例。因而,集群中的某工作节点发生故障时,其kubelet也必将不再可用,于是,节点上的Pod资源的健康状态将无从得到保证,也无法再由kubelet重启。此种场景中的Pod存活性一般要由工作节点之外的Pod控制器来保证。事实上,遭到意外删除的Pod资源的恢复也依赖于其控制器。

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

1.关于Pod控制器

我们可以把API Server类比成一个存储对象的数据库系统,它向客户端提供了API,并负责存储由用户创建的各种资源对象,至于各对象的当前状态如何才能符合用户期望的状态,则需要交由另一类称为控制器的组件来负责完成。创建完成后,每一个控制器对象都可以通过内部的和解循环(reconciliation loop),不间断地监控着由其负责的所有资源并确保其处于或不断地逼近用户定义的目标状态。

1.1 Pod控制器概述

Master的各组件中,API Server仅负责将资源存储于etcd中,并将其变动通知给各相关的客户端程序,如kubelet、kube-scheduler、kube-proxy和kube-controller-manager等,kube-scheduler监控到处于未绑定状态的Pod对象出现时遂启动调度器为其挑选适配的工作节点,然而,k8s的核心功能之一还在于要确保各资源对象的当前状态(status)以匹配用户期望的状态(spec),使当前状态不断地向期望状态”和解”(reconciliation)来完成容器应用管理,而这些则是kube-controller-manager的任务。

List-Watch是k8s实现的核心机制之一,在资源对象的状态发生变动时,由API Server负责写入etcd并通过水平触发(level-triggered)机制主动通知给相关的客户端程序以确保其不会错过任何一个事件。控制器通过API Server的watch接口实时监控目标资源对象的变动并执行和解操作,但并不会与其他控制器进行任何交互,彼此之间根本就意识不到对方的存在。

1.2 控制器与Pod对象

Pod控制器资源通过持续性地监控集群中运行着的Pod资源对象来确保其管控的资源严格符合用户期望的状态,例如资源副本的数量要精确符合期望等。通常,一个Pod控制资源至少应该包含三个基本的组成部分:

  • 标签选择器:匹配并关联Pod资源对象,并据此完成受其管控的Pod资源计数;
  • 期望的副本数:期望在集群中精确运行着的Pod资源的对象数量;
  • Pod模版:用于新建Pod资源对象的Pod模版资源;

1.3 Pod模版资源

PodTemplate是k8s API的常用资源类型,常用于为控制器指定自动创建Pod资源对象时所需的配置信息。因为要内嵌于控制器中使用,所以Pod模版的配置信息中不需要apiVersion和kind字段,但除此之外的其他内容与定义自主式Pod对象所支持的字段几乎完全相同,这包括metadata和spec及其内嵌的其他各个字段。Pod控制器类资源的spec字段通常都要内嵌replicas、selector和template字段,其中template即为Pod模版的定义。下面是一个定义在ReplicaSet资源中的模版资源示例:

  1. apiVersion: apps/v1
  2. kind: ReplicaSet
  3. metadata:
  4. name: rs-example
  5. spec:
  6. replicas: 2
  7. selector:
  8. matchLabels:
  9. app: rs-demo
  10. template:
  11. metadata:
  12. labels:
  13. app: rs-demo
  14. spec:
  15. containers:
  16. - name: myapp
  17. image: ikubernetes/myapp:v1
  18. ports:
  19. - name: http
  20. containerPort: 80

如上示例中,spec.template字段在定义时仅给出了metadata和spec两个字段,它们的使用方法与自主式Pod资源完全相同。

2.ReplicationController和ReplicaSet

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

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

虽然 ReplicaSet 可以独立使用,但一般还是建议使用 Deployment 来自动管理 ReplicaSet,这样就无需担心跟其他机制的不兼容问题(比如 ReplicaSet 不支持 rolling-update 但 Deployment 支持)。

ReplicaSet 示例:

apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: frontend
  # these labels can be applied automatically
  # from the labels in the pod template if not set
  # labels:
    # app: guestbook
    # tier: frontend
spec:
  # this replicas value is default
  # modify it according to your case
  replicas: 3
  # selector can be applied automatically
  # from the labels in the pod template if not set,
  # but we are specifying the selector here to
  # demonstrate its usage.
  selector:
    matchLabels:
      tier: frontend
    matchExpressions:
      - {key: tier, operator: In, values: [frontend]}
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3
        resources:
          requests:
            cpu: 100m
            memory: 100Mi
        env:
        - name: GET_HOSTS_FROM
          value: dns
          # If your cluster config does not include a dns service, then to
          # instead access environment variables to find service host
          # info, comment out the 'value: dns' line above, and uncomment the
          # line below.
          # value: env
        ports:
        - containerPort: 80

3.Deployment 控制器

Deploymen是K8s控制器的又一种实现,它构建于ReplicaSet控制器之上,可为Pod和ReplicaSet资源提供声明式更新。相比较而言,Pod和ReplicaSet是较低级别的资源,它们很少被直接使用。

相比较于手动创建和管理Pod资源来说,Deployment能够实现以下功能:

  • 确保Pod资源对象的数量精确反映期望值:Deploy需要确保由其控制运行的Pod副本数量精确吻合配置中定义的期望值,否则就会自动补足所缺或终止所余;
  • 确保Pod健康运行:探测到由其管控的Pod对象因其所在的工作节点故障而不可用时,自动请求由调度器于其他工作节点创建缺失的Pod副本;
  • 弹性伸缩:业务规模可以在波峰或波谷期间,可以通过Deploy控制器动态调整相关Pod资源对象的数量。此外,在必要时还可以通过HPA(HroizontalPodAutoscaler)控制器实现Pod资源规模的自动伸缩;
  • 事件和状态查看:必要时可以查看Deploy对象升级的详细进度和状态;
  • 回滚:升级操作完成后发现问题时,支持使用回滚机制将应用返回到前一个或由用户指定的历史记录中的版本上;
  • 版本记录:对Deploy对象的每一次操作都予以保存,以供后续可能执行的回滚操作使用;
  • 暂停和启动:对于每一次升级,都能够随时暂停和启动;
  • 多种自动更新方案:一是Recreate,即重建更新机制,全面停止、删除旧有的Pod后用新版本替代;另一个是RollingUpdata,即滚动升级机制,逐步替换旧有的Pod至新的版本;

3.1 创建Deployment

类似于Pod资源,创建Deployment控制对象同意可以使用YAML或JSON格式的清单文件定义其配置,它也由kind、apiVersion、metadata、spec和status这5个一级字段组成,其中status为只读字段,因此在清单文件中配置的仅为前4个字段。它的spec字段一般嵌套使用以下几个属性字段:

  • replicas : 期望的Pod对象副本数;
  • selector : 当前控制器匹配Pod对象副本的标签选择器,支持matchLabels和matchExpressions两种匹配机制;
  • template : 用于补足Pod副本数量时使用的Pod模版资源;

    示例:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myapp-deploy
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: myapp
      template:
        metadata:
          labels:
            app: myapp
        spec:
          containers:
          - name: myapp
            image: ikubernetes/myapp:v1
            ports:
            - containerPort: 80
              name: http
    

    创建:

    kubectl apply -f deploy-test.yml
    

    kubectl get deployments(或者deploy)可以列出创建的Deployment对象myapp-deploy及其信息,下面显示的字段中,UP-TO-DATE表示已经达到期望状态的Pod副本数量,AVAILABLE表示当前可用状态的应用程序的数量:

    # kubectl get deploy myapp-deploy
    NAME           READY   UP-TO-DATE   AVAILABLE   AGE
    myapp-deploy   3/3     3            3           4m34s
    

    Deployment控制器会自动创建相关的ReplicaSet控制器资源,并以”[DEPLOYMENT-NAME]-[POD_TEMPLATE-HASH-VALUE]”格式为其命名,其中的hash值由Deployment控制器自动生产,由Deployment创建的ReplicaSet对象会自动使用相同的标签选择器,因此,可以使用类似如下的命令查看其相关信息:

    # kubectl get replicasets.apps -l app=myapp
    NAME                      DESIRED   CURRENT   READY   AGE
    myapp-deploy-5cbd66595b   3         3         3       5m15s
    

    相关的Pod对象的信息也可以用相似的命令进行获取:

    # kubectl get pod -l app=myapp
    NAME                            READY   STATUS    RESTARTS   AGE
    myapp-deploy-5cbd66595b-5hms8   1/1     Running   0          5m44s
    myapp-deploy-5cbd66595b-mh94g   1/1     Running   0          5m44s
    myapp-deploy-5cbd66595b-tzgzb   1/1     Running   0          5m44s
    

    3.2 更新策略

    Deployment控制器支持两种更新策略:滚动更新(rolling update)和重新创建(recreate),默认为滚动更新。

    3.2.1 更新概念

    重新创建更新,首先删除现有的Pod对象,而后由控制器基于新模版重新创建出新版本资源对象。通常,只应该在应用的新旧版本不兼容(如依赖的后端数据库的schema不同且无法兼容)时运行时才会使用recreate策略,因为它会导致应用替换期间暂时不可用,好处在于它不存在中间状态,用户访问到的要么是应用的新版本,要么是旧版本;

    滚动升级是默认的更新策略,它在删除一部分旧版本Pod资源的同时,补充创建一部分新版本的Pod对象进行应用升级,其优势是升级期间,容器中应用提供的服务不会中断,但要求应用程序能够应对新旧版本同时工作的情形,不过,更新操作期间,不同客户端得到的响应内容可能会来自不同版本的应用。

    3.2.2 Pod数量控制

    滚动更新时,应用升级期间还要确保可用的Pod对象数量不低于某阈值以确保可以持续处理客户端的服务请求,变动的方式和Pod对象的数量范围将通过spec.strategy.rollingUpdate.maxSurgespec.strategy.rollingUpdate.maxUnavailable两个属性协同进行定义:

    • maxSurge: 指定升级期间存在的总Pod对象数量最多可超出期望值的个数,其值可以是0或正整数,也可以是一个期望值的百分比;例如,如果期望值为3,当前的属性值为1,则表示Pod对象的总数不能超过4个;
    • maxUnavailable: 升级期间正常可用的Pod副本数(包括新旧版本)最多不能低于期望数值的个数,其值可以是0或正整数,也可以是一个期望值的百分比;默认值为1,该值意味着如果期望值是3,则升级期间至少要有两个Pod对象处于正常提供服务的状态;

    注意:maxSurge和maxUnabailable属性的值不可同时为0,否则Pod对象的副本数量在符合用户期望的数量后无法作出合理变动以进行滚动更新操作。

    3.2.3 更新速度控制

    配置时,可以使用Deployment控制器的spec.minReadySeconds属性来控制应用升级的速度。新旧更替过程中,新创建的Pod对象一旦成功响应就绪探测即被视作可用,而后即可立即开始下一轮的替换操作。而spec.minReadySeconds能够定义在新的Pod对象创建后至少要等待多久才会将其视作就绪,在此期间,更新操作会被阻塞。因此,它可以用来让k8s在每次创建出Pod资源后都要等上一段时长后再开始下一轮的更替,这个时间长度的理想值是等到Pod对象中的应用已经可以接受并处理请求流量。

    事实上,一个精心设计的等待时长和就绪性探测能让k8s系统规避一部分因程序Bug而导致的升级故障。

    3.2.4 回滚

    Deployment控制器也支持用户保留其滚动更新历史中的旧ReplicaSet对象版本,这赋予了控制器进行应用回滚的能力:用户可按需回滚到指定的历史版本。控制器可保存的历史版本数量由spec.revisionHistoryLimit属性进行定义。当然,也只有保存于revision历史中的ReplicaSet版本可用于回滚,因此,要习惯性地在更新操作时指定保留旧版本。

    注意:为了保存版本升级的历史,需要更改资源时加上--record

    3.3 升级Deployment

    修改Pod模版相关的配置参数便能完成Deployment控制器资源的更新。由于是声明式配置,因此对Dployemnt资源的修改尤其适合使用apply和patch命令来进行;当前,如果仅是修改容器镜像,set image命令更为易用。

    例如使用kubectl patch命令为其spec.minReadySeconds字段定义一个等待时长5s:

    # kubectl patch deployments.apps myapp-deploy -p '{"spec":{"minReadySeconds":5}}'
    deployment.apps/myapp-deploy patched
    

    patch命令的补丁形式为JSON格式,以-p选项指定,若要改变myapp-deploy中myapp容器的镜像,也可使用patch命令,如'{"spec":{"containers":["name":"myapp","image":"ikubernetes/myapp:v2"]}}',不过修改容器镜像有更为简单的专用命令set image

    注意:修改Deployment的minReadySeconds、replicas和strategy等字段的值并不会触发Pod资源的更新操作,因为它们不属于模版的内嵌字段,对现存的Pod对象不产生任何影响。

    更改镜像:

    # kubectl set image deploy myapp-deploy myapp=ikubernetes/myapp:v2
    deployment.apps/myapp-deploy image updated
    

    kubectl rollout status可用于打印滚动更新过程中的状态信息:

    # kubectl rollout status deployment myapp-deploy
    Waiting for deployment "myapp-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "myapp-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "myapp-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "myapp-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "myapp-deploy" rollout to finish: 1 old replicas are pending termination...
    Waiting for deployment "myapp-deploy" rollout to finish: 1 old replicas are pending termination...
    Waiting for deployment "myapp-deploy" rollout to finish: 1 old replicas are pending termination...
    deployment "myapp-deploy" successfully rolled out
    

    还可以使用kubectl get deployments myapp-deploy --watch监控其更新过程。

    访问web服务,确定是否更新成功:

    # curl $(kubectl get pods myapp-deploy-6685c8c7fc-67jrx -o go-template={{.status.podIP}})
    Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
    

    3.4 金丝雀发布

    Deployment控制器还支持自定义控制更新过程中的滚动节奏,如“暂停”或“继续”更新操作,尤其是借助于前文讲到的maxSurge和maxUnavailable属性还能实现更为精巧的过程控制。比如,待第一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一小部分新版本的应用,主机部分还是旧的版本。然后,再根据用户特征筛选出小部分用户的请求路由至新版本的Pod应用,并持续观察其是否能稳定地按期望的方式运行。确定没有问题后再极细完成余下Pod资源的滚动更新,否则立即回滚更新操作。这便是所谓的金丝雀发布(Canary Release)。

    为了尽可能地降低对现有系统及其容量的影响,金丝雀发布过程通常建议采用“先添加、再删除,且可用Pod资源对象总数不低于期望值”的方式进行。首次添加的Pod对象数量取决于其接入的第一批请求的规则及单个Pod的承载能力,视具体需求而定,为了能够更简单地说明问题,接下来采用首批添加1个Pod资源的方式。将Deploy的maxSurge属性的值设置为1,并将maxUnavailable属性的值设置为0:

    # kubectl patch deployments.apps myapp-deploy -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge": 1, "maxUnavailable": 0}}}}'
    deployment.apps/myapp-deploy patched
    

    启动myapp-deploy控制器的更新过程,在修改相应容器的镜像版本后立即暂停更新进度,它会在启动第一批新版本Pod对象的创建操作之后转为暂停状态。需要注意的是,这里之所以能够在第一批更新启动后就暂停,有赖于此前为maxReadySeconds属性设置的时长,因此用户要在更新命令启动后的此时长指定的时间范围内启动暂停操作。当然,也可以直接以”&&”符号在shell中连接两个命令:

    # kubectl set image deploy myapp-deploy myapp=ikubernetes/myapp:v3 --record && kubectl rollout pause deployment myapp-deploy
    deployment.apps/myapp-deploy image updated
    deployment.apps/myapp-deploy paused
    
    # kubectl rollout status deployment myapp-deploy
    Waiting for deployment "myapp-deploy" rollout to finish: 1 out of 3 new replicas have been updated...
    

    相关的Pod列表也可能显示旧版本的ReplicaSet的所有Pod副本仍在正常运行,新版本的ReplicaSet也包含一个Pod副本,但最多不超过期望值1个,myapp-deploy原有的期望值为3,因此总数不超过4个。此时,通过Service或Ingress资源及相关路由策略等设定,即可将一部分用户的流量引入到这些Pod之上进行发布验证。运行一段时间后,如果确定没有问题,即可使用kubectl rollout resume命令继续此前的滚动更新:

    # kubectl rollout resume deployment myapp-deploy
    deployment.apps/myapp-deploy resumed
    
    # kubectl rollout status deployment myapp-deploy
    Waiting for deployment "myapp-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "myapp-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "myapp-deploy" rollout to finish: 2 out of 3 new replicas have been updated...
    Waiting for deployment "myapp-deploy" rollout to finish: 1 old replicas are pending termination...
    Waiting for deployment "myapp-deploy" rollout to finish: 1 old replicas are pending termination...
    Waiting for deployment "myapp-deploy" rollout to finish: 1 old replicas are pending termination...
    deployment "myapp-deploy" successfully rolled out
    

    3.5 回滚Deployment

    查看历史记录:

    # kubectl rollout history deployment myapp-deploy
    deployment.apps/myapp-deploy
    REVISION  CHANGE-CAUSE
    1         <none>
    2         kubectl set image deploy myapp-deploy myapp=ikubernetes/myapp:v2 --record=true
    3         kubectl set image deploy myapp-deploy myapp=ikubernetes/myapp:v3 --record=true
    

    CHANGE-CAUSE的内容是从Deployment的kubernetes.io/change-cause注解复制过来的。复制动作发生在修订版本创建时。可以通过以下方式设置CHANGE-CAUSE:

    • 使用kubectl annotate deployment.v1.apps/nginx-deployment kubernetes.ui/change-cause="image updated to 1.9.1"为Deployment添加注解;
    • 追加--record命令行标志以保存正在更改资源的kubectl命令;
    • 手动编辑资源的清单;

    查看修订历史的详细信息:

    # kubectl rollout history deployment myapp-deploy --revision=2
    deployment.apps/myapp-deploy with revision #2
    Pod Template:
      Labels:    app=myapp
        pod-template-hash=6685c8c7fc
      Annotations:    kubernetes.io/change-cause: kubectl set image deploy myapp-deploy myapp=ikubernetes/myapp:v2 --record=true
      Containers:
       myapp:
        Image:    ikubernetes/myapp:v2
        Port:    80/TCP
        Host Port:    0/TCP
        Environment:    <none>
        Mounts:    <none>
      Volumes:    <none>
    

    Deployment的回滚操作可使用kubectl rollout undo命令完成

    # kubectl rollout undo deployment myapp-deploy
    deployment.apps/myapp-deploy rolled back
    

    或者在kubectl rollout undo命令上使用--to-revision选项回滚到特定历史版本:

    # kubectl rollout undo deployments myapp-deploy --to-revision=2
    deployment.apps "myapp-deploy"
    

    3.6 扩容和缩容

    通过修改.spec.replicas即可修改Deploy中的Pod资源的副本数量,它将实时作用于控制器并直接生效。replicas属性的值可直接修改资源配置文件,然后使用kubectl apply进行应用,也可以使用kubectl edit对其进行实时修改。

    kubectl scale是专用于扩展某些控制器类型的应用规模的命令,包括Deployment和Job等。

    # kubectl scale deployment myapp-deploy --replicas=4
    deployment.apps/myapp-deploy scaled
    

    假设集群启用了Pod的水平自动缩放,可以为Deployment设置自动缩放器,并基于现有Pods的CPU利用率选择要运行的Pods个数下限和上限:

    # kubectl autoscale deployment myapp-deloy --min=10 --max=15 --cpu-percent=80
    deployment.apps/myapp-deploy scaled
    

    4.DaemonSet控制器

    DaemonSet是Pod控制器的又一种实现,用于在集群中的全部节点上同时运行一份指定的Pod资源副本,后续新加入集群的工作节点也会自动创建一个相关的Pod对象,当从集群移除节点时,此类Pod对象也将被自动回收而无须重建。管理员也可以使用节点选择器及节点标签指定仅在部分具有特定特征的节点上运行指定的Pod对象。

    DaemonSet是一种特殊的控制器,通常运行那些执行系统级操作任务的应用,其应用场景具体如下:

    • 运行集群存储的守护进程,如在各个节点上运行glusterd或ceph;
    • 在各个节点上运行日志收集守护进程,如fluentd和logstash;
    • 在各个节点上运行监控系统的代理守护进程,如Prometheus Node Exporter、collectd、Datadog agent等;

    另外,也只有必须将Pod对象运行于固定的几个节点并且需要先于其他Pod启动时,才有必要使用DaemonSet控制器,否则就应该使用Deployment控制器。

    4.1 创建DaemonSet资源对象

    DaemonSet控制器的spec字段中嵌套使用的字段同样主要包括前面讲到的Pod控制器资源支持的selector、template和minReadySeconds,并且功能和用法基本相同,但它不支持使用replicas,毕竟DaemonSet并不是基于期望的副本数来控制Pod资源数量,而是基于节点数量,但template是必选字段。

    下面示例定义了一个名为filebeat-ds的DaemonSet控制器,它将在每个节点上运行一个filebeat进程以收集容器相关的日志数据:

    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: filebeat-ds
      labels:
        app: filebeat
    spec:
      selector:
        matchLabels:
          app: filebeat
      template:
        metadata:
          labels:
            app: filebeat
          name: filebeat
        spec:
          containers:
          - name: filebeat
            image: ikubernetes/filebeat:5.6.5-alpine
            env:
            - name: REDIS_HOST
              value: db.ilinux.io:6379
            - name: LOG_LEVEL
              value: info
    

    4.2 更新DaemonSet对象

    它支持RollingUpdate(滚动更新)和OnDelete(删除时更新)两种更新策略,滚动更新为默认的更新策略,工作逻辑类似于Deployment,不过仅支持使用maxUnavailable属性定义最大不可用Pod资源副本数(默认值为1),而删除时更新的方式则是在删除相应节点的Pod资源后重建并更新为新版本。

    5.Job控制器

    与Deployment及DaemonSet控制器管理的守护进程类的服务应用不同的是,Job控制器用于调度Pod对象运行一次性任务,容器中的进程在正常运行结束后不会对其进行重启,而是将Pod对象置于Completed状态。若容器中的进程因错误而终止,则需要依配置确定重启与否,未运行完成的Pod对象因其所在的节点故障而意外终止后会被重新调度。

    实践中,有的作业任务可能需要运行不止一次,用户可以配置它们以串行或并行的方式运行。这种类型的Job控制器对象有两种:

    • 单工作队列(work queue)的串行式Job: 即以多个一次性的作业方式串行执行多次作业,直至满足期望的次数;
    • 多工作队列的并行式Job:这种方式可以设置工作队列数,即作业数,每个队列仅负责运行一个作业;也可以用有限的工作队列运行较多的作业,即工作队列数少于总作业数,相当于运行多个串行作业队列。

    Job控制器常用于管理那些运行一段时间便可完成的任务,例如计算或备份操作。

    5.1 创建Job对象

    Job控制器的spec字段内嵌的必要字段仅为template,它的使用方式与Deployment等控制器并无不同。需要注意的是,Job位于API群组”batch/v1”之内。

    示例job-example.yml:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-example
    spec:
      template:
        spec:
          containers:
          - name: myjob
            image: alpine
            command: [/bin/sh, -c, sleep 120]
          restartPolicy: Never
    

    注意:Pod模版中的spec.restartPolicy默认为Always,这对Job控制器来说并不适用,因此必须在Pod模版中显示设定restartPolicy属性的值为Never或Onfailure。

    两分钟后,待sleep命令执行完成并成功退出后,Pod资源即转换为Completed状态:

    # kubectl get pod -l job-name=job-example
    NAME                READY   STATUS      RESTARTS   AGE
    job-example-mfv45   0/1     Completed   0          16m
    

    5.2 并行式Job

    将并行度属性.spec.parallelism的值设置为1,并设置总任务.spec.completion属性便能够让Job控制器以串行方式运行多任务。下面是一个串行运行5次任务的Job控制器示例:

    apiVersion: batch/v1
    kind: Job
    metadata:
      name: job-multi
    spec:
      completions: 5
      template:
        spec:
          containers:
          - name: myjob
            image: alpine
            command: [/bin/sh, -c, sleep 20]
          restartPolicy: OnFailure
    

    可以使用kubectl get pods -l job-name=job-multi --watch来监控其变动,以了解其执行过程。

    .spec.parallelism能够定义作业执行的并行度,将其设置为2或者以上的值即可实现并行多队列作业运行。例如,某Job控制器配置中的spec字段嵌套了如下属性,表示以2个队列并行的方式,总共运行5次的作业:

    spec:
      completions: 5
      parallelism: 2
    

    5.3 Job扩容

    Job控制器的.spec.parallelism定义的并行度表示同时运行的Pod对象数,此属性值支持运行时调整从而改变其队列总数,实现扩容和缩容。使用的命令与之前的Deployment对象相同,即kubectl scale --replicas,例如在其运行过程中(未完成之前)将job-multi的并行度扩展为两路:

    # kubectl scale jobs job-multi --replicas=2
    

    根据工作节点及其资源可用量,适度提高Job的并行度,能够大大提升其完成效率,缩短运行时间。

    5.4 删除Job

    Job控制器待其Pod资源运行完成后,将不再占用系统资源。用户可按需保留或使用资源删除命令将其删除。不过,如果某Job控制器的容器应用总是无法正常结束运行,而其restartPolicy又定为了重启,则它可能会一直处于不停地重启和错误的循环当中。Job提供了两个属性用于抑制这种情况的发生:

    • .spec.activeDeadlineSeconds : Job的deadline,用于为其指定最大活动时间长度,超出此时长的作业将被终止;
    • .spec.backoffLimit : 将作业标记为失败状态之前的重试次数,默认值为6;

    例如,下面示例表示其失败重试的次数为5,如果超出100秒的时间仍未运行完成,那么其将被终止:

    spec:
      backoffLimit: 5
      activeDeadlineSeconds: 100
    

    6.CronJob控制器

    CronJob控制器用于管理Job控制器资源的运行时间。Job定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob类似与Linux系统的crontab:

    • 在未来某时间点运行作业一次
    • 在指定的时间点重复运行作业

    6.1 创建CronJob对象

    CronJob的spec字段可嵌套使用以下字段:

    • jobTemplate : Job控制器模版,用于为CronJob生成Job对象;必选字段;
    • schedule : Cron格式的作业调度运行时间点;必选字段;
    • concurrencyPolicy : 并发执行策略,可用值有”Allow”、”Forbid“和”Replace”,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业;
    • failedJobHistoryLimit : 为失败的任务执行保留的历史记录数,默认为1;
    • successfulJobsHistoryLimit : 为成功的任务执行保留的历史记录数,默认为3;
    • startingDeadlineSeconds : 因各种原因缺乏执行作业的时间点所导致的启动作业错误的超时时长,会被记入错误历史记录;
    • suspend : 是否挂起后续的任务执行,默认为false,对运行中的作业不会产生影响;
    • 示例,每隔2分钟运行一次由jobTemplate定义的简单任务:

      apiVersion: batch/v1beta1
      kind: CronJob
      metadata:
        name: cronjob-example
        labels:
          app: cronjob
      spec:
        schedule: "*/2 * * * *"
        jobTemplate:
          metadata:
            labels:
              app: cronjob-jobs
          spec:
            parallelism: 2
            template:
              spec:
                containers:
                - name: cronjob
                  image: alpine
                  command: [/bin/sh, -c, date; echo Hello; sleep 10]
                restartPolicy: OnFailure
      
      # kubectl get cronjobs.batch cronjob-example
      NAME              SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
      cronjob-example   */2 * * * *   False     0        85s             5m6s
      

      6.2 CronJob的控制机制

      CronJob是一个更高级别的资源,它以Job控制器资源为其管控对象,并借助它管理Pod资源对象。可列出的Job对象的数量取决于CronJob资源的.spec.successfulJobsHistoryLimit的属性值,默认为3。

      # kubectl get jobs.batch -l app=cronjob-jobs
      NAME                         COMPLETIONS   DURATION   AGE
      cronjob-example-1607657880   2/1 of 2      42s        4m49s
      cronjob-example-1607658000   2/1 of 2      37s        2m49s
      cronjob-example-1607658120   2/1 of 2      30s        49s
      

      如果作业重复执行时指定的时间点较近,而作业执行时长(普遍或偶尔)跨过了其两次执行的时间长度,则会出现两个Job对象同时存在的情形。有些Job对象可能会存在无法或不能同时运行的情况,这个时候就要通过.spec.concurrencyPolicy属性控制作业并存的机制:

      • Allow:默认值,即允许前后Job,甚至同属于一个CronJob的更多Job同时运行;
      • Forbid:用于禁止前后两个Job同时运行,如果前一个尚未结束,后一个则不予启动(跳过);
      • Replace:用于让后一个Job取代前一个,即终止前一个并启动后一个;

      7.Pod中断预算

      尽管Deplyment或ReplicaSet一类的控制器能够确保相应Pod对象的副本数量不断逼近期望的数量,但它却无法保证在某一时刻一定会存在指定数量或比例的Pod对象,然而这种需求在某些强调服务可用性的场景中却是必备的。Pod中断预算(PodDisruptionBudget,简称PDB)类型的资源,用于为那些资源的(Voluntary)中断做好预算方案(Budget),限制可自愿中断的最大Pod副本数或确保最好可用的Pod副本数,以确保服务的高可用性。

      Pod对象会一直存在,除非有意将其销毁,或者出现了不可避免的硬件或系统软件错误。非资源中断是指那些由不可控外界因素导致的Pod中断退出操作,例如,硬件或系统内核故障、网络故障以及节点资源不足导致Pod对象被驱逐等;而那些由用户特地执行的管理操作导致的Pod中断则称为自愿中断,例如排空节点、人为删除Pod对象等。部署在k8s的每个应用程序都可以创建一个对应的PDB对象以限制自愿中断时最大可以中断的副本数或最少应该保持可用的副本数,从而保证应用自身的高可用性。

      定义PDB资源时,其spec字段主要嵌套使用以下三个字段:

      • selector : 当前PDB对象使用的标签选择器,一般是与相关的Pod控制器使用同一个选择器;
      • minAvailable : Pod资源中断的场景中,至少要保证可用的Pod对象数量或比例,要阻止任何Pod对象发生资源中断,可将其设置为100%;
      • maxUnavailable : Pod资源中断的场景中,最多可转换为不可用状态的Pod对象数量或比例,0值意味着不允许Pod对象进行自愿中断;此字段与minAvailable互斥;
      • 下面示例,设置Pod中断预算,要求其最少可用的Pod对象数量为2个:

        apiVersion: policy/v1beta1
        kind: PodDisruptionBudget
        metadata:
          name: myapp-pdb
        spec:
          minAvailable: 2
          selector:
            matchLabels:
              app: myapp
        

        8.小结

        主要讲解了kubernetes的Pod控制器,它们是工作负载类资源的核心组成部分,是基于K8s运行应用的最重要的资源类型之一:

        • 工作负载类型的控制器根据业务需求管控Pod资源的生命周期;
        • ReplicaSet可以确保守护进程型的Pod资源始终具有精确的、处于运行状态的副本数量,并支持Pod规模的伸缩机制;它是新一代的ReplicationController控制器,不过用户通常不应该直接使用ReplicaSet,而是要使用Deployment;
        • Deployment是建构在ReplicaSet上的更加抽象的工作负载型控制器,支持多种更新策略及发布机制;
        • Job控制器能够控制相应的作业任务得以正常完成并退出,支持并行式多任务;
        • CronJob控制器用于控制周期性作业任务,其功能类似于Linux操作系统上的Crontab;
        • PodDisruptionBudget资源对象为k8s系统上额容器化应用提供了高可用能力。