05-Kubernetes控制器

1、ReplicaSet

当我们定义Pod资源配置清单的时候如果只是通过kind: Pod来进行申明,当我们删除该资源配置清单创建的Pod的时候,Pod会被直接删除,这种情况在生产环境中是相当危险的。

于是我们在线上环境中推荐使用Pod控制器来管理Pod,所谓控制器就是能够管理pod,监测pod运行状况,当pod发生故障,可以自动恢复pod。也就是说能够代我们去管理pod中间层,并帮助我们确保每一个pod资源始终处于我们所定义或者我们所期望的目标状态,一旦pod资源出现故障,那么控制器会尝试重启pod或者里面的容器,如果一直重启有问题的话那么它可能会基于某种策略来进行重新布派或者重新编排;如果pod副本数量低于用户所定义的目标数量,它也会自动补全;如果多余,也会自动终止pod资源。

1.1 什么是ReplicaSet?

ReplicaSet是kubernetes中的一种副本控制器,简称rs,主要作用是控制由其管理的pod,使pod副本的数量始终维持在预设的个数。它的主要作用就是保证一定数量的Pod能够在集群中正常运行,它会持续监听这些Pod的运行状态,在Pod发生故障时重启pod,pod数量减少时重新运行新的Pod副本。

官方推荐不要直接使用ReplicaSet,用Deployments取而代之,Deployments是比ReplicaSet更高级的概念,它会管理ReplicaSet并提供很多其它有用的特性,最重要的是Deployments支持声明式更新,声明式更新的好处是不会丢失历史变更。所以Deployment控制器不直接管理Pod对象,而是由 Deployment 管理ReplicaSet,再由ReplicaSet负责管理Pod对象。

1.2 ReplicaSet的工作原理

Replicaset核心作用在于代用户创建指定数量的pod副本,并确保pod副本一直处于满足用户期望的数量, 起到多退少补的作用,并且还具有自动扩容缩容等机制。

Replicaset控制器主要由三个部分组成:

  1. 用户期望的pod副本数:用来定义由这个控制器管控的pod副本有几个
  2. 标签选择器:选定哪些pod是自己管理的,如果通过标签选择器选到的pod副本数量少于我们指定的数量,需要用到下面的组件
  3. pod资源模板:如果集群中现存的pod数量不够我们定义的副本中期望的数量怎么办,需要新建pod,这就需要pod模板,新建的pod是基于模板来创建的

1.3 ReplicaSet资源清单文件

在编写ReplicaSet资源配置清单的时候我们可以通过下面的命令去查看ReplicaSet都拥有哪些字段信息:

kubectl explain replicaset
  • apiVersion:Api版本,如果是ReplicaSet的话,目前对应的版本为apps/v1,与kubectl explain replicaset.apiVersion命令查询结果中的VERSION的值保持一致。
  • kind:资源类型,如果是ReplicaSet的话,目前对应的类型为ReplicaSet,与kubectl explain replicaset.apiVersion命令查询结果中的KIND的值保持一致。
  • metadata:元数据,定义ReplicaSet名称等相关数据。
  • spec:定义ReplicaSet管理的Pod的副本数、标签选择器和Pod模板信息。
    • template:其内部定义的就是pod,pod模板是一个独立的对象
      • spec:定义Pod对应容器的详细信息
  • status:状态信息,在启动ReplicaSet之后自动生成,不能手动修改。

通过上面可以看到,ReplicaSet资源中有两个spec字段。第一个spec声明的是ReplicaSet定义多少个Pod副本(默认将仅部署1个Pod)、匹配Pod标签的选择器、创建pod的模板。第二个spec是spec.template.spec:主要用于Pod里的容器属性等配置。

.spec.template里的内容是声明Pod对象时要定义的各种属性,所以这部分也叫做PodTemplate(Pod模板)。还有一个值得注意的地方是:在.spec.selector中定义的标签选择器必须能够匹配到spec.template.metadata.labels里定义的Pod标签,否则Kubernetes将不允许创建ReplicaSet。

1.4 示例

通过ReplicaSet部署一个guestbook的项目:

replicaset.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
        - name: php-redis
          image: gcr.io/google_samples/gb-frontend:v3

更新资源配置文件:

kubectl apply -f replicaset.yaml

kubectl get pods -n default -o wide

部署后查看结果:

因为在资源配置清单中设置的副本数是3,所以这里会有3个Pod生成。

1.5 ReplicaSet管理Pod

ReplicaSet最核心的功能是可以动态扩容和回缩,如果我们觉得两个副本太少了,想要增加,只需要修改配置文件replicaset.yaml里的replicas的值即可。

上面的实例中副本数为3,如果要修改Pod数量,只需要修改资源配置清单中的replicas对应的数值即可。

1.5.1 扩容Pod数量

replicaset.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
        - name: php-redis
          image: gcr.io/google_samples/gb-frontend:v3

