深入浅出Kubernetes:StatefulSet概念理解


文書ディレクトリ
  • 深入浅出Kubernetes:StatefulSet概念理解
  • 背景知識及び関連概念
  • 二StatefulSetの二種類の構造
  • 2.1トポロジー
  • 2.2ストレージ構造
  • 深入浅出Kubernetes:StatefulSet概念理解
    一背景知識及び関連概念
    StatefulSetのデザインは実はとても分かりやすいです.実際の世界での応用状態を2つの状況に抽象化しています.
    トポロジーの状態.このことは、適用される複数のインスタンス間が完全に対等な関係ではないことを意味する.これらのアプリケーションインスタンスは、例えば、アプリケーションのプライマリノードAがノードBから先に起動されるなど、いくつかの順序で起動しなければならない.AとBの2つのPodを削除すると、再び作成されるときもこの順序に厳格に従わなければなりません.また、新しく作成されたPodは、元のPodのネットワークIDと同じように、元の訪問者が同じ方法でこの新しいPodにアクセスできるようにしなければならない.
    ストレージステータス.この場合、アプリケーションの複数のインスタンスがそれぞれ異なるストレージデータをバインドしていることを意味する.これらの適用例では、Pod Aが最初に読み取ったデータと、10分ぶりに再び読み取ったデータは、その間にPod Aが再作成されても同じであるべきである.この場合の最も典型的な例は、1つのデータベース・アプリケーションの複数のストレージ・インスタンスです.
    StatefulSetの核心機能は、何らかの方法でこれらの状態を記録し、Podが再作成されたときに、新しいPodのためにこれらの状態を回復することができることです.
    このサービスはどのようにしてアクセスされたのでしょうか?
    第1の方式は、サービスのVIP(Virtual IP、すなわち仮想IP)方式である.たとえば、172.2.0.25.3というサービスのIPアドレスにアクセスすると、172.0.25.3はVIPであり、サービスがエージェントするPodにリクエストを転送します.
    2つ目の方法は、サービスのDNS方式です.たとえば、「my-svc.my-namespace.svc.cluster.local」というDNSレコードにアクセスすれば、my-svcというServiceエージェントのあるPodにアクセスできます.
    二StatefulSetの二種類の構造
    2.1トポロジー
    次の例を見てみましょう.
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      selector:
        app: nginx
      ports:
        - port: 80
          name: web
      clusterIP: None
    
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web-server-gysl
      labels:
        app: nginx
    spec:
      serviceName: "nginx"
      replicas: 2
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          restartPolicy: Always
          containers:
            - name: web-server
              image: nginx:1.16.0
              imagePullPolicy: IfNotPresent
              ports:
                - containerPort: 80
                  name: web-port
    

    これらのPodの作成も,厳密に番号順に行われている.例えば、web-server-gysl-0がRunning状態に入り、かつ細分化状態(Conditions)がReadyになるまで、web-server-gysl-1はPending状態になる.
    次のコマンドを使用してテストします.
    kubectl run -i --tty  --image toolkit:v1.0.0821 dns-test --restart=Never --rm /bin/bash
    
    [root@dns-test /]# nslookup web-server-gysl-0.nginx
    Server:         10.0.0.2
    Address:        10.0.0.2#53
    
    Name:   web-server-gysl-0.nginx.default.svc.cluster.local
    Address: 172.20.25.3
    
    [root@dns-test /]# nslookup web-server-gysl-1.nginx
    Server:         10.0.0.2
    Address:        10.0.0.2#53
    
    Name:   web-server-gysl-1.nginx.default.svc.cluster.local
    Address: 172.20.72.7
    
    [root@dns-test /]# nslookup nginx
    Server:         10.0.0.2
    Address:        10.0.0.2#53
    
    Name:   nginx.default.svc.cluster.local
    Address: 172.20.72.7
    Name:   nginx.default.svc.cluster.local
    Address: 172.20.25.3
    

    最近のバージョンのbusyboxに穴があったので、DNSテストツールを自分で作成しました.Dockerfileは以下の通りです.
    FROM centos:7.6.1810
    RUN  yum -y install bind-utils
    CMD  ["/bin/bash","-c","while true;do sleep 60000;done"]
    

    Masterノードに戻って見てください.
    $ kubectl get pod -o wide
    NAME                READY   STATUS    RESTARTS   AGE   IP            NODE          NOMINATED NODE   READINESS GATES
    web-server-gysl-0   1/1     Running   0          43m   172.20.25.3   172.31.2.12   <none>           <none>
    web-server-gysl-1   1/1     Running   0          42m   172.20.72.7   172.31.2.11   <none>           <none>
    $ kubectl get svc -o wide
    NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
    nginx        ClusterIP   None         <none>        80/TCP    43m   app=nginx
    

    クラスタ内でドメイン名web-server-gysl-0をそれぞれpingするとnginx.default.svc.cluster.localとweb-server-gysl-1.nginx.default.svc.cluster.localの時、正常に対応するPod IPを返して、pingドメイン名nginx.default.svc.cluster.localの場合,2つのPod IPのうちの1つをランダムに返す.上記の内容を完全に証明した.
    上記の操作の過程で、私はランダムにこれらのPodの中の1つまたはいくつかを削除して、後で再び見に来た時、新しく作成したPodは依然として前の番号に従って編成されました.
    また、StatefulSetの1つのPodが存在するクラスタ内のノードをオフラインにし、再びPodの状況を確認し、システムは他のノードで元のPodの名前で迅速に新しいPodを作成しました.番号はいずれも0から加算され,StatefulSetの各Podインスタンスに1つずつ対応し,決して繰り返されない.
    2.2ストレージ構造
    試験環境資源が限られているため,rook−cephを用いて実験を行う予定であったが,NFSを用いて実験を行うことはできなかった.CephのPV作成に関するyamlは以下の通り.
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv-gysl
      labels:
        type: local
    spec:
      capacity:
        storage: 2Gi
      accessModes:
        - ReadWriteOnce
      rbd:
        monitors:
          - '172.31.2.11:6789'
          - '172.31.2.12:6789'
        pool: data
        image: data
        fsType: xfs
        readOnly: true
        user: admin
        keyring: /etc/ceph/keyrin
    

    NFS実験関連yaml:
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv-nfs-gysl-0
      labels:
        environment: test
    spec:
      capacity:
        storage: 1Gi
      accessModes:
        - ReadWriteOnce
      persistentVolumeReclaimPolicy: Recycle
      storageClassName: nfs
      nfs:
        path: /data-0
        server: 172.31.2.10
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pv-nfs-gysl-1
      labels:
        environment: test
    spec:
      capacity:
        storage: 1Gi
      accessModes:
        - ReadWriteOnce
      persistentVolumeReclaimPolicy: Recycle
      storageClassName: nfs
      nfs:
        path: /data-1
        server: 172.31.2.10
    ---
    apiVersion: storage.k8s.io/v1beta1
    kind: StorageClass
    metadata:
      name: nfs
    provisioner: fuseim.pri/ifs
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: statefulset-pvc-gysl
    spec:
      replicas: 2
      serviceName: "gysl-web"
      selector:
        matchLabels:
          app: pod-gysl
      template:
        metadata:
          name: web-pod
          labels:
            app: pod-gysl
        spec:
          containers:
            - name: nginx
              image: nginx
              imagePullPolicy: IfNotPresent
              ports:
                - name: web-port
                  containerPort: 80
              volumeMounts:
                - name: www-vct
                  mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
        - metadata:
            name: www-vct
            annotations:
              volume.beta.kubernetes.io/storage-class: "nfs"
          spec:
            accessModes:
              - ReadWriteOnce
            resources:
              requests:
                storage: 1Gi
            storageClassName: nfs
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: gysl-web
    spec:
      type: NodePort
      selector:
        app: pod-gysl
      ports:
        - name: web-svc
          protocol: TCP
          nodePort: 31688
          port: 8080
          targetPort: 80
    

    次のコマンドを使用して、関連するPodに検証内容を書き込みます.
    for node in 0 1;do kubectl exec statefulset-pvc-gysl-$node -- sh -c "echo \

    Node: ${node}\

    >/usr/share/nginx/html/index.html"
    ;done

    実験結果を観察する:
    $ kubectl get pod -o wide
    NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE          NOMINATED NODE   READINESS GATES
    statefulset-pvc-gysl-0   1/1     Running   0          51m   172.20.85.2   172.31.2.11   <none>           <none>
    statefulset-pvc-gysl-1   1/1     Running   0          32m   172.20.65.4   172.31.2.12   <none>           <none>
    $ kubectl get pvc
    NAME                             STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    www-vct-statefulset-pvc-gysl-0   Bound    pv-nfs-gysl-0   1Gi        RWO            nfs            51m
    www-vct-statefulset-pvc-gysl-1   Bound    pv-nfs-gysl-1   1Gi        RWO            nfs            49m
    $ kubectl get pv
    NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                    STORAGECLASS   REASON   AGE
    pv-nfs-gysl-0   1Gi        RWO            Recycle          Bound    default/www-vct-statefulset-pvc-gysl-0   nfs                     51m
    pv-nfs-gysl-1   1Gi        RWO            Recycle          Bound    default/www-vct-statefulset-pvc-gysl-1   nfs                     51m
    $ cat /data-0/index.html
    <h1>Node: 0</h1>
    $ cat /data-1/index.html
    <h1>Node: 1</h1>
    $ curl 172.31.2.11:31688
    <h1>Node: 0</h1>
    $ curl 172.31.2.11:31688
    <h1>Node: 1</h1>
    $ curl 172.31.2.12:31688
    <h1>Node: 1</h1>
    $ curl 172.31.2.12:31688
    <h1>Node: 0</h1>
    
    kubectl run -i --tty  --image toolkit:v1.0.0821 test --restart=Never --rm /bin/bash
    
    [root@test /]# curl statefulset-pvc-gysl-0.gysl-web
    

    Node: 0

    [root@test /]# curl statefulset-pvc-gysl-0.gysl-web

    Node: 0

    [root@test /]# curl statefulset-pvc-gysl-1.gysl-web

    Node: 1

    [root@test /]# curl statefulset-pvc-gysl-1.gysl-web

    Node: 1

    [root@test /]# curl gysl-web:8080

    Node: 1

    [root@test /]# curl gysl-web:8080

    Node: 1

    [root@test /]# curl gysl-web:8080

    Node: 0

    [root@test /]# curl gysl-web:8080

    Node: 0


    実験結果から,PodとPV,PVCの対応関係を見ることができ,前述のyamlと組み合わせて,我々は容易に発見できる:
  • Podは対応するPVストレージに1つずつ対応しており、Podを作成すると同時に、StatefulSetは対応するルールに従って対応するPVCを作成し、PVCは条件に合ったPVを選択してバインドしている.Podが削除された後もデータはPVに保存され、削除されたPodが再び作成されると、そのPodは直ちに元のPodとバインドされ、元の対応関係を維持する.
  • クラスタ内部では、pod名に対応するサービス名を付けて指定されたPodおよびそのバインドされたPVにアクセスすることができる.サービス名でStatefulSetにアクセスする場合、サービス名の機能はVIPの機能と似ています.