• Kubernetes控制器之StatefulSet


      参考:https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/

        https://www.kubernetes.org.cn/deployment

      StatefulSet

      StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括

    • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
    • 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
    • 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
    • 有序收缩,有序删除(即从N-1到0)

      从上面的应用场景可以发现,StatefulSet由以下几个部分组成:

    • 用于定义网络标志(DNS domain)的Headless Service
    • 用于创建PersistentVolumes的volumeClaimTemplates
    • 定义具体应用的StatefulSet

      StatefulSet中每个Pod的DNS格式为statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local,其中

    • serviceName为Headless Service的名字
    • 0..N-1为Pod所在的序号,从0开始到N-1
    • statefulSetName为StatefulSet的名字
    • namespace为服务所在的namespace,Headless Servic和StatefulSet必须在相同的namespace
    • .cluster.local为Cluster Domain 默认域就是cluster.local

      限制

    • 给定 Pod 的存储必须由 PersistentVolume 驱动 基于所请求的 storage class 来提供,由管理员预先提供。
    • 删除或者收缩 StatefulSet 并不会删除它关联的存储卷。这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
    • StatefulSet 当前需要 headless 服务 来负责 Pod 的网络标识。您需要负责创建此服务。
    • 当删除 StatefulSets 时,StatefulSet 不提供任何终止 Pod 的保证。为了实现 StatefulSet 中的 Pod 可以有序和优雅的终止,可以在删除之前将 StatefulSet 缩放为 0。
    • 在默认 Pod 管理策略(OrderedReady) 时使用 滚动更新,可能进入需要 人工干预 才能修复的损坏状态。

      组件

      下面的示例演示了 StatefulSet 的组件。

    # cat web.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 # has to match .spec.template.metadata.labels
      serviceName: "nginx"
      replicas: 3 # by default is 1
      template:
        metadata:
          labels:
            app: nginx # has to match .spec.selector.matchLabels
        spec:
          terminationGracePeriodSeconds: 10
          containers:
          - name: nginx
            #image: k8s.gcr.io/nginx-slim:0.8
            image: lowyard/nginx-slim:0.8
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: "managed-nfs-storage"
          resources:
            requests:
              storage: 1Gi
    
    • 名为 nginx 的 Headless Service 用来控制网络域名。
    • 名为web的StatefulSet .spec.replicas表明独立的3个Pod副本启动nginx容器
    • volumeClaimTemplates 将通过 PersistentVolumes 驱动提供的 PersistentVolumes 来提供稳定的存储。需要由管理员事先创建,本次创建的是NFS存储创建参考:https://www.cnblogs.com/minseo/p/12456987.html

      Pod选择器

      必须设置StatefulSet的.spec.selector字段,使之匹配其在.spec.template.metadata.labels 中设置的标签。在 Kubernetes 1.8 版本之前,被忽略 .spec.selector 字段会获得默认设置值。在 1.8 和以后的版本中,未指定匹配的 Pod 选择器将在创建 StatefulSet 期间导致验证错误。

      Pod标识

      StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。该标识和 Pod 是绑定的,不管它被调度在哪个节点上。

      有序索引

      对于具有 N 个副本的 StatefulSet,StatefulSet 中的每个 Pod 将被分配一个整数序号,从 0 到 N-1,该序号在 StatefulSet 上是唯一的。

      稳定的网络 ID

      StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。组合主机名的格式为$(StatefulSet 名称)-$(序号)。上例将会创建三个名称分别为 web-0、web-1、web-2 的 Pod

       StatefulSet 可以使用 headless 服务 控制它的 Pod 的网络域。管理域的这个服务的格式为: $(服务名称).$(命名空间).svc.cluster.local,其中 cluster.local 是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为:$(pod 名称).$(所属服务的 DNS 域名),其中所属服务由 StatefulSet 的 serviceName 域来设定。

      下面给出一些选择集群域、服务名、StatefulSet 名、及其怎样影响 StatefulSet 的 Pod 上的 DNS 名称的示例:

    Cluster DomainService (ns/name)StatefulSet (ns/name)StatefulSet DomainPod DNSPod Hostname
    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,除非有其他配置。
    

       稳定的存储

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

      Pod名称标签

      当 StatefulSet 控制器 创建 Pod 时,它会添加一个标签 statefulset.kubernetes.io/pod-name,该标签设置为 Pod 名称。这个标签允许您给 StatefulSet 中的特定 Pod 绑定一个 Service。

      查看该标签

    # kubectl get pod --show-labels
    NAME                                    READY   STATUS             RESTARTS   AGE   LABELS
    db-0                                    0/1     CrashLoopBackOff   13         68m   controller-revision-hash=db-86458bcd44,project=java-demo,run=mysql,statefulset.kubernetes.io/pod-name=db-0
    nfs-client-provisioner-7db87779-jdp99   1/1     Running            0          71m   app=nfs-client-provisioner,pod-template-hash=7db87779
    web-0                                   1/1     Running            0          56m   app=nginx,controller-revision-hash=web-5d84f996d,statefulset.kubernetes.io/pod-name=web-0
    web-1                                   1/1     Running            0          56m   app=nginx,controller-revision-hash=web-5d84f996d,statefulset.kubernetes.io/pod-name=web-1
    web-2                                   1/1     Running            0          56m   app=nginx,controller-revision-hash=web-5d84f996d,statefulset.kubernetes.io/pod-name=web-2
    

       部署和扩缩保证

    • 对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 0..N-1
    • 当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0
    • 在将缩放操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
    • 在 Pod 终止之前,所有的继任者必须完全关闭。

      StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds 设置为 0。这种做法是不安全的,要强烈阻止。

      在上面的 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。


      Pod管理策略

      在 Kubernetes 1.7 及以后的版本中,StatefulSet 允许您不要求其排序保证,同时通过它的 .spec.podManagementPolicy 域保持其唯一性和身份保证。 在 Kubernetes 1.7 及以后的版本中,StatefulSet 允许您放宽其排序保证,同时通过它的 .spec.podManagementPolicy 域保持其唯一性和身份保证。

      OrderedReady Pod 管理

      OrderedReady Pod 管理是 StatefulSet 的默认设置。它实现了上面描述的功能。

      Parallel Pod 管理

      Parallel Pod 管理让 StatefulSet 控制器并行的启动或终止所有的 Pod,启动或者终止其他 Pod 前,无需等待 Pod 进入 Running 和 ready 或者完全停止状态。

      更新策略 

      在 Kubernetes 1.7 及以后的版本中,StatefulSet 的 .spec.updateStrategy 字段让您可以配置和禁用掉自动滚动更新 Pod 的容器、标签、资源请求或限制、以及注解。

      关于删除策略 

      OnDelete 更新策略实现了 1.6 及以前版本的历史遗留行为。当 StatefulSet 的 .spec.updateStrategy.type 设置为 OnDelete 时,它的控制器将不会自动更新 StatefulSet 中的 Pod。用户必须手动删除 Pod 以便让控制器创建新的 Pod,以此来对 StatefulSet 的 .spec.template 的变动作出反应。

      滚动更新  

      RollingUpdate 更新策略对 StatefulSet 中的 Pod 执行自动的滚动更新。在没有声明 .spec.updateStrategy 时,RollingUpdate 是默认配置。 当 StatefulSet 的 .spec.updateStrategy.type 被设置为 RollingUpdate 时,StatefulSet 控制器会删除和重建 StatefulSet 中的每个 Pod。 它将按照与 Pod 终止相同的顺序(从最大序号到最小序号)进行,每次更新一个 Pod。它会等到被更新的 Pod 进入 Running 和 Ready 状态,然后再更新其前身。

      分区

      通过声明 .spec.updateStrategy.rollingUpdate.partition 的方式,RollingUpdate 更新策略可以实现分区。如果声明了一个分区,当 StatefulSet 的 .spec.template 被更新时,所有序号大于等于该分区序号的 Pod 都会被更新。所有序号小于该分区序号的 Pod 都不会被更新,并且,即使他们被删除也会依据之前的版本进行重建。如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition 大于它的 .spec.replicas,对它的 .spec.template 的更新将不会传递到它的 Pod。 在大多数情况下,您不需要使用分区,但如果您希望进行阶段更新、则这些分区会非常有用。

      强制回滚

      在默认 Pod 管理策略(OrderedReady) 时使用 滚动更新 ,可能进入需要人工干预才能修复的损坏状态。

      如果更新后 Pod 模板配置进入无法运行或就绪的状态(例如,由于错误的二进制文件或应用程序级配置错误),StatefulSet 将停止回滚并等待。

      在这种状态下,仅将 Pod 模板还原为正确的配置是不够的。由于已知问题,StatefulSet 将继续等待损坏状态的 Pod 准备就绪(永远不会发生),然后再尝试将其恢复为正常工作配置。

      恢复模板后,还必须删除 StatefulSet 尝试使用错误的配置来运行的 Pod。这样,StatefulSet 才会开始使用被还原的模板来重新创建 Pod。

      部署StatefulSet

      参考:https://kubernetes.io/zh/docs/tutorials/stateful-application/basic-stateful-set/

      目标

      StatefulSets 旨在与有状态的应用及分布式系统一起使用。然而在 Kubernetes 上管理有状态应用和分布式系统是一个宽泛而复杂的话题。为了演示 StatefulSet 的基本特性,并且不使前后的主题混淆,你将会使用 StatefulSet 部署一个简单的 web 应用。

      在阅读本教程后,你将熟悉以下内容:

    • 如何创建StatefulSet
    • StatefulSet怎样管理它的Pods
    • 如何删除StatefulSet
    • 如何对StatefulSet进行扩容/缩容
    • 如果更新一个StatefulSet的Pods

      创建StatefulSet

       需要有动态存储卷本次私有NFS搭建,搭建完毕以后使用命令查看

    # kubectl get storageClass
    NAME                  PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
    managed-nfs-storage   fuseim.pri/ifs   Delete          Immediate           false                  120m
    

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

    kubectl get pods -w -l app=nginx
    

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

    kubectl apply -f web.yaml 
    service/nginx created
    statefulset.apps/web created
    

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

    kubectl get service nginx
    NAME    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
    nginx   ClusterIP   None         <none>        80/TCP    2m8s
    kubectl get statefulset web
    NAME   READY   AGE
    web    2/2     2m10s
    

       顺序创建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     Pending   0          2s
    web-0   0/1     ContainerCreating   0          2s
    web-0   1/1     Running             0          4s
    web-1   0/1     Pending             0          0s
    web-1   0/1     Pending             0          0s
    web-1   0/1     Pending             0          2s
    web-1   0/1     ContainerCreating   0          2s
    web-1   1/1     Running             0          4s
    

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

      StatefulSet中的Pod  

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

      检查Pod顺序索引

    #参数-l更加标签进行匹配
    kubectl get pods -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          4m6s
    web-1   1/1     Running   0          4m1s
    

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

      本次示例的statefulset name为web
      序号从0开始至设定的副本数n-1

       使用稳定的网络身份标识

      每个Pod都拥有一个基于其顺序索引的稳定的主机名。

    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 run -it --image busybox:1.28 dns-test --restart=Never --rm
    nslookup web-0.nginx
    Server:    10.0.0.2
    Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local
    
    Name:      web-0.nginx
    Address 1: 172.17.71.3 web-0.nginx.default.svc.cluster.local
    

       DNS格式为 $(Pod名).$(所属服务的 DNS 域名).$(命名空间).svc.cluster.local

      所属服务的 DNS 域名由StatefulSet的serviceName设置

      其中域设置默认为cluster.local

      在一个终端查看StatefulSet的Pods

    kubectl get pods -w -l app=nginx
    

       在另一个终端删除StatefulSet中的Pods

    kubectl delete pod -l app=nginx
    pod "web-0" deleted
    pod "web-1" deleted
    

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

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          19m
    web-1   1/1     Running   0          19m
    web-0   1/1     Terminating   0          20m
    web-1   1/1     Terminating   0          20m
    web-1   0/1     Terminating   0          20m
    web-0   0/1     Terminating   0          20m
    web-1   0/1     Terminating   0          20m
    web-1   0/1     Terminating   0          20m
    web-0   0/1     Terminating   0          20m
    web-0   0/1     Terminating   0          20m
    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          1s
    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          1s
    

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

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

       

    kubectl run -it --image busybox:1.28 dns-test --restart=Never --rm
    If you don't see a command prompt, try pressing enter.
    / # nslookup web-0.nginx
    Server:    10.0.0.2
    Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local
    
    Name:      web-0.nginx
    Address 1: 172.17.71.3 web-0.nginx.default.svc.cluster.local
    

       Pod 的序号、主机名、SRV 条目和记录名称没有改变,但和 Pod 相关联的 IP 地址可能发生了改变。本次没有改变。这就是为什么不要在其他应用中使用 StatefulSet 中的 Pod 的 IP 地址进行连接,这点很重要。

      如果你需要查找并连接一个 StatefulSet 的活动成员,你应该查询 Headless Service 的 CNAME。和 CNAME 相关联的 SRV 记录只会包含 StatefulSet 中处于 Running 和 Ready 状态的 Pod。

      如果你的应用已经实现了用于测试 liveness 和 readiness 的连接逻辑,你可以使用 Pod 的 SRV 记录(web-0.nginx.default.svc.cluster.local, web-1.nginx.default.svc.cluster.local)。因为他们是稳定的,并且当你的 Pod 的状态变为 Running 和 Ready 时,你的应用就能够发现它们的地址。

      写入稳定的存储

      获取 web-0 和 web-1 的 PersistentVolumeClaims。

    kubectl get pvc -l app=nginx
    NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
    www-web-0   Bound    pvc-bbc399de-fac4-4f10-bd77-fc40fbf949a9   1Gi        RWO            managed-nfs-storage   131m
    www-web-1   Bound    pvc-4d147825-2087-41d8-8fc1-a174de4c3bf6   1Gi        RWO            managed-nfs-storage
    

       StatefulSet 控制器创建了两个 PersistentVolumeClaims,绑定到两个 PersistentVolumes。由于本教程使用的集群配置为动态提供 PersistentVolume,所有的 PersistentVolume 都是自动创建和绑定的。

      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; do kubectl exec web-$i -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; done
    
    for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done
    web-0
    web-1
    
    注意:
    请注意,如果你看见上面的 curl 命令返回了 403 Forbidden 的响应,你需要像这样修复使用 volumeMounts(due to a bug when using hostPath volumes)挂载的目录的权限:
    
    for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done
    在你重新尝试上面的 curl 命令之前。
    

       在一个终端查看 StatefulSet 的 Pod。

    kubectl get pods -w -l app=nginx
    

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

    kubectl delete pod -l app=nginx
    pod "web-0" deleted
    pod "web-1" deleted
    

       在第一个终端里检查kubectl get命令的输出,等待所有Pod变成Running和Ready状态

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          117m
    web-1   1/1     Running   0          117m
    web-0   1/1     Terminating   0          118m
    web-1   1/1     Terminating   0          118m
    web-1   0/1     Terminating   0          118m
    web-0   0/1     Terminating   0          118m
    web-0   0/1     Terminating   0          118m
    web-0   0/1     Terminating   0          118m
    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          1s
    web-1   0/1     Terminating         0          118m
    web-1   0/1     Terminating         0          118m
    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          0s
    

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

    for i in 0 1;do kubectl exec -it web-$i -- curl localhost; done
    web-0
    web-1
    

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

      扩容/缩容StatefulSet

      扩容/缩容 StatefulSet 指增加或减少它的副本数。这通过更新 replicas 字段完成。你可以使用kubectl scale 或者kubectl patch来扩容/缩容一个 StatefulSet。

      扩容

      在一个终端窗口观察 StatefulSet 的 Pod。

    kubectl get pods -w -l app=nginx
    

       在另一个终端窗口使用 kubectl scale 扩展副本数为 5。

    kubectl scale sts web --replicas=5
    statefulset.apps/web scaled
    

       在第一个 终端中检查 kubectl get 命令的输出,等待增加的 3 个 Pod 的状态变为 Running 和 Ready。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          6m17s
    web-1   1/1     Running   0          6m12s
    web-2   0/1     Pending   0          0s
    web-2   0/1     Pending   0          0s
    web-2   0/1     Pending   0          1s
    web-2   0/1     ContainerCreating   0          1s
    web-2   1/1     Running             0          3s
    web-3   0/1     Pending             0          0s
    web-3   0/1     Pending             0          0s
    web-3   0/1     Pending             0          1s
    web-3   0/1     ContainerCreating   0          1s
    web-3   1/1     Running             0          2s
    web-4   0/1     Pending             0          0s
    web-4   0/1     Pending             0          0s
    web-4   0/1     Pending             0          1s
    web-4   0/1     ContainerCreating   0          1s
    web-4   1/1     Running             0          3s
    

       缩容

      在一个终端观察 StatefulSet 的 Pod。

    kubectl get pods -w -l app=nginx
    

       在另一个终端使用 kubectl patch 将 StatefulSet 缩容回三个副本。

    kubectl patch sts web -p '{"spec":{"replicas":3}}'
    statefulset.apps/web patched
    

       等待 web-4 和 web-3 状态变为 Terminating。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          9m49s
    web-1   1/1     Running   0          9m44s
    web-2   1/1     Running   0          2m37s
    web-3   1/1     Running   0          9s
    web-4   1/1     Running   0          8s
    web-4   1/1     Terminating   0          52s
    web-4   0/1     Terminating   0          53s
    web-4   0/1     Terminating   0          59s
    web-4   0/1     Terminating   0          59s
    web-3   1/1     Terminating   0          60s
    web-3   0/1     Terminating   0          61s
    web-3   0/1     Terminating   0          65s
    web-3   0/1     Terminating   0          65s
    

       顺序终止Pod

      控制器会按照与 Pod 序号索引相反的顺序每次删除一个 Pod。在删除下一个 Pod 前会等待上一个被完全关闭。

      获取 StatefulSet 的 PersistentVolumeClaims。

    kubectl get pvc -l app=nginx
    NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
    www-web-0   Bound    pvc-bbc399de-fac4-4f10-bd77-fc40fbf949a9   1Gi        RWO            managed-nfs-storage   151m
    www-web-1   Bound    pvc-4d147825-2087-41d8-8fc1-a174de4c3bf6   1Gi        RWO            managed-nfs-storage   151m
    www-web-2   Bound    pvc-28fb3584-78ce-44a3-9523-6e89907cb9fc   1Gi        RWO            managed-nfs-storage   5m22s
    www-web-3   Bound    pvc-256cfeec-46d5-4b35-9357-acd12ef8ee9c   1Gi        RWO            managed-nfs-storage   5m19s
    www-web-4   Bound    pvc-26beab6f-852d-46fd-ac32-35632b1dc65e   1Gi        RWO            managed-nfs-storage   5m17s
    

       五个 PersistentVolumeClaims 和五个 PersistentVolumes 仍然存在。查看 Pod 的 稳定存储,我们发现当删除 StatefulSet 的 Pod 时,挂载到 StatefulSet 的 Pod 的 PersistentVolumes 不会被删除。当这种删除行为是由 StatefulSet 缩容引起时也是一样的。

      更新StatefulSet

      Kubernetes1.7版本的StatefulSet控制器支持自动更新。更新策略有StatefulSet API Object的spec.updateSreategy字段决定。这个特性能够用来更新一个StatefulSet中的Pod的container images,resource requests,limits和annotaions。RollingUpdate滚动更新是StatefulSet默认策略。

      Rolling Update 策略

      RollingUpdate 更新策略会更新一个 StatefulSet 中所有的 Pod,采用与序号索引相反的顺序并遵循 StatefulSet 的保证。

    kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
    statefulset.apps/web patched (no change)
    

       因为默认的更新策略是RollingUpdate所以提示没有修改

      在一个终端窗口中 patch web StatefulSet 来再次的改变容器镜像。

    kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx"}]'
    

       把镜像值修改成nginx,该镜像需要是可以下载的否则更新时会出现下载镜像错误提示ErrImagePull可以通过命令查看是否更新

    kubectl edit sts web
    

       在另一个终端监控 StatefulSet 中的 Pod。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS        RESTARTS   AGE
    web-2   0/1     Pending             0          0s
    web-2   0/1     Pending             0          0s
    web-2   0/1     ContainerCreating   0          0s
    web-2   1/1     Running             0          5s
    web-1   1/1     Terminating         0          5s
    web-1   0/1     Terminating         0          6s
    web-1   0/1     Terminating         0          10s
    web-1   0/1     Terminating         0          10s
    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          2s
    web-0   1/1     Terminating         0          14s
    web-0   0/1     Terminating         0          14s
    web-0   0/1     Terminating         0          15s
    web-0   0/1     Terminating         0          15s
    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          1s
    

       StatefulSet 里的 Pod 采用和序号相反的顺序更新。在更新下一个 Pod 前,StatefulSet 控制器终止每个 Pod 并等待它们变成 Running 和 Ready。请注意,虽然在顺序后继者变成 Running 和 Ready 之前 StatefulSet 控制器不会更新下一个 Pod,但它仍然会重建任何在更新过程中发生故障的 Pod,使用的是它们当前的版本。已经接收到更新请求的 Pod 将会被恢复为更新的版本,没有收到请求的 Pod 则会被恢复为之前的版本。像这样,控制器尝试继续使应用保持健康并在出现间歇性故障时保持更新的一致性。

      获取 Pod 来查看他们的容器镜像。

    for p in 0 1 2; do kubectl get po web-$p --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
    nginx
    nginx
    nginx
    

       小窍门:你还可以使用 kubectl rollout status sts/<name> 来查看 rolling update 的状态。

      分段更新

      你可以使用 RollingUpdate 更新策略的 partition 参数来分段更新一个 StatefulSet。分段的更新将会使 StatefulSet 中的其余所有 Pod 保持当前版本的同时仅允许改变 StatefulSet 的 .spec.template

      Patch web StatefulSet 来对 updateStrategy 字段添加一个分区。

    kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
    

       再次 Patch StatefulSet 来改变容器镜像。

    kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"lowyard/nginx-slim:0.8"}]'
    

       删除 StatefulSet 中的 Pod。

    kubectl delete pod web-2
    pod "web-2" deleted
    

       等待 Pod 变成 Running 和 Ready。

      获取Pod容器镜像

    kubectl get po web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
    nginx
    

       请注意,虽然更新策略是 RollingUpdate,StatefulSet 控制器还是会使用原始的容器恢复 Pod。这是因为 Pod 的序号比 updateStrategy 指定的 partition 更小。

      灰度扩容

      把分段数改成2

    kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
    statefulset.apps/web patched
    

       删除web-2以后就使用新的镜像更新了

    kubectl get po web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
    lowyard/nginx-slim:0.8
    

       当你改变 partition 时,StatefulSet 会自动的更新 web-2 Pod,这是因为 Pod 的序号小于或等于 partition

      删除web-1等待running

    kubectl delete pod web-1
    

       查看镜像还是旧的镜像

    kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
    nginx
    

       web-1 被按照原来的配置恢复,因为 Pod 的序号小于分区。当指定了分区时,如果更新了 StatefulSet 的 .spec.template,则所有序号大于或等于分区的 Pod 都将被更新。如果一个序号小于分区的 Pod 被删除或者终止,它将被按照原来的配置恢复。

      分阶段扩容

      你可以使用类似灰度扩容的方法执行一次分阶段的扩容(例如一次线性的、等比的或者指数形式的扩容)。要执行一次分阶段的扩容,你需要设置 partition 为希望控制器暂停更新的序号。

      分区当前为2。请将分区设置为0

    kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
    statefulset.apps/web patched
    

       等待 StatefulSet 中的所有 Pod 变成 Running 和 Ready。

      获取Pod的容器

    for p in 0 1 2; do kubectl get po web-$p --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
    lowyard/nginx-slim:0.8
    lowyard/nginx-slim:0.8
    lowyard/nginx-slim:0.8
    

       PS:一般扩容都是整体扩容,分阶段扩容用的较少,但在某些特定场合适用

      删除StatefulSet

      StatefulSet 同时支持级联和非级联删除。使用非级联方式删除 StatefulSet 时,StatefulSet 的 Pod 不会被删除。使用级联删除时,StatefulSet 和它的 Pod 都会被删除。

      非级联删除

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

    kubectl get pods -w -l app=nginx
    

       使用 kubectl delete 删除 StatefulSet。请确保提供了 --cascade=false 参数给命令。这个参数告诉 Kubernetes 只删除 StatefulSet 而不要删除它的任何 Pod。

    kubectl delete statefulset web --cascade=false
    statefulset.apps "web" deleted
    

       获取pod状态

    # kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          3m41s
    web-1   1/1     Running   0          3m46s
    web-2   1/1     Running   0          7m51s
    web-0   1/1     Running   0          4m36s
    web-2   1/1     Running   0          8m46s
    web-1   1/1     Running   0          4m41s
    

       虽然 web 已经被删除了,但所有 Pod 仍然处于 Running 和 Ready 状态。 删除 web-0

    kubectl delete pod web-0
    

       获取 StatefulSet 的 Pod。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-1   1/1     Running   0          6m13s
    web-2   1/1     Running   0          10m
    

       由于 web StatefulSet 已经被删除,web-0没有被重新启动。

      在一个终端监控 StatefulSet 的 Pod。

    kubectl get pods -w -l app=nginx
    

       在另一个终端里重新创建 StatefulSet。请注意,除非你删除了 nginx Service (你不应该这样做),你将会看到一个错误,提示 Service 已经存在。

    kubectl apply -f web.yaml 
    service/nginx unchanged
    statefulset.apps/web created
    

       在第一个终端中运行并检查 kubectl get 命令的输出。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-1   1/1     Running   0          10m
    web-2   1/1     Running   0          14m
    web-1   1/1     Running   0          10m
    web-2   1/1     Running   0          14m
    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          1s
    web-2   1/1     Terminating         0          14m
    web-2   0/1     Terminating         0          14m
    web-2   0/1     Terminating         0          14m
    web-2   0/1     Terminating         0          14m
    web-2   0/1     Terminating         0          14m

       当重新创建 web StatefulSet 时,web-0被第一个重新启动。由于 web-1 已经处于 Running 和 Ready 状态,当 web-0 变成 Running 和 Ready 时,StatefulSet 会直接接收这个 Pod。由于你重新创建的 StatefulSet 的 replicas 等于 2,一旦 web-0 被重新创建并且 web-1 被认为已经处于 Running 和 Ready 状态时,web-2将会被终止。

      让我们再看看被 Pod 的 web 服务器加载的 index.html 的内容。

    for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done
    web-0
    web-1
    

       尽管你同时删除了 StatefulSet 和 web-0 Pod,但它仍然使用最初写入 index.html 文件的主机名进行服务。这是因为 StatefulSet 永远不会删除和一个 Pod 相关联的 PersistentVolumes。当你重建这个 StatefulSet 并且重新启动了 web-0 时,它原本的 PersistentVolume 会被重新挂载。

      级联删除

      在一个终端窗口观察 StatefulSet 里的 Pod。

    kubectl get pods -w -l app=nginx
    

       另一个窗口中再次删除这个 StatefulSet。这次省略 --cascade=false 参数。

    kubectl delete statefulset web
    statefulset.apps "web" deleted
    

       在第一个终端检查 kubectl get 命令的输出,并等待所有的 Pod 变成 Terminating 状态。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          114s
    web-1   1/1     Running   0          12m
    web-0   1/1     Terminating   0          2m30s
    web-1   1/1     Terminating   0          12m
    web-0   0/1     Terminating   0          2m31s
    web-1   0/1     Terminating   0          12m
    web-0   0/1     Terminating   0          2m32s
    web-0   0/1     Terminating   0          2m32s
    

       如同你在缩容一节看到的,Pod 按照和他们序号索引相反的顺序每次终止一个。在终止一个 Pod 前,StatefulSet 控制器会等待 Pod 后继者被完全终止。

    请注意,虽然级联删除会删除 StatefulSet 和它的 Pod,但它并不会删除和 StatefulSet 关联的 Headless Service。你必须手动删除nginx Service。

    kubectl delete svc nginx
    service "nginx" deleted
    

       再一次重新创建 StatefulSet 和 Headless Service。

    kubectl apply -f web.yaml 
    service/nginx created
    statefulset.apps/web created
    

       当 StatefulSet 所有的 Pod 变成 Running 和 Ready 时,获取它们的 index.html 文件的内容。

    for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done
    web-0
    web-1
    

       即使你已经删除了 StatefulSet 和它的全部 Pod,这些 Pod 将会被重新创建并挂载它们的 PersistentVolumes,并且 web-0 和 web-1 将仍然使用它们的主机名提供服务。

      Pod管理策略

      对于某些分布式系统来说,StatefulSet 的顺序性保证是不必要和/或者不应该的。这些系统仅仅要求唯一性和身份标志。为了解决这个问题,在 Kubernetes 1.7 中我们针对 StatefulSet API Object 引入了 .spec.podManagementPolicy

      OrderedReady Pod 管理策略

      OrderedReady pod 管理策略是 StatefulSets 的默认选项。它告诉 StatefulSet 控制器遵循上文展示的顺序性保证。

      Parallel Pod 管理策略

      Parallel pod 管理策略告诉 StatefulSet 控制器并行的终止所有 Pod,在启动或终止另一个 Pod 前,不必等待这些 Pod 变成 Running 和 Ready 或者完全终止状态。

    # cat web-parallel.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 # has to match .spec.template.metadata.labels
      serviceName: "nginx"
      podManagementPolicy: "Parallel"
      replicas: 2 # by default is 1
      template:
        metadata:
          labels:
            app: nginx # has to match .spec.selector.matchLabels
        spec:
          terminationGracePeriodSeconds: 10
          containers:
          - name: nginx
            #image: k8s.gcr.io/nginx-slim:0.8
            image: lowyard/nginx-slim:0.8
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: "managed-nfs-storage"
          resources:
            requests:
              storage: 1Gi
    

       这份清单和你在上文下载的完全一样,只是 web StatefulSet 的 .spec.podManagementPolicy 设置成了 Parallel

      在另一个终端窗口创建清单中的 StatefulSet 和 Service。

    kubectl apply -f web-parallel.yaml 
    service/nginx created
    statefulset.apps/web created
    

       查看你在第一个终端中运行的 kubectl get 命令的输出。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   0/1     Pending   0          0s
    web-1   0/1     Pending   0          0s
    web-0   0/1     Pending   0          0s
    web-1   0/1     Pending   0          0s
    web-0   0/1     ContainerCreating   0          0s
    web-1   0/1     ContainerCreating   0          0s
    web-0   1/1     Running             0          1s
    web-1   1/1     Running             0          2s
    

       StatefulSet 控制器同时启动了 web-0 和 web-1

      保持第二个终端打开,并在另一个终端窗口中扩容 StatefulSet。

    kubectl scale statefulset/web --replicas=4
    statefulset.apps/web scaled
    

       在 kubectl get 命令运行的终端里检查它的输出。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   0/1     Pending   0          0s
    web-1   0/1     Pending   0          0s
    web-0   0/1     Pending   0          0s
    web-1   0/1     Pending   0          0s
    web-0   0/1     ContainerCreating   0          0s
    web-1   0/1     ContainerCreating   0          0s
    web-0   1/1     Running             0          1s
    web-1   1/1     Running             0          2s
    web-2   0/1     Pending             0          0s
    web-3   0/1     Pending             0          0s
    web-2   0/1     Pending             0          0s
    web-3   0/1     Pending             0          0s
    web-2   0/1     ContainerCreating   0          0s
    web-3   0/1     ContainerCreating   0          0s
    web-3   1/1     Running             0          1s
    web-2   1/1     Running             0          2s
    

       StatefulSet 控制器启动了两个新的 Pod,而且在启动第二个之前并没有等待第一个变成 Running 和 Ready 状态。

      保持这个终端打开,并在另一个终端删除 web StatefulSet。

     kubectl delete sts web
    statefulset.apps "web" deleted
    

       在另一个终端里再次检查 kubectl get 命令的输出。

    kubectl get pods -w -l app=nginx
    NAME    READY   STATUS    RESTARTS   AGE
    web-0   1/1     Running   0          2m33s
    web-1   1/1     Running   0          2m33s
    web-2   1/1     Running   0          91s
    web-3   1/1     Running   0          91s
    web-2   1/1     Terminating   0          98s
    web-1   1/1     Terminating   0          2m40s
    web-3   1/1     Terminating   0          98s
    web-0   1/1     Terminating   0          2m40s
    web-1   0/1     Terminating   0          2m41s
    web-2   0/1     Terminating   0          99s
    web-0   0/1     Terminating   0          2m42s
    web-2   0/1     Terminating   0          100s
    web-2   0/1     Terminating   0          100s
    web-1   0/1     Terminating   0          2m42s
    web-2   0/1     Terminating   0          100s
    web-3   0/1     Terminating   0          100s
    web-0   0/1     Terminating   0          2m48s
    web-0   0/1     Terminating   0          2m48s
    web-1   0/1     Terminating   0          2m53s
    web-1   0/1     Terminating   0          2m53s
    web-3   0/1     Terminating   0          111s
    web-3   0/1     Terminating   0          111s
    

       StatefulSet 控制器将并发的删除所有 Pod,在删除一个 Pod 前不会等待它的顺序后继者终止。





      

  • 相关阅读:
    一元多项式乘法
    将博客搬至CSDN
    Tomcat的几种部署方式
    Visual Studio 2012以后无法保存只读文件的问题
    WPF中的Generic.xaml, theme以及custom control
    WPF的页面导航
    WPF MVVM系列文章
    tomcat中同时部署两个项目的问题
    Windows多线程系列
    XML DTD和XML Schema
  • 原文地址:https://www.cnblogs.com/minseo/p/12540281.html
Copyright © 2020-2023  润新知