【k8s 控制器:ReplicaSet、Deployment、StatefulSet ...】

控制器

 ReplicaSet、Deployment、StatefulSet 和 DaemonSet 都是 Kubernetes 中的控制器对象,用于管理 Pod 的创建、扩展、缩减和更新等操作。

一、Deployment

 适用无状态服务应用部署;Deployment 是在 ReplicaSet 的基础上提供了更高级功能的控制器。它提供了滚动更新和回滚功能

1、配置文件
yaml 复制代码
apiVersion: apps/v1 # deployment api 版本
kind: Deployment # 资源类型为 deployment
metadata: # 元信息
  labels: # 标签
    app: nginx-deploy # 具体的 key: value 配置形式
  name: nginx-deploy # deployment 的名字
  namespace: default # 所在的命名空间
spec:
  replicas: 3 # 期望副本数,最终运行的副本数
  revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数
  selector: # 选择器,用于找到匹配的 ReplicaSet(RS)
    matchLabels: # 按照标签匹配
      app: nginx-deploy # 匹配的标签key/value
  strategy: # 更新策略
    rollingUpdate: # 滚动更新配置
      maxSurge: 25% # 进行滚动更新时,允许超过期望副本数的额外副本个数或比例,
      maxUnavailable: 25% # 进行滚动更新时,不可用的最大副本个数或比例
    type: RollingUpdate # 更新类型,采用滚动更新
  template: # pod 模板
    metadata: # pod 的元信息
      labels: # pod 的标签
        app: nginx-deploy
    spec: # pod 期望信息
      containers: # pod 的容器
      - image: nginx:1.7.9 # 镜像
        imagePullPolicy: IfNotPresent # 拉取策略
        name: nginx # 容器名称
      restartPolicy: Always # 重启策略
      terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间

关于期望副本数replicas、maxSurgemaxUnavailable:

  1. replicas:期望副本数;
  2. maxSurge:允许超过期望副本数的额外副本数,绝对数值或百分比。例如,如果 maxSurge 设置为 1,那么在滚动更新期间,可以将副本数量增加到期望副本数加上1;
  3. maxUnavailable:不可用的最大副本数。绝对数值或百分比。例如,如果 maxUnavailable 设置为 1,那么在滚动更新期间,最多允许有一个副本不可用;

举例:

yaml 复制代码
spec:
  replicas: 3 # 期望副本数
  ...
  strategy:
    rollingUpdate: 
      maxSurge: 1 # 进行滚动更新时,允许超过期望副本数的额外副本个数或比例
      maxUnavailable:1 # 进行滚动更新时,不可用的最大副本个数或比例
    type: RollingUpdate # 更新类型,采用滚动更新

 在此情况下,Kubernetes会根据maxSurge的值先增加1个额外的副本,使得总共有4个副本。然后再逐个替换旧的副本,直到所有旧副本被替换为止。这样可以保证在更新过程中至少有3个可用的副本。

 然而,由于Deployment的replicas值是3,Kubernetes会自动将副本数量调整回到3个。它会逐个替换旧的副本,直到满足replicas定义的副本数为止。这样确保了最终运行的副本数与replicas的值保持一致。

2、创建/查看
shell 复制代码
# 创建一个 deployment(nginx-deploy)
[root@k8s-master ~]# kubectl create deploy nginx-deploy --image=nginx:1.7.9
deployment.apps/nginx-deploy created
# 或执行 "kubectl create -f xxx.yaml --record"
# --record 会在 annotation 中记录当前命令创建或升级了资源,后续可以查看做过哪些变动操作

# 查看部署信息
[root@k8s-master ~]# kubectl get deployments --show-labels
NAME           READY   UP-TO-DATE   AVAILABLE   AGE     LABELS
nginx-deploy   1/1     1            1           7h22m   app=nginx-deploy

# 查看 rs
[root@k8s-master ~]# kubectl get rs --show-labels
NAME                      DESIRED   CURRENT   READY   AGE    LABELS
nginx-deploy-78d8bf4fd7   1         1         1       4h2m   app=nginx-deploy,pod-template-hash=78d8bf4fd7

