天天看點

hualinux 進階 1.15:StatefulSet有狀态應用(一)

目錄

​​一、StatefulSets介紹​​

​​二、StatefulSet 狀态分類​​

​​2.1 拓撲狀态​​

​​2.2 存儲狀态​​

​​三、建立一個簡單的拓撲狀态的StatefulSet​​

​​3.1 編寫statefulSet的YAML檔案​​

​​3.2 pod對應的域名​​

​​3.3 通路測試​​

​​3.3.1 安裝centos pod​​

​​3.3.2 測試​​

​​四、穩定的存儲​​

​​4.1 pv和pvc的關系​​

​​4.1.1 pv生命周期​​

​​4.1.2 pv回收政策​​

​​4.1.3 pv的聲明類型​​

​​4.2 storageClassName ​​

​​4.3 pv pvc sc之間的關系​​

​​Deployment​​​ 是一種無狀态的應用,群集中pod都是一樣的,是以适合當負載均衡,但是實際中還有不少是有狀态的應用,群集中的pod并不一樣的,所有講到今天的主角​​StatefulSets​​

一、StatefulSets介紹

​​Deployment​​ 認為,一個應用的所有 Pod,是完全一樣的。是以,它們互相之間沒有順序,也無所謂運作在哪台主控端上。需要的時候,Deployment 就可以通過 Pod 模闆建立新的 Pod;不需要的時候,Deployment 就可以“殺掉”任意一個 Pod。

在實際的場景中,并不是所有的應用都可以滿足這樣的要求。尤其是分布式應用,它的多個執行個體之間,往往有依賴關系,比如:主從關系、主備關系。

還有就是資料存儲類應用,它的多個執行個體,往往都會在本地磁盤上儲存一份資料。而這些執行個體一旦被殺掉,即便重建出來,執行個體與資料之間的對應關系也已經丢失,進而導緻應用失敗。

是以,這種執行個體之間有不對等關系,以及執行個體對外部資料有依賴關系的應用,就被稱為“有狀态應用”(Stateful Application)。

Kubernetes 有狀态與無狀态介紹

無狀态:deployment

  - 認為所有pod都是一樣的,不具備與其他執行個體有不同的關系。

  - 沒有順序的要求。

  - 不用考慮再哪個Node運作。

  - 随意擴容縮容。

有狀态:SatefulSet

  - 叢集節點之間的關系。

  - 資料不完全一緻。

  - 執行個體之間不對等的關系。

  - 依靠外部存儲的應用。

  - 通過dns維持身份

​​StatefulSets​​​更多的相關的知識,可以看它的​​官方中文​​,我這裡不多介紹

二、StatefulSet 狀态分類

2.1 拓撲狀态

拓撲狀态。這種情況意味着,應用的多個執行個體之間不是完全對等的關系。這些應用執行個體,必須按照某些順序啟動,比如應用的主節點 A 要先于從節點 B 啟動。而如果你把 A 和 B 兩個 Pod 删除掉,它們再次被建立出來時也必須嚴格按照這個順序才行。并且,新建立出來的 Pod,必須和原來 Pod 的網絡辨別一樣,這樣原先的通路者才能使用同樣的方法,通路到這個新 Pod。

2.2 存儲狀态

存儲狀态。這種情況意味着,應用的多個執行個體分别綁定了不同的存儲資料。對于這些應用執行個體來說,Pod A 第一次讀取到的資料,和隔了十分鐘之後再次讀取到的資料,應該是同一份,哪怕在此期間 Pod A 被重新建立過。這種情況最典型的例子,就是一個資料庫應用的多個存儲執行個體。

是以,StatefulSet 的核心功能,就是通過某種方式記錄這些狀态,然後在 Pod 被重新建立時,能夠為新 Pod 恢複這些狀态。

三、建立一個簡單的拓撲狀态的StatefulSet

說了那麼多,我先建立一個簡單的StatefulSet,綁定節點hostpath的,我在《​​hualinux 進階 1.7:kubeadm1.18搭建k8s群集 ​​》的基礎上建立的

3.1 編寫statefulSet的YAML檔案

#節點上操作
#建立相關目錄,我這裡隻有一個節點是vm821
mkdir -p /disk1/www/hualinux.com
echo 'vm821 index.html'>/disk1/www/hualinux.com/index.html


#在master上操作
mkdir -pv /disk1/myk8s
cd /disk1/myk8s/

cat>nginx-statefulset.yaml<<EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx-ser-none
  labels:
    web: nginx18
spec:
  selector:
   #查找比對的标簽的pod
    web: nginx18
  ports:
    - protocol: TCP
      #services對外端口
      port: 80
  #statefulSet一定要使用handless services
  clusterIP: None    
#多個Yaml檔案可以用 --- 分隔
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx-ser-none"
  replicas: 2
  selector:
    matchLabels:
      web: nginx18
  template:
    metadata:
      labels:
        web: nginx18
    spec:
      containers:
      - name: nginx
        image: nginx:1.18
        ports:
        - containerPort: 80
          name: nginx
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: hualinux
      volumes:
      - name: hualinux
        hostPath:
          # directory location on host
          path: /disk1/www/hualinux.com
