k8s-statefulset介紹


概述

該篇介紹 statefulset 的特點,我們通過實踐部分驗證 statefulset 的特點 .

有狀態應用和無狀態應用

實例之間有不對等關系,以及實例對外部數據有依賴關系的應用,就被稱為“有狀態應用”(Stateful Application),例如 : web 應用和數據庫,比如我們可以用;相反,沒有與外界有關系的應用,例如一個計算模塊(輸入一個值經過計算返回一個結果的模塊應用),比如一個推送模塊 , StatefulSet 和 Deployment 可以說對應着有狀態應用和無狀態應用 ,也可以說 StatefulSet 是一種特殊的 Deployment。

StatefulSet

k8s 這個工具是能夠支持有狀態應用的部署的, 得益於“控制器模式”的設計思想,Kubernetes 項目很早就在 Deployment 的基礎上,擴展出了對“有狀態應用”的初步支持。這個編排功能,就是:StatefulSet。 StatefulSet 的設計其實非常容易理解。它把真實世界里的應用狀態,抽象為了兩種情況:

  1. 拓撲狀態。這種情況意味着,應用的多個實例之間不是完全對等的關系。這些應用實例,必須按照某些順序啟動,比如應用的主節點 A 要先於從節點 B 啟動。而如果你把 A 和 B 兩個 Pod 刪除掉,它們再次被創建出來時也必須嚴格按照這個順序才行。並且,新創建出來的 Pod,必須和原來 Pod 的網絡標識一樣,這樣原先的訪問者才能使用同樣的方法,訪問到這個新 Pod。(操作的時候有順序)

  2. 存儲狀態。這種情況意味着,應用的多個實例分別綁定了不同的存儲數據。對於這些應用實例來說,Pod A 第一次讀取到的數據,和隔了十分鍾之后再次讀取到的數據,應該是同一份,哪怕在此期間 Pod A 被重新創建過。這種情況最典型的例子,就是一個數據庫應用的多個存儲實例。(依賴於某個特定的外部條件)

我們細讀上面這兩個特點,可不可以用兩種場景來表述呢?

  • 拓撲關系,我必須保證 A 優先於 B 啟動,當刪除的時候,B 必須優先於 A 先刪除
  • 存儲關系,比如 A 的 DNS 是 example.a.com , B的是 example.b.com , 當某些情況下,A 和 B 都異常退出后 ,k8s 會重新為 A B分配 node ,再次啟動 pod , 那么此時我訪問 example.a.com ,必須訪問的還是 A , 訪問 example.b.com 還是訪問的 。

所以,StatefulSet 的核心功能,就是通過某種方式記錄這些狀態,然后在 Pod 被重新創建時,能夠為新 Pod 恢復這些狀態。

Headless Service

    以下描述來自課程,非原創 

我在和你一起討論 Kubernetes 架構的時候就曾介紹過,Service 是 Kubernetes 項目中用來將一組 Pod 暴露給外界訪問的一種機制。比如,一個 Deployment 有 3 個 Pod,那么我就可以定義一個 Service。然后,用戶只要能訪問到這個 Service,它就能訪問到某個具體的 Pod。

那么,這個 Service 又是如何被訪問的呢?

  • 第一種方式,是以 Service 的 VIP(Virtual IP,即:虛擬 IP)方式。比如:當我訪問 10.0.23.1 這個 Service 的 IP 地址時,10.0.23.1 其實就是一個 VIP,它會把請求轉發到該 Service 所代理的某一個 Pod 上。這里的具體原理,我會在后續的 Service 章節中進行詳細介紹。

  • 第二種方式,就是以 Service 的 DNS 方式。比如:這時候,只要我訪問“my-svc.my-namespace.svc.cluster.local”這條 DNS 記錄,就可以訪問到名叫 my-svc 的 Service 所代理的某一個 Pod。

而在第二種 Service DNS 的方式下,具體還可以分為兩種處理方法:

  • 第一種處理方法,是 Normal Service。這種情況下,你訪問“my-svc.my-namespace.svc.cluster.local”解析到的,正是 my-svc 這個 Service 的 VIP,后面的流程就跟 VIP 方式一致了。

  • 第二種處理方法,正是 Headless Service。這種情況下,你訪問“my-svc.my-namespace.svc.cluster.local”解析到的,直接就是 my-svc 代理的某一個 Pod 的 IP 地址。可以看到,這里的區別在於,Headless Service 不需要分配一個 VIP,而是可以直接以 DNS 記錄的方式解析出被代理 Pod 的 IP 地址。

