天天看點

hualinux 進階 1.15:StatefulSet有狀态應用(一)一、StatefulSets介紹二、StatefulSet 狀态分類三、建立一個簡單的拓撲狀态的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是帶有序列的
[[email protected] 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的
[[email protected] 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
[[email protected] myk8s]# 
[[email protected] myk8s]# kubectl get statefulSet -o wide
NAME   READY   AGE     CONTAINERS   IMAGES
web    2/2     4m39s   nginx        nginx:1.18
           

3.2 pod對應的域名

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

[[email protected] 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
[[email protected] 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鏡像下載下傳,我之前修改為華為雲了,速度會快很多
[[email protected] myk8s]# kubectl get po centos7 
NAME      READY   STATUS    RESTARTS   AGE
centos7   1/1     Running   0          8m27s
[[email protected] 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名>
[[email protected] /]# 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

[[email protected] /]# 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

[[email protected] /]# 
#發現使用hostpath,卷都成共享的了,指向中一個目錄
[[email protected] /]# curl web-0.nginx-ser-none
vm821 index.html
[[email protected] /]# 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的,好像調用接口

sc則是實作自動完成pv的建立和綁定的,實作動态綁定功能,如果k8s有相關的sc,那麼你就可以不用寫pv了,是不是很爽

hualinux 進階 1.15:StatefulSet有狀态應用(一)一、StatefulSets介紹二、StatefulSet 狀态分類三、建立一個簡單的拓撲狀态的StatefulSet四、穩定的存儲

繼續閱讀