ReplicaSet
縮寫為 RS
,是一種 POD
控制器。用來確保所管控的 POD
副本數在任一時間都能保證用戶所期望的需求。下圖演示了 RS
操作,RS
觸發後會去找匹配 selector
的 POD
,當前啟動的數量與期望的數量不符合時,會進行一些操作,像是多的數量則刪除,少的話透過 POD
模板建立。當期數量符合用戶定義時則不斷的循環。
from Kubernetes in action
RS
的建立通常以三個字段定義,selector
、replicas
和 template
,如下圖所示。當中 selector
、replicas
或 template
隨時可按照需求做更改。replicas
的定義數量為其直接影響;selector
可能讓當前的標籤不再匹配,讓 RS
不再對當前的 POD
進行控制;template
的修改是對後續創建新 POD
才產生影響。
from Kubernetes in action
對於一般手動建立 POD
,RS
帶來的好處有以下
- 確保
POD
數量符合定義數量 - 當節點發生故障時,自動請求自其它節點創建缺失的
POD
POD
的水平縮放
必要時可透 HPA(HroizontalPodAutoscaler) 控制器針對資源來自動縮放
建立 RS
再前面描述有說明 RS
重要的資源創建字段有哪些。這邊再說明一個字段 minReadySecond
預設值為 0 秒,在建立新 POD
時,啟動後有多長時間無出現異常就可被視為READY 狀態。以下為建立一個 RS
的範例。
# rs-demo.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp
namespace: default
spec:
replicas: 2 # 期望數量
selector: # 標籤選擇器
matchLabels:
app: myapp
release: canary
template: # POD 模板定義
metadata:
name: myapp-pod
labels:
app: myapp
release: canary
env: qa
spec:
containers:
- name: myapp-container
image: nginx:1.10
ports:
- name: http
containerPort: 80
使用 apply
部署 yaml
定義的資源
$ kubectl apply -f rs-demo.yaml
查看 POD
清單,有趣的是在 NAME
欄位,只要 POD
被 myapp
這個 RS
控制,該 POD
名稱前綴會有該 RS
名稱。
$ kubectl get pods -l app=myapp,release=canary -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-9xkht 1/1 Running 0 98s 10.4.0.13 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-mvkjk 1/1 Running 0 98s 10.4.2.12 gke-cluster-1-default-pool-7dc8b11b-r7rg <none> <none>
查看 RS
清單,當前按照想要的副本數建立了 2 個 POD
。
$ kubectl get rs -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
myapp 2 2 2 63s myapp-container nginx:1.10 app=myapp,release=canary
- DESIRED
- 期望要有幾個
Ready
的POD
- 期望要有幾個
- CURRENT
- 當前運行幾個
POD
- 當前運行幾個
- READY
READY
的POD
有幾個
RS 控管 POD
這邊會用幾個場景來說明 RS
的能力。
缺少 POD
可能某些原因導致 RS
控管的 POD
少了幾個,然而 RS
自動的將其補齊。
這邊實作的話要用 tmux
或多開終端機來觀察。
$ kubectl delete pod myapp-9xkht # 嘗試刪除 RS 中一個 POD
pod "myapp-9xkht" deleted
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp 2 2 2 12m
$ kubectl get pods -l app=myapp,release=canary
NAME READY STATUS RESTARTS AGE
myapp-flv45 1/1 Running 0 2m53s # 自動新增了
myapp-mvkjk 1/1 Running 0 14m
利用 -w
持續監控,這邊是監控 RS
所控制的 POD
$ kubectl get pods -l app=myapp,release=canary -o wide -w
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-9xkht 1/1 Running 0 11m 10.4.0.13 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-mvkjk 1/1 Running 0 11m 10.4.2.12 gke-cluster-1-default-pool-7dc8b11b-r7rg <none> <none>
myapp-9xkht 1/1 Terminating 0 12m 10.4.0.13 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-flv45 0/1 Pending 0 0s <none> <none> <none> <none>
myapp-flv45 0/1 Pending 0 0s <none> gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-flv45 0/1 ContainerCreating 0 0s <none> gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-9xkht 0/1 Terminating 0 12m 10.4.0.13 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-9xkht 0/1 Terminating 0 12m 10.4.0.13 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-flv45 1/1 Running 0 2s 10.4.0.14 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-9xkht 0/1 Terminating 0 12m 10.4.0.13 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-9xkht 0/1 Terminating 0 12m 10.4.0.13 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
假設我們嘗試將 RS
下的 POD
強制移除標籤。
$ kubectl label pods myapp-flv45 app= --overwrite # --overwrite 表示當前要修改的標籤存在,需用此選項選擇覆蓋
pod/myapp-flv45 labeled
$ kubectl get pods -l app=myapp,release=canary
NAME READY STATUS RESTARTS AGE
myapp-mvkjk 1/1 Running 0 20m
myapp-xt9wr 1/1 Running 0 14s
$ kubectl get pods # myapp-flv45 雖然不被 RS 控管,但它還是回存在
NAME READY STATUS RESTARTS AGE
myapp-flv45 1/1 Running 0 12m
myapp-mvkjk 1/1 Running 0 24m
myapp-xt9wr 1/1 Running 0 3m45s
pod-nodename-demo 1/1 Running 0 4d4h
從結果來看,myapp-flv45
這個 POD
的 app
標籤被移除,這不符合 RS
所定義要控管的 POD
標籤,因此該 POD
不再被 RS
控管,同時間少了一個 POD
,所以 RS
又建立了 myapp-xt9wr
。從這些結果來看,對於刪除或節點故障都會導致永久性消失。
多出 Pod
我們嘗試將 myapp-flv45
的標籤打回去,這樣 RS
就會控管 3 份 POD
。
$ kubectl label pods myapp-flv45 app=myapp --overwrite
$ kubectl get pods -l app=myapp,release=canary
NAME READY STATUS RESTARTS AGE
myapp-flv45 1/1 Running 0 18m
myapp-mvkjk 1/1 Running 0 30m
$ kubectl get pods -l app=myapp,release=canary -w
NAME READY STATUS RESTARTS AGE
myapp-mvkjk 1/1 Running 0 28m
myapp-xt9wr 1/1 Running 0 7m40s
myapp-flv45 1/1 Running 0 17m
myapp-flv45 1/1 Running 0 17m
myapp-xt9wr 1/1 Terminating 0 9m22s
myapp-xt9wr 0/1 Terminating 0 9m22s
myapp-xt9wr 0/1 Terminating 0 9m23s
myapp-xt9wr 0/1 Terminating 0 9m23s
上面結果來說,3 份 POD
對於 myapp
來說多了一份,因此這邊看到它終止了 myapp-xt9wr
。這說明了自主式的 POD
或屬於其它控制器的 POD
,當標籤資源被做了變動一不小心可能就終止了其它 POD 資源。
查看 RS 資源事件
這邊使用 describe
,去查看 RS
。其內容如下面結果所式,有名稱、所屬 namespace
、selector
定義等。在 Events
中可以看出過往新增或刪除了什麼 POD
。RS
之所以能做出這些的反應,是因為它有向 API Server
註冊 watch
,這樣就可以有相關資源變動訊息,API Server
在變動觸發時會通知給相關有註冊 watch
的客戶端。
$ kubectl describe rs myapp
Name: myapp
Namespace: default
Selector: app=myapp,release=canary
Labels: <none>
Annotations: <none>
Replicas: 2 current / 2 desired
Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=myapp
env=qa
release=canary
Containers:
myapp-container:
Image: nginx:1.10
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 37m replicaset-controller Created pod: myapp-9xkht
Normal SuccessfulCreate 37m replicaset-controller Created pod: myapp-mvkjk
Normal SuccessfulCreate 25m replicaset-controller Created pod: myapp-flv45
Normal SuccessfulCreate 16m replicaset-controller Created pod: myapp-xt9wr
Normal SuccessfulDelete 7m22s replicaset-controller Deleted pod: myapp-xt9wr
更新 RS
一開始 RS
控管的 POD
使用 nginx:1.10
$ kubectl get rs -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
myapp 2 2 2 63s myapp-container nginx:1.10 app=myapp,release=canary
更新 Template
edit
使用 edit
進行修改,將 nginx:1.10
改 nginx:1.18
$ kubectl edit rs myapp
$ kubectl get rs -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
myapp 2 2 2 54m myapp-container nginx:1.18 app=myapp,release=canary
$ kubectl get pods -l app=myapp,release=canary -o \
> custom-columns=Name:metadata.name,Image:spec.containers[0].image
Name Image
myapp-flv45 nginx:1.10
myapp-mvkjk nginx:1.10
apply
$ kubectl apply -f rs-demo.yaml # 將鏡像改 1.15 版
replicaset.apps/myapp configured
$ kubectl get pods -l app=myapp,release=canary -o custom-columns=Name:metadata.name,Image:spec.containers[0].image
Name Image
myapp-flv45 nginx:1.10
myapp-mvkjk nginx:1.10
$ kubectl get rs -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
myapp 2 2 2 59m myapp-container nginx:1.15 app=myapp,release=canary
最後發現還是 1.10
版,但 RS
資源卻顯示我們要改的版本。這需要將當前的 POD
進行刪除或修改標籤選擇器,才會觸發新模板的建立,過程如下圖
如果對於新舊的交替也就是更新會有以下操作可能
- 一次刪除所有
POD
或是更新標籤選擇器- 會有一段時間導致客戶端無法存取
- 分批刪除
POD
或是更新標籤選擇器- 更新時會有新舊版本供存
雖然這樣可以達到以手動方式更新,但 K8s
提供了一個 Deployment
控制器(下一篇會說明),其能夠更完善的實現滾動更新和回滾,同時也可讓客戶端定義更新策略。
POD 伸縮
只要更改 replicas
的數量即可達到 POD
的水平擴充。可用 scale
做更改,也可使用 edit
等方式。
使用 scale
擴展成 5 個 POD
$ kubectl scale rs myapp --replicas=5
$ kubectl get rs myapp
NAME DESIRED CURRENT READY AGE
myapp 5 5 5 7h34m
$ kubectl get pods -l app=myapp,release=canary
NAME READY STATUS RESTARTS AGE
myapp-fh6cp 1/1 Running 0 31s
myapp-flv45 1/1 Running 0 7h22m
myapp-mvkjk 1/1 Running 0 7h34m
myapp-ts7zd 1/1 Running 0 31s
myapp-vbtnn 1/1 Running 0 31s
使用 edit
減少至 3 個 POD
$ kubectl edit rs myapp # 找到 rcplicas 字段,進行數量修改
$ kubectl get rs myapp
NAME DESIRED CURRENT READY AGE
myapp 3 3 3 7h37m
$ kubectl get pods -l app=myapp,release=canary
NAME READY STATUS RESTARTS AGE
myapp-flv45 1/1 Running 0 7h25m
myapp-mvkjk 1/1 Running 0 7h37m
myapp-ts7zd 1/1 Running 0 3m56s
刪除 RS 資源
可以使用 delete
方式進行 RS
資源刪除。一般使用 delete
時 RS
會將其控管的 POD
也一併刪除,當中有 --cascade=false
可選,可將 RS
控管的 POD
,變成自主式的 POD
,同時也少了 RS
控管的優點。
$ kubectl delete -f rs-demo.yaml # 刪除 RS 資源
$ kubectl delete rs myapp # 刪除 RS 資源
$ kubectl delete rs myapp --cascade=false
replicaset.extensions "myapp" deleted
$ kubectl get rs # RS 資源被刪除
No resources found in default namespace.
$ kubectl get pods # 設置了 --cascade=false,讓 POD 變成自主式
NAME READY STATUS RESTARTS AGE
myapp-flv45 1/1 Running 0 7h32m
myapp-mvkjk 1/1 Running 0 7h44m
myapp-ts7zd 1/1 Running 0 10m
Other
因為實驗環境使用 GKE
這邊實驗將一個節點關閉,觀察 RS
的處裡,這邊先將 replicas
更改為 5 個並重新部署。
$ kubectl get pods -l app=myapp,release=canary -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-5t5qf 1/1 Running 0 3m56s 10.4.0.17 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-dnbf6 1/1 Running 0 3m56s 10.4.1.11 gke-cluster-1-default-pool-7dc8b11b-cxs1 <none> <none>
myapp-flv45 1/1 Running 0 7h38m 10.4.0.14 gke-cluster-1-default-pool-7dc8b11b-z2gx <none> <none>
myapp-mvkjk 1/1 Running 0 7h50m 10.4.2.12 gke-cluster-1-default-pool-7dc8b11b-r7rg <none> <none>
myapp-tmft2 1/1 Running 0 3m56s 10.4.2.14 gke-cluster-1-default-pool-7dc8b11b-r7rg <none> <none>
這邊將 gke-cluster-1-default-pool-7dc8b11b-r7rg
節點進行網卡關閉,最後結果也是一樣,兩個狀態變為 Unknown
而 RS
又新增兩個,該節點網路如果又恢復,想必又終止 2 個 POD
,這過程完全不用人為干預。
$ gcloud compute ssh gke-cluster-1-default-pool-7dc8b11b-r7rg --zone=us-central1-c
$ sudo ifconfig eth0 down
$ kubectl get node
NAME STATUS ROLES AGE VERSION
gke-cluster-1-default-pool-7dc8b11b-cxs1 Ready <none> 11d v1.15.12-gke.2
gke-cluster-1-default-pool-7dc8b11b-r7rg NotReady <none> 11d v1.15.12-gke.2
gke-cluster-1-default-pool-7dc8b11b-z2gx Ready <none> 11d v1.15.12-gke.2
$ kubectl get pods -l app=myapp,release=canary
NAME READY STATUS RESTARTS AGE
myapp-5t5qf 1/1 Running 0 14m
myapp-dnbf6 1/1 Running 0 14m
myapp-flv45 1/1 Running 0 7h49m
myapp-gjpp6 1/1 Running 0 3m8s # 新增
myapp-mvkjk 1/1 Unknown 0 8h # this
myapp-t82p4 1/1 Running 0 3m8s # 新增
myapp-tmft2 1/1 Unknown 0 14m # this
將網路恢復
$ gcloud compute instances reset gke-cluster-1-default-pool-7dc8b11b-r7rg --zone=us-central1-c
Updated [https://www.googleapis.com/compute/v1/projects/sunny-catwalk-286908/zones/us-central1-c/instances/gke-cluster-1-default-pool-7dc8b11b-r7rg].
$ kubectl get node
NAME STATUS ROLES AGE VERSION
gke-cluster-1-default-pool-7dc8b11b-cxs1 Ready <none> 11d v1.15.12-gke.2
gke-cluster-1-default-pool-7dc8b11b-r7rg Ready <none> 8h v1.15.12-gke.2
gke-cluster-1-default-pool-7dc8b11b-z2gx Ready <none> 11d v1.15.12-gke.2
當節點網路恢復時,其狀態回到 Ready
,並且狀態為 Unknown
的 POD
將被刪除。
結論
透過本篇文章,詳細的知道 ReplicaSet
的原理以及操作。也比較說與自主式 POD
的差異。而 Replica
能夠實現出對於相同的 POD
的負載均衡和保持 POD
想要維持的數量。但要注意的是 ReplicaSet
只是負責 POD
數量有無符合期望值,並不會知道 POD
的容器是掛掉的還是怎樣。