关于云计算:浅析Kubernetes控制器

7次阅读

共计 11062 个字符,预计需要花费 28 分钟才能阅读完成。

什么是控制器?

Kubernetes 内领有许多的控制器类型,用来管制 pod 的状态、行为、正本数量等等,控制器通过 Pod 的标签来管制 Pod,从而实现对利用的运维,如伸缩、降级等。

罕用的控制器类型如下:

  1. ReplicationController、ReplicaSet、Deployment:无状态服务,保障在任意工夫运行 Pod 指定的正本数量,可能保障 Pod 总是可用的,反对滚动更新、回滚。典型用法:web 服务。
  2. DaemonSet:确保集群内全副(或局部)node 节点上都调配一个 pod,如果新加 node 节点,也会主动再调配对应的 pod。典型用法:filebeat 日志收集、prometheus 资源监控。
  3. StatefulSet:有状态服务,如各种数据存储系统。StatefullSet 内的服务有着稳固的长久化存储和网络标识,有序部署,有序伸缩。
  4. Job:只运行一次的作业。
  5. CronJob:周期性运行的作业。典型用法:数据库定时备份。
  6. Horizontal Pod Autoscaling(HPA):依照冀望的 pod 的 cpu 或内存来主动伸缩 pod 数量。

为什么须要控制器?

如果咱们当初有一个 Pod 正在提供线上的服务,咱们来想想一下咱们可能会遇到的一些场景:

  • 某次经营流动十分胜利,网站访问量忽然暴增
  • 运行以后 Pod 的节点产生故障了,Pod 不能失常提供服务了

针对第一种状况,可能比拟好应答,个别流动之前咱们会大略计算下会有多大的访问量,提前多启动几个 Pod,流动完结后再把多余的 Pod 杀掉,尽管有点麻烦,然而应该还是可能应答这种状况的。

针对第二种状况,可能某天夜里收到大量报警说服务挂了,而后起来关上电脑在另外的节点上重新启动一个新的 Pod,问题也很好的解决了。

如果咱们都人工的去解决遇到的这些问题,仿佛又回到了以前刀耕火种的时代了,是吧。要是有一种工具可能来帮忙咱们治理 Pod 就好了,Pod 不够了主动帮咱们新增一个,Pod 挂了主动帮咱们在适合的节点上重新启动一个 Pod,如果这样的话,是不是遇到下面的问题,咱们都不须要手动去解决了。

Pod 分类

Pod 分为自主式 Pod 和控制器治理的 Pod 这两类。

  • 自主式 Pod:Pod 退出后不会被主动创立,如图中的Pod A
  • 控制器治理的 Pod:在控制器的生命周期里,始终要维持 Pod 的正本数目,如图中的Pod B

ReplicationController、ReplicaSet、Deployment

ReplicationController(RC)

简略来说,RC 能够保障在任意工夫运行 Pod 的正本数量,可能保障 Pod 总是可用的。如果理论 Pod 数量比指定的多,那就完结掉多余的,如果理论数量比指定的少,就新启动一些 Pod。当 Pod 失败、被删除或者挂掉后,RC 都会去主动创立新的 Pod 来保障正本数量,所以即便只有一个 Pod,咱们也应该应用 RC 来治理咱们的 Pod。

简而言之,RC 用于确保其管控的 Pod 对象正本数满足冀望的数值,它能实现以下性能:

  • 确保 Pod 的资源数量准确反馈期望值
  • 确保 Pod 衰弱运行
  • 弹性伸缩

ReplicationController 的示例如下所示:

apiVersion: v1
kind: ReplicationController
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    app: kubia
  template:
    metadata:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: luksa/kubia
        ports:
        - containerPort: 8080

一个 ReplicationController 有三个次要局部:

  • label selector (标签选择器):用于确定 ReplicationController 作用域中有哪些 pod
  • replica count (正本个数):指定应运行的 Pod 数量
  • pod template (Pod 模板):用于创立新的 Pod 正本

RC 存在的问题:

