简介
statefulset也是一种POD控制器,那为什么要放在PV/PVC之后再简介呢?这是因为statefulset是必须也有持久化数据,每个POD所对应的PV都是不一样的。相对于Deployment所创建的POD是无状态的,那statefulset是属于有状态的,即可以保留POD的状态信息。其特点有:
- 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
- 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现,
- 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
- 有序收缩,有序删除(即从N-1到0),当Pod被删除时,它们被终止的顺序是从N-1到0。kubectl delete -f pod.yaml然后手动删除pv。
- 有序的滚动更新
- 有序扩展: 当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。
补充:
- StatefulSet为每个Pod副本创建了一个DNS域名,这个域名的格式为:S(podname).(headless servername),也就意味着服务间是通过Pod域名来通信而非PodIP,因为当Pod所在Node发生故障时,Pod会被飘移到其它 Node上,PodIP会发生变化,但是Pod域名不会有变化
- StatefulSet使用Headless服务来控制Pod的域名,这个域名的FQDN为:S(servicename).$(namespace).svc.cluster.local。其中,“cluster.local”指的是集群的域名
headless service这东西有什么用?这是因为statefulset的特性所决定的:在statefulset中是要求有序的,每一个pod的名称必须是固定的。当节点挂了,重建之后的标识符是不变的,每一个节点的节点名称是不能改变的。pod名称是作为pod识别的唯一标识符,必须保证其标识符的稳定并且唯一。为了实现标识符的稳定,这时候就需要一个headless service 解析直达到pod,还需要给pod配置一个唯一的名称。
Statefulset使用场景
- 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC 来实现。
- 稳定的网络标识符,即Pod 重新调度后其iPodName 和 HostName不变。
- 有序部署,有序扩展,基于init containers 来实现。
-
NFS实例演示
在一台机器上面安装NFS
yum install -y nfs-common nfs-utils rpcbind
mkdir /data/nfs{0..3}
chmod 755 /data/nfs{0..3}
chown nfsnobody /data/nfs{0..3}
cat >/etc/exports <<EOF
/data/nfs0 *(rw,no_root_squash,no_all_squash,sync)
/data/nfs1 *(rw,no_root_squash,no_all_squash,sync)
/data/nfs2 *(rw,no_root_squash,no_all_squash,sync)
/data/nfs3 *(rw,no_root_squash,no_all_squash,sync)
EOF
systemctl start rpcbind && systemctl enable rpcbind
systemctl start nfs && systemctl enable nfs
/data/nfs0:共享的数据目录
- *:表示任何人都有权限连接,当然也可以是一个网段,一个 IP,也可以是域名
- rw:读写的权限
- sync:表示文件同时写入硬盘和内存
- no_root_squash:当登录 NFS 主机使用共享目录的使用者是 root 时,其权限将被转换成为匿名使用者,通常它的 UID 与 GID,都会变成 nobody 身份
在另一台机器可以使用showmount测试下。
[root@master ~]# showmount -e 192.168.1.61
Export list for 192.168.1.61:
/data/nfs3 *
/data/nfs2 *
/data/nfs1 *
/data/nfs0 *
再实际挂载测试下:
mkdir /nfs{0..3}
mount -t nfs 192.168.1.61:/data/nfs0 /nfs0
date >/nfs0/index.html
umount /nfs0/
没有出现异常的话,说明NFS工作是正常的了。
或者使用docker来实现NFS:
docker run -d --net=host --privileged --name nfs-server katacoda/contained-nfs-server:centos7 /exports/data-0001 /exports/data-0002
这时以/etc/exports这个配置,就相当于在192.168.1.61创建了一个共享目录,分别为:/data/nfs0 , /data/nfs1 /data/nfs2 , /data/nfs3,这几个以下需要使用到。
statefulset实例
创建一个NFS共享的PV,以下配置申明了一个名为pv-volume0的5G空间,挂载的目录为 192.168.1.61:/data/nfs0。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-volume0
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: slow
# mountOptions:
# - hard
# - nfsvers=4.1
nfs:
path: /data/nfs0
server: 192.168.1.61
再创建一个StatefulSet
# 创建一个名为nginx的headless svc,端口80为内部使用,注意跟nodePort的区别
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
# 副本为3的StatefulSet,serviceName名为nginx
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
# volumeClainTemplate作用:当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,从而有自己专用的存储卷。
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "slow"
resources:
requests:
storage: 1Gi
无注释版本
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:
selector:
matchLabels:
app: nginx
serviceName: "nginx"
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "slow"
resources:
requests:
storage: 1Gi
由于StatefulSet是3个副本,但是看到只创建了web-0。在创建web-1时出错了。这是由于没有可用的PV了。所以一个POD是绑定一个PVC的,每个POD所对应的存储空间是不一样的。
[root@master nfs]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 3m44s 10.32.0.5 master <none> <none>
web-1 0/1 Pending 0 42s <none> <none> <none> <none>
按上面的创建PV的模板修改一下,再创建2个PV。之后就可以看到正常运行了。
[root@master nfs]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 4m2s 10.32.0.5 master <none> <none>
web-1 1/1 Running 0 3m56s 10.44.0.5 node1 <none> <none>
web-2 1/1 Running 0 60s 10.32.0.7 master <none> <none>
[root@master nfs]# curl 10.32.0.5
/data/nfs0 ---> web-0
[root@master nfs]# curl 10.44.0.5
/data/nfs1 ---> web-1
[root@master nfs]# curl 10.32.0.7
/data/nfs2 ---> web-2
查看一下PV以及PVC
[root@master nfs]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-volume0 5Gi RWO Recycle Bound default/www-web-0 slow 26m
pv-volume1 8Gi RWO Retain Bound default/www-web-1 slow 8m10s
pv-volume2 10Gi RWO Retain Bound default/www-web-2 slow 7m25s
[root@master nfs]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound pv-volume0 5Gi RWO slow 11m
www-web-1 Bound pv-volume1 8Gi RWO slow 10m
www-web-2 Bound pv-volume2 10Gi RWO slow 8m3s
删除pvc时,回收策略为Recycle,会自动删除文件,而Retain,会保存文件。
清空PV绑定信息CLAIM
现在所有的PV都有default/www-web-xx,那其他的pvc就无法和pv进行绑定,即使我们删除与之绑定的pvc,状态会变成Released,但是CLAIM信息还在,其他pvc依旧无法进行绑定,因此需要清空PV绑定信息CLAIM
kubectl edit pv pv-volume0
statefulset总结
- 匹配 Pod name ( 网络标识 ) 的模式为:$(statefulset名称)-$(序号),从零开始,比如上面的示例:web-0,web-1, web-2
- StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会 被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化
- StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:
$(service name).$(namespace).svc.cluster.local
- 根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式: (volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Pod name=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2
- 删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv
Statefulset的启停顺序:
- 有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1),并且,在下一个 Pod运行之前所有之前的Pod必须都是Running和Ready状态。
- 有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
- 有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。