【kubernetes系列】Kubernetes之statefulset和daemonset控制器

news/2024/6/3 18:31:02 标签: kubernetes, docker, 容器

本章节将分享kubernetes中的StatefulSet控制器和DaemonSet控制器,这是用于另外两种场景的pod控制器。

一、StatefulSet

1、StatefulSet介绍

在Kubernetes系统中,Pod的管理对象RC(RS)、Deployment、DaemonSet和Job都是面向无状态的服务。但现实中有很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Kafka集群、Zookeeper集群等。为了解决这一问题,就引入了StatefulSet用于保留Pod的状态信息。

这些应用集群有以下一些共同点:

  • 每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并且通信。
  • 集群的规模是比较固定的,集群规模不能随意变动。
  • 集群里的每个节点都是有状态的,通常会持久化数据到永久存储中。
  • 如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。

而如果用 RC/Deployment 控制 Pod 副本数的方式来实现上述有状态的集群,则我们会发现第一点是无法满足的,因为Pod的名字是随机产生的,Pod的IP地址也是在运行期才确定且可能有变动的,我们事先无法为每个Pod确定唯一不变的ID,为了能够在其他节点上恢复某个失败的节点,这种集群中的Pod需要挂接某种共享存储,为了解决这个问题,Kubernetes从v1.4版本开始引入了PetSet这个新的资源对象,并且在v1.5版本时更名为StatefulSet,StatefulSet从本质上来说,可以看作 Deployment/RC 的一个特殊变种,它有如下一些特性。

  • StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名字叫zookeeper,那么第一个Pod叫zookeeper-0,第二个Pod叫zookeeper-1,第三个叫zookeeper-2,以此类推
  • StatefulSet控制的Pod副本的启停顺序是受控的,有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现。而有序收缩,有序删除(即从N-1到0)
  • StatefulSet里的Pod采用稳定的持久化存储卷,通过 PV/PVC 来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。

StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service(无头服务)配合使用,即在每个StatefulSet的定义中要声明它属于哪个Headless Service。Headless Service与普通Service的关键区别在于,它没有Cluster IP,如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例创建了一个DNS域名,这个域名的格式为:

( p o d n a m e ) . (podname). (podname).(headless service name)

比如一个3节点的 StatefulSet 集群,对应的 Headless Service 的名字为zookeeper,StatefulSet的名字为zk,则 StatefulSet 里面的 3 个 Pod 的 DNS 名称分别为zk-0.zookeeper、zk-1.zookeeper、zk-2.zookeeper,这些DNS名称可以直接在集群的配置文件中固定下来。

从上面的应用场景可以发现,StatefulSet由以下几个部分组成:

  • 用于定义网络标志(DNS domain)的Headless Service(无头服务)
  • 用于创建PersistentVolumes的volumeClaimTemplates (存储卷申请模板)
  • 定义具体应用的StatefulSet

2、为什么要有headless??

在deployment中,每一个pod是没有名称,是随机字符串,是无序的。而statefulset中是要求有序的,每一个pod的名称必须是固定的。当节点挂了,重建之后的标识符是不变的,每一个节点的节点名称加粗样式是不能改变的。pod名称是作为pod识别的唯一标识符,必须保证其标识符的稳定并且唯一。
为了实现标识符的稳定,这时候就需要一个headless service 解析直达到pod,还需要给pod配置一个唯一的名称。

3、为什么要 有volumeClainTemplate??

大部分有状态副本集都会用到持久存储,比如分布式系统来说,由于数据是不一样的,每个节点都需要自己专用的存储节点。而在deployment中pod模板中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,而statefulset定义中的每一个pod都不能使用同一个存储卷,由此基于pod模板创建pod是不适应的,这就需要引入volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,从而有自己专用的存储卷。

4、statefulSet使用演示

在创建StatefulSet之前需要准备的东西,值得注意的是创建顺序非常关键,创建顺序如下:

  • volume
  • Persistent Volume
  • Persistent Volume Claim
  • Service
  • StatefulSet
    Volume可以有很多种类型,比如nfs、ceeph、gluster等,我们这里使用的NFS来创建。接下来我们按顺序一步一步操作以statefulset的方式部署zookeeper集群。