大部分状况下,咱们能够通过定义一个 RC 实现的 Pod 的创立和正本数量的管制
RC 中蕴含一个残缺的 Pod 定义模块(不蕴含 apiversion 和 kind),RC 是通过 label selector 机制来实现对 Pod 正本的管制的。通过扭转 RC 外面的 Pod 正本数量,能够实现 Pod 的扩缩容性能。通过扭转 RC 外面的 Pod 模板中镜像版本,能够实现 Pod 的滚动降级性能(然而不反对一键回滚,须要用雷同的办法去批改镜像地址)

ReplicaSet(RS)

随着 Kubernetes 的高速倒退,官网曾经举荐咱们应用 RS 和 Deployment 来代替 RC 了,实际上 RS 和 RC 的性能基本一致,
目前惟一的一个区别就是 RC 只反对基于等式的 selector(如:env=dev 或 environment!=qa),但 RS 还反对基于汇合的 selector(如:version in (v1.0, v2.0)),这对简单的运维治理就更不便了。

ReplicaSet 的示例如下所示:

apiVersion: apps/v1beta2
kind: ReplicaSet
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    matchLabels:
      app: kubia
  template:
    metadata:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: luksa/kubia

ReplicationController 和 ReplicaSet 惟一的区别 就是在选择器中, 不用在 selector 属性中间接列出 pod 须要的标签,
而是在 selector.matchLabels 下指定它们。
这是在 ReplicaSet 中定义标签选择器的更简略(也更不具表达力)的形式。

除了 selector.matchLabels 指定之外,还能够应用 selector.matchExpressions 指定,如下所示:

selector:
  matchExpressions:
    - key: app
      operator: In
      values:
        - kubia

应用 ReplicaSet 进行更富表达力的标签选择器有四个无效的运算符:

  • In : Label 的值 必须与其中 一个指定的 values 匹配。
  • NotIn : Label 的值与任何指定的 values 不匹配。
  • Exists : pod 必须蕴含 一个指定名称的标签(值不重要)。应用此运算符时,不应指定 values 字段。
  • DoesNotExist : pod 不得蕴含有指定名称的标签。values 属性不得指定。

如果你指定了多个表达式,则所有这些表达式都必须为 true 能力使选择器与 pod 匹配。如果同时指定 matchLabels 和 matchExpressions, 则所有标签都必须匹配,并且所有表达式必须计算为 true 以使该 pod 与选择器匹配。

Deployment

不过咱们也很少会去独自应用 RS,它次要被 Deployment 这个更加高层的资源对象应用,除非用户须要自定义降级性能或基本不须要降级 Pod,
Deployment 为 Pod 和 ReplicaSet 提供了一个申明式的定义方法。

典型的利用场景:

用来创立 Pod 和 ReplicaSet、滚动更新和回滚、扩容和缩容、暂停与复原。

在个别状况下,咱们举荐应用 Deployment 而不间接应用 Replica Set。

Deployment 基于 ReplicaSet 之上,可为 Pod 和 ReplicaSet 资源提供申明式更新,它具备以下个性:

  • 事件和状态查看:能够查看 Deployment 对象降级的具体进度和状态
  • 回滚:当降级操作实现后发现问题时,反对将利用返回到指定的历史版本中
  • 版本记录:对 Deployment 对象的每一次操作都予以保留
  • 暂停和启动:每一次降级,都能够随时暂停和启动
  • 多种自动更新计划:Recreate- 重建更新、RollingUpdate- 滚动更新

Deployment 的更新策略形容如下:

  • RollingUpdate 策略:旧控制器的 Pod 数量一直缩小,同时新控制器的 Pod 一直减少,以下两个属性:maxSurge:降级期间存在的总 Pod 数量最多可超过期望值的个数,能够是数值或百分比。maxUnavailabe:降级期间失常可用的 Pod 数 (新旧版本) 最多不能低于冀望的个数,能够是数值或百分比。
  • Recreate 策略:在删除旧的 pod 之后才开始创立新的 pod。如果你的应用程序不反对多个版本同时对外提供服务,须要在启动新版本之前齐全停用旧版本,那么须要应用这种策略。然而应用这种策略的话,会导致应用程序呈现短暂的不可用。

