• Kubernetes之StatefulSet


    什么是StatefulSet

    StatefulSet 是Kubernetes中的一种控制器,他解决的什么问题呢?我们知道Deployment是对应用做了一个简化设置,Deployment认为一个应用的所有的pod都是一样的,他们之间没有顺序,也无所谓在那台宿主机上。需要扩容的时候就可以通过pod模板加入一个,需要缩容的时候就可以任意杀掉一个。但是实际的场景中,并不是所有的应用都能做到没有顺序等这种状态,尤其是分布式应用,他们各个实例之间往往会有对应的关系,例如:主从、主备。还有数据存储类应用,它的多个实例,往往会在本地磁盘存一份数据,而这些实例一旦被杀掉,即使从建起来,实例与数据之间关系也会丢失,而这些实例有不对等的关系,实例与外部存储有依赖的关系的应用,被称作“有状态应用”。StatefulSet与Deployment相比,相同于他们管理相同容器规范的Pod,不同的时候,StatefulSet为pod创建一个持久的标识符,他可以在任何编排的时候得到相同的标识符。

    StatefulSet的应用特点:

    • 稳定且有唯一的网络标识符 当节点挂掉,既pod重新调度后其PodName和HostName不变,基于Headless Service来实现
    • 稳定且持久的存储  当节点挂掉,既pod重新调度能访问到相同的持久化存储,基于PVC实现
    • 有序、平滑的扩展、部署 即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现。
    • 有序、平滑的收缩、删除 既Pod是有顺序的,在收缩或者删除的时候要依据定义的顺序依次进行(既从N-1到0,既倒序)。

    我们可以把这些抽象成两种应用状态:

    • 拓扑状态。是应用多个实例之间的不完全对等的关系,这些应用实例是必须按照定义的顺序启动的。例如主应用A先于从应用B启动,如果把A和B删掉,应用还要按照先启动主应用A再启动从应用B,且创建的应用必须和原来的应用的网络标识一样(既PodName和HostName)。这样他们就可以按照原来的顺序创建了。
    • 之间不是完全对等的关系

      极客时间版权所有: https://time.geekbang.org/column/article/41017

      存储状态。应用实例分别绑定了不同的数据存储,Pod A第一次读到的数据要和10分钟后读到的数据,是同一份。哪怕这期间Pod A被重建。这种典型的例子就是数据库应用的多个存储实例。

    所以 StatefulSet的核心功能就是,通过某种方式记录应用状态,在Pod被重建的时候,通过这种方式还可以恢复原来的状态。

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

    • Headless Service 用于定义网络标识(DNS)
    • volumeClaimTemplates  用于创建PV
    • StatefulSet  用于定义具体应用

    讲解Headless Service

    我们知道kubernetes中的service是定义pod暴露外部访问的一种机制,例如:3个pod,我们可以定义一个service通过标签选择器选到这三个pod,然后让问这个service就可以访问这个pod。可以出门左转,看一下 Service的讲解。 我们这里具体讲一下Headless service。

    Headless service是Service通过DNS访问的其中一种方式,只要我们访问"mypod.stsname.namespace.svc.cluster.local",我们就会访问到stsname下的mypod。而Service DNS的方式下有两种处理方法:

    • Normal Service 这里访问"mypod.stsname.namespace.svc.cluster.local"的时候会得到mypod的service的IP,既VIP。
    • Headless Service 这里访问"mypod.stsname.namespace.svc.cluster.local"的时候会得到mypod的IP,这里我们可以看到区别是,Headless Service 不需要分配一个VIP,而是通过DNS访问的方式可以解析出带代理的Pod的IP

    Headleaa Service的定义方式:

    apiVersion: v1
    kind: Service
    metadata:
      name: myapp-headless
      namespace: default
    spec:
      selector:
        app: myapp
        release: dev
      clusterIP: "None"
      ports:
      - port: 80
        targetPort: 80

    Headless Service也是一个标准的Service的YAML文件,只不过clusterIP定义为None,既,这个service没有VIP作为"头"。以DNS会记录的方式记录所有暴露它代理的Pod。而它代理的Pod依然会采用标签选择器的机制选择。既:所有携带了 app=myapp 标签的pod。都会被service代理起来。

    DNS格式:

    pod-name.svc-name.namespace.svc.cluster.local

    StatefulSet详解

    kubectl explain sts.spec 主要字段解释:

    • replicas   副本数
    • selector  那个pod是由自己管理的
    • serviceName  必须关联到一个无头服务商
    • template 定义pod模板(其中定义关联那个存储卷)
    • volumeClaimTemplates  生成PVC

    部署一个statefulset服务

    这里我们用的storageClass,PV和PVC会动态创建。这个跑成功必须创建一个rook服务的动态存储,创建方法参考 rook官网

    apiVersion: v1
    kind: Service
    metadata:
      name: myapp-sts
      labels:
        app: myapp-sts
    spec:
      ports:
      - port: 80
        name: web
      clusterIP: "None"
      selector:
        app: myapp-pod
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: myapp
    spec:
      serviceName: myapp-sts-svc
      replicas: 2
      selector:
        matchLabels:
          app: myapp-pod
      template:
        metadata:
          labels:
            app: myapp-pod
        spec:
          containers:
          - name: myapp
            image: ikubernetes/myapp:v1
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: myappdata
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: myappdata
        spec:
          accessModes: ["ReadWriteOnce"]
          storageClassName: "rook-ceph-block"
          resources:
            requests:
              storage: 5Gi

    查看一下服务

    $ kubectl get svc
    NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
    myapp-sts    ClusterIP   None           <none>        80/TCP    5m

     查看pv/pvc

    $ kubectl get pvc
    NAME                STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
    myappdata-myapp-0   Bound     pvc-550493cf-dc1f-11e8-b8c9-005056930126   5Gi        RWO            rook-ceph-block   43m
    myappdata-myapp-1   Bound     pvc-5a4276b4-dc1f-11e8-b8c9-005056930126   5Gi        RWO            rook-ceph-block   43m
    $  kubectl get pv
    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM                       STORAGECLASS      REASON    AGE
    pvc-550493cf-dc1f-11e8-b8c9-005056930126   5Gi        RWO            Delete           Bound     default/myappdata-myapp-0   rook-ceph-block             43m
    pvc-5a4276b4-dc1f-11e8-b8c9-005056930126   5Gi        RWO            Delete           Bound     default/myappdata-myapp-1   rook-ceph-block             43m

     查看pod

    $ kubectl get pod
    NAME                          READY     STATUS    RESTARTS   AGE
    myapp-0                       1/1       Running   0          17h
    myapp-1                       1/1       Running   0          17h

    验证解析

    $ kubectl exec -it myapp-1 -- /bin/sh 
    / # nslookup  myapp-1.myapp-sts-svc.default.svc.cluster.local
    nslookup: can't resolve '(null)': Name does not resolve
    
    Name:      myapp-1.myapp-sts-svc.default.svc.cluster.local
    Address 1: 10.244.3.37 myapp-1.myapp-sts-svc.default.svc.cluster.local

    扩容(扩展应该是按顺序扩展)

    $ kubectl scale sts myapp --replicas=5
    
    $ kubectl get pod -w 
    NAME                          READY     STATUS    RESTARTS   AGE
    myapp-0                       1/1       Running   0          18h
    myapp-1                       1/1       Running   0          18h
    myapp-2   0/1       Pending   0         0s
    myapp-2   0/1       Pending   0         0s
    myapp-2   0/1       Pending   0         0s
    myapp-2   0/1       ContainerCreating   0         0s
    myapp-2   1/1       Running   0         15s
    myapp-3   0/1       Pending   0         0s
    myapp-3   0/1       Pending   0         1s
    myapp-3   0/1       Pending   0         1s
    myapp-3   0/1       ContainerCreating   0         1s
    $ kubectl get pvc
    NAME                STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
    myappdata-myapp-0   Bound     pvc-550493cf-dc1f-11e8-b8c9-005056930126   5Gi        RWO            rook-ceph-block   18h
    myappdata-myapp-1   Bound     pvc-5a4276b4-dc1f-11e8-b8c9-005056930126   5Gi        RWO            rook-ceph-block   18h
    myappdata-myapp-2   Bound     pvc-25e46dbd-dcba-11e8-b8c9-005056930126   5Gi        RWO            rook-ceph-block   2m
    myappdata-myapp-3   Bound     pvc-2f420e8c-dcba-11e8-b8c9-005056930126   5Gi        RWO            rook-ceph-block   1m
    myappdata-myapp-4   Bound     pvc-4607d3c8-dcba-11e8-b8c9-005056930126   5Gi        RWO            rook-ceph-block   1m

    缩容 (按倒序缩容)

    $ kubectl scale sts myapp --replicas=3
    
    $  kubectl get pod -w 
    NAME                          READY     STATUS    RESTARTS   AGE
    myapp-0                       1/1       Running   0          18h
    myapp-1                       1/1       Running   0          18h
    myapp-2                       1/1       Running   0          3m
    myapp-3                       1/1       Running   0          3m
    myapp-4                       1/1       Running   0          2m
    myapp-4   1/1       Terminating   0         2m
    myapp-4   0/1       Terminating   0         2m
    myapp-4   0/1       Terminating   0         2m
    myapp-4   0/1       Terminating   0         2m
    myapp-3   1/1       Terminating   0         3m
    myapp-3   0/1       Terminating   0         3m
    myapp-3   0/1       Terminating   0         3m
    myapp-3   0/1       Terminating   0         3m

    升级

    查看升级策略

    kubectl explain sts.spec.updateStrategy

    • rollingUpdate  滚动更新

    kubectl explain sts.spec.updateStrategy.rollingUpdate

    • partition  分区更新,默认partition的值是0,当partition等N,N+的都会更新。

    默认partition是从0开始更新

     当partition等于4的时候,4以后的都要更新。

    $ kubectl set image sts/myapp myapp=ikubernetes/myapp:v2
    $ kubectl describe pod  myapp-2  
    Name:               myapp-2
    Namespace:          default
    Priority:           0
    PriorityClassName:  <none>
    Node:               k8s-node01/172.16.138.41
    Start Time:         Thu, 01 Nov 2018 02:33:00 -0400
    Labels:             app=myapp-pod
                        controller-revision-hash=myapp-5775ff7474
                        statefulset.kubernetes.io/pod-name=myapp-2
    Annotations:        <none>
    Status:             Running
    IP:                 10.244.1.13
    Controlled By:      StatefulSet/myapp
    Containers:
      myapp:
        Container ID:   docker://75d5e6b3958f053908eb3e5fa1c2846ce91d90c9ff696f27e6220a66d2e8cc7c
        Image:          ikubernetes/myapp:v2
        Image ID:       docker-pullable://ikubernetes/myapp@sha256:85a2b81a62f09a414ea33b74fb8aa686ed9b168294b26b4c819df0be0712d358
        Port:           80/TCP
        Host Port:      0/TCP
    .....

    Normal Service

    极客时间版权所有

  • 相关阅读:
    java 异常捕获机制
    读取java当前路径的文件输出在控制台
    java 简易记事本 程序
    java 利用文件输入输出流实现文件复制
    IutputStreamWriterDemo
    OutputStreamWriterDemo
    Longest Substring Without Repeat Characters
    Add Two Numbers
    Two Sum
    使用 Canvas 绘图
  • 原文地址:https://www.cnblogs.com/xzkzzz/p/9871837.html
Copyright © 2020-2023  润新知