EOF
kubectl apply -f nginx-statefulset.yaml      

檢視狀态得

#和Deployment不同的時pod是帶有序列的
[root@vm82 myk8s]# kubectl get po -o wide
NAME    READY   STATUS    RESTARTS   AGE     IP          NODE    NOMINATED NODE   READINESS GATES
web-0   1/1     Running   0          4m31s   10.44.0.1   vm821   <none>           <none>
web-1   1/1     Running   0          2m38s   10.44.0.2   vm821   <none>           <none>
handless無頭服務,是沒有群集IP的
[root@vm82 myk8s]# kubectl get svc -o wide
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE     SELECTOR
kubernetes       ClusterIP   10.96.0.1    <none>        443/TCP   7d23h   <none>
nginx-ser-none   ClusterIP   None         <none>        80/TCP    4m34s   web=nginx18
[root@vm82 myk8s]# 
[root@vm82 myk8s]# kubectl get statefulSet -o wide
NAME   READY   AGE     CONTAINERS   IMAGES
web    2/2     4m39s   nginx        nginx:1.18      

3.2 pod對應的域名

看到沒狀态和有狀态的差別的嗎,有狀态它是有序列的,我們再看一下它的hosts

[root@vm82 myk8s]# kubectl exec web-0 -c nginx -- grep web /etc/hosts
#這個有域名的域名格式為<pod名>.<services名>.<namespaces>.svc.cluster.local
10.44.0.1 web-0.nginx-ser-none.default.svc.cluster.local  web-0
[root@vm82 myk8s]# kubectl exec web-1 -c nginx -- grep web /etc/hosts
10.44.0.2 web-1.nginx-ser-none.default.svc.cluster.local  web-      

上面中可以看到域名綁定了本地docker的hosts檔案,格式如下:

這個有域名的域名格式為<pod名>.<services名>.<namespaces>.svc.cluster.local

具體的可以看官網的staefulSet中的​​穩定的網絡 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 Domain Service (ns/name) StatefulSet (ns/name) StatefulSet Domain Pod DNS Pod 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​

​​,除非有​​其他配置​​。

3.3 通路測試

3.3.1 安裝centos pod

如果想通路它,那麼可以再安裝一個pod,然後通過pod之間進行通路,主控端是不能直接通路的哈

我這裡再安裝一個centos7的docker看一下

# 使用 kubectl explain Pod.spec.containers 得倒docker exec -it指令在k8s pod的表達方式
#   stdin <boolean>
#  tty  <boolean>

cat>centos7.yaml<<EOF
apiVersion: v1
kind: Pod
metadata:
  name: centos7
  labels:
    sys: centos7
spec:
  containers:
  - name: centos7
    image: centos:7
    stdin: true
    tty: true
    args: ["/bin/bash"]
 

EOF
kubectl apply -f centos7.yaml

#因為沒有安裝centos7會去docker的hub鏡像下載下傳,我之前修改為華為雲了,速度會快很多
[root@vm82 myk8s]# kubectl get po centos7 
NAME      READY   STATUS    RESTARTS   AGE
centos7   1/1     Running   0          8m27s
[root@vm82 myk8s]#      

這樣就安裝完了,安裝完之後,那怎麼通路呢,因為沒有群IP,是以隻能在容器間,通過域名通路,我們在安裝kubeadm的時候預設就安裝了dns服務,是以通過域名是可以通路的。

3.3.2 測試

上面的pod運作正常後,可以登入centos7容器中,進行測試,操作如下:

#進入容器  不懂可以 使用 kubectl exec --help
kubectl exec -it centos7 -c centos7 -- bash
#在容器中執行域名解析
yum install  bind-utils telnet -y
nslookup web-0      
#檢視web-0的域名,<pod名>.<services名>
[root@centos7 /]# nslookup  web-0.nginx-ser-none
Server:   10.96.0.10
Address:  10.96.0.10#53

Name: web-0.nginx-ser-none.default.svc.cluster.local
Address: 10.44.0.1

[root@centos7 /]# nslookup  web-1.nginx-ser-none
Server:   10.96.0.10
Address:  10.96.0.10#53

Name: web-1.nginx-ser-none.default.svc.cluster.local
Address: 10.44.0.2

[root@centos7 /]# 
#發現使用hostpath,卷都成共享的了,指向中一個目錄
[root@centos7 /]# curl web-0.nginx-ser-none
vm821 index.html
[root@centos7 /]# curl web-1.nginx-ser-none
vm821 index.html      

四、穩定的存儲

上面的例子中建立的卷使用的是hostPath直接在節點上建立的,而且變成了共享目錄,我們知道statefulSet是有狀态的,如果變成有狀态的,比如mysql主從,現在共享一個目錄,沒意義啊

更緻命的是hostPath隻是綁定所在節點上,比如你node1 pod挂了,從另一個節點起來,那麼hostPath指定的目錄就是空的啊!!資料還在node1上了,萬一伺服器磁盤壞了,那怎搞?!

