无状态与有状态

Deployment控制器设计原则:管理的所有Pod一模一样,提供同一个服务,也不考虑在哪台Node 运行,可随意扩容和缩容。这种应用称为“无状态”,例如Web服务

在实际的场景中,并不能满足所有应用,尤其是分布式应用,会部署多个实例,这些实例之间往往有 依赖关系,例如主从关系、主备关系,这种应用称为“有状态”,例如MySQL主从、Etcd集群

StatefulSet 控制器概述

StatefulSet控制器用于部署有状态应用,满足一些有状态应 用的需求:

  • Pod有序的部署、扩容、删除和停止

  • Pod分配一个稳定的且唯一的网络标识

  • Pod分配一个独享的存储

StatefulSet 控制器:网络标识

稳定的网络标识:使用Headless Service(相比普通Service只是将spec.clusterIP定义为None)来维 护Pod网络身份,会为每个Pod分配一个数字编号并且按照编号顺序部署。还需要在StatefulSet添加 serviceName: “nginx”字段指定StatefulSet控制器要使用这个Headless Service。

稳定主要体现在主机名和Pod A记录:

主机名:<statefulset名称>-<编号>

Pod DNS A记录:<statefulset名称-编号>...svc.cluster.local

部署statefulset控制器

vim statefulset-web.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: statefulset-web
spec:
serviceName: "handless-service"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web

部署handless-service

vim handless-service.yaml

apiVersion: v1
kind: Service
metadata:
name: handless-service
spec:
clusterIP: None
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 9376

验证查看

kubectl apply -f statefulset-web.yaml
kubectl apply -f handless-service.yaml
kubectl get pod,svc
kubectl get statefulsets.apps

查看Pod创建顺序:

查看主机名:

kubectl exec statefulset-web-0 -- hostname
kubectl exec statefulset-web-1 -- hostname
kubectl exec statefulset-web-2 -- hostname

测试A记录解析:

kubectl run -it dns-test --rm --image=busybox:1.28.4 -- sh
nslookup handless-service

解析出对应的三个Pod IP记录,其他Pod可使用这个名称访问:

StatefulSet 控制器:独享存储

独享存储:StatefulSet的存储卷使用VolumeClaimTemplate创建,称为卷申请模板,当StatefulSet使用 VolumeClaimTemplate创建一个PersistentVolume时,同样也会为每个Pod分配并创建一个编号的PVC, 每个PVC绑定对应的PV,从而保证每个Pod都拥有独立的存储。

部署nfs

#安装nfs安装包(每个k8s节点都要安装)
yum install nfs-utils

#创建nfs共享目录
mkdir -p /nfs/kubernetes

#修改nfs配置文件
vim /etc/exports
/nfs/kubernetes *(rw,no_root_squash)

#启动nfs并加入开机自启
systemctl start nfs
systemctl enable nfs

#尝试在别的K8s节点挂载nfs共享目录
mount -t nfs 192.168.0.13:/nfs/kubernetes /mnt/

部署nfs-storage

git clone https://github.com/kubernetes-incubator/external-storage 
cd nfs-client/deploy
kubectl apply -f rbac.yaml # 授权访问apiserver
kubectl apply -f deployment.yaml # 部署插件,需修改里面NFS服务器地址与共享目录
kubectl apply -f class.yaml # 创建存储类

kubectl get sc # 查看存储类

部署statefulset控制器

vim statefulset-web.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: statefulset-web
spec:
serviceName: "handless-service"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
storageClassName: "managed-nfs-storage"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1G

kubectl delete -f statefulset-web.yaml 
kubectl apply -f statefulset-web.yaml

nfs服务器共享目录

测试在这三个pv目录下创建三个不同内容的index.html页面,然后访问对应不同的Pod的IP

验证访问

从上面图中,我们可以看到每个pod的数据都是独立存储的。

测试删除statefulset控制器

从上图可以看出,当我们删除pod的时候,并没有删除对应pvc和pv,而是做了数据的持久化。

再次重建测试能否访问之前的数据

从上图可以看出,重建之后,数据还是之前的数据,每个pvc对应绑定每个pv,pod的IP发生了变化。

kubernetes根据pod的编号找同样编号的pvc去挂载,从而实现了数据持久化。

通过编号保证每个pod的启动顺序

通过编号为每个pod创建不同的主机名以及dns A记录

通过编号为每个pod创建独立的pvc

StatefulSet 控制器:小结

StatefulSet与Deployment区别:有身份的!

身份三要素:

  • 域名

  • 主机名

  • 存储(PVC)