Kubernetes 系列(五):deployment

k8s的控制器类型

  • 控制器又被称为工作负载,pod通过控制器实现应用的运维,比如伸缩、升级等
  • Kubernetes中内建了很多controller(控制器),这些相当于一个状态机,用来控制Pod的具体状态和行为

1、deployment:适合无状态的服务部署

2、StatefullSet:适合有状态的服务部署

3、DaemonSet:一次部署,所有的node节点都会部署,例如一些典型的应用场景:

  • 运行集群存储 daemon,例如在每个Node上运行 glusterd、ceph
  • 在每个Node上运行日志收集 daemon,例如 fluentd、 logstash
  • 在每个Node上运行监控 daemon,例如 Prometheus Node Exporter

4、Job:一次性的执行任务

5、Cronjob:周期性的执行任务

 

Deployment控制器

Deployment实现了Pod 的“水平扩展 / 收缩”(horizontal scaling out/in),适合部署无状态的应用服务,用来管理pod和replicaset,具有上线部署、副本设定、滚动更新、回滚等功能,还可提供声明式更新,例如只更新一个新的Image。

Deployment如何控制pod

这些控制器被统一放在 kubernetes/pkg/controller 目录下,因为它们都遵循 Kubernetes 项目中的一个通用编排模式,即:控制循环(control loop)。

比如,现在有一种待编排的对象 X,它有一个对应的控制器。

那么,我就可以用一段 Go 语言风格的伪代码,为你描述这个控制循环:

for {
  实际状态 := 获取集群中对象X的实际状态(Actual State)
  期望状态 := 获取集群中对象X的期望状态(Desired State)
  if 实际状态 == 期望状态{
    什么都不做
  } else {
    执行编排动作,将实际状态调整为期望状态
  }
}

在具体实现中,实际状态往往来自于 Kubernetes 集群本身。比如,kubelet 通过心跳汇报的容器状态和节点状态,或者监控系统中保存的应用监控数据,或者控制器主动收集的它自己感兴趣的信息,这些都是常见的实际状态的来源。

而期望状态,一般来自于用户提交的 YAML 文件。比如,Deployment 对象中 Replicas 字段的值。很明显,这些信息往往都保存在 Etcd 中。

接下来,以 Deployment 为例,我和你简单描述一下它对控制器模型的实现:

  • Deployment 控制器从 Etcd 中获取到所有携带了“app: nginx”标签的 Pod,然后统计它们的数量,这就是实际状态;
  • Deployment 对象的 Replicas 字段的值就是期望状态;Deployment 控制器将两个状态做比较,然后根据比较结果,确定是创建 Pod,还是删除已有的 Pod。
  • 可以看到,一个 Kubernetes 对象的主要编排逻辑,实际上是在第三步的“对比”阶段完成的。这个操作,通常被叫作调谐(Reconcile)。这个调谐的过程,则被称作“Reconcile Loop”(调谐循环)或者“Sync Loop”(同步循环)。

Deployment依赖ReplicaSet控制pod

当你更新了yaml模板中关于容器的相关配置(比如,修改了容器的镜像),那么 Deployment 就需要遵循一种叫作“滚动更新”(rolling update)的方式,来升级现有的容器。

而这个能力的实现,依赖的是 Kubernetes 项目中的一个非常重要的概念(API 对象):ReplicaSet。

在你通过yaml创建(API 对象)ReplicaSet的 Metadata 里,都有一个字段叫作 ownerReference,用于保存当前这个 API 对象的拥有者(Owner)的信息。

