iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
0
Cloud Native

30 天準備 LPI DevOps Tools Engineer 證照系列 第 24

[Day 24] Kubernetes (2)

今天來稍微瞭解一下 Kubernetes 的元件及架構,這部分取自官方文件的 Concepts,請參考 https://kubernetes.io/docs/concepts/

首先在 overview 的第一段提到使用 Kubernetes 基本上是使用它的 API 物件來描述此叢集的理想狀態 (desired state) ,包括要運行那些應用程式、這些應用程式會使用什麼映像檔、要有多少的 replica、使用那種網路及磁碟資源等等。要建立這些物件,通常會透過 CLI,也就是 kubectl 來達成。一旦設定好叢集的理想狀態,Kubernetes 的控制層 (control plane) 就會開始工作,讓目前叢集的狀態能與已設定的理想狀態互相匹配,這些工作包括啟動或重啟容器、調整 replica 的數量等等。

Kubernetes Objects

這些物件被提出來的作用,是要將系統的狀態作一個抽象化的描述,基本的物件包括:

  • Pod
  • Service
  • Volume
  • Namespace

而在這些物件之上,Kubernetes 提供了更高階的抽象層,稱作控制器 (controller),提供其他便利的功能,包括:

  • ReplicaSet
  • Deployment
  • StatefulSet
  • DaemonSet
  • Job

每個 Kubernetes 的物件都會有兩個欄位,用來管理 (govern) 物件的設置,一個是 spec,另一個是 statusspec 是由用戶所提供的,用來描述物件的理想狀態,而 status 則是描述物件實際的狀態,是由 Kubernetes 所提供並更新的,在任何時候,Kubernetes 控制層都會主動去管理物件的實際狀態,讓它們能達到用戶提供的理想狀態。昨天有提過在建立物件時,會用 JSON 或 YAML 來描述,一般會用 YAML 來編寫,kubectl 會再將其轉成 JSON,並隨 API 呼叫時作為參數傳送。接下來大概說明設定檔的格式。

Kubernetes 物件的設置描述檔需要四個欄位:apiVersionkindmetadata 以及 spec

  1. apiVersion - 指定創立物件所使用的 Kubernetes API 版本。
  2. kind - 指定要創立的物件類別。
  3. metadata - 用來辨別此物件的識別符號,如名稱 (name)、命名空間 (namespace) 等。
  4. 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 的重點,這邊再複習一下:

  1. 是 Kubernetes 中最小的建立或部署單元。
  2. 會共享資源,例如 volume、networking 等等。
  3. 可能包含一個或多個容器。

那要如何決定一個 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']

ReplicaSet

先前在講到 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

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,底下一樣有 metadataspec 兩個欄位,metadata 中的 labels 替這個 Pod 添加了 app: nginx 標籤,spec 定義 Pod 含有一個容器,該容器使用的映像檔、開啟的 port 號等資訊。

接下來使用以下的指令來創建這個 Deployment:

$ kubectl create -f nginx-deployment.yaml

使用 kubectl get deployments/nginx-deployment 來查看這個 Deployment 的狀態,昨天已經使用過了。先前有提到物件會有 status 欄位表示目前物件的狀態,若有查看此欄位,可用以下指令:

$ 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,它們剛好對應到容器中網路及儲存這兩個議題。


上一篇
[Day 23] Kubernetes (1)
下一篇
[Day 25] Kubernetes (3)
系列文
30 天準備 LPI DevOps Tools Engineer 證照30

尚未有邦友留言

立即登入留言