k8s | 深入理解 StatefulSet
StatefulSet 是 Kubernetes 中用于管理有状态应用的资源对象。有状态的含义是:
- 稳定的网络标识:每个pod都有一个稳定的网络标识,在pod重启时保持不变,可以通过DNS或者其他服务发现访问
- 有序的部署和扩展:StatefulSet能够按照一定的顺序逐个更新Pod,在新的Pod启动之前,旧的Pod仍然可以提供服务
- 持久化存储:statefulset 使用 PVC(PersistentVolumeClaim) 为每个pod分配独立的持久化存储,数据重启或重新调度不会丢失
Statefulset 和 deployment
Deployment 和 StatefulSet 都是用来管理pod,都可以用来控制pod副本数和更新策略。
区别:
- 稳定性和唯一性:deployment是无状态服务,pod都是相互的,没有唯一标识,扩容更新时pod名称会发生改变。而每个由
StatefulSet
创建出来的Pod
都拥有一个序号(从0开始)和一个固定的网络标识,扩容更新时pod不会改变 - Pod 更新:Deployment 会按照指定的更新策略(如 RollingUpdate)逐步替换 Pod,而 StatefulSet 会按照顺序依次更新每个 Pod,确保每个 Pod 在更新前后都处于稳定状态
- 存储卷:Deployment 默认使用临时的存储卷(emptyDir),每次 Pod 重启时数据会丢失。而 StatefulSet 支持使用持久化存储卷,可以保留数据
Deployment 和 StatefulSet 是 Kubernetes 中两种不同类型的控制器对象,用于管理无状态和有状态应用的 Pod。Deployment 适用于无状态应用,支持滚动更新,Pod 没有唯一标识;而 StatefulSet 适用于有状态应用,支持有序更新和稳定唯一的 Pod 标识,可以保留数据。
保持应用的拓扑状态
StatefulSet 的拓扑状态是指它的 Pod 在集群中的部署位置和网络标识的状态。StatefulSet 中的 Pod 是按照序号进行命名和部署的,每个 Pod 都有一个唯一的序号。这使得 StatefulSet 中的 Pod 具有固定的标识和顺序,不会随着 Pod 的创建和删除而改变
想要维护应用的拓扑状态,必须保证能用固定的网络标识访问到固定的Pod
实例,Kubernetes
是通过**Headless Service
**给每个Endpoint(Pod)
添加固定网络标识的
Headless Service 是 Kubernetes 中 Service 的一种类型,它不为 Pod 提供 Cluster IP,而是通过 DNS 解析提供每个 Pod 的 IP 地址。Headless Service 可以通过访问 Pod 的 DNS 名称来直接与每个 Pod 进行通信,而无需经过 Service 负载均衡。
Headless Service 与 StatefulSet 结合使用时,可以实现有状态应用程序的服务发现。StatefulSet 创建的每个 Pod 都会被赋予一个稳定的 DNS 名称,通常是 Pod 名称加上 StatefulSet 名称。这样,其他应用程序可以通过 DNS 解析直接访问每个 Pod,从而实现有状态应用程序之间的直接通信。
为什么需要 Headless Service?
普通的Service
都有ClusterIP
,它其实就是一个虚拟IP
,会把请求转发到该Service
所代理的某一个Pod
上,DNS通过Service
名直接解析出Pod
名对应的IP是不可以的,因为Service
有ClusterIp
,直接被DNS解析了。
怎么才能让DNS通过Service
解析Pod
的IP呢?这个时候就需要用到了 Headless Service
实例:创建一个Headless Service
代理两个应用Pod
实例
# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
name: app-headless-svc
spec:
clusterIP: None # <-- Don't forget!!
selector:
app: go-app
ports:
- protocol: TCP
port: 80
targetPort: 3000
创建 Service
kubectl apply -f headless-service.yaml service/app-headless-svc created
可以查看到 这个Service
在DNS里对应的A记录
/app # nslookup app-headless-svc.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: app-headless-svc.default.svc.cluster.local
Address: 10.1.0.38
Name: app-headless-svc.default.svc.cluster.local
Address: 10.1.0.39
DNS查询会返回HeadlessService
代理的两个Endpoint (Pod)
对应的IP,这样客户端就能通过Headless Service
拿到每个EndPoint
的 IP,如果有需要可以自己在客户端做些负载均衡策略。Headless Service
还有一个重要用处(也是使用StatefulSet
时需要Headless Service
的真正原因),它会为代理的每一个StatefulSet
创建出来的Endpoint
也就是Pod
添加DNS域名解析,这样Pod
之间就可以相互访问。
这个分配给Pod
的DNS域名就是Pod
的固定唯一网络标识,即使发生重建和调度DNS域名也不会改变
总结
Headless Service
为代理的每一个StatefulSet
创建出来的Pod
添加DNS域名解析。所以在用StatefulSet
编排实例之间有主从关系这样的有状态应用时,Pod
相互之间就能以podName.serviceName.namesapce.svc.cluster.local 这个域名格式进行通信,这样就不用在担心Pod
被重新调度到其他的节点上后IP的变化
保持实例的存储状态
StatefulSet 中的 Pod 使用持久化存储PV,PVC( PersistentVolume 或 PersistentVolumeClaim),每个 Pod 都有自己独立的持久化存储,这使得 Pod 可以保留它们的数据状态,即使 Pod 被重新调度到其他节点
为什么不用volume?
在一个Pod
里声明 Volume
,由于hostPath
类型的Volume是基于宿主机目录的,如果一旦Pod
发生重新调度,去了其他节点,就没有办法在新节点上把Pod
的存储数据恢复回来了
集群持久数据卷资源的配置和使用是通过PV
和PVC
完成的
关系
Persistent Volume(PV)
持久卷PV是集群资源,就像节点也是集群资源一样。它 是管理员提前预分配的存储资源,独立于任何特定的Pod或Namespace。PV的生命周期与集群中的资源无关,因此即使在Pod被删除后,PV仍然可以保持数据。
Persistent Volume Claim(PVC)
PVC 是一个用于请求PV的声明。PVC允许用户向Kubernetes请求一定数量和类型的存储资源。PVC 是由开发者创建的,并绑定到集群中的一个或多个PV上。当PVC绑定到PV上后,Pod可以通过PVC来访问PV提供的持久化存储
可以用编程领域的接口和实现的关系来理解
PVC
和PV
的关系。PVC
创建出来后需要和PV
完成绑定才能使用, 我们不需要关注绑定的细节
实例:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: pv-pod
spec:
containers:
- name: pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: pv-storage
volumes:
- name: pv-storage
persistentVolumeClaim:
claimName: pv-claim
只需要声明它的类型是 persistentVolumeClaim
,然后指定 PVC
的名字,完全不用关心持久卷本身的定义
PVC模板
在StatefulSet
的定义里我们可以额外添加了一个spec.volumeClaimTemplates
字段。它跟 Pod
模板(spec.template
字段)的作用类似。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.9.1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Statefulset 总结
StatefulSet
就像是一种特殊的Deployment
,它使用Kubernetes
里的两个标准功能:Headless Service
和 PVC
,实现了对的拓扑状态和存储状态的维护。
StatefulSet
通过Headless Service
, 为它管控的每个Pod
创建了一个固定保持不变的DNS域名,来作为Pod
在集群内的网络标识。加上为Pod
进行编号并严格按照编号顺序进行Pod
调度,这些机制保证了StatefulSet
对维护应用拓扑状态的支持。
而借由StatefulSet
定义文件中的volumeClaimTemplates
声明Pod
使用的PVC
,它创建出来的PVC
会以名称编号这些约定与它创建出来的Pod
进行绑定,借由PVC
独立于Pod
的生命周期和两者之间的绑定机制的帮助,StatefulSet
完成了应用存储状态的维护。
评论区