statefulSet

444 阅读5分钟

这是我参与 8 月更文挑战的第 22 天,活动详情查看: 8月更文挑战

  1.  StatefulSet从本质上来说,可以看作Deployment/RC的一个特殊变种,它有如下特性:

◎ StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名称为kafka,那么第1个Pod叫kafka-0,第2个叫kafka-1,以此类推。◎ StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。

◎ StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。

StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合使用,即在每个StatefulSet定义中都要声明它属于哪个Headless Service。

StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例都创建了一个DNS域名,这个域名的格式为:

比如一个3节点的Kafka的StatefulSet集群对应的Headless Service的名称为kafka,StatefulSet的名称为kafka,则StatefulSet里的3个Pod的DNS名称分别为kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,这些DNS名称可以直接在集群的配置文件中固定下来。

使用statefulSet部署一个应用

部署一个statefulSet的应用的前提是部署pv,有pv可以使用。这样在statefulSet的yaml文件里指定的pvc部署完以后会有pv可以绑定。

作为开始,使用如下示例创建一个StatefulSet。它和StatefulSets概念中的示例相似。它创建了一个Headless Service的nginx用来发布 StatefulSet web中的Pod的IP地址。

cat statefulset.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
     app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
     labels:
       app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        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的应用的前提是部署pv,有pv可以使用。这样在statefulSet的yaml文件里指定的pvc部署完以后会有pv可以绑定。

作为开始,使用如下示例创建一个StatefulSet。它和StatefulSets概念中的示例相似。它创建了一个Headless Service的nginx用来发布 StatefulSet web中的Pod的IP地址。

cat statefulset.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
     app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
     labels:
       app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        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的应用的前提是部署pv,有pv可以使用。这样在statefulSet的yaml文件里指定的pvc部署完以后会有pv可以绑定。

作为开始,使用如下示例创建一个StatefulSet。它和StatefulSets概念中的示例相似。它创建了一个Headless Service的nginx用来发布 StatefulSet web中的Pod的IP地址。

cat statefulset.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
     app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
     labels:
       app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        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部署pod,并且观察pod创建的过程

你需要使用两个终端窗口。在第一个终端中,使用kubectl get来查看 StatefulSet的Pods的创建情况。

kubectl get pods -w -l app=nginx

在另一个终端中,使用 kubectl apply -f statefulset.yaml来创建定义在statefulset.yaml中的 Headless Service 和StatefulSet。

kubectl apply -f statefulset.yaml

上面的命令创建了两个 Pod,每个都运行了一个NGINX web 服务器。获取nginx Service 和web StatefulSet来验证是否成功的创建了它们。

查看service

kubectl get service nginx

NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE

nginx None <none> 80/TCP 12s

查看statefulset 

kubectl get statefulset web

显示如下:

NAME   READY   AGE

web    2/2     20m

顺序创建 Pod

对于一个拥有 N 个副本的 StatefulSet,Pod 被部署时是按照 {0..N-1}的序号顺序创建的。在第一个终端中使用 kubectl get 检查输出。这个输出最终将看起来像下面的样子。

kubectl get pods -w -l app=nginx

****NAME READY STATUS RESTARTS AGE****

****web-0**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-0**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-0**** ****0**** ****/1 ContainerCreating**** ****0**** ****0s****

****web-0**** ****1**** ****/1 Running**** ****0**** ****19s****

****web-1**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-1**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-1**** ****0**** ****/1 ContainerCreating**** ****0**** ****0s****

****web-1**** ****1**** ****/1 Running**** ****0**** ****18s****

请注意在web-0 Pod处于Running和Ready 状态后web-1 Pod才会被启动。

对于一个拥有 N 个副本的 StatefulSet,Pod 被部署时是按照 {0..N-1}的序号顺序创建的。在第一个终端中使用 kubectl get 检查输出。这个输出最终将看起来像下面的样子。

kubectl get pods -w -l app=nginx

****NAME READY STATUS RESTARTS AGE****

****web-0**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-0**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-0**** ****0**** ****/1 ContainerCreating**** ****0**** ****0s****

****web-0**** ****1**** ****/1 Running**** ****0**** ****19s****

