當要架設到 Kubernates 需要以有狀態系統的方式設計時,可以考慮使用 StatefulSets。
例如:Deploy 需要依循一定的順序等等。
這邊我們用 ZooKeeper 做一個簡單的示範
本篇教學就不詳述ZooKeeper的所有功能了,Apache ZooKeeper 是一個開源的協調開源的分散式協調服務。 ZooKeeper 允許你讀取、寫入數據和發現資料更新。資料按其結構組織在檔案系統中,複製到 ensemble(一個 ZooKeeper 服務的集合) 中所有的 ZooKeeper 服務。對資料的所有操作都是原子的和順序一致的。 ZooKeeper 通過 Zab 一致性協議在 ensemble 的所有服務之間複製一個狀態來確保這個特性。
ensemble 使用 Zab 協議選舉一個 leader,在選舉出 leader 前不能寫入數據。一旦選舉出了 leader,ensemble 使用 Zab 保證所有寫入被複製到一個 quorum,然後這些寫入操作才會被確認並對客戶端可用。如果沒有遵照加權 quorums,一個 quorum 表示包含當前 leader 的 ensemble 的多數成員。例如,如果 ensemble 有3個服務,一個包含 leader 的成員和另一個服務就組成了一個 quorum。如果 ensemble 不能達成一個 quorum,數據將不能被寫入。
ZooKeeper 在記憶體中存著它們的所有的狀態,但是每個改變都被寫入一個在存儲介質上的持久 WAL(Write Ahead Log)。當一個服務故障時,它能夠通過回放 WAL 恢復之前的狀態。為了防止 WAL 無限制的增長,ZooKeeper 服務會定期的將內存狀態快照保存到存儲介質。這些快照能夠直接加載到內存中,所有在這個快照之前的 WAL 條目都可以被安全的丟棄。
首先我們建立一個 包含有ZooKeeper
apiVersion: v1
kind: Service
metadata:
name: zk-headless
labels:
app: zk-headless
spec:
ports:
- port: 2888
name: server
- port: 3888
name: leader-election
clusterIP: None
selector:
app: zk
---
apiVersion: v1
kind: ConfigMap
metadata:
name: zk-config
data:
ensemble: "zk-0;zk-1;zk-2"
jvm.heap: "2G"
tick: "2000"
init: "10"
sync: "5"
client.cnxns: "60"
snap.retain: "3"
purge.interval: "1"
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-budget
spec:
selector:
matchLabels:
app: zk
minAvailable: 2
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: zk
spec:
serviceName: zk-headless
replicas: 3
template:
metadata:
labels:
app: zk
annotations:
pod.alpha.kubernetes.io/initialized: "true"
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk-headless
topologyKey: "kubernetes.io/hostname"
containers:
- name: k8szk
imagePullPolicy: Always
image: gcr.io/google_samples/k8szk:v1
resources:
requests:
memory: "4Gi"
cpu: "1"
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: server
- containerPort: 3888
name: leader-election
env:
- name : ZK_ENSEMBLE
valueFrom:
configMapKeyRef:
name: zk-config
key: ensemble
- name : ZK_HEAP_SIZE
valueFrom:
configMapKeyRef:
name: zk-config
key: jvm.heap
- name : ZK_TICK_TIME
valueFrom:
configMapKeyRef:
name: zk-config
key: tick
- name : ZK_INIT_LIMIT
valueFrom:
configMapKeyRef:
name: zk-config
key: init
- name : ZK_SYNC_LIMIT
valueFrom:
configMapKeyRef:
name: zk-config
key: tick
- name : ZK_MAX_CLIENT_CNXNS
valueFrom:
configMapKeyRef:
name: zk-config
key: client.cnxns
- name: ZK_SNAP_RETAIN_COUNT
valueFrom:
configMapKeyRef:
name: zk-config
key: snap.retain
- name: ZK_PURGE_INTERVAL
valueFrom:
configMapKeyRef:
name: zk-config
key: purge.interval
- name: ZK_CLIENT_PORT
value: "2181"
- name: ZK_SERVER_PORT
value: "2888"
- name: ZK_ELECTION_PORT
value: "3888"
command:
- sh
- -c
- zkGenConfig.sh && zkServer.sh start-foreground
readinessProbe:
exec:
command:
- "zkOk.sh"
initialDelaySeconds: 15
timeoutSeconds: 5
livenessProbe:
exec:
command:
- "zkOk.sh"
initialDelaySeconds: 15
timeoutSeconds: 5
volumeMounts:
- name: datadir
mountPath: /var/lib/zookeeper
securityContext:
runAsUser: 1000
fsGroup: 1000
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 20Gi
輸入以下指令開始建立 Zookeeper
$ kubectl create -f https://k8s.io/docs/tutorials/stateful-application/zookeeper.yaml
上述指令建立了 zk-headless Headless Service、zk-config ConfigMap、zk-budget PodDisruptionBudget 和 zk StatefulSet。
service "zk-headless" created
configmap "zk-config" created
poddisruptionbudget "zk-budget" created
statefulset "zk" created
在使用 kubectl get 查看 StatefulSet 建立的 Pods。
$ kubectl get pods -w -l app=zk
NAME READY STATUS RESTARTS AGE
zk-0 0/1 Pending 0 0s
zk-0 0/1 Pending 0 0s
zk-0 0/1 ContainerCreating 0 0s
zk-0 0/1 Running 0 19s
zk-0 1/1 Running 0 40s
zk-1 0/1 Pending 0 0s
zk-1 0/1 Pending 0 0s
zk-1 0/1 ContainerCreating 0 0s
zk-1 0/1 Running 0 18s
zk-1 1/1 Running 0 40s
zk-2 0/1 Pending 0 0s
zk-2 0/1 Pending 0 0s
zk-2 0/1 ContainerCreating 0 0s
zk-2 0/1 Running 0 19s
zk-2 1/1 Running 0 40s
由於ZooKeeper 的 leader 功能 無法在Private Network中停止,因為Zab 要求顯式的進行成員關係配置,以執行 leader 選舉,因此 Ensemble 中的每個服務都需要具有一個獨一無二的標識符,所有的服務均需要知道標識符的全集,並且每個label 都需要和一個IP相關聯。
$ for i in 0 1 2; do kubectl exec zk-$i -- hostname; done
zk-0
zk-1
zk-2
ZooKeeper ensemble 中的服務使用自然數作為唯一標識符,每個服務的標識符都保存在服務的數據目錄中一個名為 myid 的文件裡。
檢查每個服務的 myid 文件的內容。
$ for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done