9 工作负载
k8s提供了几个内置的API来声明式管理工作负载及其组件,最终你的应用以容器的形式在Pods中运行,工作负载帮你接管Pod的管理,你可以使用k8s API管理工作负载对象,这些对象所表达的是比Pod更高级别的抽象概念,k8s控制平面根据你定义的工作负载对象规约自动管理Pod对象。
用于管理工作负载的内置API包括:
- Deployment(间接包括ReplicaSet),管理无状态应用工作负载,任何pod都是可互换的,在需要时进行替换。Deployment替代Replication Controller API。
- StatefulSet,允许创建具有不同身份标识的Pod,Pod不是可换的,管理有状态应用工作负载,最常见的是能简历Pod和持久化存储之间的关联,也就是说假如pod挂了仍然会连接到原来的存储卷上。
- DaemonSet,在特定节点或每个节点运行的负载,例如某种驱动、日志组件、node-exporter等。
- Job、CronJob,定义一次性任务和定时任务。
9.1 Deployments
kubectl rollout status -n=cloudbases-system deployment cb-dashboard
可以查看滚动升级时的状态
Deployment为Pod和ReplicaSet提供声明式的更新能力,你负责描述deployemnt的目标状态,deployment控制器更改实际状态,使其变为期望状态。你可以定义deployment以创建新的ReplicaSet。
Deployment 典型用例
- 创建deployment将ReplicaSet上线。ReplicaSet后台创建Pod
- 更新Deployment的PodTemplateSpec。新的ReplicaSet将被创建,将Pod从旧的ReplicaSet逐步迁移到新ReplicaSet。每次偶会更新Deployment的修订版本
- 如果Deployment当前状态不稳定。可以回滚到较早的Deployment,但是每次回滚也会更新Deployment的修订版本
- 扩大Deployment副本数以创建更多负载
- 暂停Deployment的上线,修改后恢复
- 使用Deployment状态判断上线状态
- 清理旧的不用的ReplicaSet
9.1.1 创建deployment将ReplicaSet上线。ReplicaSet后台创建Pod
#deployment-nginx示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment #deploy的名称
labels:
app: nginx
spec:
replicas: 3 #启动三个Pod副本
selector: #selector字段定义了replicaSet如何查找要管理的pod
matchLabels:
#matchLabels字段是kv键值对映射的,
#等效于matchExpressions中的一个元素key为key、operator为In、values为value。
#需要注意需要满足matchLabels或matchExpressions的所有条件才算匹配成功
app: nginx
template:
metadata:
labels:
app: nginx #打了个app: nginx标签,这里也是为了和selector中的matchLabels对应
spec:
containers:
- name: nginx #容器名称
image: nginx:1.14.2 #镜像名称:tag版本
ports:
- containerPort: 80
- 查看deploy状态 kubectl get deploy nginx-deployment -o wide
- 分析deploy状态 kubectl describe deploy nginx-deployment
- 查看deploy创建出的pod kubectl get pods。 pod的标签里也有pod-template-hash标签,与replicaset中的标签值一样
- 查看deploy创建出的rs kubectl get rs。需要注意rs的名称为 [deploymen名称]-[哈希] 哈希值和rs的pod-template-hash标签一致,并且此标签是由deployment的PodTemplate通过哈希处理而来
9.1.2 更新Deployment的PodTemplateSpec。新的ReplicaSet将被创建,将Pod从旧的ReplicaSet逐步迁移到新ReplicaSet。每次偶会更新Deployment的修订版本
例如修改 deploy的镜像kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
kubectl get rs 会看到多出来一个副本。
在更新时deployment确保一定数量的pod处于运行状态,默认75%,也就是最大不可用比例为25%
deployment还确保更新时数量只比期望pod高一部分,默认125%,也就是最大多出副本量的25%
当deployment被修改时,会创建一个新的replicaSet,旧的replicaSet将被缩容,新的replicaSet将被扩容
9.1.3 如果Deployment当前状态不稳定。可以回滚到较早的Deployment,但是每次回滚也会更新Deployment的修订版本
默认情况下,deploy所有上线记录都被保留在系统中,以便随时回滚,可以修改修订历史记录限制。
deployment被触发上线时,并且spec.template被修改时系统创建deployment新的修订版本,而仅修改副本数这样的操作不会重建rs
回滚步骤:
- 检查历史版本
kubectl rollout history deployment/nginx-deployment
包含有 REVISION CHANGE-CAUSE,例如2 kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
其中CHANGE-CAUSE是从deployment 的kubernetes.io/change-cause
注解复制过来的 - 查看某个历史版本详细信息
kubectl rollout history deployment/nginx-deployment --revision=2
- 执行回滚到上一个版本
kubectl rollout undo deployment/nginx-deployment
或者执行--to-version
可以回滚到特定修订版本kubectl rollout undo deployment/nginx-deployment --to-revision=2
9.1.4 扩大Deployment副本数以创建更多负载
- 缩放deployment,
kubectl scale deployment/nginx-deployment --replicas=8
将副本数设为8 - 水平自动缩放,如果集群启用了Pod的水平自动缩放,可以设置自动缩放器HPA,
kubectl autoscale deployment/nginx-deployment --min=5 --max=15 --cpu-percent=80
在cpu超过80%时进行缩放,最小5最大15
9.1.5 暂停Deployment的上线,修改后恢复
当你更新deploy时,想做很多操作,而不想每次操作都会导致重启,就可以先暂停deploy新版本的上线,等全部修改完成后再恢复上线,在暂停期间,对deploy做修改都不会导致pod重启或修改
- 暂停
kubectl rollout pause deployment/nginx-deployment
- 一系列修改deploy…
- 恢复
kubectl rollout resume deployment/nginx-deployment
9.1.6 使用Deployment状态判断上线状态
deploy创建的rs可能处于Progressing(进行中)、Complete(已完成)、Failed(失败),以至于无法继续进行
- Progressing进行中的Deployment:deploy正在创建新的rs、deploy;deploy为新的rs扩容;deploy为旧的rs缩容;pod就绪或可用
- Complete完成的Deployment:deploy所有副本已更新;所有副本都可用;旧副本未运行
- Failed失败的Deployment:一直处于未完成的状态。出现的原因可能有:配额不足、就绪探针失败、镜像拉取错误、权限不足、限制范围、应用运行时错误
可以配置超时时间,在这期间来检查错误原因kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'
一般使用describe
、log
或get -o yaml 中的status
排错
9.1.7 清理旧的不用的ReplicaSet
deployment的.spec.revisionHistoryLimit
字段指定保留多少个replicaSet,多余版本的ReplicaSet会被垃圾回收
。默认值为10。如果将此值设为0,代表没有历史记录,也就是不能使用回滚的功能
9.1.8 金丝雀部署
创建两个deploy 按一定比例设置副本数,标签有公共的有能区分的,然后让service绑定到pod的公共标签,就可以实现按比例分配流量
9.1.9 编写deployment规约
首先需要.apiVersion .kind .metadata还需要.spec,.spec中必须填写.spec.template和.spec.selector
- .spec.template是一个Pod模板,
- .spce.selector中指定deploy的Pod标签选择算符。匹配的是.spec.template.metadata.labels。在apps/v1版本selector是不可变的
- .spec.replicas 可选,可指定副本数,默认1
- .spec.strategy 新pod替换旧pod的策略。type可以是Recreate(先杀pod,再建pod)、RollingUpdate(默认,滚动更新) .spec.strategy.rollingUpdate.maxUnavailable 不可用pod上限(比例或个数) .spec.strategy.rollingUpdate.maxSurge 可超出的数量或百分比。
- .spec.progresDeadlineSeconds 进度期限秒数,默认600毫秒,代表控制器再600毫秒内持续重试
- .spec.minReadySeconds 可选字段,用于指定新创建的pod再没任何容器崩溃情况下的最小就绪时间,也就是说超过这个时间Pod才被视为可用 默认0(也就是就绪后Pod就可用)
- .spec.revisionHistoryLimit 可选字段,默认10,设定保留旧版本ReplicaSet的数量
- .spec.paused 用于暂停和恢复deployment
需要注意,如果多个控制器选择算符发生重叠,会导致控制器冲突,无法正常工作,所以不建议手动创建ReplicaSet或Pod。
9.2 ReplicaSet
9.2.1 replicaSet定义
维护一组Pod副本稳定集合,用来保证pod数量、pod相同和可用性。包括选择算符、副本数、pod模板。 pod的metadata.ownerReferences连接了replicaSet
.spec.template。是一个pod模板,当需要删除Pod时,按如下顺序剔除。首先剔除悬决(Pending)、其次按照删除开销注解(controller.kubernetes.io/pod-deletion-cost)先删数值小的,优先删除所处节点上副本个数较多的Pod、创建时间较近的
.spec.selector。标签选择算符.spec.template.metadata.labels需要和此匹配,否则配置将被API拒绝。当然直接修改pod的标签可以让pod摆脱replicaset的控制
.spec.replicas。pod副本数,
删除replicaset,垃圾收集器自动删除所有依赖的pod。如果只想删除replicaset而不删除pod那么可以使用 kubectl delete --cascade=orphan
9.2.2 pod删除开销
pod删除开销controller.kubernetes.io/pod-deletion-cost注解。数值小的先被删除,不设置默认为0,
可以通过为kube-apiserver和kube-controller-manager设置特性门控PodDeletionCost禁用此功能
9.2.3 replicaset作为hpa目标
如下例
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: frontend-scaler
spec:
scaleTargetRef:
kind: ReplicaSet
name: frontend
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 50
9.2.4 ReplicaSet 的替代方案
使用Deployment可以自动管理replicaSet。
裸Pod
Job
DaemonSet
ReplicationController
9.3 StatefulSet
statefulset用来管理有状态应用的工作负载API对象,用来管理Pod集合的部署和扩缩,并为这些Pod提供持久存储和持久标识符
与deployment相似,statefulset管理基于相同容器规约的一组Pod,特殊的statefulset给每个Pod都提供了一个Id,比如-0 -1 -2。
在希望使用存储卷提供持久存储时,可以用statefulset,当pod故障重启时,仍然会挂载到原来的存储卷上。
优点
- 稳定、唯一的网络标识符。例如test-nginx-0、test-nginx-1
- 稳定、持久的存储。
- 有序、优雅的部署和扩缩
- 有序、自动的滚动更新
限制
- 存储必须由PersistentVolume Provisioner基于storageclass制备,或者管理员预先制备好
- 扩缩statefulSet不会删除与他关联的存储卷。保证了数据安全
- statefulSet需要无头服务来负责Pod的网络标识。所以你需要创建此服务
- 当删除StatefulSet时,不提供任何终止Pod的保证,也就是说没有体面终止的功能。
- 在默认Pod管理策略(OrderedReady)时使用滚动更新,需要人工干预
配置
- .spec.selctor pod选择算符,匹配.spec.template.metadata.labels
- .spec.volumeClaimTemplates,卷申领模板,使用pv制备程序提供的pv提供稳定的存储
- .spec.minReadySeconds,最短就绪秒数,指定pod最短经历多长时间后才被视为可用,默认为0
- pod标识
- pod的名称固定,包括顺序标识、网络标识、存储。例如test-nginx-0、test-nginx-1。
- .spec.ordinals.start可以设置起始序号
- 稳定的网络ID, $(StatefulSet 名称)-$(序号)、$(服务名称).$(名字空间).svc.cluster.local
- 例如 在myns下有mynginx 的 statefulset,并且有同名的服务mynginx 。里边有mynginx-0、mynginx-1
- statefulset的域名 mynginx.myns.svc.cluster.local
- pod的域名 mynginx-0.myns.svc.cluster.local
- 稳定的存储,定义了VolumeClaimTemplate,每个Pod都会基于sc制备一个pv,当Pod被调度或重新调度时,自动挂载pvc相关的pv,需要注意statefulset删除时,pv不会删除。需要手动删除
- pod名称标签,创建pod时会给添加上一个标签statefulset.kubernetes.io/pod-name,用这个标签可以给特定Pod绑一个svc
- .spec.replicas 副本数,当部署和扩缩时是依次创建顺序为0,1,2…N-1,扩缩时前面的Pod都要是Running或Ready。删除时是逆序终止,Pod终止之前继任者必须完全关闭
- .spec.updateStrategy.type,配置和禁用掉自动滚动更新Pod的容器、标签、资源请求、限制、注解。取值为
- OnDelete:控制器不会更新pod,用户需要手动删除Pod来让控制器重建pod
- RollingUpdate:默认,执行自动滚动更新,自动删除或重建Pod。
- .spec.updateStrategy.rollingUpdate.partition。可以实现分区,就是当更新statefulset时,只有大于该分区序号的pod会被更新
- .spec.updateStrategy.rollingUpdate.maxUnavailable。配置最大不可用Pod的数量,可以为数量或百分比
- .spec.persistentVolumeClaimRetentionPolicy,配置pvc保留,也就是删除pod时是否删除pvc。需要启用StatefulSetAutoDeletePVC特性门控。
- whenDeleted。删除statefulset时保留卷,需要注意只有删除statefulset资源时才有效。就是如果单纯的删Pod的操作并不会删除卷。
- whenScaled。当配置statefulset副本缩容时卷是否保留,Retain(默认)保留、Delete删除,需要注意这个配置只有对缩容操作有用。
#statefulset示例
apiVersion: v1
kind: Service
metadata:
name: nginx # headless service来控制网络域名
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,运行三个副本
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: nginx # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates: #存储卷声明模板,提供稳定存储
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
9.4 DaemonSet
确保每个(或者某些)节点上运行一个Pod,当然,当一个节点加入集群时,也会新增一个Pod,删除节点时也会收回一个Pod
典型用法:运行集群守护进程、运行日志收集守护进程、运行监控守护进程
- 必需字段 apiVersion、kind、metadata、spec
- .spec.template。pod模板。需要指定合理的标签,RestartPolicy必须为Always(默认)
- .spec.selector表示pod选择算符,匹配.spec.template.metadata.labels匹配,并且是不可修改的。matchLabels、matchExpression,如果两种方式都定义了按"与"处理
- .spec.template.spec.nodeSelector。选择节点,匹配的节点上创建 Pod
- .spec.template.spec.affinity。如果指定了亲和性,则将在亲和性匹配的节点上创建pod。默认没指定将在所有节点创建
- .spec.template.spec.schedulerName,可以为pod设置不同的调度程序
关于DaemonSet自动添加的污点和容忍度
DaemonSet控制器将自动添加一组容忍度到DaemonSet的Pod,当然你也可以自己加容忍度到DaemonSet的Pod。
- node.kubernetes.io/not-ready。NoExecute。DemonSet的pod可以被调度不健康或还不准备接受pod的节点上且不被驱逐
- node.kubernetes.io/unreachable。NoExecute。DaemonSet的Pod可以被调度到节点控制器不可达的节点上且不被驱逐
- node.kubernetes.io/disk-pressure。NoSchedule。DaemonSet Pod 可以被调度到具有磁盘压力问题的节点上。
- node.kubernetes.io/memory-pressure。NoSchedule。DaemonSet Pod 可以被调度到具有内存压力问题的节点上。
- node.kubernetes.io/pid-pressure。NoSchedule。DaemonSet Pod 可以被调度到具有进程压力问题的节点上。
- node.kubernetes.io/unschedulable。NoSchedule。DaemonSet Pod 可以被调度到不可调度的节点上。例如需要一个集群联网的网络插件Pod运行,只有网络插件运行了节点才能被标记为就绪,所以这时候使用daemonset能把网络插件放在节点上。
- node.kubernetes.io/network-unavailable。NoSchedule。仅针对请求主机联网的 DaemonSet Pod 添加此容忍度,即 Pod 具有 spec.hostNetwork: true。这些 DaemonSet Pod 可以被调度到网络不可用的节点上。
和daemonSet的pod通信的几种模式
- 推送(push):配置DaemonSet的pod,将信息发送到另一个服务,这种方式不需要客户端。例如统计数据库
- NodeIP和已知端口:DaemonSet的Por可以使用hostPort。可以通过节点IP访问到Pod。
- DNS:创建无头服务,使用endpoints资源或从DNS发现DaemonsSet
- Service:创建DaemonSet Pod的服务,让服务随机访问到某个节点的pod
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchFields:
- key: metadata.name
operator: In
values:
- target-host-name
tolerations:
# 这些容忍度设置是为了让该守护进程集在控制平面节点上运行
# 如果你不希望自己的控制平面节点运行 Pod,可以删除它们
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
9.5 Job
会创建一个或多个Pod,并将执行Pod,直到指定数量的Pod成功终止,删除Job的操作会删除所有创建的Pod,挂起Pod会删除Job所有活跃的Pod
- 基本信息,需要apiVersion、kind、metadata、spec
- Job标签。为job-name和controller-uid加上batch.kubernetes.io/ 前缀
- .spec.template。需要设置合适的标签、重启策略(Never、Onfailure)
- .spec.selector。选择算符
Job并行执行
Job运行的任务主要有三种
- 非并行job,通常只启动一个pod,pod成功终止时,job为完成状态。
- 具有确定完成数的并行job。.spec.completions。当成功的pod数到达设定时才被视为完成。
- 带工作队列的并行job。不设置spec.completions。需要设置.spec.parallelism为非负整数。至少一个pod完成,那么job就完成
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: perl:5.34.0
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never
backoffLimit: 4
已完成 Job 的自动清理
当job结束时,TTL-after-finished控制器提供了一种TTL机制来限制已完成执行job对象的生命期。
通过.spec.ttlSecondsAfterFinished来清理已经结束的job(Complete或Failed),当TTL过期时,Job被级联删除
9.6 CronJob
定时job,可用于备份、生成报告等操作,类似于linux 的crontab。用cron格式编写,周期性创建job
- .spec.schedule字段是必须的。*(分钟) *(小时) *(某天) *(月份) *(周的某天) 。 */2 代表每隔2个单位执行一次
- .spec.jobTemplate 创建job模板
- .spec.startingDeadlineSeconds 由于某种原因错过时间后,容忍多长时间内创建job。
- .spec.concurrencyPolicy 声明cronjob创建的任务执行时发生重叠如何处理。Allow(默认),允许并发执行、Forbid不允许并发任务,优先执行老任务、Replace不允许并发,优先执行新任务
- .spec.suspend 挂起,挂起cronjob,但是不会影响已开始的任务
- .spec.successfulJobsHistoryLimit(默认3)和 .spec.failedJobsHistoryLimit(默认1) 。代表应保留多少已完成和失败的任务
- .spec.timeZone 可以设置一个有效时区名称,例如"Etc/UTC"。
apiVersion: batch/v1
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 from the Kubernetes cluster
restartPolicy: OnFailure
9.7 ReplicationControlle
已淘汰