以下为《深入理解StatefulSet用Kubernetes编排有状态应用》的无排版文字预览,完整内容请下载
前言
作为一个后端工程师,因为负责的大部分项目都是Web服务这类的“无状态应用”,在平时工作中接触到的最常用的Kubernetes控制器是Deployment,但是Deployment只适合于编排“无状态应用”,它会假设一个应用的所有 Pod是完全一样的,互相之间也没有顺序依赖,也无所谓运行在哪台宿主机上。正因为每个Pod都一样,在需要的时候可以水平扩/缩,增加和删除Pod。
但是并不是所有应用都是无状态的,尤其是每个实例之间有主从关系的应用和数据存储类应用,针对这类应用使用Deployment控制器无法实现正确调度,所以Kubernetes里采用了另外一个控制器StatefulSet负责调度有状态应用的Pod,保持应用的当前状态始终等于应用定义的所需状态。
什么是StatefulSet
和Deployment一样StatefulSet也是一种可以帮助你部署和扩展Kubernetes Pod的控制器,使用Deployment时多数时候你不会在意Pod的调度方式。但当你需要关心Pod的部署顺序、对应的持久化存储或者要求Pod拥有固定的网络标识(即使重启或者重新调度后也不会变)时,StatefulSet控制器会帮助你,完成调度目标。
每个由StatefulSet创建出来的Pod都拥有一个序号(从0开始)和一个固定的网络标识。你还可以在YAML定义中添加VolumeClaimTemplate来声明Pod存储使用的PVC。当StatefulSet部署Pod时,会从编号0到最终编号逐一部署每个Pod,只有前一个Pod部署完成并处于运行状态后,下一个Pod才会开始部署。
/
StatefulSet,是在Deployment的基础上扩展出来的控制器,在1.9版本之后才加入Kubernetes控制器家族,它把有状态应用需要保持的状态抽象分为了两种情况:
拓扑状态。这种情况意味着,应用的多个实例之间不是完全对等的关系。这些应用实例,必须按照某些顺序启动,比如应用的主节点 A 要先于从节点 B 启动。而如果你把 A 和 B 两个 Pod 删除掉,它们再次被创建出来时也必须严格按照这个顺序才行。并且,新创建出来的 Pod,必须和原来 Pod 的网络标识一样,这样原先的访问者才能使用同样的方法,访问到这个新 Pod。
存储状态。这种情况意味着,应用的多个实例分别绑定了不同的存储数据。对于这些应用实例来说,Pod A 第一次读取到的数据,和Pod A 被重新创建后再次读取到的数据,应该是同一份 。这种情况最典型的例子,就是一个数据库应用的多个存储实例。
所以,StatefulSet 的核心功能,就是通过某种方式记录这些状态,然后在 Pod 被重新创建时,能够为新 Pod 恢复这些状态。
保持应用的拓扑状态
想要维护应用的拓扑状态,必须保证能用固定的网络标识访问到固定的Pod实例,Kubernetes是通过Headless Service给每个Endpoint(Pod)添加固定网络标识的,所以接下来我们花些时间了解下Headless Service。
HeadlessService
在文章学练结合,快速掌握Kubernetes Service 写过
Service是在逻辑抽象层上定义了一组Pod,为他们提供一个统一的固定IP和访问这组Pod的负载均衡策略。
对于 ClusterIP 模式的 Service 来说,它的 A 记录的格式是:
serviceName.namespace.svc.cluster.local,当你访问这条 A 记录的时候,它解析到的就是该 Service 的 VIP 地址。
对于指定了 clusterIP=None 的 Headless Service来说,它的A记录的格式跟上面一样,但是访问记录后返回的是Pod的IP地址集合。Pod 也会被分配对应的 DNS A 记录,格式为:podName.serviceName.namesapce.svc.cluster.local
普通的Service都有ClusterIP,它其实就是一个虚拟IP,会把请求转发到该Service所代理的某一个Pod上。
还是拿文章学练结合,快速掌握Kubernetes Service 里用过的例子来分析,使用的Service和Deployment的定义如下:
apiVersion: v1kind: Servicemetadata:
name: app-servicespec:
type: NodePort #创建NodePort类型Service时会先创建一个ClusterIp类型的Service
selector:
app: go-app
ports:
- name: http
protocol: TCP
nodePort: 30080
port: 80
targetPort: 3000
---apiVersion: apps/v1kind: Deploymentmetadata:
name: my-go-appspec:
replicas: 2
selector:
matchLabels:
app: go-app
template:
metadata:
labels:
app: go-app
spec:
containers:
- name: go-app-container
image: kevinyan001/kube-go-app
ports:
- containerPort: 3000复制代码
在Kubernetes里创建好上述资源后,可以进入其中一个Pod查看Service的A记录
? kubectl exec -it my-go-app-69d6844c5c-gkb6z -- /bin/sh
/app # nslookup app-service.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: app-service.default.svc.cluster.local
Address: 10.108.26.155复制代码
如果想让DNS通过刚才的Service名直接解析出Pod名对应的IP是不可以的:
/app # nslookup my-go-app-69d6844c5c-gkb6z.app-service.default.svc.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
** server can't find my-go-app-69d6844c5c-gkb6z.app-service.default.svc.cluster.local: NXDOMAIN复制代码
因为Service有ClusterIp,直接被DNS解析了,那怎么才能让DNS通过Service解析Pod的IP呢?所以就有了Headless Service。
创建Headless Service跟创建普通Service时唯一的不同就是在YAML定义里指定spec:clusterIP: None,也就是不需要ClusterIP的Service。
下面我创建一个Headless Service代理上面例子中的那两个应用Pod实例,它的YAML定义如下
# headless-service.yamlapiVersion: v1kind: Servicemetadata:
name: app-headless-svcspec:
clusterIP: None # 请点击下方选择您需要的文档下载。
以上为《深入理解StatefulSet用Kubernetes编排有状态应用》的无排版文字预览,完整内容请下载
深入理解StatefulSet用Kubernetes编排有状态应用由用户“feifeiwua6666”分享发布,转载请注明出处