# Workload
- 用 kubectl 命令手动管理 Pod 比较麻烦,因此一般通过控制器(Controller)自动管理 Pod ,统称为工作负载(workload)。
- Workload 分为 Pod 、Deployment 、Job 等多种类型。
- 用户需要编写 workload 配置文件,描述如何部署一个 Pod 。然后创建该 workload 对象,k8s 就会自动创建并部署 Pod 。
# Deployment
:用于部署无状态的 Pod 。
# 配置
例:
apiVersion: v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1" # k8s 自动添加该字段,表示当前配置文件是第几次修改的版本,从 1 开始递增
name: nginx
namespace: default
# generation: 1 # k8s 自动添加该字段,表示配置文件的版本序号,从 1 开始递增
spec:
# minReadySeconds: 0 # 一个 Pod 需要保持 Ready 状态多久,才视作可用
# paused: false # 是否暂停编排,默认为 false 。如果为 true ,则修改 spec.template 时不会触发更新部署
# progressDeadlineSeconds: 600 # 如果 Deployment 停留在 Progressing 状态超过一定时长,则变为 Failed 状态,但会继续尝试部署。执行 rollout pause 时不会累计该时长
replicas: 2 # 期望运行的 Pod 副本数,默认为 1
revisionHistoryLimit: 2 # 保留最近多少个历史版本的 ReplicaSet(不包括当前版本),可用于回滚(rollback)。默认为 10 ,设置为 0 则不保留
selector: # 选择 Pod
matchLabels:
k8s-app: nginx
# strategy: # 更新部署的策略,默认为 RollingUpdate
# type: RollingUpdate
template: # 定义 Pod 模板
metadata: # Pod 的元数据
labels:
k8s-app: nginx
spec: # Pod 的规格
containers: # 定义该 Pod 中的容器
- name: nginx # 该 Pod 中的第一个容器名
image: nginx:1.20
Deployment 会自动创建 ReplicaSet 对象,用于运行指定数量的 Pod 副本。
删除一个 Deployment 时,会级联删除其下级对象 ReplicaSet ,和 ReplicaSet 的下级对象 Pod 。
Pod name 的格式为
<Deployment_name>-<ReplicaSet_id>-<Pod_id>
,例如:nginx-65d9c7f6fc-szgbk
- 因此不能事先知道 Deployment 创建的 Pod name ,通常通过 spec.selector 来筛选 Pod 。
每个 Pod 中,容器的命名格式为
k8s_<container_name>_<pod_name>_<k8s_namespace>_<pod_uid>_<restart_id>
,例如:k8s_POD_nginx-65d9c7f6fc-szgbk_default_c7e3e169-08c9-428f-9a62-0fb5d14336f8_0 # Pod 中内置的 pause 容器,其容器名为 POD k8s_nginx_nginx-65d9c7f6fc-szgbk_default_c7e3e169-08c9-428f-9a62-0fb5d14336f8_0
- Pod 重启时,会创建一组新的容器,容器名末尾的 restart_id 从 0 开始递增。
spec.selector 是必填字段,称为标签选择器,用于与 spec.template.metadata.labels 进行匹配,从而筛选 Pod 进行管理,筛选结果可能有任意个(包括 0 个)。
- 当 selector 中没有设置筛选条件时,默认选中所有对象。
- 当 selector 中设置了多个筛选条件时,会选中同时满足所有条件的对象。
- 例:
selector: matchLabels: k8s-app: nginx # 要求 labels 中存在该键值对 matchExpressions: - key: k1 # 要求存在 app 标签,且取值包含于指定列表 operator: In values: - v1 - key: k2 operator: Exists # 运算符可以为 In 、NotIn 、Exists 、DidNotExist
spec.template 是必填字段,表示 Pod 配置文件的模板。
- 当用户修改了 spec.template 之后,Deployment 会自动创建一个新版本的 ReplicaSet ,并将旧版本的 ReplicaSet 的 replicas 减至 0 。该过程称为更新部署。
- ReplicaSet 会根据 template 创建 Pod ,并添加
metadata.ownerReferences
和metadata.labels.pod-template-hash
,用于区分不同版本的 Pod 。 - Deployment 会保留最近 revisionHistoryLimit 个历史版本的 ReplicaSet ,可用于回滚。
- ReplicaSet 会根据 template 创建 Pod ,并添加
- 修改 Deployment 的其它配置,比如 replicas ,不算版本变化,不会触发更新部署。
- 创建 Deployment 之后不允许修改 spec.selector 。
- 当用户修改了 spec.template 之后,Deployment 会自动创建一个新版本的 ReplicaSet ,并将旧版本的 ReplicaSet 的 replicas 减至 0 。该过程称为更新部署。
Deployment 的更新部署策略(strategy)有两种:
- Recreate
- :直接重建。先删除旧 ReplicaSet 的 Pod ,再创建新 ReplicaSet 的 Pod 。
- RollingUpdate
- :滚动更新。先创建新 Pod ,等它们可用了,再删除旧 Pod 。
- 这可以保证在更新过程中不中断服务。但新旧 Pod 短期内会同时运行,可能引发冲突,比如同时访问挂载的数据卷。
- 例:滚动更新时,会尽量保持
strategy: type: RollingUpdate rollingUpdate: # 滚动更新的配置 maxUnavailable: 0 # 允许同时不可用的 Pod 数量。可以为整数或百分数,默认为 25% maxSurge: 1 # 为了同时运行新、旧 Pod ,允许 Pod 总数超过 replicas 一定数量。可以为整数或百分数,默认为 25%
replicas - maxUnavailable ≤ availableReplicas ≤ replicas + maxSurge
。
- Recreate
# 状态
Deployment 存在多种 conditions :
Progressing # 处理中,例如正在部署或删除 Pod Complete # 处理完成,例如部署完所有 Pod 且可用,或者该 Deployment 是停止运行的旧版本 Available # 可用,即 availableReplicas 不低于 maxUnavailable ReplicaFailure # 处理失败,例如不能部署 Pod 、停留在 Progressing 状态超时
- 例如 Deployment 处于 Available 状态时,可能同时处于 Progressing 或 Complete 状态。
Deployment 的状态示例:
status: availableReplicas: 1 # 可用的 Pod 数,允许接收 service 的流量 observedGeneration: 3 # 可用的 Pod 采用的 Deployment 版本。如果小于 metadata.generation 则说明不是最新版本 readyReplicas: 1 # 就绪状态的 Pod 数 replicas: 1 # 期望运行的 Pod 数 updatedReplicas: 1 # 采用最新版本 Deployment 的 Pod 数。如果等于 replicas ,则说明已全部更新 conditions: - type: Progressing status: "True" reason: NewReplicaSetAvailable # 处于当前状态的原因 message: ReplicaSet "nginx-bbf945bc9" has successfully progressed. # reason 的详细信息 lastTransitionTime: "2022-02-10T15:53:46Z" # 最近一次改变该 condition 的 status 的时刻 lastUpdateTime: "2022-02-10T15:53:55Z" # 最近一次更新该 condition 的时刻 - type: Available status: "True" reason: MinimumReplicasAvailable message: Deployment has minimum availability. lastTransitionTime: "2022-02-10T15:53:46Z" lastUpdateTime: "2022-02-10T15:53:46Z"
- 一般的 Pod 通过 readinessProbe 健康检查之后,就会进入 Ready 状态,加入 EndPoints 。但 Deployment 要求一个 Pod 保持 Ready 状态至少 minReadySeconds 秒,才视作可用(available),加入 EndPoints 。
Deployment 的常见问题:
- 没有一个可用的 Pod 。
- Pod 未全部可用。
- Pod 未全部更新到最新版本。
# ReplicaSet
:副本集(RC),用于运行指定数量的 Pod 副本。
- ReplicaSet 取代了 k8s 早期的副本控制器(Replication Controller ,RS)。
- 使用 Deployment 时,会自动创建、管理 ReplicaSet ,不必用户直接操作 ReplicaSet 。
- 假设用户指定运行 replicas 个 Pod ,ReplicaSet 会自动控制 available Pod 数量,使其等于 replicas 。
- 如果可用的 Pod 数多于 replicas ,则停止多余的 Pod 。
- 如果可用的 Pod 数少于 replicas ,则创建空缺数量的 Pod 。包括以下情况:
- 已有的 Pod 故障,比如部署失败、部署之后未通过健康检查。
- 已有的 Pod 需要的部署资源不足,比如 Pod 所在 Node 故障。
- 通过修改 replicas ,可以方便地调整某个应用的 Pod 副本数量,实现横向扩容、缩容,称为缩放(scale)。
- 减少 replicas 时,优先删除以下 Pod :
- unschedulable、pending 状态的 Pod 。
- 给 Pod 添加注释
controller.kubernetes.io/pod-deletion-cost: ±<int>
,表示删除 Pod 的成本,默认为 0 。优先删除成本较低的 Pod 。 - 调度的 Pod 副本数较多的节点上的 Pod 。
- 创建时间较晚的 Pod 。
- 减少 replicas 时,优先删除以下 Pod :
# StatefulSet
:与 Deployment 类似,但用于部署有状态的 Pod 。
- 相关概念:
- 无状态应用
- :历史执行的任务不会影响新执行的任务。因此 Pod 可以随时销毁并从镜像重新创建。
- 有状态应用
- :历史执行的任务会影响新执行的任务,因此需要存储一些历史数据。
- 将数据存储在容器中时,会随着容器一起销毁。因此建议将数据存储到挂载卷,或容器外的数据库。
- 无状态应用
- 与 Deployment 相比,StatefulSet 的特点:
有序性
- :StatefulSet 会按顺序创建 replicas 个 Pod (不会同时创建),给每个 Pod 分配一个从 0 开始递增的序号。
- 不过删除 StatefulSet 不会有序地删除 Pod ,可以先手动删除。
唯一性
- :每个 Pod 副本是独特的,不可相互替换。
- Pod name 的格式为
<StatefulSet_name>-<Pod_id>
。
持久性
- 即使重启、重新创建 Pod ,同一序号的 Pod 使用的 Pod Name、Pod IP 不变
- 删除 Pod 时,不会自动删除其挂载的 volume
- 例:
apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: clusterIP: None ports: - port: 80 protocol: TCP targetPort: 80 selector: k8s-app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: nginx namespace: default spec: # podManagementPolicy: OrderedReady replicas: 2 # revisionHistoryLimit: 10 selector: matchLabels: k8s-app: nginx serviceName: nginx template: metadata: labels: k8s-app: nginx spec: containers: - name: nginx image: nginx:1.20 # updateStrategy: # 更新部署的策略,注意与 strategy 的语法不同 # rollingUpdate: # partition: 0 # type: RollingUpdate
- 上例创建了一个 Headless Service ,并配置到 StatefulSet 的 serviceName 。此时会自动给每个 Pod 创建子域名,格式为
<Pod_name>.<Service_domain>
,例:nginx-0.nginx.nlp-web.svc.cluster.local nginx-1.nginx.nlp-web.svc.cluster.local
- 上例创建了一个 Headless Service ,并配置到 StatefulSet 的 serviceName 。此时会自动给每个 Pod 创建子域名,格式为
# DaemonSet
:与 Deployment 类似,但是在每个 node 上只部署一个 Pod 副本。适合监控、日志等 daemon 服务。
- DaemonSet 默认会调度到每个节点,可通过 nodeSelector 等方式指定可调度节点。
- 例:
apiVersion: apps/v1 kind: DaemonSet metadata: name: nginx namespace: default spec: selector: matchLabels: k8s-app: nginx # strategy: # type: RollingUpdate template: metadata: labels: k8s-app: nginx spec: containers: - name: nginx image: nginx:1.20
- DaemonSet 的更新部署策略(strategy)有两种:
- OnDelete :等用户删除当前版本的 Pod ,才自动创建新版本的 Pod 。
- RollingUpdate :默认采用。
# Job
:一次性任务,适合部署运行一段时间就会自行终止、不需要重启的 Pod 。
# CronJob
:定时任务,用于定时创建并执行某个 Job 。
- 由 kube-controller-manager 定时调度,而后者默认采用 UTC 时区。