Deployment 的示例如下所示:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: kubia
spec:
  replicas: 3
  template:
    metadata:
      name: kubia
      labels:
        app: kubia
    spec:
      containers:
      - image: luksa/kubia:v1
        name: nodejs

ReplicationController、ReplicaSet、Deployment 的协调流程

ReplicationController、ReplicaSet、Deployment 的区别

ReplicaSet 是新一代的 ReplicationController, 并举荐应用它代替 ReplicationController 来复制和治理 Pod。

同时,在应用 Deployment 时,理论的 Pod 是由 Deployment 的 Replicaset 创立和治理的,而不是由 Deployment 间接创立和治理的。

StatefulSet

背景

Deployment 不足以笼罩所有的利用编排问题,因为在它看来,一个利用的所有 Pod,是齐全一样的,所以它们之间就没有程序,也无所谓运行在哪台宿主机上。须要时,Deployment 就通过 Pod 模板创立新的 Pod。不须要时,就“杀掉”任意一个 Pod。然而在理论场景中,并不是所有利用都满足这样的要求。比方: 主从关系,主备关系,还有就是数据存储类利用,多个实例通常会在本地磁盘上保留一份数据,而这些实例一旦被杀掉,即便重建进去, 实例与数据之间的对应关系也曾经失落,从而导致利用失败。这种实例之间有不对等关系,或者有依赖关系的利用,被称为“有状态利用”。

StatefulSet 简述

StatefulSet 实质上是 Deployment 的一种变体,它为了解决有状态服务的问题,它所治理的 Pod 领有固定的 Pod 名称,启停程序,在 StatefulSet 中,Pod 名字称为网络标识 (hostname),还必须要用到共享存储。
在 Deployment 中,与之对应的服务是 Service,而在 StatefulSet 中与之对应的 Headless Service,Headless Service,即无头服务,与 service 的区别就是它没有 Cluster IP,解析它的名称时将返回该 Headless Service 对应的全副 Pod 的 Endpoint 列表。
除此之外,StatefulSet 在 Headless Service 的根底上又为 StatefulSet 管制的每个 Pod 正本创立了一个 DNS 域名,这个域名的格局为:
$(podname).(headless server name)
FQDN(全限定域名)格局为:$(podname).(headless server name).namespace.svc.cluster.local

StatefulSet 将真实世界里的利用状态,形象为了两种状况:

  • 拓扑状态:这种状况是说,利用的多个实例之间不是齐全对等的关系。这些利用实例,必须依照某些程序启动,比方某个利用的主节点 A 要先于 B 启动,那么当我把 A 和 B 两个节点删除之后,从新创立进去时,也要是这个程序才行。并且,新创建进去的 A 和 B,必须和原来的 A 和 B 网络标识一样,这样原先的访问者能力应用同样的办法, 拜访到这个新 Pod。
  • 存储状态:这种状况是说,利用的多个实例别离绑定了不同的存储数据。对于这些利用实例来说,Pod A 第一次读取到的数据,和隔了十分钟之后再次读取到的数据,应该是同一份,哪怕在此期间 Pod A 被从新创立过。

所以,StatefulSet 的外围性能就是通过某种形式,记录这些状态,而后在 Pod 被从新创立时,可能为新 Pod 复原这些状态。

Headless Service

在深刻理解 StatefulSet 之前,咱们先来讲讲 Headless Service。

