工作负载是在Kubernetes上运行的应用程序,主要功能是对pod进行管理。包括以下几类:

  • 无状态工作负载

无状态工作负载管理的pod是相互等价的,需要的时候可以互相替换。包括DeploymentReplicaSetReplicationController

  • 有状态工作负载

有状态的工作负载为每个pod维护了一个唯一的ID,能够保证pod的顺序和唯一性,每个pod是不可替代的。可使用持久存储来保存服务产生的状态,如StatefulSet

  • 守护进程工作负载

守护进程工作负载如DaemonSet,管理节点上运行的守护进程。

  • 批处理工作负载

批处理工作负载如JobCronJob,用于处理一次性或周期性任务。

本文将按顺序对这几种工作负载进行介绍。

Deployments

Deployment控制器根据Deployment中的目标状态来创建和修改目标,使其达到期望状态。

创建Deployment

下面是一个Deployment示例,其中创建了一个ReplicaSet,负载启动三个运行nginx的pod:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

在该例中,创建了名为nginx-deployment的无状态工作负载Deployment,该Deployment会创建3个pod,每个pod中运行了一个nginx容器。

selector字段定义了Deployment如何查找要管理的pods,这里通过标签进行匹配。

通过以下命令来创建Deployment:

kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml

运行kubectl get deployments 检查Deployment是否已创建好。要查看Deployment创建的ReplicaSet,可以使用命令kubectl get rs

注意ReplicaSet的名称始终被格式化为[Deployment名称]-[随机字符串]

要查看每个pod自动生成的标签,可以运行以下命令:

kubectl get pods --show-labels

你必须在Deployment中指定适当的选择符和pod模板标签(如app:nginx)。标签或选择符不要与其他控制器(包括Deployment和StatefulSet)相同。虽然Kubernetes本身不限制这样做,但如果多个控制器具有相同的选择符,可能在执行某些操作时发生冲突。

更新Deployment

当Deployment的pod模板(即.spec.template)发生改变时,例如模板的标签或容器镜像被更新,才会触发Deployment上线,其他更新不会出触发上线,如Deployment执行扩缩容的操作。

下面来更新容器镜像,可以使用如下命令:

kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1

或者

kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1

当然,也可以直接用edit命令来编辑yaml文件:

kubectl edit deployment/nginx-deployment

注意,负载类型后面使用斜杠和空格都是可以的。

查看上线状态:
kubectl rollout status deployment nginx-deployment

会显示出deployment的上线状态:
deployment "nginx-deployment" successfully rolled out

通过kubectl get rs查看ReplicaSet,结果类似于:

NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         3       6s
nginx-deployment-2035384211   0         0         0       36s

可以看出Deployment创建了新的ReplicaSet并将其扩容到3个副本,同时旧的ReplicaSet缩容到0个副本。通过此方式完成了pod的更新操作。

查看Deployment的信息:

kubectl describe deployment nginx-deployment

在描述信息的events中可以看到以下信息:

Events:
    Type    Reason             Age   From                   Message
    ----    ------             ----  ----                   -------
    Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set nginx-deployment-2035384211 to 3
    Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 1
    Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 2
    Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 2
    Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 1
    Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 3
    Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 0

可以看出,当deployment首次创建时,它创建了一个ReplicaSet并扩容到3个pod。更新deployment时,它创建了一个新的ReplicaSet并将其扩容到1个pod,等待其就绪;然后将旧ReplicaSet缩容到2,同时将新的ReplicaSet扩容到2。然后使用相同的滚动更新策略继续对ReplicaSet进行扩缩容。最终新的ReplicaSet扩容到3,旧的ReplicaSet缩容到0。

在整个过程中,可用的pod数量始终保持在3-4之间。这是因为默认的滚动更新策略导致的。在上面describe deployment得到的结果中可以看到这样一行:

RollingUpdateStrategy:  25% max unavailable, 25% max surge

表示在pod滚动更新过程中,最大不可用的pod数量不能超过25%,最大增加的pod数量不可超过25%。如果没有显式指定滚动更新策略,25%就是默认的值。这种更新策略可以使得服务在上线升级的时候平滑发布。

注意,对新的ReplicaSet进行扩容和对旧的ReplicaSet进行缩容并不一定有先后顺序,可以同时进行。只需要保证可用的pod数量不低于指定值(默认为:期望值x75%),总的pod数量不高于指定值(默认为:期望值x125%)。

翻转

上面介绍的deployment的更新是在原有的pod都启动完成之后的更新,如果当deployment正在上线时被更新呢?在这种情况下,deployment会针对更新创建一个新的ReplicaSet并开始对其扩容,之前正在被扩容的ReplicaSet会被添加到旧的ReplicaSets列表并开始缩容。

更改标签选择符

通常不建议修改标签选择符,如果确实要更新,则必须要了解这个过程可能发生的所有事情。

在API版本apps/v1中,deployment的标签选择符在创建后是不可变的。

更新选择符需要同步更新deployment规约中的pod模板标签,这会导致deployment重新上线,并创建新的ReplicaSet,而原来创建的ReplicaSet和Pod将不会被筛选到,也就是说,旧ReplicaSet会被孤立。

