在K8s中創建StatefulSet
遇到的問題:
使用Deployment創建的Pod是無狀態的,當掛在Volume之后,如果該Pod掛了,Replication Controller會再run一個來保證可用性,但是由於是無狀態的,Pod掛了的時候與之前的Volume的關系就已經斷開了,新起來的Pod無法找到之前的Pod。但是對於用戶而言,他們對底層的Pod掛了沒有感知,但是當Pod掛了之后就無法再使用之前掛載的磁盤了。
解決方案
使用K8s v1.5版本推出的StatefulSet可以保留Pod的狀態。
參考博客
因為Kubernetes1.5版本發布也沒過多久,國內的資料相當的少,除了tonybai等大牛博客上有一些StatefulSet的資料外,只能去外網上搜索了。通過在google上搜索StatefulSet的創建方式,最終發現了一篇英文博客,鏈接如下。對於這種新興框架,還是要多去google啊。
https://thenewstack.io/deploy-highly-available-wordpress-instance-statefulset-kubernetes-1-5/
如何創建
在創建StatefulSet之前需要准備的東西,值得注意的是創建順序非常關鍵,創建順序如下:
- 1、Volume
- 2、Persistent Volume
- 3、Persistent Volume Claim
- 4、Service
- 5、StatefulSet
Volume可以有很多種類型,比如nfs、glusterfs等,我們這里使用的ceph RBD來創建。
創建Volume
sudo rbd create {volume_name} --size 1024 -m {ceph-monitor-ip} -k /etc/ceph/ceph.client.admin.keyring
//禁止掉一些rdb的feature,否則掛載會失敗
rbd feature disable volume101 exclusive-lock, object-map, fast-diff, deep-flatten
創建PersistentVolume
創建pv.yaml,內容如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: {pv_name}
labels:
{label_key}:{label_value}
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
rbd:
monitors:
- {ceph-monitor-ip}
pool: rbd
image: {volume_name}
user: admin
secretRef:
name: ceph-secret
fsType: ext4
readOnly: false
persistentVolumeReclaimPolicy: Recycle
使用
kubectl create -f pv.yaml
來創建PV。
創建PersistentVolumeClaim
這一步非常非常的關鍵,因為如果創建的PVC的名稱和StatefulSet中的名稱沒有對應上,那么StatefulSet中的Pod就肯定創建不成功,我在這一步被卡了一天之久,還好看到上面那篇外文博客,才發現PVC和StatefulSet中的命名的規律。接下來細說一下需要注意的地方。
創建pvc.yaml如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-mysql-0
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
我們在這里創建了一個叫做db-mysql-0的PVC,這個名字是不是很奇怪,而且在這個yaml里並沒有提到PV的名字,所以PV和PVC是怎么bound起來的呢?是通過labels標簽下的key:value鍵值對來進行匹配的,我們在創建PV時指定了label的鍵值對,在PVC里通過selector可以指定label。
然后再回到這個PVC的名稱定義:db-mysql-0,為什么叫這樣一個看似有規律的名字呢,這里需要看一下下一小節創建StatefulSet中的yaml,首先我們看到StatefulSet的name叫mysql,設置的replicas為2個,volumeMounts和volumeClaimTemplates的name必須相同,為db,所以StatefulSet創建的第一個Pod的name應該為mysql-0,第二個為mysql-1。這里StatefulSet中的Pod與PVC之間的綁定關系是通過名稱來匹配的,即:
PVC_name === volumeClaimTemplates_name + "-" + pod_name
所以這個問題就有點意思了,我們要先創建PVC,但是PVC的名稱實際上是由StatefulSet來確定的。我真的是不知道K8S設計者咋想的·····而且官方文檔里創建StatefulSet的例子是直接給了個yaml文件,並沒有告訴說要先創建什么再創建什么。
創建Service 和 StatefulSet
在上一步中我們已經創建了名為db-mysql-0的PVC了,接下來創建一個service和statefulset,service的名稱可以隨意取,但是statefulset的名稱已經定死了,為mysql,並且statefulset中的volumeClaimTemplates_name必須為db,volumeMounts_name也必須為db。只有這樣,statefulset中的pod才能通過命名來匹配到PVC,否則會創建失敗。
statefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql-service
labels:
app: mysql
spec:
ports:
- port: 80
name: my-port
clusterIP: None
selector:
app: mysql
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: "mysql-service"
replicas: 2
template:
metadata:
labels:
app: mysql
spec:
terminationGracePeriodSeconds: 10
containers:
- name: mysqlpod
image: mysql:latest
ports:
- containerPort: 80
name: my-port
volumeMounts:
- name: db
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: db
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
運行以下命令:
kubectl create -f statefulset.yaml
就可以創建statefulset了。