咱们晓得,Service 是 Kubernetes 我的项目中用来将一组 Pod 裸露给外界拜访的一种机制。比方,一个 Deployment 有 3 个 Pod,那么我就能够定义一个 Service,而后用户只有能拜访到这个 Service,就能拜访到某个具体的 Pod。然而,这个 Service 是怎么被拜访到的呢?

  • 第一种形式,以 Service 的 VIP(Virtual IP,即:虚构 IP)形式。比方:当我拜访 192.168.0.1 这个 Service 的 IP 地址时,它就是一个 VIP。在理论中,它会把申请转发到 Service 代理的具体 Pod 上。
  • 第二种形式,就是以 Service 的 DNS 形式。在这里又分为两种解决办法:第一种是Normal Service。这种状况下,当拜访 DNS 记录时,解析到的是 Service 的 VIP。第二种是Headless Service。这种状况下,拜访 DNS 记录时,解析到的就是某一个 Pod 的 IP 地址。

能够看到,Headless Service 不须要调配一个 VIP,而是能够间接以 DNS 记录的形式解析出被代理 Pod 的 IP 地址。(Headless Service 是将 Service 的公布文件中的 spec.clusterIP 示意为 None,不让其获取 clusterIP,DNS 解析的时候间接走 pod。) 这样设计有什么益处呢?

这样设计能够使 Kubernetes为 Pod 调配惟一“可解析身份”。而有了这个身份之后,只有晓得了一个 Pod 的名字以及它对应的 Service 的名字,就能够十分确定地通过这条 DNS 记录拜访到 Pod 的 IP 地址。

创立 StatefulSet

与 ReplicaSet 不同,由 StatefulSet 创立的 Pod 领有规定的名称(和主机名)。

StatefulSet 的失败重启机制

StatefulSet 应用标识完全一致的新的 Pod 替换,ReplicaSet 则是应用一个不相干的新的 Pod 替换。

StatefulSet 的示例如下所示:

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: kubia
spec:
  serviceName: kubia
  replicas: 2
  template:
    metadata:
      labels:
        app: kubia
      spec:
        containers:
        - name: kubia
          image: luksa/kubia-pet
          ports:
          - name: http
            containerPort: 8080
          volumeMounts:
          - name: data
            mountPath: /var/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      resources:
        requests:
          storage: 1Mi
      accessModes:
      - ReadWriteOnce

在这个 YAML 文件中,多了一个 serviceName=kubia 字段。这个字段的作用,就是通知 StatefulSet 控制器,在执行管制循环时,要应用 kubia 这个 Headless Service 来保障 Pod 的“可解析身份”。这样,在创立 Pod 过程中,StatefulSet 给它所治理的所有 Pod 名字,进行编号,使得每个 Pod 实例不反复。而更重要的是,这些 Pod 的创立,也是严格依照编号程序来进行的。

StatefulSet 和 Deployment 的区别

  • Deployment 利用于无状态利用;StatefulSet 利用于有状态利用。
  • Deployment 的 Pod 之间没有程序;StatefulSet 的 Pod 部署、扩大、更新、删除都要有程序。
  • Deployment 的所有 pod 共享存储;StatefulSet 的每个 pod 都有本人存储,所以都用 volumeClaimTemplates,为每个 pod 都生成一个本人的存储,保留本人的状态。
  • Deployment 的 pod 名字蕴含随机数字;StatefulSet 的 pod 名字始终是固定的。
  • Deployment 的 Service 都有 ClusterIP, 能够负载平衡;StatefulSet 的 Service 没有 ClusterIP,是 Headless Service,所以无奈负载平衡,返回的都是 pod 名,所以 pod 名字都必须固定,StatefulSet 在 Headless Service 的根底上又为 StatefulSet 管制的每个 Pod 正本创立了一个 DNS 域名:$(podname).(headless server name).namespace.svc.cluster.local

DaemonSet

DaemonSet 控制器确保集群中的每一个 Node 只运行一个特定的 Pod 正本,实现零碎级的后台任务,也具备标签选择器。

也能够指定局部满足条件的 Node 运行一个 Pod 正本,比方监控具备 ssd 存储的 Node 节点。

罕用来部署一些集群的日志、监控或者其余系统管理利用。典型的利用包含:

  • 日志收集,比方 fluentd、logstash 等。
  • 系统监控,比方 Prometheus Node Exporter、collectd、New Relic agent、Ganglia gmond 等。
  • 零碎程序,比方 kube-proxy、kube-dns、Glusterd、Ceph 等。