下面是一個標准的 Headless Service 對應的 YAML 文件:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None  ## 看這個
  selector:
    app: nginx

可以看到,所謂的 Headless Service,其實仍是一個標准 Service 的 YAML 文件。只不過,它的 clusterIP 字段的值是:None,即:這個 Service,沒有一個 VIP 作為“頭”。這也就是 Headless 的含義。所以,這個 Service 被創建后並不會被分配一個 VIP,而是會以 DNS 記錄的方式暴露出它所代理的 Pod。 當你按照這樣的方式創建了一個 Headless Service 之后,它所代理的所有 Pod 的 IP 地址,都會被綁定一個這樣格式的 DNS 記錄,如下所示:

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

這個 DNS 記錄,正是 Kubernetes 項目為 Pod 分配的唯一的“可解析身份”(Resolvable Identity)。有了這個“可解析身份”,只要你知道了一個 Pod 的名字,以及它對應的 Service 的名字,你就可以非常確定地通過這條 DNS 記錄訪問到 Pod 的 IP 地址。

實踐

創建 Headless Service 和 StatefulSet

Headless Service 的 yaml 文件

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx

StatefulSet 的 yaml 文件

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.9.1
        ports:
        - containerPort: 80
          name: web

然后我們通過命令運行這兩個 yaml

$ kubectl create -f svc.yaml
$ kubectl get service nginx
NAME      TYPE         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP    None         <none>        80/TCP    10s
$ kubectl create -f statefulset.yaml
$ kubectl get statefulset web
NAME      DESIRED   CURRENT   AGE
web       2         1         19s
[root@k8s-master ~]# kubectl get pod 
NAME                                READY   STATUS    RESTARTS   AGE
lifecycle-demo                      1/1     Running   4          18d
nginx-deployment-5d59d67564-bdn4d   1/1     Running   0          3h54m
nginx-deployment-5d59d67564-cj9qh   1/1     Running   0          3h54m
test-projected-volume               1/1     Running   3          17d
web-0                               1/1     Running   0          3h54m
web-1                               1/1     Running   0          3h54m

我們可以看到 web-0web-1是 statefulset 生成的,可以看到默認生成的pod 命令上有編號,該編號就是啟動順序相關的。

$ kubectl exec web-0 -- sh -c 'hostname'
web-0
$ kubectl exec web-1 -- sh -c 'hostname'
web-1

上面的命令的可以查詢到這兩個pod的主機名。下面我們需要驗證是否可以通過 DNS 找到對應的 pod .

通過 DNS 查詢到對應 pod 的 ip

我們通過創建一個新的 pod ,然后在 pod 里查詢 web 的信息。

$ kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh 

通過這條命令,我們啟動了一個一次性的 Pod ,對應的鏡像是 busybox:1.28.4,因為–rm 意味着 Pod 退出后就會被刪除掉。然后,在這個 Pod 的容器里面,我們嘗試用 nslookup 命令,解析一下 Pod 對應的 Headless Service:

$ nslookup web-0.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
 
Name:      web-0.nginx
Address 1: 10.244.1.7
 
$ nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
 
Name:      web-1.nginx
Address 1: 10.244.2.7

然后我們在另外的會話中,我們先刪除我們剛創建的pod,不出意外的話,新的 pod 將會創建 ,那么我們重新執行上面的步驟,在新的pod里查看 DNS 信息,假如依舊是保持原有的 DNS 信息,那么就成功驗證了 statefulset 第二條的 "存儲狀態" 的特點.

$ kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

如果手速夠快,我們還可以看到 pod 的創建過程, web-0 總是優先於 web-1 ,刪除的話則是相反的順序 .

$ kubectl get pod -w -l app=nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         32s

總結

StatefulSet 這個控制器的主要作用之一,就是使用 Pod 模板創建 Pod 的時候,對它們進行編號,並且按照編號順序逐一完成創建工作。而當 StatefulSet 的“控制循環”發現 Pod 的“實際狀態”與“期望狀態”不一致,需要新建或者刪除 Pod 進行“調諧”的時候,它會嚴格按照這些 Pod 編號的順序,逐一完成這些操作。

參考資料

  • 深入剖析Kubernetes課程
  • 重要-動手實踐 https://kubernetes.io/zh/docs/tasks/run-application/run-replicated-stateful-application/
  • https://blog.csdn.net/weixin_43936969/article/details/106289127
  • https://draveness.me/kubernetes-statefulset/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM