Kubernetes針對有狀態服務數據持久化之StatefulSet(自動創建PVC)

一、Kubernetes無狀態服務VS有狀態服務

1)Kubernetes無狀態服務

Kubernetes無狀態服務特徵:
1)是指該服務運行的實例不會在本地存儲需要持久化的數據,並且多個實例對於同一請求響應的結果是完全一致的;
2)多個實例可以共享相同的持久化數據。例如:nginx實例、tomcat實例等;
3)相關的Kubernetes資源有:ReplicaSet、ReplicationController、Deployment等,由於是無狀態服務,所以這些控制器創建的Pod名稱都是隨機性的。並且在縮容時並不會明確縮容某一個Pod,而是隨機的,因爲所有實例得到的返回值都是一樣的,所以縮容任何一個Pod都可以;

2)Kubernetes有狀態服務

Kubernetes有狀態服務特徵:
1)有狀態服務可以說是需要數據存儲功能的服務、或者指多線程類型的服務、隊列等。(比如:mysql數據庫、kafka、zookeeper等);
2)每個實例都需要自己獨立的持久化存儲,並且在Kubernetes中通過聲明模板的方式來進行定義。持久卷聲明模板在創建pod之前創建,綁定到pod中,模板可以定義多個;
3)相關的Kubernetes資源有:StatefulSet。由於是有狀態的服務,所以每個Pod都有特定的名稱和網絡標識。比如Pod名稱是由StatefulSet名+有序的數字組成(0、1、2……);
4)在進行縮容操作時,可以明確知道會縮容那一個Pod,從數字最大的開始。並且StatefulSet在已有實例不健康的情況下是不允許做縮容操作的;

3)無狀態服務和有狀態服務的區別

主要表現在以下方面:
1)實例數量:無狀態服務可以有一個或多個實例,因此支持兩種服務容量調節模式;有狀態服務職能有一個實例,不允許創建多個實例,因此也不支持服務容量的調節;
2)存儲卷:無狀態服務可以有存儲卷,也可以沒有,即使有也無法備份存儲卷中的數據;有狀態服務必須要有存儲卷,並且在創建服務時,必須指定該存儲卷分配的磁盤空間大小;
3)數據存儲: 無狀態服務運行過程中的所有數據(除日誌和監控數據)都存在容器實例裏的文件系統中,如果實例停止或者刪除,則這些數據都將丟失,無法找回;而對於有狀態服務,凡是已經掛載了存儲卷的目錄下的文件內容都可以隨時進行備份,備份的數據可以下載,也可以用於恢復新的服務。但對於沒有掛載卷的目錄下的數據,仍然是無法備份和保存的,如果實例停止或者刪除,這些非掛載卷裏的文件內容同樣會丟失。

4)StatefulSet概述

StatefulSet是Kubernetes提供的管理有狀態應用的負載管理控制器API。在Pods管理的基礎上,保證Pods的順序和一致性。與Deployment一樣,StatefulSet也是使用容器的Spec來創建Pod,與之不同StatefulSet創建的Pods在生命週期中會保持持久的標記(例如Pod Name)。

5)StatefulSet特點

1)穩定的持久化存儲,即Pod重新調度後還是能訪問到相同的持久化數據,基於PVC來實現;
2)穩定的網絡標誌,即Pod重新調度後其PodName和HostName不變,基於Headless Service(即沒有Cluster IP的Service)來實現;
3)有序部署,有序擴展,即Pod是有順序的,在部署或者擴展的時候要依據定義的順序依次依次進行(即從0到N-1,在下一個Pod運行之前所有之前的Pod必須都是Running和Ready狀態),基於init containers來實現;
4)有序收縮,有序刪除(即從N-1到0);

二、使用StatefulSet實現自動創建PVC

1)搭建NFS共享存儲

爲了方便,就直接在master節點上部署NFS存儲了!

[root@master ~]# yum -y install nfs-utils rpcbind
[root@master ~]# vim /etc/exports
/nfsdata *(rw,sync,no_root_squash)
[root@master ~]# mkdir /nfsdata
[root@master ~]# systemctl start nfs-server
[root@master ~]# systemctl start rpcbind
[root@master ~]# showmount -e
Export list for master:
/nfsdata *

2)創建rbac授權

[root@master ~]# vim rbac-rolebind.yaml
apiVersion: v1                            #創建一個用於認證的服務賬號
kind: ServiceAccount
metadata:
  name: nfs-provisioner
---
apiVersion: rbac.authorization.k8s.io/v1        #創建羣集規則
kind: ClusterRole
metadata:
  name: nfs-provisioner-runner