回滚Deployment

默认情况下,Deployment的所有上线记录都保留在系统中以便随时回滚。

Deployment被触发上线时,系统会创建Deployment的新的修订版本。这意味着仅当deployment的pod模板发生变更时,才会创建新修订版本,其他更新(如扩缩容)不会创建修订版本。这是为了方便同时执行手动缩放或自动缩放。换句话说,当你回滚到较早的修订版本时,只有deployment的pod模板部分会被回滚。

假如你在某次更新deployment时出现问题需要回滚,首先检查deployment的更新记录:

kubectl rollout history deployment nginx-deployment

输出结果类似于:

deployment.apps/nginx-deployment 
REVISION  CHANGE-CAUSE
3         <none>
4         <none>

注意,修订历史保存数量可以用spec.revisionHistoryLimit指定,默认为10。

这里的版本是3和4,更新原因是空的。通过describe命令查看deployment有如下信息:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "4"
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx-deployment","namespace":"default"},"spec":{"replicas":3,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"labels":{"app":"nginx"}},"spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx","ports":[{"containerPort":80}]}]}}}}
...

metadata注解下保存了修订版本号和上次更新的配置。如果在更新deployment时在metadata注解中添加kubernetes.io/change-cause,则更新记录中除了版本号外,还会显示添加的变更原因。

要查看修订历史的详细信息,只要指定修订版本号即可:

kubectl rollout history deployment nginx-deployment --revision=3

假设现在想回滚到上一个版本,即revision=3,执行以下命令:

kubectl rollout undo deployment nginx-deployment --to-revision=3

输出类似于:

deployment.apps/nginx-deployment

如果不指定版本号,则默认回退到上一个版本

缩放Deployment

你可以使用如下指令缩放deployment:

kubectl scale deployment nginx-deployment --repllicas=10

假设集群开启了pod的水平自动缩放,则可以通过如下命令基于CPU利用率来设置自动缩放:

kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80

暂停、恢复deployment的上线

如果你要更新deployment的多个配置,建议使用edit命令一次更新完,然后上线deployment。

但是如果分多次进行deployment的更新,又不想触发不必要的deployment的上线操作,则可以在更新之前暂停deployment上线,确定全部更新完成后再恢复上线。

暂停上线命令如下:

kubectl rollout pause deployment nginx-deployment

接着更新deployment镜像:

kubectl set image deployment nginx-deployment nginx=nginx:1.16.1

执行describe deployment命令,可以看到如下信息:

Conditions:
  Type           Status   Reason
  ----           ------   ------
  Available      True     MinimumReplicasAvailable
  Progressing    Unknown  DeploymentPaused
OldReplicaSets:  nginx-deployment-6595874d85 (3/3 replicas created)
NewReplicaSet:   nginx-deployment-66b957f9d (0/0 replicas created)

状况栏显示上线暂停,而且有新的ReplicaSet,但没有扩容。继续执行其他更新操作,当所有更新都完成后,只要恢复deployment的上线就可以应用所有这些更新:

kubectl rollout resume deployment nginx-deployment

此时再次查看deployment,发现新ReplicaSet扩容到期望值,旧ReplicaSet缩容到0。

注意,处于暂停状态的Deployment不能回滚,除非先恢复其执行状态。

Deployment状态

Deployment的生命周期中会有许多状态,如Processing、Complete或Failed。

当deployment执行以下任务时,其状态是Processing的:

  • 创建新的ReplicaSet
  • 为新的ReplicaSet扩容
  • 为旧的ReplicaSet缩容
  • 新的pods已经就绪或者可用(持续运行minReadySeconds 时间)

.sepc.minReadySeconds表示新创建的pod被视为可用时需要持续就绪的时间,默认为0。

此时,deployment的控制器会向其conditions中添加如下信息:

Progressing    True    NewReplicaSetCreated |FoundNewReplicaSet | ReplicaSetUpdated

当deployment的ReplicaSet副本均已更新到指定的最新版本时,其状态是Complete的。

此时起conditions为:

Processing    True    NewReplicaSetAvailable

如果Deployment在尝试部署其最新的ReplicaSet时受挫,且超过指定的超时时间,则其状态就是Failed的。部署超时时间是通过参数spec.procgressDeadlineSeconds指定的,单位是秒,默认为600s。

执行以下命令,将超时时间更改为60s:

kubectl patch deployment nginx-deployment -p '{"spec": {"progressDeadlineSeconds":60}}'

然后更新deployment的镜像,选择一个不存在的镜像,如1.22.9:

kubectl set image deployment nginx-deployment nginx=nginx:1.22.9

等待60s后,describe deployment的到如下信息:

Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    False   ProgressDeadlineExceeded
OldReplicaSets:  nginx-deployment-66b957f9d (3/3 replicas created)
NewReplicaSet:   nginx-deployment-6cd7c48f65 (1/1 replicas created)

除了报告Reason=ProgressDeadlineExceeded外,kubernetes对已停止的deployment不执行任何操作。可以手动将deployment回滚到以前的版本。