今天來稍微瞭解一下 Kubernetes 的元件及架構,這部分取自官方文件的 Concepts,請參考 https://kubernetes.io/docs/concepts/。
首先在 overview 的第一段提到使用 Kubernetes 基本上是使用它的 API 物件來描述此叢集的理想狀態 (desired state) ,包括要運行那些應用程式、這些應用程式會使用什麼映像檔、要有多少的 replica、使用那種網路及磁碟資源等等。要建立這些物件,通常會透過 CLI,也就是 kubectl 來達成。一旦設定好叢集的理想狀態,Kubernetes 的控制層 (control plane) 就會開始工作,讓目前叢集的狀態能與已設定的理想狀態互相匹配,這些工作包括啟動或重啟容器、調整 replica 的數量等等。
這些物件被提出來的作用,是要將系統的狀態作一個抽象化的描述,基本的物件包括:
而在這些物件之上,Kubernetes 提供了更高階的抽象層,稱作控制器 (controller),提供其他便利的功能,包括:
每個 Kubernetes 的物件都會有兩個欄位,用來管理 (govern) 物件的設置,一個是 spec
,另一個是 status
。spec
是由用戶所提供的,用來描述物件的理想狀態,而 status
則是描述物件實際的狀態,是由 Kubernetes 所提供並更新的,在任何時候,Kubernetes 控制層都會主動去管理物件的實際狀態,讓它們能達到用戶提供的理想狀態。昨天有提過在建立物件時,會用 JSON 或 YAML 來描述,一般會用 YAML 來編寫,kubectl 會再將其轉成 JSON,並隨 API 呼叫時作為參數傳送。接下來大概說明設定檔的格式。
Kubernetes 物件的設置描述檔需要四個欄位:apiVersion
、kind
、metadata
以及 spec
。
apiVersion
- 指定創立物件所使用的 Kubernetes API 版本。kind
- 指定要創立的物件類別。metadata
- 用來辨別此物件的識別符號,如名稱 (name)、命名空間 (namespace) 等。spec
- 描述物件的規格或理想狀態 (desired state),隨物件類型而有所不同。以下先看一個例子,是一個用來建立 Deployment 的描述檔。接下來在介紹各類型物件或資源時會再說明它的設定檔寫法。
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
編寫完設定檔後,用 kubectl create 來建立物件
,例如:
$ kubectl create -f deployment.yaml
首先來看一下 Pod。昨天有提過幾個關於 pod 的重點,這邊再複習一下:
那要如何決定一個 pod 裡面要放那些容器呢?文件裡有提到,除非容器之間是緊密耦合、需要共享資源,例如它們必須共用同一個檔案系統的資料,否則不要放在同一個 pod 之中,這邊有舉一個例子,例如有一個容器是負責伺服器服務,提供外界存取某個 volume 中檔案的功能,另一個容器負責更新 volume 中的檔案,那麼它們就適合放在同一個 pod 裡。同一個 pod 之中容器間的相依必須要小心的設計,所以若非必要,盡量將不同的容器拆成不同的 pod。但一般很少會直接去管理 Pod,像是建立或刪除這類的工作,通常會透過 controller 來進行,像是昨天操作過的 deployment。以下是 Pod 的設置檔,不過在文件中稱它是一個樣版 (template),因為 Pod 設定檔會被包含在其他物件設定檔中,並以 template 作為鍵值。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']
先前在講到 controller 時提到了 ReplicaSet,而在文件中 controller 這一段還有另外一個叫 ReplicationController 的東西,先來看一下它們是什麼。文件說 ReplicaSet 是下一代 (next-generation) 的 Replication controller,然後開始說明這兩者之間的差異。在 When to use a ReplicaSet
這一節中提到 ReplicaSet 用來確保在任何時間,都有指定數量的 pod replicas 在運行,其他的部分感覺起來對於 ReplicaSet 的用途好像沒有更詳細的說明,所以就到 ReplicationController 的頁面看一下它怎麼說,結果最上面的 note 指出,要設定 replication 的話,使用 Deployment 來設置 ReplicaSet 是比較建議的作法,所以大致可以推論 ReplicaSet 是用於維護 Pod replicas 的數量。不過在昨天的操作中並沒有使用到 ReplicaSet,再回到 ReplicaSet 的網頁,發現後面這樣說:然而,Deployment 是管理 ReplicaSet 的高階概念,並且提供以宣告方式更新 Pod 及其他有用的功能。因此,除非需要客製化的編配更新,或者根本不需要更新,否則建議使用 Deployment,而不要直接操作 ReplicaSet。
接下來來看一個 ReplicaSet 的設定檔範例:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# modify replicas according to your case
replicas: 3
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: guestbook
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
resources:
requests:
cpu: 100m
memory: 100Mi
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
在這個 ReplicaSet 設定檔中,第一層的 spec
鍵值下有一個 template
鍵值,其內容就是 pod 的設定值。
Deployment 是 controller 的其中一種,文件說它提供了 Pod 和 ReplicaSet 的宣告式更新,可以定義 Deployment,用來建立新的 ReplicaSet。下面的 note 提示不應該去管理Deployment 擁有的 ReplicaSet 。這樣看來,Deployment 應該是用來管理 ReplicatSet,而 ReplicaSet 又是用來管理 Pod。文件接下來的說明,有些地方會實際在 Kubernetes 叢集中進行操作,因此請先啟動昨天的 Minikube,接下來建立一個 controllers 目錄,並在裡面加入一個名為 nginx-deployment.yaml
檔案,內容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.4
ports:
- containerPort: 80
這個 Deployment 名稱為 nginx-deployment
,是由 metadata
下的 name
所指定的。在文件中會以 .metadata.name
的方法來表示巢狀的值,這裡也沿用這樣的方式。.metadata.label
用來給予 Deployment 標籤,這裡添加的標籤是 app: nginx
的鍵值對,key 是 app
,value 是 nginx
。接下來看一下 spec
的描述,replicas
說明 Pod 要有三個 replicas,selector
用來告訴 Deployment 要如何找到它該管理的 Pod,matchLabels
指定 Pod 必須要符合 app: nginx
這個 label,它在下方的 Pod template 中也被定義了。文件中寫說 .spec.selector
必須要跟 .spec.template.metadata.labels
相同,否則會被 API 拒絕。.spec.template
用來描述 Pod,底下一樣有 metadata
和 spec
兩個欄位,metadata
中的 labels
替這個 Pod 添加了 app: nginx
標籤,spec
定義 Pod 含有一個容器,該容器使用的映像檔、開啟的 port 號等資訊。
接下來使用以下的指令來創建這個 Deployment:
$ kubectl create -f nginx-deployment.yaml
使用 kubectl get deployments/nginx-deployment
來查看這個 Deployment 的狀態,昨天已經使用過了。先前有提到物件會有 statu
s 欄位表示目前物件的狀態,若有查看此欄位,可用以下指令:
$ kubectl get deployments/nginx-deployment -o yaml
(省略)
status:
availableReplicas: 3
conditions:
- lastTransitionTime: 2018-11-05T02:08:37Z
lastUpdateTime: 2018-11-05T02:08:37Z
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: "True"
type: Available
- lastTransitionTime: 2018-11-05T02:08:06Z
lastUpdateTime: 2018-11-05T02:08:37Z
message: ReplicaSet "nginx-deployment-86d59dd769" has successfully progressed.
reason: NewReplicaSetAvailable
status: "True"
type: Progressing
observedGeneration: 1
readyReplicas: 3
replicas: 3
updatedReplicas: 3
接下來查看這個 Deployment 所建立的 ReplicaSet,昨天沒有特別提過,指令如下:
$ kubectl get rs -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
nginx-deployment-86d59dd769 3 3 3 41m nginx nginx:1.15.4 app=nginx,pod-template-hash=4281588325
接下來文件中花了不少篇幅在介紹 Deployment 如何進行更新與擴展。更新它用的是 rollout / rollover 這樣的字眼,有一點隱含它的更新是以漸進式的方式進行的,這部分昨天已經提過,這裡再說明一下。首先是進行更新時的進度查詢。建立 Deployment 其實也可以算是一種更新,用來查詢的指令如下:
$ kubectl rollout status deployment/nginx-deployment
如果是在新建立的 Deployment,以 rollout status 查詢的輸出可能如下:
Waiting for rollout to finish: 0 of 3 updated replicas are available...
Waiting for rollout to finish: 1 of 3 updated replicas are available...
Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out
接下來進行更新 (rollout),更新指的是 Pod 內容有所改變,更明確指的是 Deployment 中 .spec.template
的內容有所變化,最常見的狀況是容器映像檔的改變。昨天使用 kubectl set image 來更新容器映像檔,例如:
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.15.4
它會將名為 nginx 的容器的映像檔更新為 nginx:1.15.4
。另外一個方式是以更改設定的方式進行更新,指令如下:
$ kubectl edit deployment/nginx-deployment
或
$ kubectl edit -f nginx-deployment.yaml
會出現文字編輯器,內容為 nginx-deployment
此 Deployment 的狀態,但不是直接編輯 nginx-deployment.yaml
。這個文字編輯器所編輯的內容會比 nginx-deployment.yaml
詳細,而修改後也不會套用到 nginx-deployment.yaml
上。修改後就會自動進行 rollout,以 kubectl roll out status
查看會有類似下列的結果,它會建立新的 replicas,再將舊的 replica 給消滅。
Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for rollout to finish: 2 old replicas are pending termination...
Waiting for rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
接下來查看 ReplicaSet,會發現多了一個 ReplicaSet。
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-86d59dd769 3 3 3 3m
nginx-deployment-c4747d96c 0 0 0 1h
在進行 rollout 時可能會需要記錄變更的原因以供未來查詢,可以在 rollout 時加上 --record=true
的參數,或在設定檔中修改。例如:
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.15.4
或者在使用 kubectl edit
時,在 .metadata.annotations
欄位修改訊息。
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "3"
kubernetes.io/change-cause: kubectl set image deployment/nginx-deployment nginx=nginx:1.7.9
--record=true
creationTimestamp: 2018-11-05T03:42:44Z
generation: 5
labels:
app: nginx
name: nginx-deployment
namespace: default
resourceVersion: "65606"
selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/nginx-deployment
uid: dad1dadc-e0ac-11e8-9fa6-08002797b9be
有了變更原因的標註 (annotation),就可以方便地使用 kubectl rollout history
查看過往 rollout 的記錄,例如:
$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 kubectl set image deployment/nginx-deployment nginx=nginx:1.7.9 --record=true
可以看到如果在 rollout 時添加 --record=true
參數,在 CHANGE-CAUSE
欄位就會標註出變更原因。
如果要復原 rollout,昨天有示範過可以使用 kubectl rollout undo
指令,也可以加上 --to-revison=<n>
參數來指令要回復到那一個版本,例如:
$ kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment.apps "nginx-deployment"
接下來是擴展的部分,如同昨天示範的,使用 kubectl scale
並加上 --replicas=<n>
參數,例如:
$ kubectl scale deployment/nginx-deployment --replicas=5
deployment.extensions "nginx-deployment" scaled
今天就先介紹到這裡,明天再來看看 Kubernetes 的 Service 物件及 volume,它們剛好對應到容器中網路及儲存這兩個議題。