1)volume创建

volume的创建可以通过手动管理,也可以通过provisioner自动管理。这里测试我选择直接手动进行创建管理。

#首先搭建nfs
[root@k8s-m1 k8s-volumes]# vim install_nfs.sh 
#!/bin/bash
yum install -y nfs-utils rpcbind
mkdir /data/volumes -pv
#注意网段添加正确
echo -e "/data/volumes 192.168.2.0/24(rw,sync,no_root_squash)" > /etc/exports
exportfs -arv
systemctl restart  nfs

[root@k8s-m1 k8s-volumes]# sh install_nfs.sh 

2)Persistent Volume

手动创建4个2G的pv,多创建一个后面用于扩展

[root@k8s-m1 k8s-volumes]# vim pv-demo.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
  labels:
    name: pv001
spec:
  nfs:
    path: /data/volumes/v1
    server: 192.168.2.140
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002
  labels:
    name: pv002
spec:
  nfs:
    path: /data/volumes/v2
    server: 192.168.2.140
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003
  labels:
    name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: 192.168.2.140
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv004
  labels:
    name: pv004
spec:
  nfs:
    path: /data/volumes/v4
    server: 192.168.2.140
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 2Gi

[root@k8s-m1 k8s-volumes]# kubectl apply  -f pv-demo.yaml 
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created

#注意上面的目录不需要手动去其他服务器挂载

3)Persistent Volume Claim

pvc不需要手动创建,后面k8s会自动绑定。

4)以statefulset的方式部署zookeeper集群

zookeeper的部署方式很多,看自己需要,可以用不用的pod控制器部署。具体看使用场景

##所需要部署的service文件,集群内部之间调用需要。
[root@k8s-m1 k8s-volumes]# cat zookeeper-svc.yml 
apiVersion: v1
kind: Service
metadata:
  name: zk-cs
  namespace: zookeeper
  labels:
    app: zk
spec:
  ports:
    - port: 2181
      name: client
  selector:
    app: zk
---
apiVersion: v1
kind: Service
metadata:
  name: zk-hs
  namespace: zookeeper
  labels:
    app: zk
spec:
  ports:
    - port: 2888
      name: server
    - port: 3888
      name: leader-election
  clusterIP: None
  selector:
    app: zk
    
##所需要部署的StatefulSet文件
[root@k8s-m1 k8s-volumes]# cat zookeeper.yml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zk
  namespace: zookeeper
spec:
  selector:
    matchLabels:
      app: zk
  serviceName: zk-hs
  replicas: 3
  updateStrategy:
    type: RollingUpdate
  podManagementPolicy: Parallel
  template:
    metadata:
      labels:
        app: zk
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - zk
              topologyKey: kubernetes.io/hostname
      containers:
        - name: kubernetes-zookeeper
          imagePullPolicy: IfNotPresent
          image: 'kubebiz/zookeeper:3.4.10'
          resources:
            requests:
              memory: 1Gi
              cpu: '0.5'
          ports:
            - containerPort: 2181
              name: client
            - containerPort: 2888
              name: server
            - containerPort: 3888
              name: leader-election
          command:
            - sh
            - '-c'
            - >-
              start-zookeeper --servers=3 --data_dir=/var/lib/zookeeper/data
              --data_log_dir=/var/lib/zookeeper/data/log
              --conf_dir=/opt/zookeeper/conf --client_port=2181
              --election_port=3888 --server_port=2888 --tick_time=2000
              --init_limit=10 --sync_limit=5 --heap=512M --max_client_cnxns=60
              --snap_retain_count=3 --purge_interval=12
              --max_session_timeout=40000 --min_session_timeout=4000
              --log_level=INFO
          readinessProbe:
            exec:
              command:
                - sh
                - '-c'
                - zookeeper-ready 2181
            initialDelaySeconds: 10
            timeoutSeconds: 5
          livenessProbe:
            exec:
              command:
                - sh
                - '-c'
                - zookeeper-ready 2181
            initialDelaySeconds: 10
            timeoutSeconds: 5
          volumeMounts:
            - name: datadir
              mountPath: /var/lib/zookeeper
  volumeClaimTemplates:
    - metadata:
        name: datadir
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
##部署生效
[root@k8s-m1 k8s-volumes]# kubectl apply -f zookeeper-svc.yml  
[root@k8s-m1 k8s-volumes]# kubectl apply -f zookeeper.yml

5)检查集群部署情况

[root@k8s-m1 k8s-volumes]# kubectl get po -n zookeeper 
NAME   READY   STATUS    RESTARTS   AGE
zk-0   1/1     Running   0          30m
zk-1   1/1     Running   0          30m
zk-2   1/1     Running   0          28m

[root@k8s-m1 k8s-volumes]# kubectl get svc -n zookeeper 
NAME    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
zk-cs   ClusterIP   10.102.223.72   <none>        2181/TCP            22m
zk-hs   ClusterIP   None            <none>        2888/TCP,3888/TCP   22m
#可以看到zk-hs(headless service)的clusterIP为none

[root@k8s-m1 k8s-volumes]# kubectl describe  svc -n zookeeper zk-hs 
Name:              zk-hs
Namespace:         zookeeper
Labels:            app=zk
Annotations:       <none>
Selector:          app=zk
Type:              ClusterIP
IP:                None
Port:              server  2888/TCP
TargetPort:        2888/TCP
Endpoints:         10.244.11.49:2888,10.244.42.168:2888,10.244.81.174:2888
Port:              leader-election  3888/TCP
TargetPort:        3888/TCP
Endpoints:         10.244.11.49:3888,10.244.42.168:3888,10.244.81.174:3888
Session Affinity:  None
Events:            <none>

##查看pvc,pv的绑定情况
[root@k8s-m1 ~]# kubectl get pvc  -n  zookeeper 
NAME           STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
datadir-zk-0   Bound    pv003    2Gi        RWO,RWX                       4h22m
datadir-zk-1   Bound    pv002    2Gi        RWO,RWX                       4h22m
datadir-zk-2   Bound    pv001    2Gi        RWO,RWX                       4h22m

[root@k8s-m1 ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE
pv001   2Gi        RWO,RWX        Retain           Bound    zookeeper/datadir-zk-2                           4h49m
pv002   2Gi        RWO,RWX        Retain           Bound    zookeeper/datadir-zk-1                           4h49m
pv003   2Gi        RWO,RWX        Retain           Bound    zookeeper/datadir-zk-0                           4h49m
pv004   2Gi        RWO,RWX        Retain           Available                                                 4h49m

6)删除和重新部署

而当删除的时候是从zk-2开始进行删除的,关闭是逆向关闭

[root@k8s-m1 k8s-volumes]# kubectl delete -f zookeeper.yml 
statefulset.apps "zk" deleted

[root@k8s-m1 k8s-volumes]# kubectl get po -n zookeeper -w
NAME   READY   STATUS        RESTARTS   AGE
zk-0   1/1     Terminating   0          33s
zk-1   1/1     Terminating   0          33s
zk-2   1/1     Terminating   0          32s
zk-2   0/1     Terminating   0          62s
zk-0   0/1     Terminating   0          64s
zk-1   0/1     Terminating   0          64s
zk-1   0/1     Terminating   0          65s
zk-1   0/1     Terminating   0          65s
zk-2   0/1     Terminating   0          65s
zk-2   0/1     Terminating   0          65s
zk-0   0/1     Terminating   0          72s
zk-0   0/1     Terminating   0          72s
#效果不太明显,可以将集群缩减到2个pod,会发现zk-2被删除

此时PVC依旧存在的,再重新创建pod时,依旧会重新去绑定原来的pvc

[root@k8s-m1 k8s-volumes]# kubectl apply  -f zookeeper.yml 
statefulset.apps/zk created
[root@k8s-m1 k8s-volumes]# kubectl get po,pvc -n zookeeper 
NAME       READY   STATUS    RESTARTS   AGE
pod/zk-0   0/1     Running   0          7s
pod/zk-1   0/1     Running   0          7s
pod/zk-2   0/1     Running   0          7s

NAME                                 STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/datadir-zk-0   Bound    pv003    2Gi        RWO,RWX                       4h31m
persistentvolumeclaim/datadir-zk-1   Bound    pv002    2Gi        RWO,RWX                       4h31m
persistentvolumeclaim/datadir-zk-2   Bound    pv001    2Gi        RWO,RWX                       4h31m
[root@k8s-m1 k8s-volumes]# kubectl get pv -n zookeeper 
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE
pv001   2Gi        RWO,RWX        Retain           Bound    zookeeper/datadir-zk-2                           4h56m
pv002   2Gi        RWO,RWX        Retain           Bound    zookeeper/datadir-zk-1                           4h56m
pv003   2Gi        RWO,RWX        Retain           Bound    zookeeper/datadir-zk-0                           4h56m
pv004   2Gi        RWO,RWX        Retain           Available                                                 4h56m

5、管理策略、滚动更新、扩展伸缩、版本升级、修改更新策略

1)管理策略

StatefulSet 允许放宽其排序保证,通过 .spec.podManagementPolicy 来设置。

  • 顺序 Pod 管理
    podManagementPolicy: "OrderedReady"是默认设置。它保证了Pod顺序。
  • 并行 Pod 管理
    podManagementPolicy: "Parallel"让 StatefulSet 控制器并行的启动或终止所有的 Pod,启动或者终止其他 Pod 前,无需等待 Pod 进入 Running 和 ready 或者完全停止状态。 这个选项只会影响扩缩操作的行为,更新不会被影响。
[root@k8s-m1 k8s-volumes]# kubectl explain statefulset.spec.podManagementPolicy

2)滚动更新

StatefulSet中的 .spec.updateStrategy用于配置和禁用掉自动滚动更新 Pod 的容器、标签、资源请求或限制、以及注解。有两个允许的值:

  • OnDelete
    当 StatefulSet 的 .spec.updateStrategy.type 设置为 OnDelete 时, 它的控制器将不会自动更新 StatefulSet 中的 Pod。 用户必须手动删除 Pod 以便让控制器创建新的 Pod。
  • RollingUpdate
    RollingUpdate 更新策略对 StatefulSet 中的 Pod 执行自动的滚动更新。这是默认的更新策略。StatefulSet 控制器将在 StatefulSet 中删除并重新创建每个 Pod。 它将以与 Pod 终止相同的顺序进行(从最大的序数到最小的序数),每次更新一个 Pod。 在更新其前身之前,它将等待正在更新的 Pod 状态变成正在运行并就绪。如下操作的滚动更新是有2-0的顺序更新。
[root@k8s-m1 k8s-volumes]# kubectl explain statefulset.spec.updateStrategy.type

[root@k8s-m1 k8s-volumes]# vim zookeeper.yml   #修改image版本为3.5.8,注意由于版本升级,yaml文件其他地方也有一定变化
.....
          image: 'kubebiz/zookeeper:3.5.8'....

[root@k8s-m1 k8s-volumes]# kubectl apply -f zookeeper.yml 
statefulset.apps/zk configured
[root@k8s-m1 k8s-volumes]# kubectl get po -n zookeeper -w
NAME   READY   STATUS        RESTARTS   AGE
zk-0   1/1     Running       0          24m
zk-1   1/1     Running       0          24m
zk-2   0/1     Terminating   4          3m23s
zk-2   0/1     Terminating   4          3m25s
zk-2   0/1     Terminating   4          3m25s
zk-2   0/1     Pending       0          0s
zk-2   0/1     Pending       0          0s
zk-2   0/1     ContainerCreating   0          1s
zk-2   0/1     ContainerCreating   0          1s
zk-2   0/1     Running             0          3s
zk-2   1/1     Running             0          16s
zk-1   1/1     Terminating         0          24m
zk-1   0/1     Terminating         0          24m
zk-1   0/1     Terminating         0          24m
zk-1   0/1     Terminating         0          24m
zk-1   0/1     Pending             0          0s
zk-1   0/1     Pending             0          0s
zk-1   0/1     ContainerCreating   0          1s

在创建的每一个Pod中,每一个pod自己的名称都是可以被解析的,可以使用nslookup测试。

3)扩展伸缩

方法一:scale方式