DaemonSet 的示例如下所示:

前提:

# 列出所用的节点
kubectl get node

# 给节点名为 minikube 的节点增加 disk=ssd 标签
kubectl label node minikube disk=ssd

上面创立一个模仿运行 ssd-monitor 监控器过程的 DaemonSet, 该过程每 5 秒会将“SSD OK”打印到规范输入。它将运行 一个基于 luksa/ssd-monitor 容器镜像的单容器 pod。该 pod 的实例将在每个具备 disk=ssd 标签的节点上创立。

apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
  name: ssd-monitor
spec:
  selector:
    matchLabels:
      app: ssd-monitor
  template:
    metadata:
      labels:
        app: ssd-monitor
    spec:
      nodeSelector:
        disk: ssd
      containers:
      - name: main
        image: luksa/ssd-monitor

Job

到目前为止,咱们只议论了须要 继续运行的 pod。你会遇到只想运行实现工作后就终止工作的清况。ReplicationController、ReplicaSet 和 DaemonSet 会继续运行工作,永远达不到实现态。这些 pod 中的过程在退出时会重新启动。然而在一个可实现的工作中,其过程终止后,不应该再重新启动。

Job 控制器用于配置 Pod 对象运行一次性工作,容器中的过程在失常运行完结后不会进行重启,而是将 Pod 对象置于“Completed”状态。若容器中的过程因为谬误而终止,则须要配置确定是否要重启。

Job 控制器对象次要有两种:

  • 单工作队列地串行式 Job:多个一次性的作业形式串行执行屡次作业,直到满足冀望的次数
  • 多工作队列的并行式 Job:多个工作队列并行运行多个一次性作业

配置项:

  • completions:总的执行的作业数
  • parallelism:作业执行的并行度
  • activeDeadlineSeconds:最大流动工夫长度,超出此时长的作业将被终止
  • backoffLimit:将作业标记为失败状态之前的重试次数,默认为 6
  • ttlSecondsAfterFinished:Completed 的 job 默认不会清理。此配置项设置当 job 实现后的保留 xx 秒就主动清理这个 job。当 ttl controller 清理 job 的时候是级联删除的,会把这个 job 下的 pod 一并删除。如果设置为 0,job 会被立刻删除。如果不指定,job 则不会被删除。

Job 的示例如下所示:

定义了一个 Job 类型的资源,它将运行 luksa/batch-job 镜像,该镜像调用 一个运行 120 秒的过程,而后退出。

apiVersion: batch/v1
kind: Job
metadata:
  name: batch-job
spec:
  completions: 5
  parallelism: 2
  activeDeadlineSeconds: 100
  backoffLimit: 5
  ttlSecondsAfterFinished: 100
  template:
    metadata:
      labels:
        app: batch-job 
    spec:
      restartPolicy: OnFailure
      containers:
      - name: main
        image: luksa/batch-job

下面没有指定 pod 选择器,它将依据 pod 模板中的标签进行创立。

阐明:

在一个 pod 的定义中,能够指定在容器中运行的过程完结时,Kubernetes 应该做什么?

这是通过 pod 配置的属性 restartPolicy 实现的,默认为 Always。Job 类型的资源 pod 不能应用默认策略,因为它们不是要无限期地运行。因而,须要明确地将重启策略设置为 OnFailure 或 Never。此设置避免容器在实现工作时重新启动。

CronJob

CronJob 控制器执行周期性工作作业,管制其运行的工夫点及反复运行的形式,相似于 Linux 操作系统的周期性工作作业打算的形式管制其运行的工夫点和反复运行的形式。