****web-1**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-1**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-1**** ****0**** ****/1 ContainerCreating**** ****0**** ****0s****

****web-1**** ****1**** ****/1 Running**** ****0**** ****18s****

请注意在web-0 Pod处于Running和Ready 状态后web-1 Pod才会被启动。

对于一个拥有 N 个副本的 StatefulSet,Pod 被部署时是按照 {0..N-1}的序号顺序创建的。在第一个终端中使用 kubectl get 检查输出。这个输出最终将看起来像下面的样子。

kubectl get pods -w -l app=nginx

****NAME READY STATUS RESTARTS AGE****

****web-0**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-0**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-0**** ****0**** ****/1 ContainerCreating**** ****0**** ****0s****

****web-0**** ****1**** ****/1 Running**** ****0**** ****19s****

****web-1**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-1**** ****0**** ****/1 Pending**** ****0**** ****0s****

****web-1**** ****0**** ****/1 ContainerCreating**** ****0**** ****0s****

****web-1**** ****1**** ****/1 Running**** ****0**** ****18s****

请注意在web-0 Pod处于Running和Ready 状态后web-1 Pod才会被启动。

检查 Pod 的顺序索引

获取 StatefulSet 的 Pod。

kubectl get pods -l app=nginx

NAME      READY     STATUS    RESTARTS   AGE

web-0     1/1       Running   0          1m

web-1     1/1       Running   0          1m

如同StatefulSets 概念中所提到的, StatefulSet中的Pod拥有一个具有黏性的、独一无二的身份标志。这个标志基于StatefulSet控制器分配给每个 Pod的唯一顺序索引。 Pod的名称的形式为-。web StatefulSet 拥有两个副本,所以它创建了两个 Pod:web-0 和 web-1

使用稳定的网络身份标识

每个Pod都拥有一个基于其顺序索引的稳定的主机名(statefulset创建的pod的主机名由statefulset的名称和有序索引组成)。使用kubectl exec 在每个 Pod 中执行hostname

for i in 0 ****1 **; **do kubectl exec web- $i -- sh -c **'hostname' **; done

显示如下:

web-0

web-1

使用kubectl run运行一个提供nslookup命令的容器,该命令来自于dnsutils包。通过对Pod的主机名执行nslookup,你可以检查他们在集群内部的DNS地址。

kubectl exec -it web-1 -- /bin/bash

apt-get install dnsutils -y

nslookup web-0.nginx.default.svc.cluster.local

删除statefulSet

在一个终端中查看 StatefulSet 的 Pod。

kubectl get pod -w -l app = nginx

在另一个终端中使用 kubectl delete 删除 StatefulSet 中所有的 Pod。

kubectl delete pod -l app=nginx

pod "web-0" deleted

pod "web-1" deleted

等待 StatefulSet 重启它们,并且两个 Pod 都变成 Running 和 Ready 状态。

kubectl get pod -w -l app=nginx

NAME      READY     STATUS              RESTARTS   AGE

web-0     0/1       ContainerCreating   0          0s

NAME      READY     STATUS    RESTARTS   AGE

web-0     1/1       Running   0          2s

web-1     0/1       Pending   0         0s

web-1     0/1       Pending   0         0s

web-1     0/1       ContainerCreating   0         0s

web-1     1/1       Running   0         34s

使用 kubectl exec 和 kubectl run 查看 Pod 的主机名和集群内部的 DNS 表项

for i in 0 ****1 **; **do kubectl exec web- $i -- sh -c **'hostname' ****; **done

web-0

web-1

web-2

部署和扩缩

1.对于包含N个副本的StatefulSet,当部署Pod时,它们是依次创建的,顺序为 0..N-1。

2.当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0。

3.在将缩放操作应用到Pod之前,它前面的所有Pod 必须是 Running 和 Ready 状态。

4.在 Pod 终止之前,所有的继任者必须完全关闭。

StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds 设置为 0。这种做法是不安全的,要强烈阻止。更多的解释请参考 强制删除 StatefulSet Pod

在上面的 nginx 示例被创建后,会按照 web-0、web-1的顺序部署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。