0 前言

基于 centos7.9docker-ce-20.10.18kubelet-1.22.3-0

1 滚动升级

1.1 实现机制

滚动升级的实现机制

两个 replicaset 控制器分别控制旧版本的 pod 和新版本 pod,replicaset2 启动一个新版版本 pod,相应的 replicaset1 停止一个旧版本 pod,从而实现滚动升级。在这过程中,无法保证业务流量完全不丢失。

image-20221003113645777

1.2 简单示例

升级

kubectl set image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N [options]
# 示例
kubectl set image deployment/demo-rollout nginx=nginx:1.15 --record=true
# --record=true 表示将升级的命令记录到升级记录中

回滚

# 上次升级状态
kubectl rollout status deployment demo-rollout
# 升级记录
kubectl rollout history deployment demo-rollout
# 回滚至上个版本
kubectl rollout undo deployment demo-rollout
# 回滚至指定版本
kubectl rollout undo deployment demo-rollout --to-revision=2

1.3 升级

在所有 work 节点先创建几个 busybox 镜像的 tag 用于升级演示

[root@k8s-node3 ~]# for i in {1..3}; do docker tag busybox:latest busybox:v${i}; done
[root@k8s-node3 ~]# docker images | grep busybox
busybox                                              latest    7cfbbec8963d   3 weeks ago     4.86MB
busybox                                              v1        7cfbbec8963d   3 weeks ago     4.86MB
busybox                                              v2        7cfbbec8963d   3 weeks ago     4.86MB
busybox                                              v3        7cfbbec8963d   3 weeks ago     4.86MB

创建 v1 版本的 deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-rollout
  labels:
    app: demo-rollout
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo-rollout
  template:
    metadata:
      labels:
        app: demo-rollout
    spec:
      containers:
      - name: busybox
        image: busybox:v1
        command: ['/bin/sh', '-c', 'sleep 36000']

也可以使用命令创建

[root@k8s-node1 ~]# kubectl create deployment demo-rollout --image=busybox:v1 --replicas=3 -- sleep 3600
deployment.apps/demo-rollout created
[root@k8s-node1 ~]# kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
demo-rollout-5d847fd86c-678pr   1/1     Running   0          4s
demo-rollout-5d847fd86c-9mj4v   1/1     Running   0          4s
demo-rollout-5d847fd86c-xhvf7   1/1     Running   0          4s

升级至 v2 和 v3

# 升级
[root@k8s-node1 ~]# kubectl set image deployment/demo-rollout busybox=busybox:v2 --record=true
[root@k8s-node1 ~]# kubectl set image deployment/demo-rollout busybox=busybox:v3 --record=true
# 查看升级状态
[root@k8s-node1 ~]# kubectl rollout status deployment demo-rollout
deployment "demo-rollout" successfully rolled out
[root@k8s-node1 ~]# kubectl rollout history deployment demo-rollout
deployment.apps/demo-rollout
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl set image deployment/demo-rollout busybox=busybox:v2 --record=true
3         kubectl set image deployment/demo-rollout busybox=busybox:v3 --record=true
# 查看实际镜像版本
[root@k8s-node1 ~]# kubectl get deployment demo-rollout -o jsonpath='{.spec.template.spec.containers}'
[root@k8s-node1 ~]# kubectl describe deployment demo-rollout | grep -i image:
    Image:      busybox:v3

1.4 回滚

回滚至 v1 版本

[root@k8s-node1 ~]# kubectl rollout undo deployment/demo-rollout --to-revision=1
deployment.apps/demo-rollout rolled back
[root@k8s-node1 ~]# kubectl describe deployment demo-rollout | grep -i image:
    Image:      busybox:v1
[root@k8s-node1 ~]# kubectl rollout history deployment demo-rollout
deployment.apps/demo-rollout
REVISION  CHANGE-CAUSE
2         kubectl set image deployment/demo-rollout busybox=busybox:v2 --record=true
3         kubectl set image deployment/demo-rollout busybox=busybox:v3 --record=true
4         <none>

可以看到 rollout history 删除了第一次的记录, 重新记录到第四条

恢复到 v2 版本

[root@k8s-node1 ~]# kubectl rollout undo deployment/demo-rollout --to-revision=2
deployment.apps/demo-rollout rolled back
[root@k8s-node1 ~]# kubectl describe deployment demo-rollout | grep -i image:
    Image:      busybox:v2
[root@k8s-node1 ~]# kubectl rollout history deployment demo-rollout
deployment.apps/demo-rollout
REVISION  CHANGE-CAUSE
3         kubectl set image deployment/demo-rollout busybox=busybox:v3 --record=true
4         <none>
5         kubectl set image deployment/demo-rollout busybox=busybox:v2 --record=true

2 自动伸缩

手动扩容

 kubectl scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME) [options]
 # 示例
 kubectl scale deployment demo-rollout --replicas=10

自动扩容

实现自动扩容需满足两个条件:

Horizontal Pod Autoscaling: pod 水平扩容,k8s 中的一个 api 资源,使用 autoscale 时会创建一个 hpa 资源

HPA 基本原理:

  • 查询指定的资源中所有 Pod 的资源平均使用率,并且与创建时设定的值和指标做对比,从而实现自动伸缩的功能.
  • HPA 自动伸缩副本时会使 POD 的资源使用率趋近于预设的 target 值
    • 比如只有一个 POD 时, 资源使用率达到了 180%/70%, HPA 会将 POD 数量扩容到 3 个, 此时资源使用率将会是 60%/70%.
  • 当 pod 资源使用率回到正常水平, controller-manager 会默认等待 5 分钟的时间再缩容 pod,以免再次出现突发流量.
kubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [options]
# 基于cpu指标进行扩容
kubectl autoscale deployment demo-rollout --min=3 --max=10 --cpu-percent=10

# 查看hpa
kubectl get hpa

# replicaset控制器记录了pod的详细伸缩记录
kubectl get rs
kubectl describe rs demo-rollout-54fdcc5676

2.1 基于 CPU

创建 deployment 资源

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-demo
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: 50Mi
            cpu: 50m
---
apiVersion: v1
kind: Service
metadata:
  name: hpa-demo
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  type: NodePort
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30002

创建 hpa 资源

cpu 使用率 = 已使用 / request

--cpu-percent=60 代表所有 pod 的平均 cpu 使用率达到百分之 60 时触发扩容

[root@k8s-node1 ~]# kubectl autoscale deployment hpa-demo --cpu-percent=60 --min=1 --max=10
horizontalpodautoscaler.autoscaling/hpa-demo autoscaled
[root@k8s-node1 ~]# kubectl get hpa
NAME       REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
hpa-demo   Deployment/hpa-demo   0%/60%    1         10        1          18s

压测

[root@k8s-node1 ~]# yum install -y httpd-tools
[root@k8s-node1 ~]# ab -n 1000000 -c 200 http://1.1.1.1:30002/

hpa 自动扩容, pod 数量增加到了 10 个

[root@k8s-node1 ~]# kubectl get hpa
NAME       REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
hpa-demo   Deployment/hpa-demo   160%/10%    1         10        10         50m

[root@k8s-node1 ~]# kubectl describe hpa hpa-demo
Events:
  Type     Reason                        Age                From                       Message
  ----     ------                        ----               ----                       -------
  Warning  FailedGetScale                17m (x8 over 19m)  horizontal-pod-autoscaler  deployments/scale.apps "hpa-demo" not found
  Normal   SuccessfulRescale             11m                horizontal-pod-autoscaler  New size: 4; reason: cpu resource utilization (percentage of request) above target
  Normal   SuccessfulRescale             11m                horizontal-pod-autoscaler  New size: 8; reason: cpu resource utilization (percentage of request) above target
  Normal   SuccessfulRescale             10m                horizontal-pod-autoscaler  New size: 10; reason:

[root@k8s-node1 ~]# kubectl describe deployment hpa-demo
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  16m    deployment-controller  Scaled up replica set hpa-demo-6b4467b546 to 1
  Normal  ScalingReplicaSet  10m    deployment-controller  Scaled up replica set hpa-demo-6b4467b546 to 4
  Normal  ScalingReplicaSet  9m53s  deployment-controller  Scaled up replica set hpa-demo-6b4467b546 to 8
  Normal  ScalingReplicaSet  9m38s  deployment-controller  Scaled up replica set hpa-demo-6b4467b546 to 10

压测结束后也并不会立即减少 pod 数量,会等一段时间后减少 pod 数量,防止流量再次激增。默认时间大概是 5 分钟左右

2.2 基于内存

使用 busybox 容器测试, 另挂载一个 configMap 用于内存压力测试, 由于用到了 mount 命令, 还需要将 container 声明为特权模式.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-mem
spec:
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      labels:
        app: busybox
    spec:
      containers:
      - name: busybox
        image: busybox
        command: ["/bin/sh", "-c", "sleep 36000"]
        volumeMounts:
        - name: increase-mem-script
          mountPath: /opt/
        resources:
          requests:
            memory: 50Mi
            cpu: 50m
        securityContext:
          privileged: true
      volumes:
      - name: increase-mem-script
        configMap:
          name: increase-mem-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: increase-mem-config
data:
  increase-mem.sh: |
    #!/bin/sh
    mkdir /tmp/memory
    mount -t tmpfs -o size=40M tmpfs /tmp/memory
    dd if=/dev/zero of=/tmp/memory/block
    sleep 60
    rm /tmp/memory/block
    umount /tmp/memory
    rmdir /tmp/memory    

获取 hpa 的模板 yaml 文件

[root@k8s-node1 ~]# kubectl autoscale deployment hpa-mem --min=1 --max=10 --dry-run=client -o yaml > hpa-mem-hpa.yml
[root@k8s-node1 ~]# vim hpa-mem-hpa.yml

使用 yaml 创建 hpa, 默认使用的是 autoscaling/v1 版本的 api, 它不支持基于内存的自动扩容, 需要修改为 autoscaling/v2beta1

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-mem
spec:
  maxReplicas: 10
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hpa-mem
  metrics:
  - type: Resource
    resource:
      name: memory
      targetAverageUtilization: 60

执行脚本进行压测, 随着脚本执行, hpa 自动将副本数扩容到了两个

[root@k8s-node1 ~]# kubectl exec -it hpa-mem-c6c7d4957-fpsfb -- /bin/sh /opt/increase-mem.sh
[root@k8s-node1 ~]# kubectl get hpa -w
NAME      REFERENCE            TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
hpa-mem   Deployment/hpa-mem   0%/60%    1         10        1          2m55s
hpa-mem   Deployment/hpa-mem   80%/60%   1         10        1          4m1s
hpa-mem   Deployment/hpa-mem   80%/60%   1         10        2          4m16s

脚本执行 60s 后会使内存使用率自动恢复正常, 副本数过段时间也会自动恢复

2.3 基于自定义指标

待续……

以上