rules:
   -  apiGroups: [""]
      resources: ["persistentvolumes"]
      verbs: ["get", "list", "watch", "create", "delete"]
   -  apiGroups: [""]
      resources: ["persistentvolumeclaims"]
      verbs: ["get", "list", "watch", "update"]
   -  apiGroups: ["storage.k8s.io"]
      resources: ["storageclasses"]
      verbs: ["get", "list", "watch"]
   -  apiGroups: [""]
      resources: ["events"]
      verbs: ["watch", "create", "update", "patch"]
   -  apiGroups: [""]
      resources: ["services", "endpoints"]
      verbs: ["get","create","list", "watch","update"]
   -  apiGroups: ["extensions"]
      resources: ["podsecuritypolicies"]
      resourceNames: ["nfs-provisioner"]
      verbs: ["use"]
---
kind: ClusterRoleBinding                #將服務認證用戶與羣集規則進行綁定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-provisioner
    namespace: default                    #必寫字段,否則會提示錯誤
roleRef:
  kind: ClusterRole
  name: nfs-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
[root@master ~]# kubectl apply -f rbac-rolebind.yaml    

3)創建nfs-deployment.資源

[root@master ~]# vim nfs-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1                              #指定副本數量爲1
  strategy:
    type: Recreate                      #指定策略類型爲重置
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccount: nfs-provisioner            #指定rbac yanl文件中創建的認證用戶賬號
      containers:
        - name: nfs-client-provisioner
          image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner     #使用的鏡像 
          volumeMounts:
            - name: nfs-client-root
              mountPath:  /persistentvolumes             #指定容器內掛載的目錄
          env:
            - name: PROVISIONER_NAME           #容器內的變量用於指定提供存儲的名稱
              value: lzj
            - name: NFS_SERVER                      #容器內的變量用於指定nfs服務的IP地址
              value: 192.168.1.1
            - name: NFS_PATH                       #容器內的變量指定nfs服務器對應的目錄
              value: /nfsdata
      volumes:                                                #指定掛載到容器內的nfs的路徑及IP
        - name: nfs-client-root
          nfs:
            server: 192.168.1.1
            path: /nfsdata
[root@master ~]# kubectl apply -f nfs-deployment.yaml
[root@master ~]# kubectl get pod 
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-66df958f9c-mbvhv   1/1     Running   0          2m34s

4)創建SC(Storage Class)

[root@master ~]# vim sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: stateful-nfs
  namespace: xiaojiang-test
provisioner: lzj                  #這個要和nfs-client-provisioner的env環境變量中的PROVISIONER_NAME的value值對應。
reclaimPolicy: Retain               #指定回收策略爲Retain(手動釋放)
[root@master ~]# kubectl apply -f sc.yaml 
[root@master ~]# kubectl get StorageClass
NAME           PROVISIONER   AGE
stateful-nfs   lzj           17s

5)創建Pod

[root@master ~]# vim statefulset.yaml 
apiVersion: v1
kind: Service
metadata:
  name: headless-svc                    #從名稱就可以是無頭服務
  labels:
    app: headless-svc
spec:
  ports:
  - port: 80
    name: myweb
  selector:
    app: headless-pod
  clusterIP: None                        #不分配羣集的IP地址,所以不具備負載均衡的能力
---
apiVersion: apps/v1
kind: StatefulSet                          #定義pod中運行的應用
metadata:
  name: statefulset-test
spec:
  serviceName: headless-svc
  replicas: 3
  selector:
    matchLabels:
      app: headless-pod
  template:
    metadata:
      labels:
        app: headless-pod
    spec:
      containers:
      - image: httpd
        name: myhttpd
        ports:
        - containerPort: 80
          name: httpd
        volumeMounts:
        - mountPath: /usr/local/apache2/htdocs
          name: test
  volumeClaimTemplates:                       #定義創建PVC使用的模板
  - metadata:
      name: test
      annotations:  #這是指定storageclass
        volume.beta.kubernetes.io/storage-class: stateful-nfs
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 100Mi
[root@master ~]# kubectl apply -f statefulset.yaml 
[root@master ~]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-66df958f9c-mbvhv   1/1     Running   0          10m
statefulset-test-0                        1/1     Running   0          29s
statefulset-test-1                        1/1     Running   0          18s
statefulset-test-2                        1/1     Running   0          11s
[root@master ~]# kubectl get pv
[root@master ~]# kubectl get pvc              #PV與PVC已經生成
[root@master ~]# ls /nfsdata/
default-test-statefulset-test-0-pvc-54d0b06c-698e-4f1a-8327-255b10978cbe
default-test-statefulset-test-1-pvc-1b499d49-a787-4f2b-b238-404b05f75fd7
default-test-statefulset-test-2-pvc-7766f8da-6f3b-4c1f-9eb8-dfadda1e656f
[root@master ~]# echo "hello world" > /nfsdata/default-test-statefulset-test-0-pvc-54d0b06c-698e-4f1a-8327-255b10978cbe/index.html
[root@master ~]# kubectl get pod -o wide | grep test-0
statefulset-test-0                        1/1     Running   0          4m53s   10.244.2.4   node02   <none>           <none>
[root@master ~]# curl 10.244.2.4
hello world
[root@master ~]# curl -I 10.244.2.4
HTTP/1.1 200 OK
Date: Wed, 12 Feb 2020 09:52:04 GMT
Server: Apache/2.4.41 (Unix)
Last-Modified: Wed, 12 Feb 2020 09:45:37 GMT
ETag: "c-59e5dd5ac0a63"
Accept-Ranges: bytes
Content-Length: 12
Content-Type: text/html
#可以看出現在提供web頁面的服務是Apache

