k8s基础篇-资源调度

发布时间 2023-07-31 08:27:01作者: 就往丶

k8s基础篇-资源调度

Replicaset控制器(了解)

ReplicaSet是kubernetes中的一种副本控制器,简称rs,主要作用是控制由其管理的pod,使pod副本的数量始终维持在预设的个数。它的主要作用就是保证一定数量的Pod能够在集群中正常运行,它会持续监听这些Pod的运行状态,在Pod发生故障时重启pod,pod数量减少时重新运行新的 Pod副本。官方推荐不要直接使用ReplicaSet,用Deployments取而代之,Deployments是比ReplicaSet更高级的概念,它会管理ReplicaSet并提供很多其它有用的特性,最重要的是Deployments支持声明式更新,声明式更新的好处是不会丢失历史变更。所以Deployment控制器不直接管理Pod对象,而是由 Deployment 管理ReplicaSet,再由ReplicaSet负责管理Pod对象。

Replication Controller(简称 RC)可确保 Pod 副本数达到期望值,也就是 RC 定义的数量。 换句话说,Replication Controller 可确保一个 Pod 或一组同类 Pod 总是可用。

如果存在的 Pod 大于设定的值,则 Replication Controller 将终止额外的 Pod。如果太小, Replication Controller 将启动更多的 Pod 用于保证达到期望值。与手动创建 Pod 不同的是,用 Replication Controller 维护的 Pod 在失败、删除或终止时会自动替换。因此即使应用程序只需要 一个 Pod,也应该使用 Replication Controller 或其他方式管理。Replication Controller 类似于进程 管理程序,但是 Replication Controller 不是监视单个节点上的各个进程,而是监视多个节点上的 多个 Pod。

无状态资源管理Deployment

Deployment一般用于部署公司的无状态服务,这个也是最常用的控制器,因为企业内部现在都是以微服务为主,而微服务实现无状态化也是最佳实践,可以利用Deployment的高级功能做到无缝迁移、自动扩容缩容、自动灾难恢复、一键回滚等功能

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


一个Kubernetes集群,包含多个节点。中间是一名开发者,他在笔记本电脑上编写一个deployment的YAML文件。YAML文件定义了一个简单的nginx deployment,指定了replicas数为3。

然后这名开发者使用kubectl apply命令将deployment YAML文件应用到Kubernetes集群中。图片右边显示了这个deployment创建的过程:

首先deployment controller根据replicas数创建了3个pod。这3个pod分布在集群的不同节点上。

每个pod里都运行了一个nginx容器。deployment controller会确保一直维持3个pod处于运行状态。

如果有pod死掉,deployment会自动创建新的pod来替代;如果节点死掉,deployment会将pod重新调度到其他节点。

这样deployment可以提供一个稳定的pod数量,实现应用的高可用。

image-20230730090005410

创建 Deployment

命令创建

kubectl create deployment nginx --image=nginx

文件声明

从 Kubernetes 1.16 版本开始,彻底废弃了其他的 APIVersion,只能使用 apps/v1, 1.16 以下的版本可以使用 extension 等。

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

示例解析:

1. nginx-deployment:Deployment的名称; 
2. replicas: 创建Pod的副本数;
3.  selector:定义Deployment如何找到要管理的Pod,与template的label(标签)对应,apiVersion 为apps/v1必须指定该字段; 
4. template字段包含以下字段: 
   - app: nginx使用label(标签)标记Pod;
   - spec:表示Pod运行一个名字为nginx的容器
   -  image:运行此Pod使用的镜像;
   -  Port:容器用于发送和接收流量的端口。

使用 kubectl create 创建此 Deployment:

# kubectl create -f dp-nginx.yaml
deployment.apps/nginx-deployment created

使用 kubectl get 或者 kubectl describe 查看此 Deployment 的状态:

# kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   0/3     3            0           8s

➢ NAME:集群中Deployment的名称;

➢ READY:Pod就绪个数和总副本数;

➢ UP-TO-DATE:显示已达到期望状态的被更新的副本数;

➢ AVAILABLE:显示用户可以使用的应用程序副本数,当前为0,说明目前还没有达到期望的Pod;

➢ AGE:显示应用程序运行的时间。

可以使用 rollout 命令查看整个 Deployment 创建的状态:

# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out

当 rollout 结束时,再次查看此 Deployment,可以看到 AVAILABLE 的数量和 yaml 文件中定义的 replicas 相同:

# kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           10m

查看此 Deployment 当前对应的 ReplicaSet:

# kubectl get rs -l app=nginx
NAME                  DESIRED   CURRENT   READY   AGE
nginx-deployment-5c689d88bb   3         3         3       12m

➢ DESIRED:应用程序副本数;

➢ CURRENT:当前正在运行的副本数;

当 Deployment 有过更新,对应的 RS 可能不止一个,可以通过-o yaml 获取当前对应的 RS 是哪个,其余的 RS 为保留的历史版本,用于回滚等操作。

查看此 Deployment 创建的 Pod,可以看到 Pod 的 hash 值 5c689d88bb 和上述 Deployment 对应的 ReplicaSet 的 hash 值一致:

# kubectl get pods --show-labels
NAME                                READY   STATUS    RESTARTS   AGE   LABELS
nginx-deployment-5c689d88bb-6b95k   1/1     Running   0          13m   app=nginx,pod-template-hash=5c689d88bb
nginx-deployment-5c689d88bb-9z5z2   1/1     Running   0          13m   app=nginx,pod-template-hash=5c689d88bb 
nginx-deployment-5c689d88bb-jc8hr   1/1     Running   0          13m   app=nginx,pod-template-hash=5c689d88bb

更新 Deployment

当且仅当 Deployment 的 Pod 模板(即.spec.template)更改时,才会触发 Deployment 更新,例如更改内存、CPU 配置或者容器的 image。

假如更新 Nginx Pod 的 image 使用 nginx:latest,并使用--record 记录当前更改的参数,后期回滚时可以查看到对应的信息:

# kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record
deployment.extensions/nginx-deployment image updated

当然也可以使用 edit 命令直接编辑 Deployment,效果相同:

# kubectl edit deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment edited

同样可以使用 kubectl rollout status 查看更新过程:

# kubectl rollout status deployment.v1.apps/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish:
  1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish:
  2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish:
  2 out of 3 new replicas have been updated... 
Waiting for deployment "nginx-deployment" rollout to finish:
  2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish:
  1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish:
  1 old replicas are pending termination... 
deployment "nginx-deployment" successfully rolled out

可以看出更新过程为新旧交替更新,首先新建一个 Pod,当 Pod 状态为 Running 时,删除一个旧的 Pod,同时再创建一个新的 Pod。当触发一个更新后,会有新的 ReplicaSet 产生,旧的 ReplicaSet 会被保存,查看此时 ReplicaSet,可以从 AGE 或 READY 看出来新旧 ReplicaSet:

# kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-5c689d88bb   0         0         0       34m
nginx-deployment-6987cdb55b   3         3         3       5m14s

通过 describe 查看 Deployment 的详细信息:

# kubectl describe deploy nginx-deployment
Name:                   nginx-deployment
Namespace:              default
...
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-6987cdb55b (3/3 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  36m   deployment-controller  Scaled up replica set nginx-deployment-5c689d88bb to 3
  Normal  ScalingReplicaSet  7m16s deployment-controller  Scaled up replica set nginx-deployment-6987cdb55b to 1
  Normal  ScalingReplicaSet  5m18s deployment-controller  Scaled down replica set nginx-deployment-5c689d88bb to 2
  Normal  ScalingReplicaSet  5m18s deployment-controller  Scaled up replica set nginx-deployment-6987cdb55b to 2
  Normal  ScalingReplicaSet  4m35s deployment-controller  Scaled down replica set nginx-deployment-5c689d88bb to 1
  Normal  ScalingReplicaSet  4m34s deployment-controller  Scaled up replica set nginx-deployment-6987cdb55b to 3
  Normal  ScalingReplicaSet  3m30s deployment-controller  Scaled down replica set nginx-deployment-5c689d88bb to 0

在 describe 中可以看出,第一次创建时,它创建了一个名为 nginx-deployment-5c689d88bb 的 ReplicaSet,并直接将其扩展为 3 个副本。更新部署时,它创建了一个新的 ReplicaSet,命名为 nginx-deployment-6987cdb55b,并将其副本数扩展为 1,然后将旧的 ReplicaSet 缩小为 2,这样至少可以有 2 个 Pod 可用,最多创建了 4 个 Pod。以此类推,使用相同的滚动更新策略向上和向下扩展新旧 ReplicaSet,最终新的 ReplicaSet 可以拥有 3 个副本,并将旧的 ReplicaSet 缩小为 0。

回滚 Deployment

当更新了版本不稳定或配置不合理时,可以对其进行回滚操作,假设我们又进行了几次更新 (此处以更新镜像版本触发更新,更改配置效果类似):

# kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record
# kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record

使用 kubectl rollout history 查看更新历史:

# kubectl rollout history deployment/nginx-deployment
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record=true
3         kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
4         kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true

查看 Deployment 某次更新的详细信息,使用--revision 指定某次更新版本号:

# kubectl rollout history deployment/nginx-deployment --revision=3
deployment.apps/nginx-deployment with revision #3
Pod Template:
  Labels:       app=nginx
        pod-template-hash=645959bf6b
  Annotations:  kubernetes.io/change-cause: kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
  Containers:
   nginx:
    Image:      dotbalo/canary:v1
    Port:       80/TCP
    Host Port:  0/TCP
    Environment:        <none>
    Mounts:     <none>
  Volumes:      <none>

如果只需要回滚到上一个稳定版本,使用 kubectl rollout undo 即可:

# kubectl rollout undo deployment/nginx-deployment
deployment.apps/nginx-deployment

再次查看更新历史,发现 REVISION5 回到了 canary:v1:

# kubectl rollout history deployment/nginx-deployment   
REVISION  CHANGE-CAUSE
1         <none>  
2         kubectl set image deployment nginx-deployment nginx=nginx:1.9.1 --record=true
4         kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
5         kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true

如果要回滚到指定版本,使用--to-revision 参数:

# kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment.extensions/nginx-deployment  

扩容 Deployment

当公司访问量变大,或者有预期内的活动时,三个 Pod 可能已无法支撑业务时,可以提前对其进行扩展。

使用 kubectl scale 动态调整 Pod 的副本数,比如增加 Pod 为 5 个:

# kubectl scale deployment.v1.apps/nginx-deployment --replicas=5
deployment.apps/nginx-deployment scaled

查看 Pod,此时 Pod 已经变成了 5 个:

# kubectl get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-5f89547d9c-5r56b    1/1     Running   0          90s
nginx-deployment-5f89547d9c-htmn7    1/1     Running   0          25s
nginx-deployment-5f89547d9c-nwxs2    1/1     Running   0          99s 
nginx-deployment-5f89547d9c-rpwlg    1/1     Running   0          25s
nginx-deployment-5f89547d9c-vlr5p    1/1     Running   0          95s

暂停和恢复 Deployment 更新

上述演示的均为更改某一处的配置,更改后立即触发更新,大多数情况下可能需要针对一个资源文件更改多处地方,而并不需要多次触发更新,此时可以使用 Deployment 暂停功能,临时禁用更新操作,对 Deployment 进行多次修改后在进行更新。

使用 kubectl rollout pause 命令即可暂停 Deployment 更新:

# kubectl rollout pause deployment/nginx-deployment
deployment.extensions/nginx-deployment paused  

然后对 Deployment 进行相关更新操作,比如先更新镜像,然后对其资源进行限制(如果使用的是 kubectl edit 命令,可以直接进行多次修改,无需暂停更新,kubectl set 命令一般会集成在 CICD 流水线中):

# kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1
deployment.apps/nginx-deployment image updated

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

通过 rollout history 可以看到没有新的更新:

# kubectl rollout history deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment
REVISION  CHANGE-CAUSE
1         <none>
5         kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v1 --record=true
7         kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
8         kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true

进行完最后一处配置更改后,使用 kubectl rollout resume 恢复 Deployment 更新:

# kubectl rollout resume deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment resumed

可以查看到恢复更新的 Deployment 创建了一个新的 RS(ReplicaSet 缩写):

# kubectl get rs
NAME                               DESIRED   CURRENT   READY   AGE
nginx-deployment-57895845b8        5         5         4       11s

可以查看 Deployment 的 image(镜像)已经变为 nginx:1.9.1

# kubectl describe deploy nginx-deployment
Name:                   nginx-deployment
Namespace:              default
...
Annotations:        deployment.kubernetes.io/revision: 9
                    kubernetes.io/change-cause: kubectl set image deployment nginx-deployment nginx=dotbalo/canary:v2 --record=true
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.9.1
    Port:         80/TCP
    Host Port:    0/TCP

Here are some key points about updating Deployments formatted as Markdown:

更新 Deployment 的注意事项

历史版本清理策略:

在默认情况下,revision 保留 10 个旧的 ReplicaSet,其余的将在后台进行垃圾回收,可以在 .spec.revisionHistoryLimit 设置保留 ReplicaSet 的个数。当设置为 0 时,不保留历史记录。

更新策略:

  1. .spec.strategy.type==Recreate,表示重建,先删掉旧的Pod再创建新的Pod;
  2. .spec.strategy.type==RollingUpdate,表示滚动更新,可以指定maxUnavailablemaxSurge 来控制滚动更新过程;
    • .spec.strategy.rollingUpdate.maxUnavailable,指定在回滚更新时最大不可用的Pod数量,可选字段,默认为25%,可以设置为数字或百分比,如果maxSurge为0,则该值不能为0;
    • .spec.strategy.rollingUpdate.maxSurge可以超过期望值的最大Pod数,可选字段,默认为25%,可以设置成数字或百分比,如果maxUnavailable为0,则该值不能为0。

Ready 策略:

.spec.minReadySeconds 是可选参数,指定新创建的 Pod 应该在没有任何容器崩溃的情况下视为 Ready(就绪)状态的最小秒数,默认为 0,即一旦被创建就视为可用,通常和容器探针连用。

有状态资源管理StatefulSet

StatefulSet(有状态集,缩写为sts)常用于部署有状态的且需要有序启动的应用程序,比如在进行SpringCloud项目容器化时,Eureka的部署是比较适合用StatefulSet部署方式的,可以给每个Eureka实例创建一个唯一且固定的标识符,并且每个Eureka实例无需配置多余的Service,其余Spring Boot应用可以直接通过Eureka的Headless Service即可进行注册。

StatefulSet 的运行机制可以概括如下:

  • StatefulSet会确保部署的Pod按照定义的顺序逐个启动,例如web-0先启动,web-1后启动。

  • StatefulSet为每个Pod创建一个唯一的标识,这个标识跟Pod重启无关。

  • StatefulSet会确保一个Pod启动运行后,才会启动下一个Pod。

  • 当Pod被删除时,StatefulSet不会自动重新创建该Pod,需要手动重建该Pod。

  • StatefulSet会自动为每个Pod创建和绑定一个PersistentVolume,如果Pod被重新调度到其他Node上,该Volume也会跟随它迁移。

  • StatefulSet支持滚动更新,可以控制更新的过程。

  • StatefulSet可以扩容或缩容Pod的副本数。

总的来说,StatefulSet通过唯一的Pod标识、启动顺序、稳定的网络标识等来维护有状态的应用,即使Pod被重新调度也可以保证应用状态的一致性。

下面详细介绍StatefulSet的一些关键特性:

  • 稳定唯一的网络标识:每个Pod都会有一个稳定唯一的网络标识,格式为$(statefulset name)-$(ordinal),即web-0,web-1等。

  • 启动顺序:Pod是按照{0,1,2}的顺序启动的,当web-0运行起来才会启动web-1,web-2。

  • 按序终止:当删除Pod时,它会按照逆序进行终止,即web-2先停止,web-1再停止,web-0最后停止。

  • 存储卷稳定:每个Pod绑定一个PVC,如果Pod被重启,存储卷还会重新挂载,数据不会丢失。

  • 滚动更新:支持版本滚动更新,可以控制更新过程。

通过这些机制,StatefulSet可以很好地维护有状态应用的高可用。

Headless Service

和Deployment类似,一个StatefulSet也同样管理着基于相同容器规范的Pod。不同的是,StatefulSet为每个Pod维护了一个粘性标识。而StatefulSet创建的Pod一般使用HeadlessService(无头服务)进行Pod之前的通信和普通的Service的区别在于Headless Service没有clusterlP,它使用的是Endpoint进行互相通信,Headless一般的格式为:
statefulSetName-fo..N-1.serviceName.namespace.svc.cluster.local

  • serviceName为HeadlessService的名字,创建StatefulSet时,必须指定HeadlessService名称;

  • 0..N-1为Pod所在的序号,从0开始到N-1:

  • statefulSetName为StatefulSet的名字:

  • namespace为服务所在的命名空间;

  • .cluster.local为ClusterDomain(集群域)。

创建 StatefulSet

定义一个简单的 StatefulSet 的示例如下:

apiVersion: v1
kind: Service 
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: web

其中:

➢ kind: Service定义了一个名字为Nginx的Headless Service,创建的Service格式为nginx-0.nginx.default.svc.cluster.local,其他的类似,因为没有指定Namespace(命名空间),所以默认部署在default;

➢ kind: StatefulSet定义了一个名字为web的StatefulSet,replicas表示部署Pod的副本数,本实例为2。

◆ 在 StatefulSet 中必须设置Pod选择器(.spec.selector)用于匹配其标签(.spec.template.metadata.labels)。在 1.8 版本之前,如果未配置该字段(.spec.selector),将被设置为默认值,在 1.8 版本之后,如果未指定匹配 Pod Selector,则会导致 StatefulSet 创建错误。

当 StatefulSet 控制器创建 Pod 时,它会添加一个标签 statefulset.kubernetes.io/pod-name,该标签的值为 Pod 的名称,用于匹配 Service。

创建 StatefulSet:

# kubectl create -f sts-web.yaml 
# 也可以使用-n 部署到其他 namespace
service/nginx created
statefulset.apps/web created

查看创建的资源:

# kubectl get sts
NAME  DESIRED   CURRENT   AGE
web   2         2         12s

# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   7d2h
nginx        ClusterIP   None         <none>        80/TCP    16s

# kubectl get po -l app=nginx
NAME  READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          2m5s
web-1   1/1     Running   0          115s

StatefulSet 创建 Pod 流程

StatefulSet 管理的 Pod 部署和扩展规则如下:

  1. 对于具有N个副本的StatefulSet,将按顺序从0到N-1开始创建Pod;
  2. 当删除Pod时,将按照N-1到0的反顺序终止;
  3. 在缩放Pod之前,必须保证当前的Pod是Running(运行中)或者Ready(就绪);
  4. 在终止Pod之前,它所有的继任者必须是完全关闭状态。

StatefulSet 的 pod.Spec.TerminationGracePeriodSeconds(终止 Pod 的等待时间)不应该指定为 0,设置为 0 对 StatefulSet 的 Pod 是极其不安全的做法,优雅地删除 StatefulSet 的 Pod 是非常有必要的,而且是安全的,因为它可以确保在 Kubelet 从 APIServer 删除之前,让 Pod 正常关闭。

当创建上面的 Nginx 实例时,Pod 将按 web-0、web-1、web-2 的顺序部署 3 个 Pod。在 web-0 处于 Running 或者 Ready 之前,web-1 不会被部署,相同的,web-2 在 web-1 未处于 Running 和 Ready 之前也不会被部署。如果在 web-1 处于 Running 和 Ready 状态时,web-0 变成 Failed(失败)状态,那么 web-2 将不会被启动,直到 web-0 恢复为 Running 和 Ready 状态。

如果用户将 StatefulSet 的 replicas 设置为 1,那么 web-2 将首先被终止,在完全关闭并删除 web-2 之前,不会删除 web-1。如果 web-2 终止并且完全关闭后,web-0 突然失败,那么在 web-0 未恢复成 Running 或者 Ready 时,web-1 不会被删除。

StatefulSet 扩容和缩容

和 Deployment 类似,可以通过更新 replicas 字段扩容/缩容 StatefulSet,也可以使用 kubectl scale、kubectl edit 和 kubectl patch 来扩容/缩容一个 StatefulSet。

(1) 扩容

# kubectl scale sts web --replicas=5 
statefulset.apps/web scaled

查看扩容后 Pod 的状态:

# kubectl get po
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          2m58s
web-1   1/1     Running   0          2m48s  
web-2   1/1     Running   0          116s
web-3   1/1     Running   0          79s
web-4   1/1     Running   0          53s

也可使用以下命令动态查看:

kubectl get pods -w -l app=nginx

(2) 缩容

首先打开另一个终端动态查看缩容的流程:

# kubectl get pods -w -l app=nginx

在另一个终端将副本数改为 3(此处演示的为 patch 命令,patch 比 edit 和 scale 稍复杂):

# kubectl scale sts web --replicas=3
statefulset.apps/web scaled
# kubectl patch sts web -p '{"spec":{"replicas":3}}'
statefulset.apps/web patched

此时可以看到第一个终端显示 web-4 和 web-3 的 Pod 正在被有序的删除(或终止)。

StatefulSet 更新策略

(1) OnDelete 策略

OnDelete 更新策略实现了传统(1.7版本之前)的行为,它也是默认的更新策略。当我们选择这个更新策略并修改StatefulSet的 .spec.template 字段时,StatefulSet 控制器不会自动更新Pod,必须手动删除Pod才能使控制器创建新的Pod。

(2) RollingUpdate策略

RollingUpdate(滚动更新)更新策略会自动更新一个StatefulSet中的所有的Pod,采用与序号索引相反的顺序进行滚动更新。

# kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
statefulset.apps/web patched

然后改变容器的镜像触发滚动更新:

# kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"dotbalo/canary:v1"}]'
statefulset.apps/web patched

在更新过程中可以使用 kubectl rollout status sts/<name> 来查看滚动更新的状态。

分段更新

比如我们定义一个分区"partition":3,可以使用 patch 或 edit 直接对 StatefulSet 进行设置:

  1. 定义一个分区(partition),例如3
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 5
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: web
  1. 改变StatefulSet的模板,例如容器镜像,触发滚动更新
kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"new-image"}]' 
  1. 此时,序号大于或等于分区的Pod(3、4、5等)会被滚动更新到新版本

  2. 序号小于分区的Pod(0、1、2)不会被更新,仍然是旧版本

  3. 可以逐步将分区减小到0,以此将所有Pod更新到新版本

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":1}}}}' 

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'

通过这种分段的方式,可以实现类似金丝雀发布的效果,先在一部分实例上发布新版本,观察是否有问题再逐步扩大范围。

主要注意分区序号是与Pod的序号索引相比较,确定哪些Pod会被更新。这样可以精细控制更新的范围和顺序。

删除 StatefulSet

删除StatefulSet有两种方式,即级联删除和非级联删除。

(1) 非级联删除

使用 kubectl delete sts xxx 删除 StatefulSet 时,只需提供--cascade=false 参数,就会采用非级联删除,此时删除 StatefulSet 不会删除它的 Pod:

# kubectl get po  
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          16m
web-1   1/1     Running   0          16m 
web-2   1/1     Running   0          11m

# kubectl delete statefulset web --cascade=false  # 采用非级联删除
statefulset.apps "web" deleted

# kubectl get sts   # 查看此时 sts 已经被删除
No resources found.

# kubectl get po     # 该 StatefulSet 管理的 Pod 并未被删除   
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          16m
web-1   1/1     Running   0          16m
web-2   1/1     Running   0          11m

由于此时删除了 StatefulSet,它管理的 Pod 变成了“孤儿”Pod,因此单独删除 Pod 时,该 Pod 不会被重建:

# kubectl get po
NAME    READY   STATUS    RESTARTS   AGE 
web-0   1/1     Running   0          16m
web-1   1/1     Running   0          16m
web-2   1/1     Running   0          11m

# kubectl delete po web-0
pod "web-0" deleted

# kubectl get po
NAME    READY   STATUS    RESTARTS   AGE
web-1   1/1     Running   0          18m  
web-2   1/1     Running   0          12m

再次创建 sts:

# kubectl apply -f sts-web.yaml
statefulset.apps/web created

# kubectl get po
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          32s
web-1   1/1     Running   0          19m

(2) 级联删除

省略 --cascade=false 参数即为级联删除,会删除 StatefulSet 和它的 Pod。

也可以使用 -f 指定创建 StatefulSet 和 Service 的 yaml 文件,直接删除 StatefulSet 和 Service(此文件将 StatefulSet 和 Service 写在了一起)。

# kubectl delete statefulset web
statefulset.apps "web" deleted
# kubectl get po
No resources found.

# kubectl delete -f sts-web.yaml 
service "nginx" deleted
Error from server (NotFound): error when deleting "sts-web.yaml": 
statefulsets.apps "web" not found # 因为 StatefulSet 已经被删除,所以会提示该StatefulSet 不存在

字护进程DaemonSet

DaemonSet(守护进程集,缩写为ds)和守护进程类似,它在符合匹配条件的节点上均部署一个Pol。当有新节点加入集群时,也会为它们新增一个Pod,当节点从集群中移除时,这些Pod也会被回收,删除DaemonSet将会删除它创建的所有Pod。

  • 运行集群存储daemon(守护进程),例如在每个节点上运行Glusterd、Ceph等;

  • 在每个节点运行日志收集daemon,例如Fluentd、Logstash;

  • 在每个节点运行监控daemon,比如Prometheus Node Exporter、Collectd、Datadog代理、New Relic代理或 Ganglia gmond。

Here are the key points about DaemonSets formatted in Markdown:

定义一个 DaemonSet

创建一个 DaemonSet 和 Deployment 类似,比如创建一个 nginx 的 DaemonSet:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.15.12
        imagePullPolicy: IfNotPresent
        name: nginx

(1)必需字段

和其他所有 Kubernetes 配置一样,DaemonSet 需要 apiVersion、kind 和 metadata 字段,同时也需要一个.spec 配置段。

(2)Pod 模板

.spec 唯一需要的字段是.spec.template。.spec.template 是一个 Pod 模板,它与 Pod 具有相同的配置方式,但它不具有 apiVersion 和 kind 字段。

除了 Pod 必需的字段外,在 DaemonSet 中的 Pod 模板必须指定合理的标签。

(3)Pod Selector

.spec.selector 字段表示 Pod Selector,它与其他资源的.spec.selector 的作用相同。

.spec.selector 表示一个对象,它由如下两个字段组成:

(4)指定节点部署 Pod

如果指定了.spec.template.spec.nodeSelector,DaemonSet Controller 将在与 Node Selector(节点选择器)匹配的节点上创建 Pod,比如部署在磁盘类型为 ssd 的节点上(需要提前给节点定义标签 Label):

nodeSelector:
  disktype: ssd

创建 DaemonSet

