Kubernetes工作負載資源之StatefulSet
這里將介紹Kubernetes工作負載資源中的StatefulSet

創(chuàng)建StatefulSet
不同于RC、RS、Deployment可以用于部署無狀態(tài)的應用。而對于有狀態(tài)的應用而言,其要求Pod需要具有穩(wěn)定的、唯一的網(wǎng)標識符,穩(wěn)定的、持久的存儲。這對于RS、Deployment而言是無法滿足的。為此K8s中提供了一種專用于有狀態(tài)應用的StatefulSet,其可以保證當一個有狀態(tài)的Pod被刪除后(比如人工手動刪除),K8s則會創(chuàng)建一個與之完全一致的Pod出來,包括Pod名稱、網(wǎng)絡主機名、存儲等
對于StatefulSet的Pod而言,即使其被重新調度到別的節(jié)點,其也必須掛載舊Pod此前所使用的存儲。為此Pod所使用的必須是PV持久卷,且需要與Pod解耦。故需要將每個Pod綁定各自的PVC持久卷聲明。為此在StatefulSet的定義中,我們需設置PVC模板即可。以便StatefulSet能夠按照PVC模板創(chuàng)建出與Pod數(shù)量相同的PVC。而PV持久卷則既可以是管理員提前創(chuàng)建的,也可以是動態(tài)制備的
這里我們先手動創(chuàng)建兩個PV
# 創(chuàng)建持久卷PV
apiVersion: v1
kind: PersistentVolume
metadata:
# PV卷名稱
name: pv-a
spec:
# 容量
capacity:
# 存儲大小: 30MB
storage: 30Mi
# 該卷支持的訪問模式
accessModes:
- ReadWriteOnce # RWO, 該卷可以被一個節(jié)點以讀寫方式掛載
- ReadOnlyMany # ROX, 該卷可以被多個節(jié)點以只讀方式掛載
# 回收策略: 保留
persistentVolumeReclaimPolicy: Retain
# 該持久卷的實際存儲類型: 此處使用HostPath類型卷
hostPath:
path: /tmp/PvA
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-b
spec:
capacity:
storage: 30Mi
accessModes:
- ReadWriteOnce
- ReadOnlyMany
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /tmp/PvB
---
效果如下所示

對于StatefulSet而言,其還需要提前創(chuàng)建一個Headless Service
# 創(chuàng)建 Headless Service
apiVersion: v1
kind: Service
metadata:
name: my-kubia-headless-service
spec:
type: ClusterIP
# 對于ClusterIP類型服務而言, 當clusterIP為None時表示這是一個Headless Service
clusterIP: None
selector:
app: my-kubia
ports:
- port: 12121 # 服務監(jiān)聽端口
targetPort: 8080 # 服務將請求轉發(fā)到Pod的目標端口
現(xiàn)在我們開始創(chuàng)建StatefulSet,配置文件如下所示
# 創(chuàng)建 StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: my-statefulset-kubia
spec:
# 標簽選擇器
selector:
matchLabels:
app: my-kubia
# 設定Headless Service的服務名稱
serviceName: my-kubia-headless-service
# Pod的副本數(shù)
replicas: 2
# Pod模板
template:
metadata:
labels:
# 標簽信息
app: my-kubia
spec:
containers:
- name: my-app-kubia
image: luksa/kubia-pet
# 僅用于展示容器所使用的端口
ports:
- containerPort: 8080
protocol: TCP
volumeMounts:
# 將名為my-volume-data的卷掛載到容器指定路徑/var/data上
- name: my-volume-data
mountPath: /var/data
# PVC模板
volumeClaimTemplates:
- metadata:
# 卷的名稱
name: my-volume-data
spec:
# 禁止使用動態(tài)制備的卷, 即使用靜態(tài)制備
storageClassName: ""
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Mi
效果如下所示,兩個Pod分別綁定各自的PVC。而這兩個PVC進一步地分別使用了我們之前創(chuàng)建的兩個PV。與此同時,對于StatefulSet創(chuàng)建的Pod而言,其名稱命名規(guī)則是StatefulSet名稱加索引編號

測試
該應用支持通過POST請求將數(shù)據(jù)存儲到容器內的/var/data路徑下,通過GET請求獲取容器內的/var/data路徑下的數(shù)據(jù)內容。這里我們先啟動一個代理,然后利用API服務器訪問指定的Pod。效果如下所示,符合預期

對于包含N個Pod副本的StatefulSet而言,其會按照索引順序依次創(chuàng)建N個Pod。即先創(chuàng)建索引為0的Pod,再創(chuàng)建索引為1的Pod,再創(chuàng)建索引為2的Pod,以此類推。而當我們進行縮容的時候,則它們是按照索引逆序的順序依次終止Pod的,即順序為N-1、N-2、N-3、...、2、1、0?,F(xiàn)在我們將該StatefulSet的Pod副本數(shù)縮容為1
# 將StatefulSet的Pod副本數(shù)調整為指定數(shù)量
kubectl scale statefulset my-statefulset-kubia --replicas=1
# 將指定名稱的StatefulSet的Pod副本數(shù)調整為指定數(shù)量
kubectl scale statefulset <StatefulSet的名稱> --replicas=<Pod的副本數(shù)>
效果如下所示。而且即使縮容了,索引1的Pod所使用的PVC、PV也依然會被保留。這樣當你再次擴容時,其依然會創(chuàng)建一個索引為1的Pod,且依然會綁定此前索引為1的Pod所使用的PVC。無論這個新Pod會被調度哪個集群節(jié)點當中

現(xiàn)在我們重新將該StatefulSet擴容到2??梢钥吹剿饕秊?的Pod再次使用了之前的PVC。且訪問該Pod可以查詢到此前的數(shù)據(jù),進一步驗證了這個結果

查詢SRV記錄
現(xiàn)在我們創(chuàng)建一個Pod演示如何用于獲取該StatefulSet中全部Pod的網(wǎng)絡信息
# 創(chuàng)建一個包含dig、ping命令的Pod
apiVersion: v1
kind: Pod
metadata:
# Pod 名稱
name: my-dnsutils
spec:
# 容器信息
containers:
- name: my-dnsutils
# 鏡像信息
image: tutum/dnsutils
# 設置容器啟動時執(zhí)行的命令、參數(shù)
command: # 該命令可以保持容器一直運行, 而不會結束退出
- sleep
- infinity
在創(chuàng)建StatefulSet時我們指定了一個Headless Service。其目的在于該Headless Service會創(chuàng)建出相應的SRV記錄。通過SRV記錄我們就可以知道來該服務對應Pod的域名、IP、端口等信息了。其中該StatefulSet的域名規(guī)則一般如下所示
# StatefulSet的域名規(guī)則
<Headless Service的服務名稱>.<命名空間的名稱>.svc.cluster.local
# 通過dig srv命令查詢SRV記錄
dig srv my-kubia-headless-service.default.svc.cluster.local
效果如下所示。這樣我們就可以實現(xiàn)獲取StatefulSet中全部Pod的網(wǎng)絡地址了

參考文獻
Kubernetes in Action中文版 Marko Luksa著 深入剖析Kubernetes 張磊著