[root@k8s-m1 k8s-volumes]# kubectl scale -n zookeeper  statefulset zk  --replicas=4 #扩容副本增加到4个
statefulset.apps/zk scaled
[root@k8s-m1 k8s-volumes]# kubectl get po -n zookeeper -w  ##由于调度策略的原因,pod处于pending状态,可以通过修改调度策略或者增加一个node节点。
NAME   READY   STATUS    RESTARTS   AGE
zk-0   1/1     Running   0          30s
zk-1   1/1     Running   0          30s
zk-2   1/1     Running   0          30s
zk-3   0/1     Pending   0          2s

#查看pv绑定状态
[root@k8s-m1 k8s-volumes]# kubectl get pv   
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                    STORAGECLASS   REASON   AGE
pv001   2Gi        RWO,RWX        Retain           Bound       zookeeper/datadir-zk-2                           5h44m
pv002   2Gi        RWO,RWX        Retain           Bound       zookeeper/datadir-zk-1                           5h44m
pv003   2Gi        RWO,RWX        Retain           Bound       zookeeper/datadir-zk-0                           5h44m
pv004   2Gi        RWO,RWX        Retain           Bound       zookeeper/datadir-zk-3                           18m

方法二:补丁方式

[root@k8s-m1 k8s-volumes]# kubectl patch statefulsets.apps  -n zookeeper zk  -p '{"spec":{"replicas":2}}'
statefulset.apps/zk patched
[root@k8s-m1 k8s-volumes]# kubectl get po -n zookeeper -w
NAME   READY   STATUS        RESTARTS   AGE
zk-0   1/1     Running       0          3m50s
zk-1   1/1     Running       0          3m50s
zk-2   1/1     Terminating   0          3m50s
zk-2   0/1     Terminating   0          4m18s
zk-2   0/1     Terminating   0          4m25s
zk-2   0/1     Terminating   0          4m25s

4)更新策略和版本升级

为保证升级效果,和zookeeper版本兼容,我将zookeeper的镜像简单做个修改,并重新打上其他标签进行测试。

[root@k8s-m2 tmp]# cat  Dockerfile
FROM kubebiz/zookeeper:3.5.8
RUN touch /root/test.txt

[root@k8s-m2 tmp]# docker build -t kubebiz/zookeeper:v1 .
Sending build context to Docker daemon  22.02kB
Step 1/2 : FROM kubebiz/zookeeper:3.5.8
 ---> 6e212d2b24e9
Step 2/2 : RUN touch /root/test.txt
 ---> Running in c4ca3538d508
Removing intermediate container c4ca3538d508
 ---> 6ed6fe7dc4c0
Successfully built 6ed6fe7dc4c0
Successfully tagged kubebiz/zookeeper:v1
##修改更新策略,以partition方式进行更新,更新值为2,只有zk编号大于等于2的才会进行更新。类似于金丝雀部署方式。
[root@k8s-m1 k8s-volumes]# kubectl patch statefulsets.apps -n zookeeper zk -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'

##查看效果
[root@k8s-m1 k8s-volumes]# kubectl describe  sts zk  -n zookeeper |grep Partition
  Partition:        2
##升级镜像
[root@k8s-m1 k8s-volumes]# kubectl set image -n zookeeper sts/zk kubernetes-zookeeper=kubebiz/zookeeper:v1
statefulset.apps/zk image updated

##动态观察效果
[root@k8s-m1 k8s-volumes]# kubectl get po -n zookeeper  -w
NAME   READY   STATUS        RESTARTS   AGE
zk-0   1/1     Running       0          46s
zk-1   1/1     Running       0          46s
zk-2   1/1     Terminating   0          46s
zk-2   0/1     Terminating   0          77s
zk-2   0/1     Terminating   0          77s
zk-2   0/1     Terminating   0          77s
zk-2   0/1     Pending       0          0s
zk-2   0/1     Pending       0          0s
zk-2   0/1     ContainerCreating   0          0s
zk-2   0/1     ContainerCreating   0          2s
zk-2   0/1     Running             0          4s
zk-2   1/1     Running             0          12s

#检查镜像是否升级成功
[root@k8s-m1 k8s-volumes]# kubectl get pods -n zookeeper   -o yaml |grep image
              f:image: {}
              f:imagePullPolicy: {}
      image: kubebiz/zookeeper:3.5.8
      imagePullPolicy: IfNotPresent
      image: kubebiz/zookeeper:3.5.8
      imageID: docker-pullable://kubebiz/zookeeper@sha256:456f0c69efe9ba9d8ad71d42cb53d2d8009dd2df196239ad39f6926c90031c41
              f:image: {}
              f:imagePullPolicy: {}
      image: kubebiz/zookeeper:3.5.8
      imagePullPolicy: IfNotPresent
      image: kubebiz/zookeeper:3.5.8
      imageID: docker-pullable://kubebiz/zookeeper@sha256:456f0c69efe9ba9d8ad71d42cb53d2d8009dd2df196239ad39f6926c90031c41
              f:image: {}
              f:imagePullPolicy: {}
      image: kubebiz/zookeeper:v1
      imagePullPolicy: IfNotPresent
      image: kubebiz/zookeeper:v1
      imageID: docker://sha256:6ed6fe7dc4c063dada82480d971be894798abd24769c05b436238d373ba201f7
#通过结果发现只有一个pod的镜像升级了,其他的未发生变化。


将剩余的Pod也更新版本,只需要将更新策略的partition值改为0即可,如下:

[root@k8s-m1 k8s-volumes]# kubectl patch statefulsets.apps -n zookeeper zk -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'
statefulset.apps/zk patched
[root@k8s-m1 k8s-volumes]# kubectl get po -n zookeeper  -w   ##观察效果即可

二、DaemonSet

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

使用 DaemonSet 的一些典型用法:
运行集群存储 daemon,例如在每个 Node 上运行 glusterd、ceph。
在每个 Node 上运行日志收集 daemon,例如fluentd、logstash。
在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond。

示例:创建DaemonSet
以下是DaemonSet的示例spec文件,运行fluentd-elasticsearch image:

##相应的yml文件
[root@k8s-m1 k8s-volumes]# vim fluented-daemonset.yml
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: '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

##创建
[root@k8s-m1 k8s-volumes]# kubectl apply  -f fluented-daemonset.yml 
daemonset.apps/fluentd-elasticsearch created

##查看
[root@k8s-m1 k8s-volumes]# kubectl get po -n kube-system  -o wide
NAME                                        READY   STATUS             RESTARTS   AGE     IP              NODE     NOMINATED NODE   READINESS GATES
calico-kube-controllers-bcc6f659f-7q2kc     1/1     Running            2          76d     10.244.81.168   k8s-m2   <none>           <none>
calico-node-cgffm                           1/1     Running            21         223d    192.168.2.140   k8s-m1   <none>           <none>
calico-node-f6jct                           1/1     Running            3          112d    192.168.2.141   k8s-m2   <none>           <none>
calico-node-nxfv8                           1/1     Running            62         47d     192.168.2.142   k8s-m3   <none>           <none>
coredns-6d56c8448f-5p76s                    1/1     Running            1          76d     10.244.11.58    k8s-m3   <none>           <none>
coredns-6d56c8448f-ql5lr                    1/1     Running            3          112d    10.244.81.166   k8s-m2   <none>           <none>
fluentd-elasticsearch-hnlct                 1/1     Running            0          2m20s   10.244.11.43    k8s-m3   <none>           <none>
fluentd-elasticsearch-j78v4                 1/1     Running            0          2m20s   10.244.42.160   k8s-m1   <none>           <none>
fluentd-elasticsearch-t5vwh                 1/1     Running            0          2m20s   10.244.81.186   k8s-m2   <none>           <none>
......

DaemonSet同样会受到Taint(污点)的抵制,如果不在Pod中配置匹配的Toleration(容忍度),那么DaemonSet不会在拥有Taint(污点)属性的node上部署Pod。上例中有如下内容,默认情况下master节点就不会部署该服务。但是实验环境我都将所有污点清理了。

tolerations:
    - key: node-role.kubernetes.io/master
      effect: NoSchedule

系统默认为master节点增加了 “node-role.kubernetes.io/master”的Taint(污点),以抵制普通Pod部署在Master节点上,为了使master成为专用节点而添加的Taint(污点)。因为我们预期上例DaemonSet在集群内全局部署,因此需要加入相匹配的Toleration(容忍度)这样Master节点也会部署DaemonSet,或者将master上的污点去除掉。

仅在相同的 Node 上运行 Pod:
如果指定了 .spec.template.spec.nodeSelector,则DaemonSet只会在满足条件的node上部署pod。 类似这种情况,可以指定 .spec.template.spec.affinity,然后 DaemonSet Controller 将在能够与 Node Affinity 匹配的节点上创建 Pod。 如果根本就没有指定,则 DaemonSet Controller 将在所有节点上创建 Pod。
总之,可以通过Taint(污点)、Toleration(容忍度)、Affinity(亲和力)、node label控制DaemonSet部署pod的节点范围。

系统如何调度DaemonSet pod(自1.12起默认禁用):
默认情况下DaemonSet在创建pod时如果为其增加.spec.template.spec.nodeName字段,也就是说所创建的pod运行在那个节上在创建阶段就已经确定,所以DaemonSet中的pod实际上没有接受kubernetes scheduler的调度,它不需要调度,因此产生以下两个特性:

DaemonSet中的pod不遵从节点的unreachable条件,也就是即使节点被系统判定为不可达,DaemonSet仍然试图在其上部署pod。
在集群引导阶段,即使kubernetes scheduler还没有部署生效,DaemonSet仍然可以将pod部署到集群中的任何节点,此特性主要是在集群引导阶段使用。

因为DaemonSet不同于常规pod的调度特性,它带来两个问题:
pod行为不一致。普通pod被创建以后等待调度的阶段称为pending,因为DaemonSet中的pod无需调度,因而无此状态,用户会因此产生迷惑。
pod优先级特性由kubernetes scheduler实现,DaemonSet无此特性。当系统打开pod优先级功能时,pod优先级特性会被DaemonSet忽略,DaemonSet控制器将自己做出调度决策。
为了解决以上两个问题可以使用ScheduleDaemonSetPods,ScheduleDaemonSetPods允许您使用默认调度器(而不是DaemonSet controller)调度DaemonSets,方法是向DaemonSet pods添加NodeAffinity项,而不是.spec.template.spec.nodeName。然后使用默认调度程序将pod绑定到目标主机。如果DaemonSet pod的节点关联性已经存在,则替换它。DaemonSet controller仅在创建或修改DaemonSet pods时执行这些操作,并且对DaemonSet的spec.template不做任何更改。

nodeAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchFields:
      - key: metadata.name
        operator: In
        values:
        - k8s-m2

其中”tk8s-m2”就是原来.spec.nodeName的值,这样pod就会被kubernetes scheduler调度。通过以上操作解决了上述两个问题。但DaemonSet的调度有自己因有的特性,在上文中提到的“不受节点unreachable条件限制”,为了使DaemonSet在使用kubernetes scheduler时仍然保持此特性需要打开集群的”TaintNodesByCondition”特性。

如果DaemonSet使用主机网络那么必需在DaemonSet中添加如下的Toleration:

node.kubernetes.io/network-unavailable:NoSchedule
DaemonSet自动添加的Toleration:
系统在某此条件下会自动为节点添加Taint,比如硬盘不足、网络不可达等,以阻止新pod往不满足条件的节点上调度。但DaemonSet的目的是在全部有资格的node上部署,不希望被这种Taint打断,因经系统也默认为DaemonSet上的pod添加Toleration。不同版本的Toleration有所不同,请自行查看。

对 DaemonSet 执行回滚
找到想要 DaemonSet 回滚到的历史版本(revision)

列出 DaemonSet 的所有版本:

[root@k8s-m1 k8s-volumes]# kubectl rollout history daemonset -n kube-system fluentd-elasticsearch 
daemonset.apps/fluentd-elasticsearch 
REVISION  CHANGE-CAUSE
1         <none>
##该命令返回 DaemonSet 版本列表,由于我只部署了一次,所有只有一个版本

执行以下命令,来查看指定版本的详细信息:

[root@k8s-m1 k8s-volumes]# kubectl rollout history daemonset -n kube-system fluentd-elasticsearch  --revision=1
daemonset.apps/fluentd-elasticsearch with revision #1
Pod Template:
  Labels:       name=fluentd-elasticsearch
  Containers:
   fluentd-elasticsearch:
    Image:      quay.io/fluentd_elasticsearch/fluentd:v2.5.2
    Port:       <none>
    Host Port:  <none>
    Limits:
      memory:   200Mi
    Requests:
      cpu:      100m
      memory:   200Mi
    Environment:        <none>
    Mounts:
      /var/lib/docker/containers from varlibdockercontainers (ro)
      /var/log from varlog (rw)
  Volumes:
   varlog:
    Type:       HostPath (bare host directory volume)
    Path:       /var/log
    HostPathType:
   varlibdockercontainers:
    Type:       HostPath (bare host directory volume)
    Path:       /var/lib/docker/containers
    HostPathType: 
##该命令返回相应版本的详细信息


回滚到指定版本

# 在 --to-revision 中指定从上面步骤中获取到的版本序号
kubectl rollout undo daemonset --to-revision=
如果成功,命令会返回:
daemonset “” rolled back
如果 --to-revision 参数未指定,将选择最近的版本。

观察 DaemonSet 回滚进度

kubectl rollout undo daemonset 向服务器表明启动 DaemonSet 回滚。 真正的回滚是在服务器端异步完成的。

执行以下命令,来观察DaemonSet回滚进度:
kubectl rollout status ds/<daemonset-name>
回滚完成时,输出类似下面:
daemonset “” successfully rolled out

更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出


http://www.niftyadmin.cn/n/1006085.html

相关文章

springboot集成openfeign

一、Feign简介 Feign是一个声明式的伪Http客户端&#xff0c;它使得写Http客户端变得更简单。使用Feign&#xff0c;只需要创建一个接口并注解。它具有可插拔的注解特性&#xff0c;可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon&…

【C/C++】拷贝构造函数的调用 使用方法

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

leetcode:2154. 将找到的值乘以 2(python3解法)

难度&#xff1a;简单 给你一个整数数组 nums &#xff0c;另给你一个整数 original &#xff0c;这是需要在 nums 中搜索的第一个数字。 接下来&#xff0c;你需要按下述步骤操作&#xff1a; 如果在 nums 中找到 original &#xff0c;将 original 乘以 2 &#xff0c;得到新…

算法与数据结构-链表

文章目录 链表和数组的区别常见的链表类型单链表循环链表双向链表 总结 链表和数组的区别 相比数组&#xff0c;链表是一种稍微复杂一点的数据结构。对于初学者来说&#xff0c;掌握起来也要比数组稍难一些。这两个非常基础、非常常用的数据结构&#xff0c;我们常常会放到一块…

【算法与数据结构】28、LeetCode找出字符串中第一个匹配项的下标

文章目录 一、题目二、暴力穷解法三、KMP算法四、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、暴力穷解法 思路分析&#xff1a;首先判断字符串是否合法&#xff0c;然后利用for循环&#xff0c;取出子字符串…

Three.js教程:threejs语法总结

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 其他系列工具&#xff1a; NSDT简石数字孪生 threejs语法总结 本节课从JavaScript面向对象语法的角度&#xff0c;给大家总结下threejs API的使用习惯&#xff0c;这样方便大家更好的使用threejs API。 Three.js语法总结…

23年6月1日软著又面临改革,个人加分评职称和企业申报项目加分的软件著作权登记证书该如何申请?

23年6月1号&#xff0c;国家版权局对软件著作权的申请又做出了改革&#xff0c;本次改革的主要内容是全面普及线上办公。申请人无需向中心递交或邮寄登记申请纸介质材料&#xff0c;“足不出户”即可完成版权登记。 软件著作权登记实现无纸化后&#xff0c;申请人在线登记办理…

大语言模型微调和PEFT高效微调

目录标题 1 解释说明1.1 预训练阶段1.2 微调阶段2 几种微调算法2.1 在线微调2.2 高效微调2.2.1 RLHF2.2.2 LoRA2.2.3 Prefix Tuning2.2.4 Prompt Tuning2.2.5 P-Tuning v21 解释说明 预训练语言模型的成功,证明了我们可以从海量的无标注文本中学到潜在的语义信息,而无需为每一…