StatefulSet 是用来管理有状态应用
的工作负载Api对象。
StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。
和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。
但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。
如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易。
StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值:
在上面描述中,“稳定的”意味着 Pod 调度或重调度的整个过程是有持久性的。 如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应该使用 由一组无状态的副本控制器提供的工作负载来部署应用程序,比如 Deployment 能更适用于你的无状态应用部署需要。
storage class
来提供,或者由管理员预先提供。无头服务
来负责 Pod 的网络标识。你需要负责创建此服务。OrderedReady
) 时使用 滚动更新,可能进入需要人工干预 才能修复的损坏状态。StatefulSet由Service和volumeClaimTemplates组成。Service中的多个Pod将会被分别编号,并挂载volumeClaimTemplates中声明的PV。
注意: 官方提示 StatefulSets 在1.9中是稳定的
StatefulSet 除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合使用,在每个StatfulSet的定义中要声明它属于哪个Headless Service.Headless Service 与普通Service的关键区别在于, Headless Service它没有Cluster IP,如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。
StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实力创建了一个DNS域名,这个域名的格式为: ${podname}.${headless service name}
比如一个3节点的Kafka的StatefulSet集群,对应的Headless Service的名字为kafka,StatefulSet的名字为kafka,则StatefulSet 里面的3个Pod的DNS分别为kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,这些DNS名称可以直接在集群的配置文件中固定下来。
简单点说:没有部署HeadlessService的话,PetSet/StatefulSet下的pod,无法通过域名进行访问。
创建资源模板信息 web.yaml 其中包括 一个 headless-service(无头服务)和 一个 StatefulSet
经测试 svc 的 name 必须与 sts 的 spec.serviceName 相等,否则无法通过路由规则找到容器服务
这里为了方便就挂载了一个空目录,如果有自己的 storageClass 也可以使用
apiVersion: v1 kind: Service metadata: # must be equal sts .spec.serviceName name: nginx-sn labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: # must be equal headless-svc metadata.name serviceName: "nginx-sn" replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest imagePullPolicy: IfNotPresent ports: - containerPort: 80 name: web volumeMounts: - name: nginx-volume mountPath: /usr/share/nginx/html volumes: - name: nginx-volume # 为了演示方便,挂载个空目录 emptyDir: {} # 如果有自己的 storageClass 可以使用以下的方式 #volumeClaimTemplates: #- metadata: # name: www # spec: # accessModes: [ "ReadWriteOnce" ] # storageClassName: "your-storage-class" # resources: # requests: # storage: 1Gi
创建资源
$ kubectl create -f web.yaml
查看sts 信息
$ kubectl get -f web.yaml -owide # 输出内容如下 # headless svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service/nginx-sn ClusterIP None <none> 80/TCP 20m app=nginx # sts NAME READY AGE CONTAINERS IMAGES statefulset.apps/web 2/2 20m nginx nginx:latest
也可以使用命令分开查询
查看svc
$ kubectl get svc/nginx-sn -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx-sn ClusterIP None <none> 80/TCP 22m app=nginx
查看sts
$ kubectl get sts -owide NAME READY AGE CONTAINERS IMAGES web 2/2 22m nginx nginx:latest
也可以查看endpoints 列表信息,可以看到绑定到了两个pod
kubectl get endpoints/nginx-sn -owide NAME ENDPOINTS AGE nginx-sn 10.100.132.137:80,10.100.132.142:80 23m
pod信息如下
$ kubectl get po -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES web-0 1/1 Running 0 24m 10.100.132.137 k8s-woker-01 <none> <none> web-1 1/1 Running 0 24m 10.100.132.142 k8s-woker-01 <none> <none>
sts 修改删除没什么特别的,操作基本和deploy一样,这里就不再赘述,说几点验证过的结论。
对于具有 N 个副本的 StatefulSet,StatefulSet 中的每个 Pod 将被分配一个整数序号, 从 0 到 N-1,该序号在 StatefulSet 上是唯一的。比如``sts name=nginx ,replicas=3 则对应的pod名称依次为
nginx-0、nginx-1、nginx-2`,
StatefulSet 可以使用 "无头服务" 控制它的 Pod 的网络域。管理域的这个服务的格式为: $(服务名称).$(命名空间).svc.cluster.local
,其中 cluster.local
是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为: $(pod 名称).$(所属服务的 DNS 域名)
,其中所属服务由 StatefulSet 的 serviceName
域来设定。
下面给出一些选择集群域、服务名、StatefulSet 名、及其怎样影响 StatefulSet 的 Pod 上的 DNS 名称的示例:
集群域名 | 服务(名称空间/名字) | StatefulSet(名字空间/名字) | StatefulSet 域名 | Pod DNS | Pod 主机名 |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0..N-1}.nginx.foo.svc.cluster.local | web-{0..N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0..N-1}.nginx.foo.svc.kube.local | web-{0..N-1} |
说明: 集群域会被设置为
cluster.local
,除非有其他配置。
0..N-1
。N-1..0
。StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds
设置为 0。 这种做法是不安全的,要强烈阻止。如果要强制删除pod使用以下命令:
$ kubectl delete pods <pod> --grace-period=0 --force
在上面的 nginx 示例被创建后,会按照 web-0、web-1、web-2 的顺序部署三个 Pod。 在 web-0 进入 Running 和 Ready
状态前不会部署 web-1。在 web-1 进入 Running 和 Ready 状态前不会部署 web-2。 如果 web-1 已经处于 Running 和 Ready 状态,而 web-2 尚未部署,在此期间发生了 web-0 运行失败,那么 web-2 将不会被部署,要等到 web-0 部署完成并进入 Running 和 Ready 状态后,才会部署 web-2。
如果用户想将示例中的 StatefulSet 收缩为 replicas=1
,首先被终止的是 web-2。 在 web-2 没有被完全停止和删除前,web-1 不会被终止。 当 web-2 已被终止和删除、web-1 尚未被终止,如果在此期间发生 web-0 运行失败, 那么就不会终止 web-1,必须等到 web-0 进入 Running 和 Ready 状态后才会终止 web-1。
对于 StatefulSet 中定义的每个 VolumeClaimTemplate,每个 Pod 接收到一个 PersistentVolumeClaim。 当一个 Pod 被调度(重新调度)到节点上时,它的 volumeMounts
会挂载与其 PersistentVolumeClaims 相关联的 PersistentVolume。 请注意,当 Pod 或者 StatefulSet 被删除时,与 PersistentVolumeClaims 相关联的 PersistentVolume 并不会被删除。要删除它必须通过手动方式来完成。