Pod(容器组)是 Kubernetes 中最小的调度单元,能够经过 yaml 定义文件直接建立一个 Pod。但 Pod 自己并不具有自我恢复(self-healing)功能。若是一个 Pod 所在的节点出现故障,或者调度程序自身出现问题,以及节点资源不够或节点进入维护而驱逐 Pod 时,Pod 将被删除,且不能自我恢复。node
所以,Kubernetes 中咱们通常不直接建立 Pod, 而是经过 Controller(控制器)来管理 Pod。linux
Controller 能为 Pod 提供以下特性:nginx
Kubernetes 中支持的控制器包括:shell
Kubernetes 中,虽然通常使用 Deployment 来管理 Pod, 但 Deployment 中也是经过 ReplicaSet 来维护 Pod 的副本集合的,所以此处也对 ReplicaSet 进行简单介绍。api
在 ReplicaSet 的定义中,包含三部分:app
matchLabels
来与 Pod 的 label 匹配。ReplicaSet 的示例定义文档以下所示,负载均衡
apiVersion: apps/v1 # api版本 kind: ReplicaSet # 资源类型 metadata: # 元数据定义 name: nginx-ds # ReplicaSet 名称 spec: replicas: 2 # Pod 副本数量,默认1 selector: # 标签选择器 matchLabels: app: nginx template: # Pod 定义模板 metadata: # Pod 元数据定义 labels: app: nginx # Pod 标签 spec: containers: # 容器定义 - name: nginx image: nginx
ReplicaSet 经过建立、删除 Pod 容器组来确保符合 selector 选择器的 Pod 数量等于 replicas 指定的数量。 ReplicaSet 建立的 Pod 中,都有一个字段 metadata.ownerReferences
用于标识该 Pod 从属于哪个 ReplicaSet。可经过 kubectl get pod pod-name -o yaml
来查看 Pod 的 ownerReference。运维
ReplicaSet 经过 selector 字段的定义,识别哪些 Pod 应该由其管理, 不论该 Pod 是否由该 ReplicaSet 建立,即只要 selector 匹配, 经过外部定义建立的 Pod 也会被该 ReplicaSet 管理。所以须要注意 .spec.selector.matchLabels
与 .spec.template.metadata.labels
的定义一致, 且避免与其余控制器的 selector 重合,形成混乱。ide
ReplicaSet 不支持滚动更新,因此对于无状态应用,通常使用 Deployment来部署, 而不直接使用 ReplicaSet。ReplicaSet 主要是被用做 Deployment 中负责 Pod 建立、删除、更新的一种手段。ui
Deployment 对象包含 ReplicaSet 做为从属对象,而且可经过声明式、滚动更新的方式来更新 ReplicaSet 及其 Pod。ReplicaSet 如今主要是被用做 Deployment 中负责 Pod 建立、删除、更新的一种手段。使用 Deployment 时,无需关心由 Deployment 建立的 ReplicaSet,Deployment 将处理全部与之相关的细节。同时,Deployment 还能以“声明式”的方式管理 Pod 和 ReplicaSet (其本质是将一些特定场景的一系列运维步骤固化下来,以便快速准确无误的执行),并提供版本(revision)回退功能。
Deployment 定义示例,
apiVersion: apps/v1 kind: Deployment # 对象类型,固定为 Deployment metadata: name: nginx-deploy # Deployment 名称 namespace: default # 命名空间,默认为 default labels: app: nginx # 标签 spec: replicas: 4 # Pod 副本数,默认1 strategy: rollingUpdate: # 升级策略为滚动升级,因为replicas为4,则整个升级过程pod个数在3-5个之间 maxSurge: 1 # 滚动升级时超过 replicas 的最大 pod 数,也能够为百分比(replicas的百分比),默认为1 maxUnavailable: 1 # 滚动升级时不可用的最大 pod 数,也可为百分比(replicas的百分比),默认为1 selector: # 标签选择器,经过标签选择该 Deployment 管理的 Pod matchLabels: app: nginx template: # Pod 定义模板 metadata: labels: app: nginx # Pod 标签 spec: # 定义容器模板,能够包含多个容器 containers: - name: nginx image: nginx:latest ports: - containerPort: 80
可经过 kubectl explain xxx
来查看支持哪些配置选项,
# 查看 deployment 配置项 [root@kmaster ~]# kubectl explain deployment ... # 查看 deployment.spec 模块的配置项 [root@kmaster ~]# kubectl explain deployment.spec KIND: Deployment VERSION: apps/v1 RESOURCE: spec <Object> DESCRIPTION: Specification of the desired behavior of the Deployment. DeploymentSpec is the specification of the desired behavior of the Deployment. FIELDS: minReadySeconds <integer> Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready) paused <boolean> Indicates that the deployment is paused. progressDeadlineSeconds <integer> The maximum time in seconds for a deployment to make progress before it is considered to be failed. The deployment controller will continue to process failed deployments and a condition with a ProgressDeadlineExceeded reason will be surfaced in the deployment status. Note that progress will not be estimated during the time a deployment is paused. Defaults to 600s. replicas <integer> Number of desired pods. This is a pointer to distinguish between explicit zero and not specified. Defaults to 1. revisionHistoryLimit <integer> The number of old ReplicaSets to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10. selector <Object> -required- Label selector for pods. Existing ReplicaSets whose pods are selected by this will be the ones affected by this deployment. It must match the pod template's labels. strategy <Object> The deployment strategy to use to replace existing pods with new ones. template <Object> -required-
其它配置项说明:
.spec.minReadySeconds
:用来控制应用升级的速度。升级过程当中,新建立的 Pod 一旦成功响应了就绪探测即被认为是可用状态,而后进行下一轮的替换。 .spec.minReadySeconds
定义了在新的 Pod 对象建立后至少须要等待多长的时间才能会被认为其就绪,在该段时间内,更新操做会被阻塞。.spec.progressDeadlineSeconds
:用来指定在系统报告 Deployment 失败 —— 表现为状态中的 type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded
前能够等待的 Deployment 进行的秒数。Deployment controller 会继续重试该 Deployment。若是设置该参数,该值必须大于 .spec.minReadySeconds
。.spec.revisionHistoryLimit
:用来指定能够保留的旧的 ReplicaSet 或 revision(版本) 的数量。默认全部旧的 Replicaset 都会被保留。若是删除了一个旧的 RepelicaSet,则 Deployment 将没法再回退到那个 revison。若是将该值设置为0,全部具备0个 Pod 副本的 ReplicaSet 都会被删除,这时候 Deployment 将没法回退,由于 revision history 都被清理掉了。[root@kmaster test]# kubectl apply -f nginx-deploy.yaml --record
--record
会将这次命令写入 Deployment 的 kubernetes.io/change-cause 注解中。可在后面查看某一个 Deployment 版本变化的缘由。
建立 Deployment 后,Deployment 控制器将马上建立一个 ReplicaSet,并由 ReplicaSet 建立所须要的 Pod。
# 查看 Deployment [root@kmaster test]# kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE nginx-deploy 0/2 2 0 64s # 查看 ReplicaSet [root@kmaster test]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-59c9f8dff 2 2 1 2m16s # 查看 Pod,显示调度的节点,及标签 [root@kmaster test]# kubectl get pod -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS nginx-deploy-59c9f8dff-47bgd 1/1 Running 0 5m14s 10.244.1.91 knode2 <none> <none> app=nginx,pod-template-hash=59c9f8dff nginx-deploy-59c9f8dff-q4zb8 1/1 Running 0 5m14s 10.244.3.47 knode3 <none> <none> app=nginx,pod-template-hash=59c9f8dff
pod-template-hash
标签是 Deployment 建立 ReplicaSet 时添加到 ReplicaSet 上的,ReplicaSet 进而将此标签添加到 Pod 上。这个标签用于区分 Deployment 中哪一个 ReplicaSet 建立了哪些 Pod。该标签的值是 .spec.template
的 hash 值,不要去修改这个标签。由上可看出 ReplicaSet、 Pod 的命名分别遵循 <Deployment-name>-<Pod-template-hash>
、<Deployment-name>-<Pod-template-hash>-xxx
的格式。
当且仅当 Deployment 的 Pod template(.spec.template
)字段中的内容发生变动时(例如标签或容器的镜像被改变),Deployment 的发布更新(rollout)才会被触发。Deployment 中其余字段的变化(例如修改 .spec.replicas
字段)将不会触发 Deployment 的发布更新。
更新 Deployment 中 Pod 的定义(例如,发布新版本的容器镜像)。此时 Deployment 控制器将为该 Deployment 建立一个新的 ReplicaSet,而且逐步在新的 ReplicaSet 中建立 Pod,在旧的 ReplicaSet 中删除 Pod,以达到滚动更新的效果。
好比咱们将上面 Deployment 的容器镜像进行修改,
# 方式一:直接使用 kubectl 命令设置修改 [root@kmaster ~]# kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record deployment.apps/nginx-deploy image updated # 方式二:使用 kubectl edit 编辑yaml修改 [root@kmaster ~]# kubectl edit deploy nginx-deploy
查看发布更新(rollout)的状态
[root@kmaster ~]# kubectl rollout status deploy nginx-deploy Waiting for deployment "nginx-deploy" rollout to finish: 2 out of 4 new replicas have been updated...
查看 ReplicaSet,
[root@kmaster ~]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-59c9f8dff 1 1 1 3d6h nginx-deploy-d47dbbb7c 4 4 2 3m41s
咱们能够看到 Deployment 的更新是经过建立一个新的4个副本的 ReplicaSet,并同时将旧的 ReplicaSet 的副本数缩容到0个副原本达成的。
由于前面咱们将 maxSurge, 与 maxUnavailable 都设置为了1, 所以在更新的过程当中,任什么时候刻两个 ReplicaSet 的 Pod 数至多为5个(4 replicas +1 maxSurge),且可用的 Pod 数至少为3个(4 replicas - 1 maxUnavailable)。
使用 kubectl describe
命令查看 Deployment 的事件部分,以下所示
[root@kmaster ~]# kubectl describe deploy nginx-deploy ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 12m deployment-controller Scaled up replica set nginx-deploy-d47dbbb7c to 1 Normal ScalingReplicaSet 12m deployment-controller Scaled down replica set nginx-deploy-59c9f8dff to 3 Normal ScalingReplicaSet 12m deployment-controller Scaled up replica set nginx-deploy-d47dbbb7c to 2 Normal ScalingReplicaSet 10m deployment-controller Scaled down replica set nginx-deploy-59c9f8dff to 2 Normal ScalingReplicaSet 10m deployment-controller Scaled up replica set nginx-deploy-d47dbbb7c to 3 Normal ScalingReplicaSet 8m56s deployment-controller Scaled down replica set nginx-deploy-59c9f8dff to 1 Normal ScalingReplicaSet 8m56s deployment-controller Scaled up replica set nginx-deploy-d47dbbb7c to 4 Normal ScalingReplicaSet 5m55s deployment-controller Scaled down replica set nginx-deploy-59c9f8dff to 0
当更新了 Deployment 的 Pod Template 时,Deployment Controller 会建立一个新的 ReplicaSet (nginx-deploy-d47dbbb7c) ,并将其 scale up 到 1 个副本,同时将旧的 ReplicaSet(nginx-deploy-59c9f8dff) scale down 到3个副本。接下来 Deployment Controller 继续 scale up 新的 ReplicaSet 并 scale down 旧的 ReplicaSet,直到新的 ReplicaSet 拥有 replicas 个数的 Pod, 旧的 ReplicaSet Pod 数缩放到0。这个过程称为 rollout(发布更新)。
经过 .spec.strategy
字段,能够指定更新策略,除了上述使用的 RollingUpdate(滚动更新),另外一个可取的值为 Recreate(从新建立)。选择从新建立,Deployment 将先删除原有 ReplicaSet 中的全部 Pod,而后再建立新的 ReplicaSet 和新的 Pod,更新过程当中将出现一段应用程序不可用的状况。所以,线上环境通常使用 RollingUpdate。
默认状况下,kubernetes 将保存 Deployment 的全部更新(rollout)历史。能够经过设定 revision history limit(.spec.revisionHistoryLimit
配置项)来指定保存的历史版本数量。
当且仅当 Deployment 的 .spec.template
字段被修改时(例如修改容器的镜像),kubernetes 才为其建立一个 Deployment revision(版本)。Deployment 的其余更新(例如:修改 .spec.replicas
字段)将不会建立新的 Deployment revision(版本)。
查看 Deployment 的 revision,
[root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 1 kubectl apply --filename=nginx-deploy.yaml --record=true 2 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true
若是前面更新 Deployment 时没有添加 --record=true
,则此处 CHANGE-CAUSE 将为空。
咱们经过将镜像修改成一个不存在的版原本模拟一次失败的更新,并回滚到前一个版本的场景,
# 1. 修改镜像版本到一个不存在的值 [root@kmaster ~]# kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record deployment.apps/nginx-deploy image updated # 2. 查看 ReplicaSet [root@kmaster ~]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-58f69cfc57 2 2 0 2m7s nginx-deploy-59c9f8dff 0 0 0 3d7h nginx-deploy-d47dbbb7c 3 3 3 81m # 3. 查看 Pod 状态 [root@kmaster ~]# kubect get pod NAME READY STATUS RESTARTS AGE nginx-deploy-58f69cfc57-5968g 0/1 ContainerCreating 0 42s nginx-deploy-58f69cfc57-tk7c5 0/1 ErrImagePull 0 42s nginx-deploy-d47dbbb7c-2chgx 1/1 Running 0 77m nginx-deploy-d47dbbb7c-8fcb9 1/1 Running 0 80m nginx-deploy-d47dbbb7c-gnwjj 1/1 Running 0 78m # 4. 查看 Deployment 详情 [root@kmaster ~]# kubectl describe deploy nginx-deploy ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 3m57s deployment-controller Scaled up replica set nginx-deploy-58f69cfc57 to 1 Normal ScalingReplicaSet 3m57s deployment-controller Scaled down replica set nginx-deploy-d47dbbb7c to 3 Normal ScalingReplicaSet 3m57s deployment-controller Scaled up replica set nginx-deploy-58f69cfc57 to 2 # 5. 查看 Deployment 的历史版本 [root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 1 kubectl apply --filename=nginx-deploy.yaml --record=true 2 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true 3 kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true # 6. 查看某个版本的详情 [root@kmaster ~]# kubectl rollout history deploy nginx-deploy --revision=3 deployment.apps/nginx-deploy with revision #3 Pod Template: Labels: app=nginx pod-template-hash=58f69cfc57 Annotations: kubernetes.io/change-cause: kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true Containers: nginx: Image: nginx:1.161 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> # 7. 回滚到前一个版本 [root@kmaster ~]# kubectl rollout undo deploy nginx-deploy deployment.apps/nginx-deploy rolled back # 8. 回滚到指定的版本 [root@kmaster ~]# kubectl rollout undo deploy nginx-deploy --to-revision=1 deployment.apps/nginx-deploy rolled back # 9. 查看历史版本信息 [root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 3 kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true 4 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true 5 kubectl apply --filename=nginx-deploy.yaml --record=true
经过 kubectl rollout undo
命令可回滚到上一个版本或指定的版本,上述示例也可看出,回滚到历史版本,会将历史版本的序号设置为最新序号。如前所述,咱们能够经过设置 Deployment 的 .spec.revisionHistoryLimit
来指定保留多少个旧的 ReplicaSet(或 revision),超出该数字的将在后台进行垃圾回收。若是该字段被设为 0,Kubernetes 将清理掉该 Deployment 的全部历史版本(revision),此时,将没法对该 Deployment 执行回滚操做了。
能够经过 kubectl scale
命令或 kubectl edit
修改定义的方式来对 Deployment 进行伸缩,增长或减小 Pod 的副本数,
# 将 Pod 数缩放到2个 [root@kmaster ~]# kubectl scale deploy nginx-deploy --replicas=2 deployment.apps/nginx-deploy scaled # 查看 Pod [root@kmaster ~]# kubectl get pod NAME READY STATUS RESTARTS AGE nginx-deploy-59c9f8dff-7bpjp 1/1 Running 0 9m48s nginx-deploy-59c9f8dff-tpxzf 0/1 Terminating 0 8m57s nginx-deploy-59c9f8dff-v8fgz 0/1 Terminating 0 10m nginx-deploy-59c9f8dff-w8s9z 1/1 Running 0 10m # 查看 ReplicaSet,DESIRED 变为2了 [root@kmaster ~]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-deploy-58f69cfc57 0 0 0 22m nginx-deploy-59c9f8dff 2 2 2 3d8h nginx-deploy-d47dbbb7c 0 0 0 102m
若是集群启用了自动伸缩(HPA —— Horizontal Pod Autoscaling),则能够基于 CPU、 内存的使用率在一个最大和最小的区间对 Deployment 实现自动伸缩,
# 建立一个 HPA [root@kmaster ~]# kubectl autoscale deploy nginx-deploy --min=2 --max=4 --cpu-percent=80 horizontalpodautoscaler.autoscaling/nginx-deploy autoscaled # 查看 HPA [root@kmaster ~]# kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE nginx-deploy Deployment/nginx-deploy <unknown>/80% 2 4 2 16s # 删除 HPA [root@kmaster ~]# kubectl delete hpa nginx-deploy horizontalpodautoscaler.autoscaling "nginx-deploy" deleted
咱们能够将一个 Deployment 暂停(pause),而后在它上面作一个或多个更新,此时 Deployment 并不会触发更新,只有再恢复(resume)该 Deployment,才会执行该时间段内的全部更新。这种作法能够在暂停和恢复中间对 Deployment 作屡次更新,而不会触发没必要要的滚动更新。
# 1. 暂停 Deployment [root@kmaster ~]# kubectl rollout pause deploy nginx-deploy deployment.apps/nginx-deploy paused # 2. 更新容器镜像 [root@kmaster ~]# kubectl set image deploy nginx-deploy nginx=nginx:1.9.1 --record deployment.apps/nginx-deploy image updated # 3. 查看版本历史, 此时并无触发更新 [root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 3 kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true 4 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true 5 kubectl apply --filename=nginx-deploy.yaml --record=true # 4. 更新 Resource 限制,一样并不会触发更新 [root@kmaster ~]# kubectl set resources deploy nginx-deploy -c=nginx --limits=memory=512Mi,cpu=500m deployment.apps/nginx-deploy resource requirements updated # 5. 查看修改,Pod 定义已被更新 [root@kmaster ~]# kubectl describe deploy nginx-deploy Pod Template: Labels: app=nginx Containers: nginx: Image: nginx:1.9.1 Port: 80/TCP Host Port: 0/TCP Limits: cpu: 500m memory: 512Mi # 6. 恢复 Deployment [root@kmaster ~]# kubectl rollout resume deploy nginx-deploy deployment.apps/nginx-deploy resumed # 7. 查看版本历史,可见两次修改只作了一次 rollout [root@kmaster ~]# kubectl rollout history deploy nginx-deploy deployment.apps/nginx-deploy REVISION CHANGE-CAUSE 3 kubectl set image deploy nginx-deploy nginx=nginx:1.161 --record=true 4 kubectl set image deploy nginx-deploy nginx=nginx:1.16.1 --record=true 5 kubectl apply --filename=nginx-deploy.yaml --record=true 6 kubectl set image deploy nginx-deploy nginx=nginx:1.9.1 --record=true
在更新容器镜像时,由于 Deployment 处于暂停状态,因此并不会生成新的版本(Revision),当 Deployment 恢复时,才将这段时间的更新生效,执行滚动更新,生成新的版本。在暂停中的 Deployment 上作的更新, 由于没有生成版本,所以也不能回滚(rollback)。也不能对处于暂停状态的 Deployment 执行回滚操做,只有在恢复(Resume)以后才能执行回滚操做。
金丝雀发布也叫灰度发布。当咱们须要发布新版本时,能够针对新版本新建一个 Deployment,与旧版本的 Deployment 同时挂在一个 Service 下(经过 label match), 经过 Service 的负载均衡将用户请求流量分发到新版 Deployment 的 Pod 上,观察新版运行状况,若是没有问题再将旧版 Deployment 的版本更新到新版完成滚动更新,最后删除新建的 Deployment。很明显这种金丝雀发布具备必定的局限性,没法根据用户或地域来分流,若是要更充分地实现金丝雀发布,则可能须要引入 Istio 等。
金丝雀发布名称的由来: 之前,旷工在下矿洞时面临的一个重要危险是矿井中的毒气,他们想到一个办法来辨别矿井中是否有毒气,矿工们随身携带一只金丝雀下矿井,金丝雀对毒气的抵抗能力比人类要弱,在毒气环境下会先挂掉从而起到预警的做用。它背后的原理是:用较小的代价试错,即便出现了严重的错误(出现了毒气),系统整体的损失也是可承受的或者是很是小的(失去了一只金丝雀)。
Kubernetes 中最小的调度单元是 Pod, 负载建立 Pod 并控制其按必定的副本数运行的是 ReplicaSet, 而 Deployment 能够以“声明式”的方式来管理 Pod 和 ReplicaSet,并提供滚动更新与版本(revision)回退功能。因此,通常使用 Deployment 来部署应用, 而不直接操做 ReplicaSet 或 Pod。
[转载请注明出处]
做者:雨歌
欢迎关注做者公众号:半路雨歌,查看更多技术干货文章