为什么要用statefulset控制器
RC、Deployment、DaemonSet都是面向无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的,而StatefulSet是什么?顾名思义,有状态的集合,管理所有有状态的服务,比如MySQL、MongoDB集群等。 StatefulSet本质上是Deployment的一种变体,在v1.9版本中已成为GA版本,它为了解决有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序,在StatefulSet中,Pod名字称为网络标识(hostname),还必须要用到共享存储。 在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service,headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。 除此之外,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名,这个域名的格式为: |
$(podname).(headless server name)
FQDN: $(podname).(headless server name).namespace.svc.cluster.local
statefulset控制器应用
statefulset要满足一下几点
-
稳定且唯一的网络标识符;
如: Redis集群, 在Redis集群中,它是通过槽位来存储数据的,假如:第一个节点是0~1000,第二个节点是1001~2000,第三个节点2001~3000...等等,这就使得Redis集群中每个节点要通过ID来标识自己,如: 第二个节点宕机了,重建后它必须还叫第二个节点,或者说第二个节点叫R2,它必须还叫R2,这样在获取1001~2000槽位的数据时,才能找到数据,否则Redis集群将无法找到这段数据。
-
稳定且持久的存储
-
有序、平滑的部署和扩展
如 MySQL集群,要先启动主节点, 若从节点没有要求,则可一起启动,若从节点有启动顺序要求,可先启动第一个从节点,接着第二从节点等;这个过程就是有顺序,平滑安全的启动。
-
有序、平滑的终止和删除
即: 我们先终止从节点,若从节点是有启动顺序的,那么关闭时,也要按照逆序终止,即启动时是从S1~S4以此启动,则关闭时,则是先关闭S4,然后时S3,依次关闭,最后在关闭主节点。
-
有序的滚动更新
MySQL在更新时,应该先更新从节点,全部的从节点都更新完了,最后在更新主节点,因为新版本一般可兼容老版本,但是一定要注意,若新版本不兼容老版本就很很麻烦
statefulset组成
- Headless Service 用于定义网络标识(DNS)
- volumeClaimTemplates 用于创建PV
- StatefulSet 用于定义具体应用
创建一个statefulset
apiVersion: v1
kind: Service
metadata:
name: myapp-sts
labels:
app: myapp-sts
spec:
ports:
- port: 80
name: web
clusterIP: "None"
selector:
app: myapp-pod
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: myapp
spec:
serviceName: myapp-sts #声明它属于哪个Headless Service.
replicas: 2
selector:
matchLabels:
app: myapp-pod
template:
metadata:
labels:
app: myapp-pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: myappdata
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: myappdata
spec:
accessModes: ["ReadWriteOnce"]
#storageClassName: "rook-ceph-block"
resources:
requests:
storage: 1Gi
可以正确解析到ip
[root@master statfulset]# nslookup myapp-1.myapp-sts.default.svc.cluster.local 10.96.0.10
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: myapp-1.myapp-sts.default.svc.cluster.local
Address: 10.244.1.30
[root@master statfulset]# nslookup myapp-0.myapp-sts.default.svc.cluster.local 10.96.0.10
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: myapp-0.myapp-sts.default.svc.cluster.local
Address: 10.244.1.29
扩容和缩容
#扩容至三个pod
kubectl scale sts myapp --replicas=3
#然后重新缩容至两个pod
kubectl scale sts myapp --replicas=2
扩容当中出现了一个问题, 新创建的pod处于pending状态, describe看到是mount的问题, 原来是之前pod挂载的pv变成了released状态, 并没有变成available状态, 经过查找发现最关键的是PV的
spec.claimRef
字段,该字段记录着原来PVC的绑定信息,删除绑定信息,即可重新释放PV从而达到Available。
statefulset管理pod的启停顺序
- 有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。
- 有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
- 有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态
statefulset管理策略
-
OrderedReady:上述的启停顺序,默认设置。
spec: podManagementPolicy: OrderedReady
-
Parallel:告诉StatefulSet控制器并行启动或终止所有Pod,并且在启动或终止另一个Pod之前不等待前一个Pod变为Running and Ready或完全终止。
spec: podManagementPolicy: Parallel
statefulSet的更新策略:
kubectl explain sts.spec.updateStrategy.rollingUpdate
partition: 这种更新策略的含义是, 若当前statefulSet的副本数为5个,则Pod名为pod-0~pod-4,那么此时定义partition=4, 就意味着我要更新大于等于4的Pod,而只有pod-4的ID 4 是大于等于4的,所以只有pod-4会被更新,其它不会,这就是金丝雀更新。若后期发现pod-4更新后,工作一切正常,那么就可以调整partition=0,这样只要大于等于0的pod ID都将被更新。
金丝雀更新
修改滚动更新策略,查看效果
kubectl patch sts myapp -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
#修改statefulset的image
kubectl set image sts/myapp myapp=ikubernetes/myapp:v2
#查看已经修改成功了
[root@master statfulset]# kubectl get sts myapp -o wide
NAME READY AGE CONTAINERS IMAGES
myapp 4/4 16h myapp ikubernetes/myapp:v2
因为策略写的是从第二个容器开始更新
通过命令
kubectl get pod myapp-1 -o yaml
可以看到2
之前的image没有改变通过命令
kubectl get pod myapp-2 -o yaml
可以看到2
之后的image都已经改变了
#再重新把partition改为0, 则把之前的pod的image都修改生效了
kubectl patch sts myapp -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'
这种方式就可以模拟金丝雀发布 |
[root@master statfulset]# kubectl get pods -l app=myapp-pod -o custom-columns=NAME:metadata.name,IMAGE:spec.containers[0].image
NAME IMAGE
myapp-0 ikubernetes/myapp:v2
myapp-1 ikubernetes/myapp:v2
myapp-2 ikubernetes/myapp:v2
myapp-3 ikubernetes/myapp:v2
暂存更新操作
分区更新操作
将spec.updateStrategy.rollingUpdate.partition设置为Pod副本数量时,即意味着所有Pod资源都不会处于可直接更新的分区内,直到partition小于Pod数时,才会开始更新
此时,即便删除某Pod,也会按旧版本进行重建,即暂存状态的更新对所有Pod资源均不产生影响
使用go-template
自定义资源输出信息
kubectl get pod myapp-1 -o go-template --template='{{.status.podIP}}'
[root@master statfulset]# kubectl get pod myapp-1 -o go-template --template='{{range .spec.containers}}{{.image}}{{end}}'
ikubernetes/myapp:v2
因为这里查看containers的image信息是一个列表信息, 所以要用到range
关于更多的go-template
的使用方法, 可以参考这位老哥写的博客: https://www.bbsmax.com/A/gAJGgjX3JZ/
参考链接
https://www.cnblogs.com/wn1m/p/11289079.html
https://www.cnblogs.com/tylerzhou/p/11027559.html