3. 控制器(Controller)
3.1 副本集(ReplicaSet)
定義:副本集(ReplicaSet)的目的是爲了保證一組穩定的Pod副本在任意給定時刻都在運行。因此,它通常用於保證特定數量的相同Pod的可用性。
副本集使用一些字段進行定義,這些字段包含了一個選擇器(指定如何識別可以獲取的pods)、一個數字(表示應該維持多少個副本)、一個Pod模板(pod template,指定了新的Pod的創建標準)。然後,復副本集將根據需要進行創建或刪除pods以達到所需的數量,從而實現其目的。當副本集需要創建新的Pod時,就會使用Pod模板。
副本集和Pod之間的鏈接是通過Pod的metadata.ownerReferences
字段,該字段指定了當前對象是屬於哪個資源的,正是通過這個鏈接,副本集才知道它所維護的pods的狀態,並據此進行計劃。一個副本集主要是通過使用選擇器辨識新的Pod,如果一個Pod沒有OwnerReference(所屬資源)或者OwnerReference(所屬資源)不是一個控制器,它一旦和副本集的選擇器匹配上了,它將會被副本集立即獲取。
副本集(ReplicaSet)保證了特定數量的Pod副本在任意狀態都是處於運行狀態。然而,Deployment是一個相對比較高層次的概念,它負責管理副本集(ReplicaSet)以及提供對pods的聲明性更新以及許多其他有用的功能。相對於直接使用副本集(ReplicaSets),更加推薦使用Deployments來操作,除非需要自定義更新業務流程、或者根本不需要更新。因爲實際上可能從不需要操作副本集對象,所以從這個角度也是推薦使用Deployments來替換,在spec部分來定義應用。下面是直接創建副本集的一個實例:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# 下面是定義副本集的部分
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
下面的命令將上述的清單保存到frontend.yaml
並將其提交給K8S集羣,它會創建定義的副本集(ReplicaSet)和它所管理的Pod:
kubectl apply -f http://k8s.io/examples/controllers/frontend.yaml
注意yaml文件的縮進,很容易出錯的,將上述的鏡像改成我自己打包的鏡像,或者在worker node上拉鏡像再改名(我這裏是後者):
docker pull registry.cn-shanghai.aliyuncs.com/hhu/gb-frontend:v3
docker tag registry.cn-shanghai.aliyuncs.com/hhu/gb-frontend:v3 gcr.io/google_samples/gb-frontend:v3
docker rmi registry.cn-shanghai.aliyuncs.com/hhu/gb-frontend:v3
查看當前部署的副本集(ReplicaSets):
kubectl get rs
# 本地結果
NAME DESIRED CURRENT READY AGE
frontend 3 3 3 5m24s
curl-66959f6557 1 1 1 15d
jenkins-7958858b5d 1 1 1 11d
my-nginx-64fc468bd4 0 0 0 15d
可以看到剛剛創建的frontend(上述的鏡像和配置文件國內是拉不下來的),也可以檢查副本集(Replicaset)的狀態:
# 查看frontend的狀態
kubectl describe rs/frontend
# 結果
Name: frontend
Namespace: default
Selector: tier=frontend
Labels: app=guestbook
tier=frontend
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"labels":{"app":"guestbook","tier":"frontend"},"name":"frontend",...
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: tier=frontend
Containers:
php-redis:
Image: gcr.io/google_samples/gb-frontend:v3
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 4m26s replicaset-controller Created pod: frontend-chgz5
Normal SuccessfulCreate 4m26s replicaset-controller Created pod: frontend-lmqv7
Normal SuccessfulCreate 4m26s replicaset-controller Created pod: frontend-4mklc
查看Pod(有3個frontend的Pod):
# 查看Pod
kubectl get Pods
# 結果
NAME READY STATUS RESTARTS AGE
curl-66959f6557-pn49b 1/1 Running 2 15d
frontend-4mklc 1/1 Running 0 5m4s
frontend-chgz5 1/1 Running 0 5m4s
frontend-lmqv7 1/1 Running 0 5m4s
jenkins-7958858b5d-27qlx 1/1 Running 1 11d
當然還可以驗證這些Pod的owner reference是設置到了frontend副本集(ReplicaSet),可以通過獲取某個運行的Pod的yaml文件查看:
# 查看frontend-4mklc的pod的yaml文件
kubectl get pods frontend-4mklc -o yaml
# 部分輸出
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2019-04-28T06:27:56Z"
generateName: frontend-
labels:
tier: frontend
name: frontend-4mklc
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: frontend
uid: c2d0bc7c-697e-11e9-a84a-000c292a92cd
resourceVersion: "3146592"
selfLink: /api/v1/namespaces/default/pods/frontend-4mklc
uid: c2e03103-697e-11e9-a84a-000c292a92cd
spec:
containers:
- image: gcr.io/google_samples/gb-frontend:v3
imagePullPolicy: IfNotPresent
name: php-redis
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-gb6cw
readOnly: true
...
【採集非模板Pod】
當創建裸倉(bare Pod)並沒有問題,強烈推薦確保裸倉不要有和任何副本集(ReplicaSet)標籤選擇器匹配的,因爲副本集(ReplicaSet)不限制模板指定哪些Pod屬於該副本集——它可以以規定的方式獲取其他的Pod,以之前的前端副本集爲例,Pod在下面的清單中指定:
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
tier: frontend
spec:
containers:
- name: hello1
image: registry.cn-shanghai.aliyuncs.com/hhu/hello-app:2.0
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
tier: frontend
spec:
containers:
- name: hello2
image: registry.cn-shanghai.aliyuncs.com/hhu/hello-app:1.0
上述的Pod沒有控制器作爲它們的owner reference、沒有匹配的前端副本集,假設在前端副本集已經部署了、並這個副本集已經被設置爲它的初始化Pod的副本,然後創建了Pod,已經將這個Pod設置爲初始Pod副本去填充必要的副本數量:kubectl apply -f http://k8s.io/examples/pods/pod-rs.yaml
,新的Pod將會通過副本集(ReplicaSet)獲取,然後然後立即終止,因爲副本集會超過其所需的計數。注意這裏的順序是創建副本集–>再通過副本集創建Pod,如果先創建Pod(kubectl apply -f http://k8s.io/examples/pods/pod-rs.yaml
),再創建副本集(kubectl apply -f http://k8s.io/examples/controllers/frontend.yaml
),此時通過kubectl get Pods
只會發現只有一個Pod。
【副本集RepicaSet清單】
副本集也是K8S中的API對象,一個ReplicaSet需要apiVersion
、kind
以及metadata
字段,對於ReplicaSet,kind
永遠都僅僅是ReplicaSet
,副本集也需要.spec
。
Pod模板,.spec.template
字段是Pod模板,它也需要標籤,在之前的frontend.yaml
栗子中,有一個標籤tier: frontend
,注意不要和其他控制器的標籤選擇器重疊,以免它們使用這個Pod,對於Pod模板的重啓策略(restart policy字段.spec.template.spec.restartPolicy
)只能指定爲Always
(這也是默認值)。
Pod 標籤選擇器,即.spec.selector
,用於辨識Pod,在之前的frontend.yaml
栗子中,選擇器位:
matchLabels:
tier: frontend
在副本集(ReplicaSet)中,.spec.template.metadata.labels
必須和spec.selector
,否則會直接被API拒絕請求。注意:2個ReplicaSet指定相同的.spec.selector
、但指定的.spec.template.metadata.labels
和.spec.template.spec
字段不同,每個ReplicaSet會忽略其他ReplicaSet創建的pod。
副本:可以通過.spec.replicas
設置多少個Pod應該並行運行,然後由副本集控制添加、刪除Pod以滿足這個數字,如果不指定,默認爲1
。
【副本集的操作】
ReplicaSet常見的行爲有:
1.刪除副本集以及由其創建的Pod
必須將下面的-d
選項中的propagationPolicy
設置爲Background
或者Foreground
:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
> -H "Content-Type: application/json"
2.僅僅刪除副本集
必須將下面的propagationPolicy
設置爲Orphan
:
kubectl proxy --port=8080
curl -X DELETE 'localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
> -H "Content-Type: application/json"
原始的副本集刪除後,可以創建新的副本替換它並接管它之前創建的Pod,只要新的副本集中的.spec.selector
和之前的一樣就行,但新的副本集不會做出任何努力使現有的pod匹配新的、不同的pod模板。如果要升級Pod到新的,需要使用 rolling update。
3.將Pod從副本集中隔離
這裏是需要修改Pod上的標籤即可,以這種方式從副本集中隔離開來的Pod會自動被新的Pod替換。
4.對副本集進行伸縮
通過更新.spec.replicas
可以輕鬆對副本集進行伸縮,ReplicaSet Controller將確保這個數量的Pod副本。
5.作爲水平Pod自動定標器目標的副本集
副本集也可以是水平Pod自動分頻器( Horizontal Pod Autoscalers-HPA)的目標,即副本集可以由hpa自動縮放,下面是栗子:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: frontend-scaler
spec:
scaleTargetRef:
kind: ReplicaSet
name: frontend
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 50
保存上述清單到hpa-rs.yaml
並提交到K8S集羣就會創建定義的HPA,它將根據複製的Pod的CPU使用情況自動縮放目標副本集kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml
,還可以使用kubectl autoscale
完成同樣的事:kubectl autoscale rs frontend --max=10
(這種方式更簡單)。