6)對pod進行更新並擴容

[root@master ~]# vim statefulset.yaml 
apiVersion: v1
kind: Service
metadata:
  name: headless-svc
  labels:
    app: headless-svc
spec:
  ports:
  - port: 80
    name: myweb
  selector:
    app: headless-pod
  clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test
spec:
  updateStrategy:
    rollingUpdate:
      partition: 2                           #默認值爲0(表示所有都會更新),值爲2表示第三個pod進行更新
  serviceName: headless-svc
  replicas: 10
  selector:
    matchLabels:
      app: headless-pod
  template:
    metadata:
      labels:
        app: headless-pod
    spec:
      containers:
      - image: nginx                       #更換擴容時使用的鏡像
        name: myhttpd
        ports:
        - containerPort: 80
          name: httpd
        volumeMounts:
        - mountPath: /usr/share/nginx/html/                 #更換容器中的主目錄
          name: test
  volumeClaimTemplates:
  - metadata:
      name: test
      annotations:  #這是指定storageclass
        volume.beta.kubernetes.io/storage-class: stateful-nfs
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 100Mi
[root@master ~]# kubectl apply -f statefulset.yaml 
[root@master ~]# kubectl get pod -o wide
NAME                                      READY   STATUS    RESTARTS   AGE     IP           NODE     NOMINATED NODE   READINESS GATES
nfs-client-provisioner-66df958f9c-mbvhv   1/1     Running   0          35m     10.244.2.2   node02   <none>           <none>
statefulset-test-0                        1/1     Running   0          21m     10.244.2.4   node02   <none>           <none>
statefulset-test-1                        1/1     Running   0          20m     10.244.1.4   node01   <none>           <none>
statefulset-test-2                        1/1     Running   0          3m52s   10.244.1.9   node01   <none>           <none>
statefulset-test-3                        1/1     Running   0          4m54s   10.244.2.5   node02   <none>           <none>
statefulset-test-4                        1/1     Running   0          4m43s   10.244.1.6   node01   <none>           <none>
statefulset-test-5                        1/1     Running   0          4m31s   10.244.2.6   node02   <none>           <none>
statefulset-test-6                        1/1     Running   0          4m25s   10.244.1.7   node01   <none>           <none>
statefulset-test-7                        1/1     Running   0          4m19s   10.244.2.7   node02   <none>           <none>
statefulset-test-8                        1/1     Running   0          4m12s   10.244.1.8   node01   <none>           <none>
statefulset-test-9                        1/1     Running   0          4m3s    10.244.2.8   node02   <none>           <none>
[root@master ~]# ls /nfsdata/ | wc -l
10
[root@master ~]# curl -I 10.244.2.4
HTTP/1.1 200 OK
Date: Wed, 12 Feb 2020 10:05:34 GMT
Server: Apache/2.4.41 (Unix)
Last-Modified: Wed, 12 Feb 2020 09:45:37 GMT
ETag: "c-59e5dd5ac0a63"
Accept-Ranges: bytes
Content-Length: 12
Content-Type: text/html
[root@master ~]# curl -I 10.244.2.8
HTTP/1.1 403 Forbidden
Server: nginx/1.17.8
Date: Wed, 12 Feb 2020 10:05:41 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive

由此可以看出在在擴容、縮容過程中,pod的生成或刪除操作也是有順序;並不會更改pod,這就是StatefulSet的特點!

————————————本文到此結束,感謝閱讀————————————

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章