比如创建一个 nginx:

# kubectl create -f nginx-ds.yaml

此时会在每个节点创建一个 Pod:

# kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
nginx-6ssvl   1/1     Running   0          56s
nginx-8ddgd   1/1     Running   0          36s 
nginx-cfx5d   1/1     Running   0          9s
nginx-k48bt   1/1     Running   0          46s
nginx-p84ng   1/1     Running   0          11s

使用 -o wide 可以查看 Pod 所在的节点时:

# kubectl get po -o wide

这里是关于更新和回滚 DaemonSet 的内容格式化后的 Markdown:

更新和回滚 DaemonSet

如果添加了新节点或修改了节点标签(Label),DaemonSet 将立刻向新匹配上的节点添加 Pod,同时删除不能匹配的节点上的 Pod。

在 Kubernetes 1.6 以后的版本中,可以在 DaemonSet 上执行滚动更新,未来的 Kubernetes版本将支持节点的可控更新。

DaemonSet 滚动更新可参考:https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/

DaemonSet 更新策略和 StatefulSet 类似,也有 OnDelete 和 RollingUpdate 两种方式。

查看上一节创建的 DaemonSet 更新方式:

# kubectl get ds nginx-ds -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'
RollingUpdate

命令式更新,和之前 Deployment、StatefulSet 方式一致