# 查看 pod 以及展示标签,可以看到是关联的哪个 rs
[root@k8s-master ~]# kubectl get pod --show-labels
NAME                            READY   STATUS    RESTARTS   AGE    LABELS
nginx-deploy-78d8bf4fd7-d9sb4   1/1     Running   0          4h3m   app=nginx-deploy,pod-template-hash=78d8bf4fd7

通过 NMAE 标签,可以看出:deployment > ReplicaSet(RS)> pod

3、滚动更新
shell 复制代码
...
spec:
  ...
  strategy: # 更新策略
    rollingUpdate: # 滚动更新配置
      maxSurge: 25% # 进行滚动更新时,允许超过期望副本数的额外副本个数或比例,
      maxUnavailable: 25% # 进行滚动更新时,不可用的最大副本个数或比例
    type: RollingUpdate # 更新类型,采用滚动更新

 只有修改了 deployment 配置文件中的 template 中的属性后,才会触发更新操作。

shell 复制代码
# 修改 nginx 版本号
[root@k8s-master ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

# 或者 通过 kubectl edit 进行修改
[root@k8s-master ~]# kubectl edit deployment/nginx-deployment 
 
# 检查Deployment的滚动更新状态
[root@k8s-master ~]# kubectl rollout status deploy <deployment_name>

# 查看部署描述,最后展示发生的事件列表也可以看到滚动更新过程
[root@k8s-master ~]# kubectl describe deploy <deployment_name>

# 获取部署信息,UP-TO-DATE 表示已经有多少副本达到了配置中要求的数目
[root@k8s-master ~]# kubectl get deployments 
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   1/1     1            1           7h5m

# 可以看到增加了一个新的 rs
[root@k8s-master ~]# kubectl get rs --show-labels
NAME                      DESIRED   CURRENT   READY   AGE
nginx-deploy-754898b577   1         1         1       31m     app=nginx-deploy,pod-template-hash=754898b577
nginx-deploy-78d8bf4fd7   0         0         0       7h21m   app=nginx-deploy,pod-template-hash=78d8bf4fd7

# 可以看到所有 pod 关联的 rs 变成了新的
[root@k8s-master ~]# kubectl get pods --show-labels
NAME                            READY   STATUS    RESTARTS   AGE   LABELS
nginx-deploy-754898b577-gfpjh   1/1     Running   0          19m   app=nginx-deploy,pod-template-hash=754898b577

多个滚动更新并行:

 假设当前有 5 个 nginx:1.7.9 版本,要将版本更新为 1.9.1,当更新成功第三个以后,马上又将期望更新的版本改为 1.9.2,那么此时会立马删除之前的三个,并且立马开启更新 1.9.2 的任务。

4、回滚

 默认情况下,kubernetes会在系统中保存前两次的Deployment的rollout历史记录,以便随时回退(可以修改revision history limit来更改保存的revision数)。

 可以通过设置 .spec.revisonHistoryLimit 的值来指定 deployment 保留多少 revison,如果设置为 0,则不允许 deployment 回退了。

shell 复制代码
# 获取 revison 的历史列表
kubectl rollout history deployment/nginx-deploy

# 可以查看对应版本详细信息
kubectl rollout history deployment/nginx-deploy --revision=2 

# 回退到上一个版本
kubectl rollout undo deployment/nginx-deploy 

# 回退到指定的 revision 版本
kubectl rollout undo deployment/nginx-deploy --to-revision=2

# 查看
kubectl get deployment
kubectl describe deployment 
5、扩容/缩容

1、通过 kubectl scale 命令的replicas参数可以进行自动扩容/缩容

2、通过 kube edit 编辑 replcas 的值也可以实现扩容/缩容

shell 复制代码
[root@k8s-master ~]# kubectl scale --replicas=6 deploy [deployName]

[root@k8s-master ~]# kubectl edit deploy [deployName]

扩容/缩容只是直接创建/删除副本数,没有更新 pod 的 template 因此不会创建新的 rs。

6、暂停/恢复滚动更新

 由于每次对 pod template 中的信息发生修改后,都会触发更新 deployment 操作 。

 如果频繁修改信息,就会产生多次更新,同时会产生多个 rs 和多个历史版本(rollout history)而实际上只需要执行最后一次更新即可,当出现此类情况时就可以暂停 deployment 的滚动更新。

shell 复制代码
# 暂停滚动更新
kubectl rollout pause deploy <name>

# 恢复滚动更新
kubectl rollout resume deploy <name>

二、StatefulSet

 专门针对有状态服务进行部署的一个控制器;它与 ReplicaSet 类似,但是在创建和管理 Pod 副本时保持每个 Pod 的标识和稳定的网络身份。++StatefulSet 适用于需要持久化存储和稳定网络标识的应用++,如数据库或有序队列。

1、配置文件
yaml 复制代码
# "---" 表示yaml文件中嵌套了其他的yaml配置文件
---
apiVersion: v1
kind: Service
metadata:
  name: nginx # Service 对象的名字
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web # StatefulSet 对象的名字
spec:
  serviceName: "nginx" # 使用哪个 Service 来管理 dns
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports: # 容器内部要暴露的端口
        - containerPort: 80 # 暴露的端口号
          name: web # 该端口配置的名字
        volumeMounts: # 加载数据卷
        - name: www # 指定加载哪个数据卷
          mountPath: /usr/share/nginx/html # 加载到容器中的哪个目录
  volumeClaimTemplates: # 数据卷模板
  - metadata: # 数据卷描述
      name: www # 数据卷的名称
      annotations: # 数据卷注释
        volume.alpha.kubernetes.io/storage-class: anything
    spec: # 数据卷的规约
      accessModes: [ "ReadWriteOnce" ] # 访问模式
      resources:
        requests:
          storage: 1Gi # 需要 1G 的存储资源
2、创建/查看
shell 复制代码
[root@k8s-master ~]# kubectl create -f xx.yaml

# 查看 service 和 statefulset 缩写:sts
[root@k8s-master ~]# kubectl get service nginx
[root@k8s-master ~]# kubectl get statefulset web

# 查看 PVC 信息
[root@k8s-master ~]# kubectl get pvc

# 查看创建的 pod,这些 pod 是有序的
[root@k8s-master ~]# kubectl get pods -l app=nginx

# 查看这些 pod 的 dns
# 运行一个 pod,基础镜像为 busybox 工具包,利用里面的 nslookup 可以看到 dns 信息
[root@k8s-master ~]# kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
# 容器中执行:nslookup web-0.nginx
3、扩容/缩容

两种方式:

shell 复制代码
# 第一种:scale
[root@k8s-master ~]# kubectl scale statefulset web --replicas=5

# 第二种:通过 patch 更新配置文件
[root@k8s-master ~]# kubectl patch statefulset web -p '{"spec":{"replicas":3}}'
4、更新策略

 对于 StatefulSet 控制器,默认情况下使用的是 RollingUpdate 策略,即滚动更新策略,一次只更新一个Pod,确保在更新过程中应用保持可用性。

4.1、RollingUpdate

 StatefulSet 的滚动更新策略,同样是修改 pod template 属性后触发更新,但是由于 pod 是有序的,在 StatefulSet 中更新时是基于 pod 的顺序倒序更新的。

yaml 复制代码
...
spec:
  updateStrategy:
    rollingUpdate:
      partition: [n] # 只会更新那些序号(web-n) >= n 的 pod
    type: RollingUpdate

  partition 默认为 0 ,即全部更新;如果 partition 设置为 3,那么此时滚动更新时,只会更新那些 podName 序号 >= 3 的 pod。

 利用该机制,可以通过控制 partition 的值,来决定只更新其中一部分 pod,确认没有问题后再逐渐增大更新的 pod 数量,最终实现全部 pod 更新。

shell 复制代码
# 镜像更新
[root@k8s-master ~]# kubectl patch sts web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.9.1"}]'

# 或通过 edit 更新
[root@k8s-master ~]# kubectl edit sts web

[root@k8s-master ~]# kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          16m
web-1   1/1     Running   0          17m

# 获取 revison 的历史列表
[root@k8s-master ~]# kubectl rollout history sts web

# 可以查看对应版本详细信息:可以观察到有序的更新
[root@k8s-master ~]# kubectl rollout history sts web --revision=2 
4.2、OnDelete

 只有在 pod 被删除时会进行更新操作。

yaml 复制代码
...
spec:
  updateStrategy:
    type: OnDelete
5、删除
shell 复制代码
# 【级联删除】删除statefulset时会同时删除pods
kubectl delete statefulset web

# 【非级联删除】删除 sts 时不会删除 pods
# 注意:删除sts后,pods就没人管了,之后再删除pod不会重建的
kubectl deelte sts web --cascade=false

# 之后删除service
kubectl delete service nginx

# sts删除后,PVC还会保留着,数据不再使用的话也需要删除
kubectl delete pvc www-web-0 www-web-1

三、DaemonSet

 DaemonSet 保证在每个 Node 上都运行一个容器副本,常用来部署一些集群的日志、监控或者其他系统管理应用。典型的应用包括:

  • 日志收集,比如 fluentd,logstash 等
  • 系统监控,比如 Prometheus Node Exporter,collectd,New Relic agent,Ganglia gmond 等
  • 系统程序,比如 kube-proxy, kube-dns, glusterd, ceph 等
1、配置文件
yaml 复制代码
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
spec:
  selector:
    matchLabels:
      app: logging
  template:
    metadata:
      labels:
        app: logging
        id: fluentd
      name: fluentd
    spec:
      nodeSelector: 
        svc_type: microsvc
      containers:
      - name: fluentd-es
        image: agilestacks/fluentd-elasticsearch:v1.3.0
        env:
         - name: FLUENTD_ARGS
           value: -qq
        volumeMounts: # 加载数据卷,避免数据丢失
         - name: containers # 数据卷名字
           mountPath: /var/lib/docker/containers # 将数据卷挂载到容器内的哪个目录
         - name: varlog
           mountPath: /varlog
      volumes: # 定义数据卷
         - hostPath: # 数据卷类型;主机路径的模式,也就是与node的共享目录
             path: /var/lib/docker/containers # node中的共享目录
           name: containers
         - hostPath:
             path: /var/log
           name: varlog
2、指定 Node 节点

没有指定 node 节点时,系统会为每个非 master 节点部署。

DaemonSet 会忽略 Node 的 unschedulable 状态,有两种方式来指定 Pod 只运行在指定的 Node 节点上:

  • nodeSelector:只调度到匹配指定 label 的 Node 上
  • nodeAffinity:功能更丰富的 Node 选择器,比如支持集合操作
  • podAffinity:调度到满足条件的 Pod 所在的 Node 上
2.1、nodeSelector

 只调度到匹配指定标签的 Node 上;

yaml 复制代码
# 给 node1 打上标签
kubectl label nodes k8s-node1 svc_type=microsvc

# 查看节点标签
kubectl get no --show-labels

# 然后再 daemonset 配置中设置 nodeSelector
spec:
  template:
    spec:
      nodeSelector:
        svc_type: microsvc
2.2、nodeAffinity

nodeAffinity 目前支持两种:requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。

比如下面的例子代表调度到包含标签 wolfcode.cn/framework-name 并且值为 spring 或 springboot 的 Node 上,并且优选还带有标签 another-node-label-key=another-node-label-value 的Node。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: wolfcode.cn/framework-name
            operator: In
            values:
            - spring
            - springboot
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: pauseyyf/pause
2.3、podAffinity

podAffinity 基于 Pod 的标签来选择 Node,仅调度到满足条件Pod 所在的 Node 上,支持 podAffinity 和 podAntiAffinity。这个功能比较绕,以下面的例子为例:

  • 如果一个 "Node 所在空间中包含至少一个带有 auth=oauth2 标签且运行中的 Pod",那么可以调度到该 Node

  • 不调度到 "包含至少一个带有 auth=jwt 标签且运行中 Pod"的 Node 上

    apiVersion: v1
    kind: Pod
    metadata:
    name: with-pod-affinity
    spec:
    affinity:
    podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
    matchExpressions:
    - key: auth
    operator: In
    values:
    - oauth2
    topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
    podAffinityTerm:
    labelSelector:
    matchExpressions:
    - key: auth
    operator: In
    values:
    - jwt
    topologyKey: kubernetes.io/hostname
    containers:
    - name: with-pod-affinity
    image: pauseyyf/pause

3、更新

 默认使用 RollingUpdate,不建议使用 RollingUpdate,建议使用 OnDelete 模式,这样避免频繁更新 ds;

四、HPA 自动扩/缩容

 通过监控 pod 的 cpu、内存使用率或自定义指标进行自动的扩容或缩容 pod 的数量。

 通常用于 Deployment;不适用于无法扩/缩容的对象,如 DaemonSet。

  • 控制管理器每隔30s(可以通过--horizontal-pod-autoscaler-sync-period修改)查询metrics的资源使用情况
  • 支持三种metrics类型
    • 预定义metrics(比如Pod的CPU)以利用率的方式计算
    • 自定义的Pod metrics,以原始值(raw value)的方式计算
    • 自定义的object metrics
  • 支持两种metrics查询方式:Heapster和自定义的REST API
  • 支持多metrics
1、开启指标服务

 用于之后查看资源的cpu、内存的指标。

shell 复制代码
# 下载 metrics-server 组件配置文件
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server-components.yaml

# 修改镜像地址为国内的地址
sed -i 's/k8s.gcr.io\/metrics-server/registry.cn-hangzhou.aliyuncs.com\/google_containers/g' metrics-server-components.yaml

# 修改容器的 tls 配置:不验证 tls,在 containers 的 args 参数中增加 "--kubelet-insecure-tls" 参数

# 安装组件
kubectl apply -f metrics-server-components.yaml

# 查看 pod 状态
kubectl get pods --all-namespaces | grep metrics

# 查看 pods 资源使用情况
kubectl top pods
2、cpu、内存指标监控

 实现 cpu 或内存的监控,首先有个前提条件是该对象必须配置了 resources.requests.cpuresources.requests.memory 才可以,然后配置当 cpu、memory 达到上述配置的百分比后进行扩容或缩容。

创建一个 HPA:

  1. 先准备一个好一个有做资源限制的 deployment
  2. 执行命令 kubectl autoscale deploy nginx-deploy --cpu-percent=20 --min=2 --max=5(cpu利用率达到20%左右,进行扩容,上限为5个;利用率降到20%以下时,进行缩容,最低降为2个)
  3. 通过 kubectl get hpa 可以获取 HPA 信息
shell 复制代码
# 测试:找到对应服务的 service,编写循环测试脚本提升内存与 cpu 负载
while true; do wget -q -O- http://<ip:port> > /dev/null ; done
# 可以通过多台机器执行上述命令,增加负载,当超过负载后可以查看 pods 的扩容情况 

# 查看 pods 资源使用情况
kubectl top pods

# 扩容测试完成后,再关闭循环执行的指令,让 cpu 占用率降下来,然后过 5 分钟后查看自动缩容情况
3、自定义 metrics
  • 控制管理器开启--horizontal-pod-autoscaler-use-rest-clients
  • 控制管理器的--apiserver指向API Server Aggregator
  • 在API Server Aggregator中注册自定义的metrics API
相关推荐
lichenyang4531 天前
Docker 学习笔记(四):Dockerfile,把项目打成自己的镜像
docker·容器
lichenyang4531 天前
Docker 学习笔记(三):Docker 网络、bridge、子网和容器互通
docker·容器
lichenyang4531 天前
Docker 学习笔记(二):docker run 的参数到底在控制什么?
docker·容器
运维开发故事4 天前
基于 Arthas 的多集群在线诊断系统设计与实现
kubernetes
Patrick_Wilson6 天前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
探索云原生6 天前
K8s 1.36 这个 GA 特性,把 initContainer 拉模型的 hack 干掉了
ai·云原生·kubernetes
云恒要逆袭6 天前
运行你的第一个Docker容器
后端·docker·容器
Java之美7 天前
一次k8s升级引发的DevicePlugin注册失败
云原生·kubernetes
程序员老赵8 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程