1. StatefulSet

StatefulSet 是用来管理有状态应用的工作负载 API 对象。

StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。

Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值:

  • 稳定的、唯一的网络标识符。
  • 稳定的、持久的存储。
  • 有序的、优雅的部署和缩放。
  • 有序的、自动的滚动更新。

在上面描述中,“稳定的”意味着 Pod 调度或重调度的整个过程是有持久性的。 如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应该使用由一组无状态的副本控制器提供的工作负载来部署应用程序,比如 Deployment 或者 ReplicaSet 可能更适用于你的无状态应用部署需要。

1.1. 创建 StatefulSet

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

apiVersion: v1
kind: Service
metadata:
  name: statefulset-service
  namespace: default
  labels:
    app: statefulset
spec:
  type: ClusterIP
  clusterIP: None
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "statefulset-service"
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  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:
      storageClassName: "managed-nfs-storage"
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 2Gi

1.1.1. 顺序创建 Pod

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

kubectl get pod -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   0/1     ContainerCreating   0          2s
web-0   1/1     Running             0          18s
web-1   0/1     Pending             0          0s
web-1   0/1     Pending             0          0s
web-1   0/1     ContainerCreating   0          0s
web-1   0/1     ContainerCreating   0          1s
web-1   1/1     Running             0          17s
web-2   0/1     Pending             0          0s
web-2   0/1     Pending             0          0s
web-2   0/1     ContainerCreating   0          0s
web-2   0/1     ContainerCreating   0          1s
web-2   1/1     Running             0          17s

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

1.2. StatefulSet 中的 Pod

StatefulSet 中的 Pod 拥有一个唯一的顺序索引和稳定的网络身份标识。

1.2.1. 检查 Pod 的顺序索引

获取 StatefulSet 的 Pod。

kubectl get pods -l app=nginx
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          5m11s
web-1   1/1     Running   0          4m53s
web-2   1/1     Running   0          4m36s

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

1.2.2. 使用稳定的网络身份标识

每个 Pod 都拥有一个基于其顺序索引的稳定的主机名。使用kubectl exec在每个 Pod 中执行hostname

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

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

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

这将启动一个新的 shell。在新 shell 中,运行:

# Run this in the dns-test container shell
nslookup statefulset-service

输出类似于:

Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      statefulset-service
Address 1: 10.244.169.165 web-0.statefulset-service.default.svc.cluster.local
Address 2: 10.244.36.98 web-1.statefulset-service.default.svc.cluster.local
Address 3: 10.244.169.166 web-2.statefulset-service.default.svc.cluster.local

即使 Pod 重建之后 IP 发生改变,Headless Service 还是能够根据 web-{0-1}.statefulset-service.default.svc.cluster.local 这个 DNS A 记录来找到每个 pod 的 IP 地址。

1.2.3. 标准 Service 和 Headless Service 的区别

这里要提到 无状态应用控制器有状态应用控制器 的设计理念,无状态的 Pod 是完全相等的,提供相同的服务,可以飘移在任意节点,例如三个 NGINX Pod 所提供的 Web 服务。而像一些分布式应用程序,例如 zookeeper 集群、etcd 集群、mysql 主从等服务,每个实例都会维护着一种状态,每个实例都有自己的数据,并且每个实例之间必须有固定的访问地址(组建集群),这就是有状态应用。由于标准 Service 是通过访问 ClusterIP 负载均衡到一组 Pod 上,这是没有办法指定访问到某个 Pod 的(由 iptables 决定)。所以这里就出现了 Headless Service ,而且 Headless Service 不需要 ClusterIP ,它是通过访问 Pod DNS 名称解析到对应的 Pod IP,为每一个 Pod 都固定一个 DNS 名称,即使 Pod 的 IP 发生改变,Pod 的 DNS 名称还是指向对应的 Pod IP 地址。

1.2.4. 写入稳定的存储

Kubernetes 为每个 VolumeClaimTemplate 创建一个 PersistentVolume。 在上面的 nginx 示例中,每个 Pod 将会得到基于 StorageClass managed-nfs-storage 提供的 2 Gib 的 PersistentVolume。如果没有声明 StorageClass,就会使用默认的 StorageClass。 当一个 Pod 被调度(重新调度)到节点上时,它的 volumeMounts 会挂载与其 PersistentVolumeClaims 相关联的 PersistentVolume。 请注意,当 Pod 或者 StatefulSet 被删除时,与 PersistentVolumeClaims 相关联的 PersistentVolume 并不会被删除。要删除它必须通过手动方式来完成。

获取 StatefulSet 创建的 PersistentVolumeClaims。

kubectl get pvc -l app=nginx

输出类似于:

NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
www-web-0   Bound    pvc-83489225-7d08-4506-9f75-1fefd3aee287   2Gi        RWO            managed-nfs-storage   23h
www-web-1   Bound    pvc-e5afdbfa-952e-4120-8885-00cf5a524eb0   2Gi        RWO            managed-nfs-storage   23h
www-web-2   Bound    pvc-21e9a5b1-a040-41c6-9b94-6bcf7a8a8966   2Gi        RWO            managed-nfs-storage   23h

StatefulSet 控制器创建了三个 PersistentVolumeClaims,绑定到三个 PersistentVolumes。由于本教程使用的集群配置为动态提供 PersistentVolume,所有的 PersistentVolume 都是自动创建和绑定的,对于动态配置的 PersistentVolumes 来说,默认回收策略为 "Delete"。

NGINX web 服务器默认会加载位于 /usr/share/nginx/html/index.html 的 index 文件。 StatefulSets spec 中的 volumeMounts 字段保证了 /usr/share/nginx/html 文件夹由一个 PersistentVolume 支持。

将 Pod 的主机名写入它们的index.html文件并验证 NGINX web 服务器使用该主机名提供服务。

for i in 0 1 2; do kubectl exec "web-$i" -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; done

for i in 0 1 2; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done

在另一个终端删除 StatefulSet 所有的 Pod。

kubectl delete pod -l app=nginx

验证所有 web 服务器在继续使用它们的主机名提供服务。

for i in 0 1 2; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done

虽然 Podweb-{0-2} 被重新调度了,但它们仍然继续监听各自的主机名,因为和它们的 PersistentVolumeClaim 相关联的 PersistentVolume 被重新挂载到了各自的 volumeMount 上。 不管 Pod 被调度到了哪个节点上,它们的 PersistentVolumes 将会被挂载到合适的挂载点上。

Copyright © 荒原饮露 2019 all right reserved,powered by Gitbook该文件修订时间: 2021-12-17 16:10:41