这个ownerReference的go 结构体定义如下:

 1 // OwnerReference contains enough information to let you identify an owning
 2 // object. Currently, an owning object must be in the same namespace, so there
 3 // is no namespace field.
 4 type OwnerReference struct {
 5     // API version of the referent.
 6     APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"`
 7     // Kind of the referent.
 8     // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds
 9     Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
10     // Name of the referent.
11     // More info: http://kubernetes.io/docs/user-guide/identifiers#names
12     Name string `json:"name" protobuf:"bytes,3,opt,name=name"`
13     // UID of the referent.
14     // More info: http://kubernetes.io/docs/user-guide/identifiers#uids
15     UID types.UID `json:"uid" protobuf:"bytes,4,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"`
16     // If true, this reference points to the managing controller.
17     // +optional
18     Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"`
19     // If true, AND if the owner has the "foregroundDeletion" finalizer, then
20     // the owner cannot be deleted from the key-value store until this
21     // reference is removed.
22     // Defaults to false.
23     // To set this field, a user needs "delete" permission of the owner,
24     // otherwise 422 (Unprocessable Entity) will be returned.
25     // +optional
26     BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`
27 }

如果有如下yaml文件:

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

我们可以用一张图把它描述出来:通过这张图,我们就很清楚地看到,一个定义了 replicas=3 的 Deployment,与它的 ReplicaSet,以及 Pod 的关系,实际上是一种“层层控制”的关系。

Deployment 控制器实际操纵的,是 ReplicaSet 对象,而不是 Pod 对象。

 

 

 

 

定义Deployment来创建Pod和ReplicaSet

创建一个yaml文件:nginx-deployment.yaml

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.7.9
        ports:
        - containerPort: 80

创建deployment,–-record 参数。它的作用,是记录下你每次操作所执行的命令,以方便后面查看。

kubectl create -f nginx-deployment.yaml --record

通过标签查找Pod

kubectl get pod -l app=nginx

查看deployment,可以实时查看pod的创建状态

kubectl get deployments
在返回结果中,我们可以看到四个状态字段,它们的含义如下所示。
DESIRED:用户期望的 Pod 副本个数(spec.replicas 的值);
CURRENT:当前处于 Running 状态的 Pod 的个数;
UP-TO-DATE:当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致;
AVAILABLE:当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数。
可以看到,只有这个 AVAILABLE 字段,描述的才是用户所期望的最终状态。

查看Deployment 所控制的 ReplicaSet

kubectl get rs
注意:Replica Set的名字总是<Deployment的名字>-<pod template的hash值>。

describe检查

kubectl describe deployment/nginx-deployment

实时查看 Deployment 对象的状态变化

kubectl rollout status deployment/nginx-deployment

创建一个Service对象,暴露Deployment端口,给外网访问

kubectl expose deployment/nginx --port=88 --type=NodePort --target-port=80 --name=example-nginx-service
# 备注
--port=88:Service服务的端口
--target-port=80: 容器暴露的端口
--type=NodePort:会随机开放一个宿主机端口(端口范围在apiserver中定义)

查看/描述 服务

kubectl get svc
kubectl describe svc example-nginx-service
快速访问测试: curl ip:port 

查看Pod日志

kubectl logs pods/podname
注意:这个podname来自于通过标签查找的pod的name

扩容和缩容

kubectl scale deployment nginx-deployment --replicas=4

 

 

更新Deployment

kubectl edit deployment/nginx-deployment

注意:kubectl edit 指令,会帮你直接打开 nginx-deployment 的 API 对象。然后,你就可以修改这里的 Pod 模板部分了

kubectl edit 指令编辑完成后,保存退出,Kubernetes 就会立刻触发“滚动更新”的过程。你还可以通过 kubectl rollout status 指令查看 nginx-deployment 的状态变化:

kubectl rollout status deployment/nginx-deployment

继续查看 Deployment 的 Events,看到这个“滚动更新”的流程:

kubectl describe deployment nginx-deployment

整个更新是滚动更新的:将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是“滚动更新”。

Deployment Controller 会使用这个修改后的 Pod 模板,创建一个新的 ReplicaSet,这个新的 ReplicaSet 的初始 Pod 副本数是:0。

更新过程交替进行,新 ReplicaSet 管理的 Pod 副本数,从 0 个变成 1 个,再变成 2 个,最后变成 3 个。而旧的 ReplicaSet 管理的 Pod 副本数则从 3 个变成 2 个,再变成 1 个,最后变成 0 个。这样,就完成了这一组 Pod 的版本升级过程。

最后我们查看ReplicaSet

kubectl get rs

就会发现旧 ReplicaSet所管理的pod已经被“水平收缩”成了 0 个副本。

更新操作还有两种方式:一种是,手动修改yaml文件,然后执行kubectl apply -f  nginx-deployment.yaml,另一种是通过 kubectl set image 命令。

另外补充:我们对 Deployment 进行的每一次更新操作,都会生成一个新的 ReplicaSet 对象,是不是有些多余,甚至浪费资源呢?

Kubernetes 项目还提供了一个指令,使得我们对 Deployment 的多次更新操作,最后 只生成一个 ReplicaSet。具体的做法是,在更新 Deployment 前,你要先执行一条 kubectl rollout pause 指令。

具体流程:

1.在更新这个Deployment时,执行kubectl rollout pause deployment/nginx-deployment

2.更新Deployment,使用kubectl edit 或者 kubectl set image 指令

3.等Deployment 上的更新完成以后,执行kubectl rollout resume deployment/nginx-deployment

 

回滚

回滚到上一个版本

kubectl rollout undo deployment/nginx-deployment

Deployment 的控制器,会让这个旧 ReplicaSe再次“扩展”成 3 个 Pod,而让新的 ReplicaSet重新“收缩”到 0 个 Pod。

回滚到更早之前的版本

查看此deployment每次变更对应的版本

kubectl rollout history deployment/nginx-deployment

查看对应版本的 Deployment 的 API 对象的细节

kubectl rollout history deployment/nginx-deployment --revision=2

确定以后我们就可以回滚到这个版本上

kubectl rollout undo deployment/nginx-deployment --to-revision=2

 

 

编写Deployment Spec指南

在所有的Kubernetes配置中,Deployment也需要apiVersion,kind和metadata这些配置项。配置文件的通用使用说明查看部署应用,配置容器,和使用kubeclt管理资源文档。

Deployment也需要 .spec section.

Pod Template

.spec.template 是 .spec中唯一要求的字段。

.spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion 和 kind字段。

另外为了划分Pod的范围,Deployment中的pod template必须指定适当的label(不要跟其他controller重复了)和适当的重启策略。

.spec.template.spec.restartPolicy 可以设置为 Always , 如果不指定的话这就是默认配置。

Replicas

.spec.replicas 是可以选字段,指定期望的pod数量,默认是1。

Selector

.spec.selector是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围。

如果被指定, .spec.selector 必须匹配 .spec.template.metadata.labels,否则它将被API拒绝。如果 .spec.selector 没有被指定, .spec.selector.matchLabels 默认是 .spec.template.metadata.labels。

在Pod的template跟.spec.template不同或者数量超过了.spec.replicas规定的数量的情况下,Deployment会杀掉label跟selector不同的Pod。

注意: 你不应该再创建其他label跟这个selector匹配的pod,或者通过其他Deployment,或者通过其他Controller,例如ReplicaSet和ReplicationController。否则该Deployment会被把它们当成都是自己创建的。Kubernetes不会阻止你这么做。

如果你有多个controller使用了重复的selector,controller们就会互相冲突并导致不正确的行为。

策略

.spec.strategy 指定新的Pod替换旧的Pod的策略。 .spec.strategy.type 可以是”Recreate”或者是 “RollingUpdate”。”RollingUpdate”是默认值。

  • Recreate Deployment

.spec.strategy.type==Recreate时,在创建出新的Pod之前会先杀掉所有已存在的Pod。

  • Rolling Update Deployment

.spec.strategy.type==RollingUpdate时,Deployment使用rolling update 的方式更新Pod 。你可以指定maxUnavailable 和maxSurge 来控制 rolling update 进程。

  • Max Unavailable

.spec.strategy.rollingUpdate.maxUnavailable 是可选配置项,用来指定在升级过程中不可用Pod的最大数量。该值可以是一个绝对值(例如5),也可以是期望Pod数量的百分比(例如10%)。通过计算百分比的绝对值向下取整。如果.spec.strategy.rollingUpdate.maxSurge 为0时,这个值不可以为0。默认值是1。

例如,该值设置成30%,启动rolling update后旧的ReplicatSet将会立即缩容到期望的Pod数量的70%。新的Pod ready后,随着新的ReplicaSet的扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻可以用的Pod数量至少是期望Pod数量的70%。

  • Max Surge

.spec.strategy.rollingUpdate.maxSurge 是可选配置项,用来指定可以超过期望的Pod数量的最大个数。该值可以是一个绝对值(例如5)或者是期望的Pod数量的百分比(例如10%)。当MaxUnavailable为0时该值不可以为0。通过百分比计算的绝对值向上取整。默认值是1。

例如,该值设置成30%,启动rolling update后新的ReplicatSet将会立即扩容,新老Pod的总数不能超过期望的Pod数量的130%。旧的Pod被杀掉后,新的ReplicaSet将继续扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻所有的Pod数量和不会超过期望Pod数量的130%。

  • Progress Deadline Seconds

.spec.progressDeadlineSeconds 是可选配置项,用来指定在系统报告Deployment的failed progressing ——表现为resource的状态中type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded前可以等待的Deployment进行的秒数。Deployment controller会继续重试该Deployment。未来,在实现了自动回滚后, deployment controller在观察到这种状态时就会自动回滚。

如果设置该参数,该值必须大于 .spec.minReadySeconds。

  • Min Ready Seconds

.spec.minReadySeconds是一个可选配置项,用来指定没有任何容器crash的Pod并被认为是可用状态的最小秒数。默认是0(Pod在ready后就会被认为是可用状态)。进一步了解什么什么后Pod会被认为是ready状态,参阅 Container Probes。

  • Rollback To

.spec.rollbackTo 是一个可以选配置项,用来配置Deployment回退的配置。设置该参数将触发回退操作,每次回退完成后,该值就会被清除。

  • Revision

.spec.rollbackTo.revision是一个可选配置项,用来指定回退到的revision。默认是0,意味着回退到历史中最老的revision。

  • Revision History Limit

Deployment revision history存储在它控制的ReplicaSets中。

.spec.revisionHistoryLimit 是一个可选配置项,用来指定可以保留的旧的ReplicaSet数量。该理想值取决于心Deployment的频率和稳定性。如果该值没有设置的话,默认所有旧的Replicaset都会被保留,将资源存储在etcd中,是用kubectl get rs查看输出。每个Deployment的该配置都保存在ReplicaSet中,然而,一旦你删除的旧的RepelicaSet,你的Deployment就无法再回退到那个revison了。

如果你将该值设置为0,所有具有0个replica的ReplicaSet都会被删除。在这种情况下,新的Deployment rollout无法撤销,因为revision history都被清理掉了。

  • Paused

.spec.paused是可以可选配置项,boolean值。用来指定暂停和恢复Deployment。Paused和没有paused的Deployment之间的唯一区别就是,所有对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout。Deployment被创建之后默认是非paused。

 

posted @ 2020-09-08 19:57  -零  阅读(1223)  评论(1编辑  收藏  举报