配置项:

  • jobTemplate:Job 控制器模板。
  • schedule:Cron 格局的作业调度运行的工夫点。
  • concurrencyPolicy:并发执行策略,用于定义前一次作业尚未实现时如何执行下一此工作。默认是 Allow,即容许前后 Job,甚至是属于同一个 CrontJob 的更多 Job 同时运行。如果设置为 Forbid 则禁止前后两个 Job 同时运行,如果前一个尚未完结,后一个不会启动(跳过),如果设置为 Replace,则后一个 Job 会代替前一个 Job,即终止前一个,启动后一个。
  • failedJobHistoryLimit:为失败的工作执行保留的历史记录数,默认是 1。
  • successfulJobsHistoryLimit:为胜利的工作执行保留的历史记录数,默认是 3。
  • startingDeadlineSeconds:因各种起因不足执行作业的工夫点所导致的启动作业谬误的超时时长,会被记入谬误历史记录
  • suspend:是否挂起后续的工作执行,默认是 false。

CronJob 的示例如下所示:

定义了一个 CronJob 类型的资源每 15 分钟运行一次批处理工作。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: batch-job-every-fifteen-minutes
spec:
  schedule: "0,15,30,45 * * * *"
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            app: periodic-batch-job
        spec:
          restartPolicy: OnFailure
          containers:
          - name: main
            image: luksa/batch-job

配置时间表如下图所示:

时间表从左到右蕴含以下五个条目:

  • 分钟
  • 小时
  • 每月中的第几天
  • 星期几

理解调度 Job 如何运行

你可能产生 Job 或 Pod 创立并运行得绝对较晚的状况。你可能对这项作业有很高的要求,工作开始不能落后于预约的工夫过多。

在这种状况下,能够通过指定 CronJob 标准中的 startingDeadlineSeconds 字段来指定截止日期。

apiVersion: batch/v1beta1
kind: CronJob
spec:
  schedule: "0,15,30,45 * * * *"
  startingDeadlineSeconds: 15

Pod 最迟必须在预约工夫 15 秒后开始运行, 如果作业运行的工夫应该是 10:30:00。如果因为任何起因10:30:15 不启动,工作将不会运行,并将显示为 Failed。

在失常状况下,CronJob 总是为 schedule 中配置的每个执行创立一个 Job, 但可能会同时创立两个 Job, 或者基本没有建。
为了解决第一个问题,你的工作应该是幕等的(屡次(而不是一次)运行不会失去不心愿的后果)。
对于第二个问题,请确保下一个工作运行实现本应该由上一次(错过的)运行实现的任何工作。

HorizontalPodAutoscaler(HPA)

利用的资源使用率通常都有顶峰和低谷的时候,如何削峰填谷,进步集群的整体资源利用率,让 Service 中的 Pod 个数主动调整呢?这就有赖于 Horizontal Pod Autoscaling 了,顾名思义,使 Pod 程度主动缩放(依照冀望的 pod 的 cpu 或内存来主动伸缩 pod 数量)。

apiVersion: autoscaling/v1
# 资源类型是 HPA
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-test
  namespace: test
spec:
  # 最大正本数
  maxReplicas: 10 
  # 最小正本数
  minReplicas: 3   
  scaleTargetRef:   
    apiVersion: apps/v1beta1
    kind: Deployment  
    # 监控名为 deploy-test 的 Deployment
    name: deploy-test   
  # cpu 阈值 CPU 大于 80 会主动创立 pod 来分担服务压力,小于 80 则缩小 pod 数量
  targetCPUUtilizationPercentage: 80 

基于多个 Pod 度量的主动伸缩(例如:CPU 使用率和每秒查问率[QPS])的计算也并不简单。Autoscaler 独自计算每个度量的正本数,而后取最大值(例如:如果须要 4 个 pod 达到目标 CPU 使用率,以及须要 3 个 pod 来达到目标 QPS, 那么 Autoscaler 将扩大到 4 个 pod)。

总结

咱们通常将利用分为无状态利用、有状态利用、守护型利用、批处理利用这四种。Kubernetes 针对各种类型的利用设计了相应的控制器。本文简略介绍了这几种控制器的不同用处。

控制器 用处
Deployment 无状态利用
StatefulSet 有状态利用
DaemonSet 守护过程利用
Job & CronJob 批处理作业

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0