再者術業有專攻,如果你并不知道有哪些 Volume 類型可以用,要怎麼辦呢?作為運維隻些隻懂一部分,也不能兼做存儲專家吧,對開發者更是,不僅超越了開發者的知識儲備,還會有暴露公司基礎設施秘密的風險。因為會把使用者名,驗證字元串等敏感資訊寫進去。

這也是為什麼,在後來的演化中,Kubernetes 項目引入了一組叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 對象,大大降低了使用者聲明和使用持久化 Volume 的門檻。

4.1 pv和pvc的關系

​​PVC​​​ 和 ​​PV​​ 的設計,實際上類似于“接口”和“實作”的思想。開發者隻要知道并會使用“接口”,即:PVC;而運維人員則負責給“接口”綁定具體的實作即:PV

ps:一般主流的k8s都會內建相關的pv,直接拿來用就行。不少公司的相關産品也會出現相關的pv,運維編寫的時候可以參照一下,這樣也降低的門檻了。

這種解耦,就避免了因為向開發者暴露過多的存儲系統細節而帶來的隐患。此外,這種職責的分離,往往也意味着出現事故時可以更容易定位問題和明确責任,進而避免“扯皮”現象的出現。

而 PVC、PV 的設計,也使得 StatefulSet 對存儲狀态的管理成為了可能。

Volume 的類型有一堆可以看官網的​​Volume 的類型​​

pv所支援的​​Access模式​​​和​​挂載選項​​

下面是一個帶有PVC的statefulSet例子

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql8
spec:
  serviceName: "mysql8"
  replicas: 2
  selector:
    matchLabels:
     db: mysql8
  template:
    metadata:
      labels:
        db: mysql8
    spec:
      containers:
      - name: mysql8
        image: mysql:8.0.21
        env: 
          - name: MYSQL_ROOT_PASSWORD
            value: hua123
          - name: MYSQL_DATABASE
            value: /var/lib/mysql
        args: ["--default-authentication-plugin=mysql_native_password","--character-set-server=utf8mb4","--collation-server=utf8mb4_unicode_ci"]   
        ports:
        - containerPort: 3306
          name: mysql8
        volumeMounts:
        - mountPath: /var/lib/mysql
          name: data
  #和普通的statefulSet差不多,隻不過多了一個pvc模闆
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:      
      - ReadWriteOnce
      storageClassName: data-mysql8          
      resources:        
        requests:          
          storage: 30Gi      

凡是被這個 StatefulSet 管理的 Pod,都會聲明一個對應的 PVC;而這個 PVC 的定義,就來自于 volumeClaimTemplates 這個模闆字段。更重要的是,這個 PVC 的名字,會被配置設定一個與這個 Pod 完全一緻的編号。

這個自動建立的 PVC,與 PV 綁定成功後,就會進入 Bound 狀态,這就意味着這個 Pod 可以挂載并使用這個 PV 了。

當然,PVC 與 PV 的綁定得以實作的前提是,運維人員已經在系統裡建立好了符合條件的 PV(比如,我們在前面用到的 pv-volume);或者,你的 Kubernetes 叢集運作在公有雲上,這樣 Kubernetes 就會通過 Dynamic Provisioning 的方式,自動為你建立與 PVC 比對的 PV。

4.1.1 pv生命周期

pv (持久卷)和pod資源-樣,擁有生命周期,共分為以下四種:

Provisioning :正在申明

Binding :正在綁定

using :正在使用

Reclaiming :正在回收

4.1.2 pv回收政策

當pod資源被删除時,其相關pv和資料如何操作?該删除還是保留呢?

kubernetes通過persistentVolumeReclaimPolicy字段進行設定:

Delete :資料和pv都會删除

Recyle : ( 已廢棄)

Retain :資料和pv都不動

4.1.3 pv的聲明類型

PV的申明類型可分為以下兩種:

Static (靜态) :

管理者根據使用情況,人為預先進行配置

Dynamic (動态) :

基于已建立的StorageClasses (簡稱SC )存儲類,起到動态申請和建立的作用

API server需要增加一個參數配置: - enable amission-plugins,具體類型參考: ​​​storage-classes​​

4.2 storageClassName 

​​StorageClass​​(SC)作為對存儲資源的抽象定義,對使用者設定的PVC申請屏蔽後端存儲的細節,一方面減少了使用者對于存儲資源細節的關注,另一方面減輕了管理者手工管理PV的工作,由系統自動完成PV的建立和綁定,實作了動态的資源供應。基StorageClass的動态資源供應模式将逐漸成為雲平台的标準存儲配置模式。

StorageClass的定義主要包括名稱、後端存儲的提供者(provisioner)和後端存儲的相關參數配置。StorageClass一旦被建立出來,則将無法修改。如需更改,則隻能删除原StorageClass的定義重建。

4.3 pv pvc sc之間的關系

簡單來說

pv是用來定義持久卷的,好比接口的實作

pvc是用來使用pv的,好像調用接口

繼續閱讀