Kubernetes Deployment 提供了管理無狀態應用程式生命週期的完整機制。透過 Deployment,可以輕鬆地佈署、更新和縮放應用程式。Service 則負責將應用程式暴露給外部流量,並提供穩定的存取入口。本文以 Nginx 為例,示範瞭如何使用 Deployment 和 Service 佈署一個簡單的 Web 應用程式。同時,也介紹了 Readiness Probe 的作用,以及如何組態和測試 Readiness Probe,以確保應用程式在準備就緒後才接收流量。最後,文章也演示瞭如何透過修改 Deployment 的副本數量來實作應用程式的彈性縮放,以適應不同的負載需求。

使用 Kubernetes Deployment 管理無狀態工作負載

在 Kubernetes 中,Deployment 是一種用於管理無狀態工作負載的重要資源。無狀態工作負載意味著應用程式不依賴於特定的 Pod 或儲存狀態。在本章中,我們將探討如何使用 Deployment 和 Service 物件來佈署和管理無狀態應用程式。

Service 物件的作用

Service 物件的主要作用是定義一組準備就緒的 Pod,並將它們隱藏在一個穩定的 ClusterIP 後面。通常,內部客戶端不會直接使用 ClusterIP 來呼叫 Service Pod,而是使用 DNS 短名稱(與 Service 名稱相同),例如 nginx-service-example。這將由叢集的內部 DNS 服務解析為 ClusterIP。或者,它們也可以使用完全合格的網域名稱(FQDN),例如 <serviceName>.<namespaceName>.svc.<clusterDomain>,例如 nginx-service-example.default.svc.cluster.local

宣告式建立 Service

在這一部分,我們將使用 nginx-service-example Service 物件(型別為 LoadBalancer)來暴露我們的 nginx-deployment-example Deployment。步驟如下:

  1. 建立一個名為 nginx-service.yaml 的 manifest 檔案,內容如下:
# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service-example
spec:
  selector:
    app: nginx
    environment: test
  type: LoadBalancer
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80

Service 的標籤選擇器與我們的 Deployment 物件相同。Service 的規範指示我們在雲端負載平衡器的 80 埠上暴露我們的 Deployment,並將流量從目標埠 80 路由到底層的 Pod。

內容解密:

  • apiVersionkind 定義了資源的型別和版本。
  • metadata 包含了 Service 的後設資料,如名稱。
  • spec 定義了 Service 的規範,包括選擇器、型別和埠組態。
  • selector 用於選擇符合特定標籤的 Pod。
  • type 指定了 Service 的型別,這裡是 LoadBalancer。
  • ports 定義了 Service 的埠組態,包括埠、協定和目標埠。
  1. 建立 nginx-service-example Service,並使用 kubectl getkubectl describe 命令來收集有關新 Service 和相關負載平衡器的資訊:
$ kubectl apply -f nginx-service.yaml
service/nginx-service-example created
$ kubectl describe service nginx-service-example
Name: nginx-service-example
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=nginx,environment=test
...<removed for brevity>...
Endpoints: 10.244.1.2:80,10.244.2.2:80,10.244.2.3:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
  1. 現在,讓我們嘗試從另一個 Pod 存取 Service,如同在第 8 章中所做的那樣。讓我們建立一個名為 k8sutils 的 Pod,並測試 Service 存取:
$ kubectl apply -f ../Chapter07/k8sutils.yaml
pod/k8sutils created
$ kubectl exec -it k8sutils -- curl nginx-service-example.default.svc.cluster.local |grep Welcome -A2
<title>Welcome to nginx!</title>
<style>
body {
--
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p>

這表明瞭如何使用 Service 將 Deployment Pod 暴露給外部流量。

使用指令式命令建立 Service

可以使用指令式的 kubectl expose 命令來達到類別似的效果。這個命令將為我們的 Deployment 物件建立一個 Service。使用以下命令:

$ kubectl expose deployment --type=LoadBalancer nginx-deployment-example
service/nginx-deployment-example exposed

內容解密:

  • --type=LoadBalancer 指定了 Service 的型別。
  • nginx-deployment-example 是要暴露的 Deployment 物件的名稱。

就緒性、存活性和啟動探針的作用

在第 8 章中,我們瞭解到可以為每個在 Pod 中執行的容器組態三種探針:

  • 就緒性探針(Readiness probe)
  • 存活探針(Liveness probe)
  • 啟動探針(Startup probe)

這些探針對於組態 Deployment 至關重要。總是嘗試預測容器中執行的程式的可能生命週期場景,並相應地組態探針。

內容解密:

  • 就緒性探針用於檢查容器是否準備好接受流量。
  • 存活探針用於檢查容器是否存活,如果探針失敗,容器將被重新啟動。
  • 啟動探針用於檢查容器是否已完成啟動,如果探針失敗,容器將被重新啟動。

預設情況下,Pod 中執行的容器上沒有組態任何探針。Kubernetes 將向 Service 後面的 Pod 容器提供流量,但前提是容器已成功啟動,並使用預設的始終重新啟動策略重新啟動當機的容器。這意味著您需要為您的特定案例確定所需的探針型別和設定,並瞭解錯誤組態探針的可能後果和注意事項。

組態與測試 Kubernetes Deployment 的 Readiness Probe

在 Kubernetes 中,readiness probe 是用於檢測 Pod 是否準備好接收流量的機制。本章節將示範如何在 Deployment 中組態 readiness probe,並即時觀察其運作。

為何需要 Readiness Probe

預設情況下,Kubernetes 會假設 Pod 在啟動後即可接收流量。然而,在某些情況下,Pod 可能需要一些時間來完成初始化或預熱,此時 readiness probe 可以幫助我們避免將流量導向尚未準備好的 Pod。

建立測試環境

首先,我們需要建立一個簡單的 nginx Deployment,並組態 readiness probe。為了方便測試,我們將透過修改容器內的檔案來模擬 readiness probe 的成功與失敗。

  1. 刪除現有的 Deployment
$ kubectl delete deployment nginx-deployment-example
  1. 建立新的 Deployment YAML
# nginx-deployment-readinessprobe.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-readiness
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
      environment: test
  minReadySeconds: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    metadata:
      labels:
        app: nginx
        environment: test
    spec:
      containers:
      - name: nginx
        image: nginx:1.25.4
        ports:
        - containerPort: 80
        command:
        - /bin/sh
        - -c
        - |
          touch /usr/share/nginx/html/ready
          echo "You have been served by Pod with IP address: $(hostname -i)" > /usr/share/nginx/html/index.html
          nginx -g "daemon off;"
        readinessProbe:
          httpGet:
            path: /ready
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 2
          timeoutSeconds: 10
          successThreshold: 1
          failureThreshold: 2

程式碼解析:

此 YAML 組態檔定義了一個名為 nginx-deployment-readiness 的 Deployment,具有以下特點:

  • 使用 nginx:1.25.4 映象,並將容器內的 80 連線埠暴露出來。
  • 在容器啟動時執行自訂的 shell 命令:
    • touch /usr/share/nginx/html/ready:建立一個名為 ready 的空檔案,用於 readiness probe 的測試。
    • echo "You have been served by Pod with IP address: $(hostname -i)" > /usr/share/nginx/html/index.html:將 Pod 的 IP 位址寫入 index.html 檔案,用於顯示在網頁上。
    • nginx -g "daemon off;":啟動 nginx 服務。
  • 組態 readiness probe:
    • 使用 httpGet 型別,探測 /ready 路徑是否可存取。
    • initialDelaySeconds: 5:容器啟動後 5 秒開始探測。
    • periodSeconds: 2:每 2 秒探測一次。
    • timeoutSeconds: 10:探測超時時間為 10 秒。
    • successThreshold: 1:連續成功一次即視為成功。
    • failureThreshold: 2:連續失敗兩次即視為失敗。

建立與驗證 Deployment

  1. 套用新的 YAML 組態檔
$ kubectl apply -f ./nginx-deployment-readinessprobe.yaml
  1. 驗證 Service 與 Endpoint
$ kubectl describe svc nginx-service-example

輸出結果應該顯示三個 endpoint 對應到我們的 Deployment Pods,並且都已準備好接收流量。

此圖示說明瞭 readiness probe 的工作流程:
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 建立與驗證 Deployment

rectangle "延遲5秒" as node1
rectangle "探測/ready路徑" as node2
rectangle "成功" as node3
rectangle "失敗" as node4
rectangle "接收流量" as node5
rectangle "不接收流量" as node6

node1 --> node2
node2 --> node3
node3 --> node4
node4 --> node5
node5 --> node6

@enduml

此圖示呈現了 readiness probe 的運作流程,從容器啟動到探測結果的處理。透過這個機制,我們可以有效地管理 Pod 的流量分配,確保系統的穩定運作。

使用 Kubernetes Deployment 管理無狀態工作負載

測試 Nginx Web 伺服器存取與準備度探針

在前面的章節中,我們已經建立了一個名為 k8sutils 的 Pod。現在,我們將使用這個 Pod 來測試 nginx-service-example 服務的存取。

$ kubectl exec -it k8sutils -- curl nginx-service-example.default.svc.cluster.local
You have been served by Pod with IP address: 10.244.1.7

每次執行請求時,您可能會遇到不同的 Pod。這是因為我們的 Deployment 組態了三個 Pod 複本。

內容解密:

  • kubectl exec -it k8sutils -- curl nginx-service-example.default.svc.cluster.local:使用 k8sutils Pod 對 nginx-service-example 服務發起 HTTP 請求。
  • 由於 Deployment 有多個 Pod 複本,請求會被分配到不同的 Pod 上。

模擬準備度失敗

現在,讓我們模擬第一個 Pod 的準備度失敗。假設第一個 Pod 名稱為 nginx-deployment-readiness-69dd4cfdd9-4pkwr,其 IP 位址為 10.244.1.7。我們可以透過刪除容器內的 ready 檔案來模擬準備度失敗:

$ kubectl exec -it nginx-deployment-readiness-69dd4cfdd9-4pkwr -- rm /usr/share/nginx/html/ready

內容解密:

  • kubectl exec -it nginx-deployment-readiness-69dd4cfdd9-4pkwr -- rm /usr/share/nginx/html/ready:刪除指定 Pod 中的 ready 檔案,使其準備度探針失敗。
  • 準備度探針組態為每 2 秒檢查一次,若連續兩次檢查失敗,則認為該 Pod 未準備好。

檢查服務狀態

當準備度探針失敗後,我們可以檢查 nginx-service-example 服務的狀態:

$ kubectl describe svc nginx-service-example | grep Endpoints
Endpoints: 10.244.1.6:80,10.244.2.6:80

內容解密:

  • kubectl describe svc nginx-service-example | grep Endpoints:檢視服務的端點列表,確認未準備好的 Pod 已被移除。

進一步模擬全部 Pod 準備度失敗

若刪除所有 Pod 中的 ready 檔案,將導致整個服務失敗:

$ kubectl exec -it nginx-deployment-readiness-69dd4cfdd9-7n2kz -- rm /usr/share/nginx/html/ready
$ kubectl exec -it nginx-deployment-readiness-69dd4cfdd9-t7rp2 -- rm /usr/share/nginx/html/ready

此時,所有 Pod 都處於 Running 狀態,但沒有一個是 Ready 狀態。

還原服務

透過重新建立 ready 檔案,可以使其中一個 Pod 還原 Ready 狀態:

$ kubectl exec -it nginx-deployment-readiness-69dd4cfdd9-4pkwr -- touch /usr/share/nginx/html/ready

內容解密:

  • kubectl exec -it nginx-deployment-readiness-69dd4cfdd9-4pkwr -- touch /usr/share/nginx/html/ready:在指定 Pod 中重新建立 ready 檔案,使其準備度探針成功。

縮放 Deployment

Kubernetes Deployment 的一大優勢是可以快速縮放。無論是擴充套件還是縮減,新的 Pod 都會被自動發現或從端點列表中移除。

調整 Deployment 組態

首先,修改 nginx-deployment-readinessprobe.yaml 清單檔案中的副本數量:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-readiness
spec:
  replicas: 10
  ...

套用變更

使用 kubectl apply 命令將變更套用到叢集中:

$ kubectl apply -f ./nginx-deployment-readinessprobe.yaml
deployment.apps/nginx-deployment-readiness configured

檢視 Deployment 狀態

檢查 Pods 狀態,可以看到新的 Pods 正被建立:

$ kubectl get pods

檢視 Deployment 的詳細資訊,可以看到相關事件:

$ kubectl describe deployments.apps nginx-deployment-readiness
...
Events:
  Type    Reason             Age   From                   Message
  
---
-    
---
---
             
---
-  
---
-                   
---
-
---
  Normal  ScalingReplicaSet  32m   deployment-controller  Scaled up replica set nginx-deployment-readiness-69dd4cfdd9 to 3
  Normal  ScalingReplicaSet  9s    deployment-controller  Scaled up replica set nginx-deployment-readiness-69dd4cfdd9 to 10 from 3

內容解密:

  • kubectl apply -f ./nginx-deployment-readinessprobe.yaml:將更新後的 Deployment 組態套用到叢集中。
  • kubectl describe deployments.apps nginx-deployment-readiness:檢視 Deployment 的詳細資訊,包括事件日誌,以確認副本集的縮放情況。