本章节将分享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的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出