四、深入理解kubernetes

4.1、pod

Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。具有如下的特性:

  • Pod (就像在鲸鱼荚或者豌豆荚中)是一组(一个或多个) 容器
  • Pod 中的容器共享存储、网络等等资源
  • Pod 还可以包含在 Pod 启动期间运行的 Init 容器,来进行一些初始化操作
  • Pod 的建模的是特定于应用的“逻辑主机”,解决了容器的亲和性

Kubernetes 集群中的 Pod 主要有两种用法:

  • 运行单个容器的 Pod。”每个 Pod 一个容器”模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。
  • 运行多个协同工作的容器的 Pod。 Pod 可能封装由多个紧密耦合且需要共享资源的共处容器组成的应用程序。 这些位于同一位置的容器可能形成单个内聚的服务单元 —— 一个容器将文件从共享卷提供给公众, 而另一个单独的“边车”(sidecar)容器则刷新或更新这些文件。 Pod 将这些容器和存储资源打包为一个可管理的实体。

而我们在日常使用中,是不需要单独创建Pod的,通常是通过Deployment、StatfulSet、Job等控制器来创建并管理Pod。
这里先简单介绍一下如何创建一个pod,下面是Pod的yaml

  1. apiVersion: v1 # kubernetes 中的API资源
  2. kind: Pod # 创建的资源类型
  3. metadata:
  4. name: Demo
  5. spec:
  6. volumes: # 数据卷
  7. - name: varlog
  8. hostPath:
  9. path: /var/log/counter
  10. containers: # 容器
  11. - name: count
  12. image: busybox
  13. args: # 容器内执行的命令,可以覆盖dockerfile中的CMD和ENTRYPOINT
  14. - /bin/sh
  15. - -c
  16. - >
  17. i=0;
  18. while true;
  19. do
  20. echo "$i: $(date)" >> /var/log/1.log;
  21. i=$((i+1));
  22. sleep 1;
  23. done
  24. volumeMounts:
  25. - name: varlog
  26. mountPath: /var/log
  27. - name: count-log
  28. image: busybox
  29. args: [/bin/sh, -c, 'tail -n+1 -f /opt/log/1.log']
  30. volumeMounts:
  31. - name: varlog
  32. mountPath: /opt/log

我们可以通过执行 kubectl apply -f demo.yaml 创建上述pod

Pod的生命周期

Pod的生命周期如下

取值 描述
Pending(悬决) Pod 已被 Kubernetes 系统接受,但有一个或者多个容器尚未创建亦未运行。此阶段包括等待 Pod 被调度的时间和通过网络下载镜像的时间。
Running(运行中) Pod 已经绑定到了某个节点,Pod 中所有的容器都已被创建。至少有一个容器仍在运行,或者正处于启动或重启状态。
Succeeded(成功) Pod 中的所有容器都已成功终止,并且不会再重启。
Failed(失败) Pod 中的所有容器都已终止,并且至少有一个容器是因为失败终止。也就是说,容器以非 0 状态退出或者被系统终止。
Unknown(未知) 因为某些原因无法取得 Pod 的状态。这种情况通常是因为与 Pod 所在主机通信失败。

