目录
1. 与无状态服务不同,有状态的StatefulSet 中一个Pod出现故障之后,可以看到
一、使用StatefulSet
1. Deployment 的特征
回顾Deployment,可以发现它有一些很有趣的特征,比如,所有Pod地位都是平等的,当一个Pod因故障被替换后,新的容器与其余所有Pod依然相同,因此无论请求发送到哪个Pod,返回的结果都是一致的。又或者Pod被删除后,里面的数据也随之消失。
这种特性一般称其为“无状态”
2、“有状态”的应用
1. 有些情况下希望运行一些和上述不同的应用,包括使用持久化的存储,稳定的网络标志,Pod与Pod之间并不是完全平等(即使它们用同一个镜像创建)。
这类服务通常称为“有状态”的服务,
它的实现不在依靠ReplicaSet,而是用StatefulSet,它具备以下特性(支持多副本):
① 稳定的持久化存储,Pod重新调度后访问相同的持久化数据,使用PVC来实现
② 稳定的网络标识,Pod重新调度后PodName 和 HostName不变,
基于Headless Service 实现
③ Pod都有一个“序号”,可以有序的进行扩展,部署等操作
3、创建有状态应用的三步骤
参考资料: StatefulSet | Kubernetes
4、创建PV 和 PVC
1. 创建一批 PV
$ sudo mkdir /etc/exports.d /nfs{1..3} $ sudo tee /etc/exports.d/s.exports <<EOF /nfs1 *(rw,no_root_squash) /nfs2 *(rw,no_root_squash) /nfs3 *(rw,no_root_squash) EOF $ sudo apt -y install nfs-kernel-server $ sudo systemctl enable --now nfs-server.service showmount -e Export list for k8s-master: /nfs3 * /nfs2 * /nfs1 *
$ kubectl apply -f- <<EOF apiVersion: v1 kind: PersistentVolume metadata: name: mypv1 spec: storageClassName: my-sc #定义了存储类型 capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle nfs: path: /nfs1 server: 192.168.5.100 --- apiVersion: v1 kind: PersistentVolume metadata: name: mypv2 spec: storageClassName: my-sc capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle nfs: path: /nfs2 server: 192.168.5.100 --- apiVersion: v1 kind: PersistentVolume metadata: name: mypv3 spec: storageClassName: my-sc capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle nfs: path: /nfs3 server: 192.168.5.100 EOF
$ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv1 1Gi RWO Recycle Available my-sc 8s mypv2 1Gi RWO Recycle Available my-sc 8s mypv3 1Gi RWO Recycle Available my-sc 8s
参考资料: 持久卷 | Kubernetes
2. 在yaml 文件中定义 PVC 的模板,使系统在创建statefulset 时自动创建PVC
5、创建StatefulSet
1. 创建StatefulSet 时需要注意,
serviceName 这一项的值必须要和后面创建的headless Service的名称一致
2. 另外matchLabels 参数和pod 的标签一致,这与deployment 相同
3. 上一步关于 volumeClaimTemplates 的配置也需要添加进 StatefulSet 配置 yaml 中
$ kubectl apply -f- <<EOF apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx serviceName: nginx replicas: 3 template: metadata: labels: app: nginx spec: terminationGracePeriodSeconds: 10 #删除pod的时间10s containers: - name: nginx image: nginx ports: - containerPort: 80 name: web volumeMounts: - name: stor mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: stor spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "my-sc" resources: requests: storage: 1Gi EOF
6、创建结果
1. 创建完成后,可以看到Pod名称按序号排序。如果查看日志,可以发现Pod的创建顺序是第一个创建完成之后再创建第二个,依次完成
$ watch -n 1 kubectl get pod NAME READY STATUS RESTARTS AGE `web-0` 1/1 Running 0 79s `web-1` 1/1 Running 0 55s `web-2` 1/1 Running 0 30s <Ctrl-C>
2. 创建了三个PVC,名称同样以序号排序,依次和PV 做绑定
$ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv1 1Gi RWO Recycle Bound default/stor-web-0 my-sc 27m mypv2 1Gi RWO Recycle Bound default/stor-web-1 my-sc 27m mypv3 1Gi RWO Recycle Bound default/stor-web-2 my-sc 27m $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE stor-web-0 Bound mypv1 1Gi RWO my-sc 2m51s stor-web-1 Bound mypv2 1Gi RWO my-sc 2m28s stor-web-2 Bound mypv3 1Gi RWO my-sc 2m1s
7、创建 Headless 服务
1. 创建 headless 服务,注意以下几点:
① 服务名称和 statefulset 中的定义一致
② 选择器要指向正确的Pod 标签
③ 指定 clusterIP:None(没有指定,DNS解析的就是每一个pod的IP)
kubectl apply -f- <<EOF apiVersion: v1 kind: Service metadata: name: nginx #服务名要和statefulset名要一致 labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None #headless selector: app: nginx EOF
$ kubectl get service nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx ClusterIP None <none> 80/TCP 17s
$ kubectl apply -f- <<EOF apiVersion: v1 kind: Pod metadata: name: clientpod spec: containers: - name: clientpod image: busybox:1.28.3 args: - /bin/sh - -c - sleep 3h EOF $ kubectl get pod clientpod #确认pod已经运行 NAME READY STATUS RESTARTS AGE clientpod 1/1 Running 0 2m15s
8、使用有状态的服务
1. 使用nslookup 查看 DNS 记录,可以看到对该服务的访问直接指向Pod
$ kubectl exec -it clientpod -- /bin/sh #进入pod / # nslookup nginx #解析nginx Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: nginx Address 1: 172.16.194.83 web-0.nginx.default.svc.cluster.local Address 2: 172.16.126.19 web-1.nginx.default.svc.cluster.local Address 3: 172.16.194.84 web-2.nginx.default.svc.cluster.local
2. 通过对稳定的名称访问也始终可以访问到固定的Pod
/ # nslookup web-0.nginx Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: web-0.nginx Address 1: 172.16.194.83 web-0.nginx.default.svc.cluster.local
9、StatefulSet 的故障处理
1. 与无状态服务不同,有状态的StatefulSet 中一个Pod出现故障之后,可以看到
① 虽然Pod的IP地址变化了,但通过不变的域名,依然可以访问到重建后的Pod
② 即使经过了故障和重建,Pod中保持在持久化存储(卷)中的数据依然存在
2.以web-1为例,模拟故障
$ kubectl describe pod web-1 ...输出省略... ClaimName: 'stor-web-1' ...输出省略... $ kubectl get pv | grep stor-web-1 #查询所在pv 'mypv2' 1Gi RWO Recycle Bound default/stor-web-1 my-sc 70m $ kubectl describe pv mypv2 | grep -i path #查看对应的路径 Path: '/nfs2' $ sudo touch /nfs2/newfile #创建文件 $ kubectl exec -it web-1 -- /bin/sh # ls /usr/share/nginx/html #查看文件已生成 newfile
$ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES web-0 1/1 Running 0 59m 172.16.194.83 k8s-worker1 <none> <none> web-1 1/1 Running 0 59m 172.16.126.19 k8s-worker2 <none> <none> web-2 1/1 Running 0 58m 172.16.194.84 k8s-worker1 <none> <none> $ kubectl delete pod web-1 #删除web-1,后会重新创建() pod "web-1" deleted '与deployment区别,statefulset是有状态' $ kubectl get pod web-1 -o wide #查看web-1的IP已经变化 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES web-1 1/1 Running 0 3m29s 172.16.126.21 k8s-worker2 <none> <none> $ kubectl exec -it clientpod -- nslookup web-1.nginx #依旧可以继续解析 Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: web-1.nginx Address 1: 172.16.126.21 web-1.nginx.default.svc.cluster.local $ k exec -it web-1 -- ls /usr/share/nginx/html newfile
10、扩缩容和升级
1. 当扩容StatefulSet时,
可以看到Pod停止的顺序为从序号最高的开始降序终止,
并且只有在前一个pod被完全终止后,下一个pod才开始终止。
升级时,也是以相同顺序处理
2. 试验
$ watch -n 1 kubectl get pod #检测pod状态
新打开一个终端
$ kubectl scale statefulset web --replicas=1 #缩容;依次从2-1删除 $ kubectl scale statefulset web --replicas=3 #扩容;依次1-2增加
11、Pod管理策略
1. 对于某些分布式系统,StatefulSet 的顺序性保证是不必要的,或者是不应该出现的。
为了解决这个问题,在Kubernetes 1.7 中引入了podManagementPolicy
① OrderedReady Pod 管理策略
statefulSets 的默认选项,保证顺序性
② Parallel Pod 管理策略
并行启动、终止、升级、弹性伸缩Pod
在变更一个Pod时不必等前一个Pod完成
2. 案例
$ kubectl delete statefulsets web #删除statefulset $ kubectl delete pvc stor-web-0 stor-web-1 stor-web-2 #删除pvc $ kubectl get service nginx #服务还在 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx ClusterIP None <none> 80/TCP 61m kiosk@k8s-master:~$ kubectl get pv #pv还在 NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE mypv1 1Gi RWO Recycle Released default/stor-web-0 my-sc 115m mypv2 1Gi RWO Recycle Released default/stor-web-1 my-sc 115m mypv3 1Gi RWO Recycle Released default/stor-web-2 my-sc 115m
$ kubectl apply -f- <<EOF apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx serviceName: nginx # 增加 1 行 podManagementPolicy: "Parallel" #平行 replicas: 3 template: metadata: labels: app: nginx spec: terminationGracePeriodSeconds: 10 containers: - name: nginx image: nginx ports: - containerPort: 80 name: web volumeMounts: - name: stor mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: stor spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "my-sc" resources: requests: storage: 1Gi EOF
验证
$ watch -n 1 kubectl get pod #动态查看pod状态
新打开终端
$ kubectl scale statefulset web --replicas=1 $ kubectl scale statefulset web --replicas=3
会发现扩缩容为同时进行