kubectl edit ds/<daemonset-name>
kubectl patch ds/<daemonset-name> -p=<strategic-merge-patch>

更新镜像

kubectl set image ds/<daemonset-name><container-name>= <container-newimage> --record=true

查看更新状态

kubectl rollout status ds/<daemonset-name>

列出所有修订版本

kubectl rollout history daemonset <daemonset-name>

回滚到指定 revision

kubectl rollout undo daemonset <daemonset-name> --to-revision=<revision>

DaemonSet 的更新和回滚与 Deployment 类似,此处不再演示。

自动扩缩容HPA

HPA全称是 Horizontal Pod Autoscaler,也就是对k8s的workload的副本数进行自动水平扩缩容(scale)机制,也是k8s里使用需求最广泛的一种Autoscaler机制,在开始详细介绍HPA之前,先简单梳理下k8s autoscale的整个大背景。

image-20230730154004254

HPA 接口类型

◆ HPA v1为稳定版自动水平伸缩,只支持CPU指标
◆ V2为beta版本,分为v2beta1(支持CPU、内存和自定义指标)
◆ v2beta2(支持CPU、内存、自定义指标Custom和额外指标ExternalMetrics)

HPA扩缩容算法

从最基本的角度来看,Pod 水平自动扩缩控制器根据当前指标和期望指标来计算扩缩比例。

期望副本数 = ceil[当前副本数 * (当前指标 / 期望指标)]

扩容

如果计算出的扩缩比例接近 1.0, 将会放弃本次扩缩, 度量指标 / 期望指标接近1.0。

缩容

冷却/延迟: 如果延迟(冷却)时间设置的太短,那么副本数量有可能跟以前一样出现抖动。默认值是 5 分钟(5m0s)--horizontal-pod-autoscaler-downscale-stabilization

HPA 实践

必须安装metrics-server或其他自定义metrics-server 不能扩容无法缩放的对象,比如DaemonSet

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: hap-nginx
spec:
  maxReplicas: 10 # 最大扩容到10个节点(pod)
  minReplicas: 1 # 最小扩容1个节点(pod)
  metrics:
  - resource:
      name: cpu
      target:
        averageUtilization: 40 # CPU 平局资源使用率达到40%就开始扩容,低于40%就是缩容
        # 设置内存
        # AverageValue:40
        type: Utilization
    type: Resource
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hap-nginx

主要参数解释如下:

  • scaleTargetRef:目标作用对象,可以是Deployment、ReplicationController或ReplicaSet。
  • minReplicas和maxReplicas:Pod副本数量的最小值和最大值,系统将在这个范围内进行自动扩缩容操作,并维持每个Pod的内存使用率为40%,这个值就是上面设置的阈值averageUtilization。
  • metrics:目标指标值。在metrics中通过参数type定义指标的类型;通过参数target定义相应的指标目标值,系统将在指标数据达到目标值时(考虑容忍度的区间,见前面算法部分的说明)触发扩缩容操作。
  • 对于CPU使用率,在target参数中设置averageUtilization定义目标平均CPU使用率。
  • 对于内存资源,在target参数中设置AverageValue定义目标平均内存使用值。

压力测试

while true; do wget -q -O http://192.168.10.10 > /dev/null; done

使用 ab 工具进行压测

进入apache官网 http://httpd.apache.org/ 下载apache即可,或者直接通过yum安装apache都行,这里选择最简单的方式yum安装

yum install httpd -y

复制

开始压测

ab -n 100000 -c 800 http://http://192.168.10.10

#-c:并发数
#-n:总请求数