Pod 遵循一个预定义的生命周期,起始于 Pending 阶段,如果至少 其中有一个主要容器正常启动,则进入 Running,之后取决于 Pod 中是否有容器以 失败状态结束而进入 Succeeded 或者 Failed 阶段。

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
    - name: lifecycle-demo-container
      image: nginx
      lifecycle:
        postStart: # 这个回调在容器被创建之后立即被执行。 但是,不能保证回调会在容器入口点(ENTRYPOINT)之前执行。 没有参数传递给处理程序。
          exec:
            command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
        preStop: # 在容器因 API 请求或者管理事件(诸如存活态探针、启动探针失败、资源抢占、资源竞争等) 而被终止之前,此回调会被调用。 如果容器已经处于已终止或者已完成状态,则对 preStop 回调的调用将失败。 在用来停止容器的 TERM 信号被发出之前,回调必须执行结束。 Pod 的终止宽限周期在 PreStop 回调被执行之前即开始计数,所以无论 回调函数的执行结果如何,容器最终都会在 Pod 的终止宽限期内被终止。 没有参数会被传递给处理程序。
          exec:
            command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
      livenessProbe:
        tcpSocket:
          port: 80
        initialDelaySeconds: 5  # 容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。
        periodSeconds: 10 # 执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
        timeoutSeconds: 5 # 探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
        successThreshold: 1 # 探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
        failureThreshold: 3 # 当探测失败时,Kubernetes 的重试次数。 存活探测情况下的放弃就意味着重新启动容器。 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
      readinessProbe:
        tcpSocket:
          port: 80
        initialDelaySeconds: 15
        periodSeconds: 20
      startupProbe:
        tcpSocket:
          port: 80
        initialDelaySeconds: 15
        periodSeconds: 10

init 容器

init容器是一种特殊容器,在 Pod 内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本,可以使用initContainers字段来指定。
init 容器的特点如下:

  • 它们总是运行到完成
  • 每个都必须在下一个启动之前成功完成
  • Pod 对应的 restartPolicy 值为 “Never”,并且 Pod 的 Init 容器失败, 则 Kubernetes 会将整个 Pod 状态设置为失败
  • Init 容器不支持 lifecycle、livenessProbe、readinessProbe 和 startupProbe, 因为它们必须在 Pod 就绪之前运行完成
  • 如果为一个 Pod 指定了多个 Init 容器,这些容器会按顺序逐个运行。 每个 Init 容器必须运行成功,下一个才能够运行
  • 如果 Pod 重启,所有 Init 容器必须重新执行

下面是一个init容器的模板

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  restartPolicy: Always # pod重启策略
  initContainers:
    - name: mkdihttpdir
      image: busybox
      args:
        - /bin/sh
        - -c
        - mkdir /var/html
  containers:
    - name: lifecycle-demo-container
      image: nginx
      lifecycle:
        postStart:
          exec:
            command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
        preStop:
          exec:
            command: ["/bin/sh","-c","nginx -s quit; while killall -0 nginx; do sleep 1; done"]
      livenessProbe:
        tcpSocket:
          port: 80
        initialDelaySeconds: 5  # 容器启动后要等待多少秒后存活和就绪探测器才被初始化,默认是 0 秒,最小值是 0。
        periodSeconds: 10 # 执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。
        timeoutSeconds: 5 # 探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
        successThreshold: 1 # 探测器在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
        failureThreshold: 3 # 当探测失败时,Kubernetes 的重试次数。 存活探测情况下的放弃就意味着重新启动容器。 就绪探测情况下的放弃 Pod 会被打上未就绪的标签。默认值是 3。最小值是 1。
      readinessProbe:
        tcpSocket:
          port: 80
        initialDelaySeconds: 15
        periodSeconds: 20
      startupProbe:
        tcpSocket:
          port: 80
        initialDelaySeconds: 15
        periodSeconds: 10

pod拓朴约束分布

拓扑分布约束(Topology Spread Constraints) 是用来控制 Pods 在集群内故障域 之间的分布,例如区域(Region)、可用区(Zone)、节点和其他用户自定义拓扑域。 这样做有助于实现高可用并提升资源利用率。使用拓扑分布约束的先决条件为节点标签——拓扑分布约束依赖于节点标签来标识每个节点所在的拓扑域。
下面是拓扑分布约束的yaml模板

apiVersion: v1
kind: Pod
metadata:
  name: topology-demo
  labels:
    app: front
spec:
  topologySpreadConstraints:
    - maxSkew: 2
      topologyKey: app
      whenUnsatisfiable: ScheduleAnyway
      labelSelector:
        matchLabels:
          app: front
  initContainers:
    - name: mkdihttpdir
      image: busybox
      args:
        - /bin/sh
        - -c
        - mkdir /var/html
  containers:
    - name: lifecycle-demo-container
      image: nginx

你可以定义一个或多个 topologySpreadConstraint 来指示 kube-scheduler 如何根据与现有的 Pod 的关联关系将每个传入的 Pod 部署到集群中。字段包括:

  • maxSkew 描述 Pod 分布不均的程度。这是给定拓扑类型中任意两个拓扑域中 匹配的 pod 之间的最大允许差值。它必须大于零。取决于 whenUnsatisfiable 的 取值,其语义会有不同。
    • 当 whenUnsatisfiable 等于 “DoNotSchedule” 时,maxSkew 是目标拓扑域 中匹配的 Pod 数与全局最小值之间可存在的差异。
    • 当 whenUnsatisfiable 等于 “ScheduleAnyway” 时,调度器会更为偏向能够降低 偏差值的拓扑域。
  • topologyKey 是节点标签的键。如果两个节点使用此键标记并且具有相同的标签值, 则调度器会将这两个节点视为处于同一拓扑域中。调度器试图在每个拓扑域中放置数量 均衡的 Pod。上述例子中意味着均匀分布将只应用于存在标签键值对为“app:的节点
  • whenUnsatisfiable 指示如果 Pod 不满足分布约束时如何处理:
    • DoNotSchedule(默认)告诉调度器不要调度。
    • ScheduleAnyway 告诉调度器仍然继续调度,只是根据如何能将偏差最小化来对 节点进行排序。
  • labelSelector 用于查找匹配的 pod。匹配此标签的 Pod 将被统计,以确定相应 拓扑域中 Pod 的数量。