上面把replicas: 3改为了`replicas: 42,然后执行更新资源配置清单即可:

kubectl apply -f replicaset.yaml

kubectl get pods -n default -o wide

更新之后,Pod数从3变成了2:

1.5.2 缩容Pod数量

扩容使用的是直接修改资源配置清单,其实Kubernetes也支持通过kubectl edit来修改资源配置清单实现扩缩容。

kubectl edit replicasets frontend

如下图:

修改上图中replicas的数值即可,下面将replicas: 2修改为replicas: 3

1.5.3 ReplicaSet实现更新Pod

很多时候我们需要对Pod进行更新,那么基于ReplicaSet的更新和扩缩容一样,有两种方式。一般更新Pod的时候都是修改了镜像,所以我们只需要修改ReplicaSet中Pod容器的镜像即可。

replicaset.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
        - name: php-redis
          image: ikubernetes/myqpp:v2

更新资源配置清单:

kubectl apply -f replicaset.yaml

此时ReplicaSet的Pod的镜像已经修改了,但是已经存在的Pod的镜像并不会自动修改,需要我们手动删除已经存在的Pod,然后Kubernetes根据ReplicaSet自动启动的新的镜像才会使用修改后的镜像:

kubectl delete pods frontend-mvjtg

如下图,新启动的Pod才会使用修改后的镜像:

生产环境如果升级,可以删除一个pod,观察一段时间之后没问题再删除另一个pod,但是这样需要人工干预多次;实际生产环境一般采用蓝绿发布,原来有一个rs1,再创建一个rs2(控制器),通过修改service标签,修改service可以匹配到rs2的控制器,这样才是蓝绿发布,这个也需要我们精心的部署规划,我们有一个控制器就是建立在rs之上完成的,叫做Deployment。

2、Deployment

2.1 什么是Deployment

Deployment官方文档:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

Deployment是kubernetes中最常用的资源对象,为ReplicaSet和Pod的创建提供了一种声明式的定义方法,在Deployment对象中描述一个期望的状态,Deployment控制器就会按照一定的控制速率把实际状态改成期望状态,通过定义一个Deployment控制器会创建一个新的ReplicaSet控制器,通过ReplicaSet创建pod,删除Deployment控制器,也会删除Deployment控制器下对应的ReplicaSet控制器和pod资源.。

使用Deployment而不直接创建ReplicaSet是因为Deployment对象拥有许多ReplicaSet没有的特性,例如滚动升级和回滚。

扩展:声明式定义是指直接修改资源清单yaml文件,然后通过kubectl apply -f 资源清单yaml文件,就可以更改资源。

Deployment控制器是建立在rs之上的一个控制器,可以管理多个rs,每次更新镜像版本,都会生成一个新的rs,把旧的rs替换掉,多个rs同时存在,但是只有一个rs运行。

rs v1控制三个pod,删除一个pod,在rs v2上重新建立一个,依次类推,直到全部都是由rs v2控制,如果rs v2有问题,还可以回滚,Deployment是建构在rs之上的,多个rs组成一个Deployment,但是只有一个rs处于活跃状态.

2.2 Deployment工作原理

Deployment可以使用声明式定义,直接在命令行通过纯命令的方式完成对应资源版本的内容的修改,也就是通过打补丁的方式进行修改;Deployment能提供滚动式自定义自控制的更新;对Deployment来讲,我们在实现更新时还可以实现控制更新节奏和更新逻辑。

更新节奏和更新逻辑???

比如说Deployment控制5个pod副本,pod的期望值是5个,但是升级的时候需要额外多几个pod,那我们控制器可以控制在5个pod副本之外还能再增加几个pod副本;比方说能多一个,但是不能少,那么升级的时候就是先增加一个,再删除一个,增加一个删除一个,始终保持pod副本数是5个;还有一种情况,最多允许多一个,最少允许少一个,也就是最多6个,最少4个,第一次加一个,删除两个,第二次加两个,删除两个,依次类推,可以自己控制更新方式,这种滚动更新需要加readinessProbe和livenessProbe探测,确保pod中容器里的应用都正常启动了才删除之前的pod

启动第一步,刚更新第一批就暂停了也可以;假如目标是5个,允许一个也不能少,允许最多可以10个,那一次加5个即可;这就是我们可以自己控制节奏来控制更新的方法。

通过Deployment对象,你可以轻松的做到以下事情:

  1. 创建ReplicaSet和Pod
  2. 滚动升级(不停止旧服务的状态下升级)和回滚应用(将应用回滚到之前的版本)
  3. 平滑地扩容和缩容
  4. 暂停和继续Deployment

2.3 Deployment资源清单文件

Deployment资源清单文件所需要的字段以及相关信息照样可以通过kubectl explain命令来查询帮助文档。

kubectl explain deployment
  • apiVersion:API版本信息,可以通过kubectl explain deployment.apiVersion查询到你当前安装的Kubernetes版本所对应Deployment的API版本。

  • kind:资源类型,可以通过kubectl explain deployment.kind查询到你当前Kubernetes版本对应Deployment的资源类型的详细信息(Deployment,注意大小写

  • metadata:元数据,包括资源的名字和名称空间

    • name:Deployment的名称
    • namespace:Deployment所在的名称空间
  • spec:定义容器相关配置信息

    • minReadySeconds:新创建的 pod 应准备好且其容器不会崩溃的最小秒数,以使其被视为可用(容器启动并且等待多少秒之后还正常才视为容器可以正常提供服务)。 默认为0(pod 准备就绪后将被视为可用)

    • paused:布尔类型,暂停,当我们更新的时候创建pod先暂停,不是立即更新

    • progressDeadlineSeconds:部署在被认为失败之前取得进展的最长时间(以秒为单位)。 部署控制器将继续处理失败的部署,并且带有 ProgressDeadlineExceeded 原因的条件将出现在部署状态中。 请注意,在部署暂停期间不会估计进度。 默认为 600 秒。

      k8s 在升级过程中有可能由于各种原因升级卡住(这个时候还没有明确的升级失败),比如在拉取被墙的镜像,权限不够等错误。那么这个时候就需要有个 deadline ,在 deadline 之内如果还卡着,那么就上报这个情况,这个时候这个 Deployment 状态就被标记为 False,并且注明原因。但是它并不会阻止 Deployment 继续进行卡住后面的操作。完全由用户进行控制。

    • replicas:设置Pod的副本数

    • revisionHistoryLimit:保留历史版本,默认保留10个历史版本

    • selector:标签选择器,选择该Deployment关联(管理)的Pod,必须字段

    • strategy:更新策略

      • rollingUpdate:滚动更新,定义滚动更新方式,也就是pod能多几个,少几个;它有两种取值方式,第一种直接给定数量,第二种根据百分比,百分比表示原本是5个,最多可以超出20%,那就允许多一个,最多可以超过40%,那就允许多两个
        • maxSurge:更新的过程当中最多允许超出的指定的目标副本数有几个
        • maxUnavailable:最多允许几个不可用;假设有5个副本,最多一个不可用,就表示最少有4个可用
      • type
        • RollingUpdate
        • Recreate
    • template:定义Pod模板信息,必须字段

      deployment.spec.template为Pod定义的模板,和Pod定义不太一样,template中不包含apiVersion和Kind属性,要求必须有metadata。deployment.spec.template.spec为容器的属性信息,其他定义内容和Pod一致。

      • metadata定义Pod容器模板的属性

        • name:模板的名称
        • namespace:所属的名称空间
      • spec

        • activeDeadlineSeconds:设置Pod可以运行的最长时间,到达设置的值之后,该Pod会自动停止。

        • affinity:亲和性配置

        • automountServiceAccountToken:自动挂载身份认证相关的服务Token

        • containers:定义容器属性

        • dnsConfig:设置容器的DNS

          • nameservers:配置DNS名称
          • options:DNS 解析器选项列表。 这将与从 DNSPolicy 生成的基本选项合并。 重复的条目将被删除。 Options 中给出的解析选项将覆盖基本 DNSPolicy 中出现的那些。
          • searches:用于主机名查找的 DNS 搜索域列表。 这将附加到从 DNSPolicy 生成的基本搜索路径中。 重复的搜索路径将被删除。

          示例如下:

          dnsConfig:
            nameservers:
              - "192.168.126.20"
              - "192.168.126.21"
            searches:
              - "my.dns.search.local"
              - "kubernetes.svc.cluster.local"
          

          会在容器的/etc/resolv.conf中查看到。

        • dnsPolicy:配置容器中内置的DNS策略

          • None 无任何策略:使用自定义的策略
          • Default 默认:使用宿主机的dns配置,/etc/resolv.conf
          • ClusterFirst 集群DNS优先,与 Default 相反,会预先使用 kube-dns (或 CoreDNS ) 的信息当预设置参数写入到该 Pod 内的DNS配置。
          • ClusterFirstWithHostNet 集群 DNS 优先,并伴随着使用宿主机网络:同时使用 hostNetwork 与 kube-dns 作为 Pod 预设 DNS 配置。
        • enableServiceLinks:是否应将有关服务的信息注入到 pod 的环境变量中,与 Docker 链接的语法相匹配。 可选:默认为true。

        • ephemeralContainers:在此 pod 中运行的临时容器列表。 临时容器可以在现有的 pod 中运行,以执行用户启动的操作,例如调试。 此列表在创建 pod 时不能指定,也不能通过更新 pod spec 来修改。 要将临时容器添加到现有 pod,请使用 pod 的 ephemeralcontainers 子资源。 此字段是 alpha 级别的,仅由启用 EphemeralContainers 功能的服务器支持。
          临时容器没有端口配置,因此像 ports,livenessProbe,readinessProbe 这样的字段是不允许的。

        • hostAliases:HostAliases 是一个可选的主机和 IP 列表,如果指定,它们将被注入到 pod 的 hosts 文件中。 这仅对非 hostNetwork pod 有效。示例如下:

          hostAliases:
            - ip: "192.168.126.10"
              hostnames:
                - "k8s-master01"
                - "k8s-master01.staryjie.com"
          

          会在容器的/etc/hosts文件中体现:

        • hostIPC:使用主机IPC,布尔类型,默认为false。

        • hostNetwork:为此 pod 请求的主机网络。 使用主机的网络命名空间。 如果设置了此选项,则必须指定将使用的端口。 默认为false。

        • hostPID:使用主机的 pid 命名空间。 可选:默认为 false。

        • hostname:指定 Pod 的主机名 如果未指定,则 Pod 的主机名将设置为系统定义的值。

        • imagePullSecrets:拉取镜像的仓库如果需要身份认证,那么就需要配置该选项。

        • initContainers:初始化容器配置

        • nodeName:指定Pod调度到某一个指定的节点

        • nodeSelector:将Pod调度到符合标签选择器的节点上运行

        • overhead:overhead是1.16引入的字段,在没有引入 Overhead 之前,只要一个节点的资源可用量大于等于 Pod 的 requests 时,这个 Pod 就可以被调度到这个节点上。引入 Overhead 之后,只有节点的资源可用量大于等于 Overhead 加上 requests 的和时才能被调度上来。

        • preemptionPolicy:PreemptionPolicy 是抢占优先级较低的 Pod 的策略。 从不,PreemptLowerPriority 之一。 如果未设置,则默认为 PreemptLowerPriority。

        • priority:优先值。 各种系统组件使用该字段来查找 pod 的优先级。 当启用优先准入控制器时,它会阻止用户设置此字段。 准入控制器从 PriorityClassName 填充此字段。 值越高,优先级越高。

        • priorityClassName:如果指定,则指示 pod 的优先级。 “system-node-critical”和“system-cluster-critical”是两个特殊关键字,表示最高优先级,前者为最高优先级。 任何其他名称都必须通过创建具有该名称的 PriorityClass 对象来定义。 如果未指定,则 pod 优先级将为默认值,如果没有默认值,则为零。

        • readinessGates:如果指定,将评估所有就绪门的 pod 就绪情况。 当所有容器都准备好并且准备就绪门中指定的所有条件都具有等于“True”的状态时,Pod 就准备好了

        • restartPolicy:Pod重启策略,健康探测失败或者其他异常情况导致Pod不是正常退出的情况下,Pod会根据该配置执行对应的重启策略。

          • Never
          • OnFailure
          • Aways
        • runtimeClassName:RuntimeClassName 是指 node.k8s.io 组中的一个 RuntimeClass 对象,应该使用它来运行这个 pod。 如果没有 RuntimeClass 资源与命名类匹配,则 pod 将不会运行。 如果未设置或为空,将使用“旧版”RuntimeClass,这是一个具有空定义的隐式类,使用默认运行时处理程序。

        • schedulerName:如果指定,则 pod 将由指定的调度程序调度。 如果未指定,则默认调度程序会分派 pod。

        • securityContext:SecurityContext安全上下文配置,包含 pod 级别的安全属性和常见的容器设置。 可选:默认为空。 每个字段的默认值见类型描述。

        • serviceAccount:DeprecatedServiceAccount 是 ServiceAccountName 的折旧别名。 已弃用:改用 serviceAccountName。

        • serviceAccountName:ServiceAccountName 是用于运行此 pod 的 ServiceAccount 的名称。

        • setHostnameAsFQDN:布尔类型,如果为 true,则 pod 的主机名将配置为 pod 的 FQDN,而不是叶名称(默认值)。 在 Linux 容器中,这意味着在内核的 hostname 字段(structutsname 的 nodename 字段)中设置 FQDN。 在 Windows 容器中,这意味着将注册表项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters的主机名的注册表值设置为 FQDN。 如果 pod 没有 FQDN,则这不起作用。 默认为假。

        • shareProcessNamespace:在 pod 中的所有容器之间共享单个进程命名空间。 设置后,容器将能够查看来自同一 pod 中其他容器的进程并发出信号,并且每个容器中的第一个进程不会被分配 PID 1。HostPID 和 ShareProcessNamespace 不能同时设置。 可选:默认为 false。

        • subdomain:如果指定,则完全限定的 Pod 主机名将是<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>。 如果未指定,则该 pod 将根本没有域名。

        • terminationGracePeriodSeconds:pod 需要优雅终止的可选持续时间(以秒为单位)。 可以在删除请求中减少。 值必须是非负整数。 零值表示立即删除。 如果此值为 nil,则将使用默认宽限期。 宽限期是 pod 中运行的进程收到终止信号后的持续时间(以秒为单位),以及进程被终止信号强制停止的时间。 将此值设置为比您的进程的预期清理时间更长。 默认为 30 秒。

        • tolerations:定义Pod的容忍度。

        • topologySpreadConstraints:定义一组 pod 应该如何跨拓扑域分布。 调度程序将以遵守约束的方式调度 pod。 所有 topologySpreadConstraints 都是 ANDed。

        • volumes:定义Pod挂载的存储卷列表。

  • status:状态信息,在启动Deployment之后自动生成,不能手动修改。

2.4 Deployment示例

Deployment是一个三级结构,deployment管理replicaset,replicaset管理pod。

2.4.1 通过Deployment部署一个web应用

创建一个Deployment的资源配置清单文件web-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-v1
  namespace: default
  labels:
    app: myapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
      version: v1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      name: myapp
      labels:
        app: myapp
        version: v1
    spec:
      containers:
        - name: myapp
          image: ikubernetes/myapp:v1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          readinessProbe:
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 10
            successThreshold: 1
            failureThreshold: 3
            httpGet:
              path: /
              port: 80
              scheme: HTTP
          livenessProbe:
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 10
            successThreshold: 1
            failureThreshold: 3
            httpGet:
              path: /
              port: 80
              scheme: HTTP
          resources:
            requests:
              cpu: "250m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"
      restartPolicy: Always

更新资源清单:

kubectl apply -f web-deploy.yaml

更新资源配置清单之后,创建了下图中的资源对象:

创建的控制器名字是myapp-v1

  1. NAME :列出名称空间中deployment的名称。
  2. READY:显示deployment有多少副本数。它遵循ready/desired的模式。
  3. UP-TO-DATE: 显示已更新到所需状态的副本数。
  4. AVAILABLE: 显示你的可以使用多少个应用程序副本。
  5. AGE :显示应用程序已运行的时间。

创建的Replicaset的名字是myapp-v1-加一个随机数(这个随机数字是我们引用pod的模板template的名字的hash值 ):

  1. NAME: 列出名称空间中ReplicaSet资源
  2. DESIRED:显示应用程序的所需副本数,这些副本数是在创建时定义的。这是所需的状态。
  3. CURRENT: 显示当前正在运行多少个副本。
  4. READY: 显示你的用户可以使用多少个应用程序副本。
  5. AGE :显示应用程序已运行的时间。

2.4.2 通过Deployment实现Pod扩缩容

2.4.2.1 Pod扩容

目前myapp-v1这个Deployment控制的Pod的副本数为2,实现扩容只需要将资源配置清单的replicas数量增加即可:

vim web-deploy.yaml
# 将replicas: 2 改为 replicas: 3
replicas: 3

然后直接更新资源配置清单:

kubectl apply -f web-deploy.yaml

然后观察Pods数量变化和ReplicaSet的信息变化:

kubectl get pods -o wide

此时Deployment已经调度了一个新的Pod启动:

kubectl get rs -o wide

此时可以发现存在两个ReplicaSet控制器,其中一个比较新的副本数已经变成修改后的数量,另一个变成了0:

查看Deployment信息:

kubectl describe deployments.apps myapp-v1

通过查看myapp-v1这个控制器的描述信息,我们就可以很清楚的看到Deployment扩容Pod时候的详细过程:

  1. 新建一个ReplicaSet,并将副本数设为1
  2. 新的ReplicaSet的Pod启动成功后,将旧的ReplicaSet的副本数减少1
  3. 新的ReplicaSet的副本数增加1
  4. 等待新ReplicaSet副本数达到预设值之后,旧的ReplicaSet副本数继续减少1
  5. 循环上述过程,直到旧的ReplicaSet副本数为0,新的ReplicaSet副本数达到预期值。
2.4.2.2 Pod缩容

Pod缩容与扩容相反,只需要降低replicas对应的数量即可,下面将replicas: 3修改为replicas: 2,然后更新资源清单。并观察对应的资源对象:

vim web-deploy.yaml
# 将replicas: 3 改为 replicas: 
replicas: 2

然后直接更新资源配置清单:

kubectl apply -f web-deploy.yaml

然后观察Pods数量变化和ReplicaSet的信息变化:

kubectl get pods -o wide

此时Deployment已经销毁了一个Pod:

myapp-v1-7b759df895-5krc8这个Pod已经被销毁。

kubectl get rs -o wide

缩容Pod的时候不会创建新的ReplicaSet,而是在原先的ReplicaSet上直接减少副本数:

查看Deployment信息:

kubectl describe deployments.apps myapp-v1

由Deployment的描述信息也可以看到,缩容的时候只是减少了当前ReplicaSet的副本数:

2.4.3 Deployment实现滚动更新

基于上一个资源配置清单,修改对应的镜像名称即可:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-v1
  namespace: default
  labels:
    app: myapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
      version: v1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      name: myapp
      labels:
        app: myapp
        version: v1
    spec:
      containers:
        - name: myapp
          image: ikubernetes/myapp:v2  # 修改了镜像版本
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          readinessProbe:
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 10
            successThreshold: 1
            failureThreshold: 3
            httpGet:
              path: /
              port: 80
              scheme: HTTP
          livenessProbe:
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 10
            successThreshold: 1
            failureThreshold: 3
            httpGet:
              path: /
              port: 80
              scheme: HTTP
          resources:
            requests:
              cpu: "250m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"
      restartPolicy: Always

更新资源配置清单:

kubectl apply -f web-deploy.yaml

然后通过下面的命令实时观察Pod状态:

kubectl get pods -l app=myapp -o wide -w

然后查看ReplicaSet信息:

kubectl get rs -o wide

此时又新建了一个ReplicaSet:

然后查看Deployment描述信息中的Events:

  1. 首先新建一个ReplicaSet,并将副本数设为1
  2. 等待新的ReplicaSet的副本数达标后,旧的ReplicaSet副本数减1
  3. 新的ReplicaSet副本数继续增加1
  4. 继续循环2.3步骤,直到旧的ReplicaSet副本数为0,新的ReplicaSet副本数达到设置的目标数

2.4.4 Deployment按照指定版本回滚

通过下面的命令查看Deployment的历史版本信息:

kubectl rollout history deployment myapp-v1

历史版本信息如下:

然后通过下面的命令实现回滚到指定版本:

kubectl rollout undo deployment myapp-v1 --to-revision=2

回滚之后,ReplicaSet也使用到了上一个版本中使用的ReplicaSet,Pod中容器的镜像也回到了上一版本的镜像:

提示

在更新资源配置清单的时候可以加上参数--record即可记录这次操作的信息到历史版本的CHANGE-CAUSE字段中。

2.5 自定义滚动更新策略

maxSurgemaxUnavailable用来控制滚动更新的更新策略。

2.5.1 取值范围

  1. maxUnavailable: [0, 副本数]
  2. maxSurge: [0, 副本数]

注意:两者不能同时为0。

2.5.2 百分比

  1. maxUnavailable: [0%, 100%] 向下取整,比如10个副本,5%的话==0.5个,但计算按照0个;
  2. maxSurge: [0%, 100%] 向上取整,比如10个副本,5%的话==0.5个,但计算按照1个;

注意:两者不能同时为0。

2.5.3 建议配置

  1. maxUnavailable: 0
  2. maxSurge: 1

2.5.4 Kubernetes默认策略

这是我们生产环境提供给用户的默认配置。即“一上一下,先上后下”最平滑原则:

1个新版本pod ready(结合readiness)后,才销毁旧版本pod。此配置适用场景是平滑更新、保证服务平稳,但也有缺点,就是太慢了。

2.5.4 小结

maxUnavailable:和期望的副本数比,不可用副本数最大比例(或最大值),这个值越小,越能保证服务稳定,更新越平滑

maxSurge:和期望的副本数比,超过期望副本数最大比例(或最大值),这个值调的越大,副本更新速度越快

自定义策略示例:

strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0
type: RollingUpdate

3、StatefulSet

StatefulSet是为了管理有状态服务的问题而设计的。

3.1 有状态服务

StatefulSet是有状态的集合,管理有状态的服务,它所管理的Pod的名称不能随意变化。数据持久化的目录也是不一样,每一个Pod都有自己独有的数据持久化存储目录。比如MySQL主从、redis集群等。

3.2 无状态服务

RC、Deployment、DaemonSet都是管理无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的。个体对整体无影响,所有pod都是共用一个数据卷的,部署的tomcat就是无状态的服务,tomcat被删除,在启动一个新的tomcat,加入到集群即可,跟tomcat的名字无关。

3.3 StatefulSet组成

StatefulSet由以下几个部分组成:

  1. Headless Service:用来定义pod网路标识,生成可解析的DNS记录
  2. volumeClaimTemplates:存储卷申请模板,创建pvc,指定pvc名称大小,自动创建pvc,且pvc由存储类供应。
  3. StatefulSet:管理pod的

3.3.1 什么是Headless service?

Headless service不分配clusterIP,headless service可以通过解析service的DNS,返回所有Pod的dns和ip地址 (statefulSet部署的Pod才有DNS),普通的service,只能通过解析service的DNS返回service的ClusterIP。

3.3.2 为什么要用Headless service?

在使用Deployment时,创建的Pod名称是没有顺序的,是随机字符串,在用statefulset管理pod时要求pod名称必须是有序的 ,每一个pod不能被随意取代,pod重建后pod名称还是一样的。因为pod IP是变化的,所以要用Pod名称来识别。pod名称是pod唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个Pod一个唯一的名称。

  1. headless service会为service分配一个域名:<service name>.$<namespace name>.svc.cluster.local

    K8s中资源的全局FQDN格式:
     Service_NAME.NameSpace_NAME.Domain.LTD.
     Domain.LTD.=svc.cluster.local.:这是默认k8s集群的域名。

    FQDN 全称 Fully Qualified Domain Name,即全限定域名,同时带有主机名和域名的名称。FQDN = Hostname + Domain Name,例如:主机名是k8s-master,域名是staryjie.com,那么FQDN就是k8s-master.staryjie.com

  2. StatefulSet会为关联的Pod保持一个不变的Pod Name,statefulset中Pod的名字格式为$(StatefulSet name)-$(pod序号)

  3. StatefulSet会为关联的Pod分配一个dnsName,$<Pod Name>.$<service name>.$<namespace name>.svc.cluster.local

3.3.3 为什么要使用volumeClaimTemplates?

对于有状态应用都会用到持久化存储,比如mysql主从,由于主从数据库的数据是不能存放在一个目录下的,每个mysql节点都需要有自己独立的存储空间。而在deployment中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,它们数据是同步的,而statefulset定义中的每一个pod都不能使用同一个存储卷,这就需要使用volumeClainTemplate,当在使用statefulset创建pod时,volumeClainTemplate会自动生成一个PVC,从而请求绑定一个PV,每一个pod都有自己专用的存储卷。Pod、PVC和PV对应的关系图如下:

3.4 StatefulSet资源配置清单

StatefulSet的资源配置清单字段和相关配置说明可以通过下面的命令查看:

kubectl explain statefulset
  • apiVersion:StatefulSet在当前版本的Kubernetes中的API组和版本信息,apps/v1,可以通过kubectl explain statefulset.apiVersion查询。
  • kind:资源类型,通过kubectl explain statefulset.kind查询当前Kubernetes版本中资源类型信息。
  • metadata:StatefulSet资源的元数据信息
    • labels:标签
    • name:StatefulSet资源类型的名称。
    • namespace:当前StatefulSet资源对象所在的名称空间
  • spec:StatefulSet资源对象的详细配置信息
    • podManagementPolicy:Pod管理策略,podManagementPolicy 控制在初始扩展期间、替换节点上的 pod 或缩减时如何创建 pod。 默认策略是OrderedReady,其中 pod 是按升序创建的(pod-0,然后是 pod-1 等),控制器将等到每个 pod 准备就绪后再继续。 缩小时,pod 会以相反的顺序移除。 替代策略是“并行”,它将并行创建 pod 以匹配所需的规模而无需等待,并且在缩减规模时将立即删除所有 pod。
    • replicas:副本数
    • revisionHistoryLimit:revisionHistoryLimit 是将在 StatefulSet 的修订历史中维护的最大修订数。 修订历史由当前应用的 StatefulSetSpec 版本未表示的所有修订组成。 默认值为 10。
    • selector:标签选择器
    • serviceName:必选参数,指定headless Service的名称,serviceName 是管理此 StatefulSet 的服务的名称该服务必须存在于 StatefulSet 之前,并负责该集合的网络标识。 Pod 获取遵循以下模式的 DNS/主机名:pod-specific-string.serviceName.default.svc.cluster.local其中pod-specific-string由 StatefulSet 控制器管理。
    • template:StatefulSet控制器生成Pod的模板信息
      • metadata:Pod模板的元数据
        • labels:标签
        • name:Pod的名称
        • namespace:Pod所属的名称空间
      • spec:Pod模板的详细配置
        • activeDeadlineSeconds:系统主动标记该Pod失败并终止前,Pod可能在节点上相对于startTime处于活动状态的可持续时间,单位是秒,必须是正整数。
        • affinity:定义Pod亲和性
        • containers:定义容器相关配置
        • ......
    • updateStrategy:更新策略
    • volumeClaimTemplates:存储卷申请模板
  • status:状态信息,Kubernetes生成StatefulSet资源对象后自动生成的,无法手动修改和设置。

通过上面可以看到,statefulset资源中有两个spec字段。第一个spec声明的是statefulset定义多少个Pod副本(默认将仅部署1个Pod)、匹配Pod标签的选择器、创建pod的模板、存储卷申请模板,第二个spec是spec.template.spec:主要用于Pod里的容器属性等配置。

.spec.template里的内容是声明Pod对象时要定义的各种属性,所以这部分也叫做PodTemplate(Pod模板)。还有一个值得注意的地方是:在.spec.selector中定义的标签选择器必须能够匹配到spec.template.metadata.labels里定义的Pod标签,否则Kubernetes将不允许创建statefulset。

3.5 通过StatefulSet创建应用

3.5.1 创建存储类

StorageClass-web.yaml

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: storageclass-web-nfs
  namespace: default
provisioner: example.com/nfs

更新资源清单文件:

kubectl apply -f StorageClass-web.yaml

执行结果如下:

3.5.2 创建StatefulSet资源配置清单

statefulset-web.yaml

apiVersion: v1
kind: Service
metadata:
  name: statefulset-web-service
  namespace: default
  labels:
    app: nginx
spec:
  ports:
    - port: 80
      name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  serviceName: "statefulset-web-service"
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
              name: web
          volumeMounts:
            - mountPath: /usr/share/nginx/html
              name: www
  volumeClaimTemplates:
    - metadata:
        name: www
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "storageclass-web-nfs"
        resources:
          requests:
            storage: 1Gi

更新资源清单文件:

kubectl apply -f statefulset-web.yaml

更新资源清单后,Kubernetes会自动创建如下的资源:

如上图所示:

  1. Pod的名称是有序的
  2. 会自动向StorageClass申请PV并自动绑定

3.5.3 查看Pod主机名

for i in 0 1;do kubectl exec web-$i -- sh -c 'hostname';done

执行结果如下:

3.5.3 验证StatefulSet中Service的DNS

# 进入一个Pod中的容器
kubectl exec -it web-1 -- /bin/bash

# 安装nslookpu
apt-get update
apt-get install -y dnsutils
3.5.3.1 查看StatefulSet中Pod的DNS解析
nslookup web-1.statefulset-web-service.default.svc.cluster.local

StatefulSet创建的Pod也是有DNS记录的:

解析的结果是Pod的IP地址。

3.5.3.2 查询StatefulSet中Service的DNS
nslookup statefulset-web-service.default.svc.cluster.local

查询StatefulSet中Service会把对应的Pod的IP和对应的FQDN解析出来:

通过dig命令解析Service域名的A记录:

dig -t A nslookup statefulset-web-service.default.svc.cluster.local @10.10.0.10

解析结果如下:

dig的使用:

  • @指定域名服务器
  • -t指定解析类型, A表示解析A记录

3.6 StatefulSet实现应用扩容、缩容和更新

3.6.1 StatefulSet实现Pod动态扩容个

实现StatefulSet控制器管理的Pod的扩容只需要修改资源清单文件中的replicas的值即可,可以通过下面两种方式:

  • 直接修改资源清单文件
  • kubectl edit命令行修改
  • kubectl scale sts进行动态扩容,例如:kubectl scale sts web -n default --replicas=3

下面演示将replicas: 2修改为replicas: 3

然后更新资源清单文件:

kubectl apply -f statefulset-web.yaml

如下图,StatefulSet控制的Pod实现了扩容,并且扩容的Pod名称也是根据原先的序号递增的:

当然也可以通过如下命令进行扩容操作

3.6.2 StatefulSet实现Pod动态缩容

缩容与扩容一样操作,只是需要降低replicas的值即可。

比如将replicas: 3改为replicas: 2,:

kubectl scale sts web -n default --replicas=2

然后观察对应的Pod变化:

由上图可知,在StatefulSet控制的Pod缩容的时候会从序号较大的Pod开始缩容。

3.6.3 StatefulSet实现Pod更新

StatefulSet实现Pod的更新,可以通过修改镜像版本来实现,下面将image修改为如下:

apiVersion: v1
kind: Service
metadata:
  name: statefulset-web-service
  namespace: default
  labels:
    app: nginx
spec:
  ports:
    - port: 80
      name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  serviceName: "statefulset-web-service"
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: ikubernetes/myapp:v2   # 修改为这个镜像
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
              name: web
          volumeMounts:
            - mountPath: /usr/share/nginx/html
              name: www
  volumeClaimTemplates:
    - metadata:
        name: www
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "storageclass-web-nfs"
        resources:
          requests:
            storage: 1Gi

然后更新资源清单文件:

kubectl apply -f statefulset-web.yaml

更新资源清单文件后,StatefulSet控制的Pod会自动更新Pod,结果如下:

4、DaemonSet

4.1 DaemonSet概念

4.1.1 DaemonSet概述

DaemonSet控制器能够确保k8s集群所有的节点都运行一个相同的pod副本,当向k8s集群中增加node节点时,这个node节点也会自动创建一个pod副本,当node节点从集群移除,这些pod也会自动删除;删除Daemonset也会删除它们创建的pod。

4.1.2 DaemonSet工作原理

DaemonSet的控制器会监听kuberntes的daemonset对象、pod对象、node对象,这些被监听的对象之变动,就会触发syncLoop循环让kubernetes集群朝着daemonset对象描述的状态进行演进。

4.1.3 DaemonSet的应用场景

  • 在集群的每个节点上运行存储,比如:glusterd 或 ceph。
  • 在每个节点上运行日志收集组件,比如:flunentd 、 logstash、filebeat等。
  • 在每个节点上运行监控组件,比如:Prometheus、 Node Exporter 、collectd等。

4.1.4 DaemonSet与Deployment的区别

  • Deployment 部署的副本 Pod 会分布在各个 Node 上,每个 Node 都可能运行好几个副本。
  • DaemonSet部署的Pod在每个Node上最多只能运行一个副本。

4.2 DaemonSet资源配置清单

DaemonSet对象的资源配置清单可以通过下面的命令来查询对应的字段和配置信息:

kubectl explain daemonset
  • apiVersion:DaemonSet资源对象在当前Kubernetes版本中的API群组和版本信息,可以通过kubectl explain daemonset.apiVersion查看。
  • kind:资源类型,DaemonSet,可以通过kubectl explain daemonset.kind命令查询。
  • metadata:DaemonSet资源对象的元数据信息
    • labels:标签
    • name:DaemonSet对象的名称
    • namespace:DaemonSet对象所在的名称空间
  • spec:DaemonSet资源对象的详细配置信息
    • minReadySeconds:当新的pod启动几秒种后,再kill掉旧的Pod。
    • revisionHistoryLimit:保留的历史版本数量,默认为10。
    • selector:标签选择器,用于匹配对应的Pod。
    • template:定义Pod的模板
      • metadata:Pod的元数据信息
        • labels:标签
        • name:Pod的名称
        • namespace:Pod对象所在的名称空间
      • spec:Pod的详细配置信息
        • affinity:亲和性配置
        • containers:容器相关配置
        • ......
    • updateStrategy:DaemonSet管理的Pod的升级策略。
  • status:Kubernetes启动DaemonSet对象之后自动生成的,无法手动设置或者修改。

4.3 通过DaemonSet部署应用

通过DaemonSet部署日志收集组件fluentd。

daemonset-fluentd.yaml

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:
      tolerations:  # 定义容忍度
        - key: node-role.kubernetes.io/master
          effect: NoSchedule
      containers:  # 定义容器
        - name: fluentd-elasticsearch
          image: staryjie/fluentd:v2.5.1
          imagePullPolicy: IfNotPresent
          resources:  # 定义资源配额
            limits:
              memory: 200Mi
            requests:
              cpu: 100m
              memory: 200Mi
          volumeMounts:
            - mountPath: /var/log
              name: varlog
            - mountPath: /var/lib/docker/containers
              name: varlibdockercontainers
              readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: varlibdockercontainers
          hostPath:
            path: /var/lib/docker/containers

更新资源清单文件:

kubectl apply -f daemonset-fluentd.yaml

更新后,查看对应的Pod是否成功创建:

由上图可知:DaemonSet控制的Pod名称是由控制器的名称加随机数组成的。、

4.4 DaemonSet实现Pod滚动更新

4.4.1 查看DaemonSet的滚动更新策略

kubectl explain daemonset.spec.updateStrategy

如下图,DaemonSet管理的Pod支持RollingUpdateOnDelete两种更新策略:

查看RollingUpdate支持的字段及配置:

kubectl explain daemonset.spec.updateStrategy.rollingUpdate

如下图所示,RollingUpdate更新策略只支持maxUnavailable,先删除再更新:

DaemonSet管理的Pod默认的更新策略就是RollingUpdate滚动更新。

4.4.2 DaemonSet实现滚动更新

要实现Pod的更新,只需要修改自愿清单文件中Pod模板中的image版本即可。

也可以通过如下命令实现命令行更新:

kubectl set image daemonsets flutend-elasticsearch flutend-elasticsearch=ikubernetes/filebeat:5.6.6-alpine -n kube-system
posted @ 2022-05-17 22:36  StaryJie  阅读(120)  评论(0编辑  收藏  举报