但是存在一定的局限性:

  • Deployment 缩容操作可能导致 Pod 分布不平衡
  • 具有污点的节点上的 Pods 也会被统计。 参考 Issue 80921

    亲和性

    亲和性包括node亲和性和pod亲和性,下面是亲和性的demo ```yaml apiVersion: v1 kind: Pod metadata: name: affinity-demo labels: app: front spec: affinity: nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: app
              operator: Exists
              values:
                - front
    
    podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: app
        labelSelector:
          matchLabels:
            app: front
    
    podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - topologyKey: app
        labelSelector:
          matchLabels:
            app: backend
    
    initContainers:
    • name: mkdihttpdir image: busybox args:
      • /bin/sh
      • -c
      • mkdir /var/html containers:
    • name: lifecycle-demo-container image: nginx
节点亲和性可以根据节点上的标签来约束 Pod 可以调度到哪些节点,目前有两种类型的节点亲和性:

- requiredDuringSchedulingIgnoredDuringExecution:将 Pod 调度到一个节点上 _必须_满足的规则
- preferredDuringSchedulingIgnoredDuringExecution:调度器将尝试执行但不能保证的_偏好_

Pod 间亲和性与反亲和性使你可以 _基于已经在节点上运行的 Pod 的标签_ 来约束 Pod 可以调度到的节点,而不是基于节点上的标签,目前有两种类型的pod亲和性和反亲和性

- requiredDuringSchedulingIgnoredDuringExecution:亲和性的一个示例是 “将服务 A 和服务 B 的 Pod 放置在同一区域
- preferredDuringSchedulingIgnoredDuringExecution:反亲和性的示例将是 “将此服务的 pod 跨区域分布”(硬性要求是说不通的,因为你可能拥有的 Pod 数多于区域数)。

总的来说,在亲和性里面 requiredDuringSchedulingIgnoredDuringExecution 是硬需求,也就是必须满足的需求,如果不满足则会进入Pending状态,而preferredDuringSchedulingIgnoredDuringExecution为软需求,如不满足也可以进行调度。
<a name="cP4oR"></a>
### 4.2、deployment
Deployment 是kubernetes用于管理无状态服务的控制器,实际上deployment控制的是ReplicaSet 控制器,如图所示<br />![](https://cdn.nlark.com/yuque/0/2022/png/8376031/1648478798086-5f17f610-1b8f-4a7e-8f1b-4b6950b859b8.png#crop=0&crop=0&crop=1&crop=1&id=So6k1&originHeight=415&originWidth=1709&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
<a name="EZPj9"></a>
#### 创建deployment
下面是一个deployment的yaml模板
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment  # Deployment的名称
  labels:
    app: nginx
spec:
  replicas: 3 # 副本数
  strategy: # 更新策略
    rollingUpdate:  # 滚动更新策略
      maxSurge: 25% # 更新数量允许超出的pod数量
      maxUnavailable: 25% # 更新时最大不可用pod数
    type: RollingUpdate
  selector: # Deployment 需要管理的POD
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.14.2
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: "1000m"
              memory: "500Mi"
            limits:
              cpu: "2000m"
              memory: "1000Mi"
          readinessProbe:
            tcpSocket:
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 3
          livenessProbe:
            tcpSocket:
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10
            timeoutSeconds: 5
            successThreshold: 1
            failureThreshold: 3
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - topologyKey: app
              labelSelector:
                matchLabels:
                  app: nginx

下面可以执行deployment-demo01.yaml

[root@k8s01 test]# kubectl apply -f deployment-demo01.yaml
# kubectl get pod 查看pod创建结果 
[root@k8s01 test]# kubectl  get pod  --show-labels
NAME                                READY   STATUS    RESTARTS   AGE     LABELS
lifecycle-demo                      1/1     Running   0          27h     <none>
nginx-deployment-6966d7fc95-65v55   1/1     Running   0          2m41s   app=nginx,pod-template-hash=6966d7fc95
nginx-deployment-6966d7fc95-plkvv   1/1     Running   0          2m41s   app=nginx,pod-template-hash=6966d7fc95
nginx-deployment-6966d7fc95-z6ckn   1/1     Running   0          2m41s   app=nginx,pod-template-hash=6966d7fc95
# 查看deployment创建结果
[root@k8s01 test]# kubectl  get deployment  --show-labels
NAME               READY   UP-TO-DATE   AVAILABLE   AGE    LABELS
nginx-deployment   3/3     3            3           3m9s   app=nginx
# 查看deployment管理的replicaset
[root@k8s01 test]# kubectl  get replicaset  --show-labels
NAME                          DESIRED   CURRENT   READY   AGE     LABELS
nginx-deployment-6966d7fc95   3         3         3       3m30s   app=nginx,pod-template-hash=6966d7fc95

Deployment 控制器将 pod-template-hash 标签添加到 Deployment 所创建或收留的 每个 ReplicaSet 。
此标签可确保 Deployment 的子 ReplicaSets 不重叠。 标签是通过对 ReplicaSet 的 PodTemplate 进行哈希处理。 所生成的哈希值被添加到 ReplicaSet 选择算符、Pod 模板标签,并存在于在 ReplicaSet 可能拥有的任何现有 Pod 中。

更新deployment

下面我们更新一下nginx的镜像

[root@k8s01 test]# kubectl --record deployment.apps/nginx-deployment set image \
>    deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/nginx-deployment image updated
deployment.apps/nginx-deployment image updated

我们可以通过kubecctl describe deployment nginx-deployment 来追踪一下deployment的更新过程

[root@k8s01 test]# kubectl  describe deployment nginx-deployment
Name:                   nginx-deployment
Namespace:              default
CreationTimestamp:      Mon, 28 Mar 2022 22:48:02 +0800
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 2
                        kubernetes.io/change-cause:
                          kubectl deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1 --record=true
Selector:               app=nginx
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:1.16.1
    Port:         80/TCP
    Host Port:    0/TCP
    Liveness:     tcp-socket :80 delay=5s timeout=5s period=10s #success=1 #failure=3
    Readiness:    tcp-socket :80 delay=5s timeout=5s period=10s #success=1 #failure=3
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-75b888f478 (3/3 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  12m    deployment-controller  Scaled up replica set nginx-deployment-6966d7fc95 to 3
  Normal  ScalingReplicaSet  4m42s  deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 1
  Normal  ScalingReplicaSet  3m2s   deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 2
  Normal  ScalingReplicaSet  3m2s   deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 2
  Normal  ScalingReplicaSet  72s    deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 1
  Normal  ScalingReplicaSet  72s    deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 3
  Normal  ScalingReplicaSet  62s    deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 0

可以看到,当第一次创建 Deployment 时,它创建了一个 ReplicaSet(nginx-deployment-6966d7fc95) 并将其直接扩容至 3 个副本。更新 Deployment 时,它创建了一个新的 ReplicaSet (nginx-deployment-75b888f478),并将其扩容为 1,然后将旧 ReplicaSet 缩容到 2, 以便至少有 2 个 Pod 可用且最多创建 4 个 Pod。 然后,它使用相同的滚动更新策略继续对新的 ReplicaSet 扩容并对旧的 ReplicaSet 缩容。 最后,你将有 3 个可用的副本在新的 ReplicaSet 中,旧 ReplicaSet 将缩容到 0。

[root@k8s01 test]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-6966d7fc95   0         0         0       23m
nginx-deployment-75b888f478   3         3         3       15m

回滚deployment

默认情况下,Deployment 的所有上线记录都保留在系统中,以便可以随时回滚 (你可以通过修改修订历史记录限制来更改这一约束)。我们可以通过下述命令查看deployment信息

REVISION  CHANGE-CAUSE
1         <none>
2         kubectl deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1 --record=true
[root@k8s01 test]# kubectl rollout history deployment.v1.apps/nginx-deployment --revision=1
deployment.apps/nginx-deployment with revision #1
Pod Template:
  Labels:       app=nginx
        pod-template-hash=6966d7fc95
  Containers:
   nginx:
    Image:      nginx:1.14.2
    Port:       80/TCP
    Host Port:  0/TCP
    Liveness:   tcp-socket :80 delay=5s timeout=5s period=10s #success=1 #failure=3
    Readiness:  tcp-socket :80 delay=5s timeout=5s period=10s #success=1 #failure=3
    Environment:        <none>
    Mounts:     <none>
  Volumes:      <none>

可以通过如下命令进行回退

[root@k8s01 test]# kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=1
deployment.apps/nginx-deployment rolled back
[root@k8s01 test]# kubectl  describe deployment nginx-deployment
Name:                   nginx-deployment
Namespace:              default
CreationTimestamp:      Mon, 28 Mar 2022 22:48:02 +0800
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 3
Selector:               app=nginx
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:1.14.2
    Port:         80/TCP
    Host Port:    0/TCP
    Liveness:     tcp-socket :80 delay=5s timeout=5s period=10s #success=1 #failure=3
    Readiness:    tcp-socket :80 delay=5s timeout=5s period=10s #success=1 #failure=3
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-6966d7fc95 (3/3 replicas created)
Events:
  Type    Reason             Age                From                   Message
  ----    ------             ----               ----                   -------
  Normal  ScalingReplicaSet  20m                deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 1
  Normal  ScalingReplicaSet  19m                deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 2
  Normal  ScalingReplicaSet  19m                deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 2
  Normal  ScalingReplicaSet  17m                deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 1
  Normal  ScalingReplicaSet  17m                deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 3
  Normal  ScalingReplicaSet  17m                deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 0
  Normal  ScalingReplicaSet  36s                deployment-controller  Scaled up replica set nginx-deployment-6966d7fc95 to 1
  Normal  ScalingReplicaSet  26s                deployment-controller  Scaled down replica set nginx-deployment-75b888f478 to 2
  Normal  ScalingReplicaSet  26s                deployment-controller  Scaled up replica set nginx-deployment-6966d7fc95 to 2
  Normal  ScalingReplicaSet  16s (x2 over 28m)  deployment-controller  Scaled up replica set nginx-deployment-6966d7fc95 to 3
  Normal  ScalingReplicaSet  16s                deployment-controller  Scaled down replica set nginx-deployment-75b888f478 to 1
  Normal  ScalingReplicaSet  6s                 deployment-controller  Scaled down replica set nginx-deployment-75b888f478 to 0
  [root@k8s01 test]# kubectl  get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-6966d7fc95   3         3         3       29m
nginx-deployment-75b888f478   0         0         0       21m
[root@k8s01 test]# kubectl rollout history deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment 
REVISION  CHANGE-CAUSE
2         kubectl deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1 --record=true
3         <none>

可以看到deployment已经回退,并且生成了一个新的版本3

deployment扩缩容

我们在创建deployment是设置了replicas为3,可以通过如下命令进行扩缩容


[root@k8s01 test]# kubectl scale deployment.v1.apps/nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled
[root@k8s01 test]# kubectl  get pod 
NAME                                READY   STATUS    RESTARTS   AGE
lifecycle-demo                      1/1     Running   0          28h
nginx-deployment-6966d7fc95-b2ztk   1/1     Running   0          3m13s
nginx-deployment-6966d7fc95-bjlsf   1/1     Running   0          17s
nginx-deployment-6966d7fc95-cb56s   1/1     Running   0          2m53s
nginx-deployment-6966d7fc95-dkdv6   1/1     Running   0          17s
nginx-deployment-6966d7fc95-kmbxc   1/1     Running   0          3m3s
[root@k8s01 test]# kubectl  describe deployment nginx-deployment 
Name:                   nginx-deployment
Namespace:              default
CreationTimestamp:      Mon, 28 Mar 2022 22:48:02 +0800
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 3
Selector:               app=nginx
Replicas:               5 desired | 5 updated | 5 total | 5 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:1.14.2
    Port:         80/TCP
    Host Port:    0/TCP
    Liveness:     tcp-socket :80 delay=5s timeout=5s period=10s #success=1 #failure=3
    Readiness:    tcp-socket :80 delay=5s timeout=5s period=10s #success=1 #failure=3
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Progressing    True    NewReplicaSetAvailable
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-6966d7fc95 (5/5 replicas created)
Events:
  Type    Reason             Age                  From                   Message
  ----    ------             ----                 ----                   -------
  Normal  ScalingReplicaSet  23m                  deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 1
  Normal  ScalingReplicaSet  22m                  deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 2
  Normal  ScalingReplicaSet  22m                  deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 2
  Normal  ScalingReplicaSet  20m                  deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 1
  Normal  ScalingReplicaSet  20m                  deployment-controller  Scaled up replica set nginx-deployment-75b888f478 to 3
  Normal  ScalingReplicaSet  20m                  deployment-controller  Scaled down replica set nginx-deployment-6966d7fc95 to 0
  Normal  ScalingReplicaSet  3m34s                deployment-controller  Scaled up replica set nginx-deployment-6966d7fc95 to 1
  Normal  ScalingReplicaSet  3m24s                deployment-controller  Scaled up replica set nginx-deployment-6966d7fc95 to 2
  Normal  ScalingReplicaSet  3m24s                deployment-controller  Scaled down replica set nginx-deployment-75b888f478 to 2
  Normal  ScalingReplicaSet  3m14s (x2 over 31m)  deployment-controller  Scaled up replica set nginx-deployment-6966d7fc95 to 3
  Normal  ScalingReplicaSet  3m14s                deployment-controller  Scaled down replica set nginx-deployment-75b888f478 to 1
  Normal  ScalingReplicaSet  3m4s                 deployment-controller  Scaled down replica set nginx-deployment-75b888f478 to 0
  Normal  ScalingReplicaSet  38s                  deployment-controller  Scaled up replica set nginx-deployment-6966d7fc95 to 5

暂停、恢复deployment

你可以在触发一个或多个更新之前暂停 Deployment,然后再恢复其执行。 这样做使得你能够在暂停和恢复执行之间应用多个修补程序,而不会触发不必要的上线操作。
通过如下命令暂停deployment

[root@k8s01 test]# kubectl rollout pause deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment paused

接下来更新 Deployment 镜像

[root@k8s01 test]# kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
deployment.apps/nginx-deployment image updated

观察一下有没有新的上线被触发

[root@k8s01 test]# kubectl rollout history deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment 
REVISION  CHANGE-CAUSE
3         <none>
4         kubectl deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1 --record=true

[root@k8s01 test]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-6966d7fc95   5         5         5       35m
nginx-deployment-75b888f478   0         0         0       27m

对新的replica进行修改

[root@k8s01 test]# kubectl set resources deployment.v1.apps/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
deployment.apps/nginx-deployment resource requirements updated

恢复 Deployment 执行并观察新的 ReplicaSet 的创建过程

[root@k8s01 test]# kubectl rollout resume deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment resumed
[root@k8s01 test]# kubectl get rs -w
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-6799b488f4   3         3         0       8s
nginx-deployment-6966d7fc95   4         4         4       35m
nginx-deployment-75b888f478   0         0         0       28m
nginx-deployment-6799b488f4   3         3         1       10s
nginx-deployment-6966d7fc95   3         4         4       35m
nginx-deployment-6799b488f4   3         3         2       10s
nginx-deployment-6966d7fc95   3         4         4       35m
nginx-deployment-6799b488f4   4         3         2       10s
nginx-deployment-6799b488f4   4         3         2       10s
nginx-deployment-6966d7fc95   3         3         3       35m
nginx-deployment-6966d7fc95   2         3         3       35m
nginx-deployment-6799b488f4   4         4         2       10s
nginx-deployment-6799b488f4   4         4         3       10s
nginx-deployment-6799b488f4   5         4         3       10s
nginx-deployment-6966d7fc95   2         3         3       35m
nginx-deployment-6966d7fc95   2         2         2       35m
nginx-deployment-6799b488f4   5         4         3       10s
nginx-deployment-6966d7fc95   1         2         2       35m
nginx-deployment-6799b488f4   5         5         3       10s
nginx-deployment-6966d7fc95   1         2         2       35m
nginx-deployment-6966d7fc95   1         1         1       35m
^C[root@k8s01 test]# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-6799b488f4   5         5         5       23s
nginx-deployment-6966d7fc95   0         0         0       36m
nginx-deployment-75b888f478   0         0         0       28m

注意:你不可以回滚处于暂停状态的 Deployment,除非先恢复其执行状态

deployment状态

deployment的状态有如下三种

  • progressing:创建新的 ReplicaSet;为最新的 ReplicaSet 扩容;为旧有的 ReplicaSet(s) 缩容;新的 Pods 已经就绪或者可用
  • complete:所有副本都已更新到指定的最新版本;未运行 Deployment 的旧副本
  • failed:配额(Quota)不足;就绪探测(Readiness Probe)失败;镜像拉取错误;权限不足;限制范围(Limit Ranges)问题;应用程序运行时的配置错误

4.3、StatefulSets

StatefulSet 是用来管理有状态应用的工作负载 API 对象。
StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。
Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。
如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易。
StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值:

  • 稳定的、唯一的网络标识符。
  • 稳定的、持久的存储。
  • 有序的、优雅的部署和缩放。
  • 有序的、自动的滚动更新。

同时StatefulSets也存在有局限:

  • 给定 Pod 的存储必须由 PersistentVolume 驱动 基于所请求的 storage class 来提供,或者由管理员预先提供。
  • 删除或者收缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
  • StatefulSet 当前需要无头服务 来负责 Pod 的网络标识。你需要负责创建此服务。
  • 当删除 StatefulSets 时,StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序地且体面地终止,可以在删除之前将 StatefulSet 缩放为 0。
  • 在默认 Pod 管理策略(OrderedReady) 时使用 滚动更新,可能进入需要人工干预 才能修复的损坏状态。

4.4、DeamonSet

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。 当有节点加入集群时, 也会为他们新增一个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。
DaemonSet 的一些典型用法:

  • 在每个节点上运行集群守护进程
  • 在每个节点上运行日志收集守护进程
  • 在每个节点上运行监控守护进程

下面是一个deamonset的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 # 用于容忍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
            - name: varlibdockercontainers
              mountPath: /var/lib/docker/containers
              readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
        - name: varlog
          hostPath:
            path: /var/log
        - name: varlibdockercontainers
          hostPath:
            path: /var/lib/docker/containers

pod选择算符

.spec.selector 字段表示 Pod 选择算符,它与 Job 的 .spec.selector 的作用是相同的。
从 Kubernetes 1.8 开始,您必须指定与 .spec.template 的标签匹配的 Pod 选择算符。 用户不指定 Pod 选择算符时,该字段不再有默认值。 选择算符的默认值生成结果与 kubectl apply 不兼容。 此外,一旦创建了 DaemonSet,它的 .spec.selector 就不能修改。 修改 Pod 选择算符可能导致 Pod 意外悬浮
spec.selector 是一个对象,如下两个字段组成:

  • matchLabels - 与 ReplicationController 的 .spec.selector 的作用相同。
  • matchExpressions - 允许构建更加复杂的选择器,可以通过指定 key、value 列表以及将 key 和 value 列表关联起来的 operator。

当上述两个字段都指定时,结果会按逻辑与(AND)操作处理。
如果指定了 .spec.selector,必须与 .spec.template.metadata.labels 相匹配。 如果与后者不匹配,则 DeamonSet 会被 API 拒绝。

仅在某些节点上运行 Pod

可以使用nodeSelector或者affinity来控制

  • 默认是通过默认调度器来调度的
  • Pod 行为的不一致性:正常 Pod 在被创建后等待调度时处于 Pending 状态, DaemonSet Pods 创建后不会处于 Pending 状态下。
  • Pod 抢占 由默认调度器处理。启用抢占后,DaemonSet 控制器将在不考虑 Pod 优先级和抢占 的情况下制定调度决策。
  • ScheduleDaemonSetPods 允许您使用默认调度器而不是 DaemonSet 控制器来调度 DaemonSets, 方法是将 NodeAffinity 条件而不是 .spec.nodeName 条件添加到 DaemonSet Pods。 默认调度器接下来将 Pod 绑定到目标主机。 如果 DaemonSet Pod 的节点亲和性配置已存在,则被替换 (原始的节点亲和性配置在选择目标主机之前被考虑)。 DaemonSet 控制器仅在创建或修改 DaemonSet Pod 时执行这些操作, 并且不会更改 DaemonSet 的 spec.template。

    与 Daemon Pods 通信

  • 推送(Push):配置 DaemonSet 中的 Pod,将更新发送到另一个服务,例如统计数据库。 这些服务没有客户端。

  • NodeIP 和已知端口:DaemonSet 中的 Pod 可以使用 hostPort,从而可以通过节点 IP 访问到 Pod。客户端能通过某种方法获取节点 IP 列表,并且基于此也可以获取到相应的端口。
  • DNS:创建具有相同 Pod 选择算符的 无头服务, 通过使用 endpoints 资源或从 DNS 中检索到多个 A 记录来发现 DaemonSet。
  • Service:创建具有相同 Pod 选择算符的服务,并使用该服务随机访问到某个节点上的 守护进程(没有办法访问到特定节点)。