返回文章列表

Drone CI/CD Pipeline 建立與混沌工程實踐

本文介紹如何使用 Drone 建立 CI/CD Pipeline,佈署應用程式到 Kubernetes 叢集,並透過混沌工程驗證應用程式韌性。文章涵蓋 Drone Pipeline 設定、Kubernetes 佈署、混沌實驗步驟以及版本控制、釋出和佈署的最佳實踐,提供開發者一套完整的 CI/CD 流程參考。

Web 開發 DevOps

Drone CI/CD 的匯入能有效提升軟體交付速度和品質。本文詳細說明如何設定 Drone Pipeline,整合 Kubernetes 叢集佈署,並結合混沌工程驗證系統的穩定性。從 Kubernetes API 端點設定、Service Account 建立到 Drone Pipeline 的 YAML 檔案撰寫,提供逐步操作。同時也涵蓋了建置、測試、發布和佈署等關鍵步驟,並示範如何進行滾動更新。最後,文章介紹了使用 Chaos Toolkit 進行混沌實驗,模擬真實環境中的突發狀況,以驗證應用程式在壓力下的可靠性。

設定 Drone CI/CD Pipeline 並進行混沌實驗

在現代化的軟體開發流程中,持續整合(CI)與持續佈署(CD)是提升開發效率和軟體品質的關鍵步驟。本文將介紹如何使用 Drone 建立 CI/CD Pipeline,並將應用程式佈署到 Kubernetes 叢集,最後進行混沌實驗以驗證應用的韌性。

取得 Kubernetes API 端點

首先,需要取得 Kubernetes 叢集的 API 端點。可使用以下指令:

kubectl cluster-info

執行後應可看到類別似以下的輸出:

Kubernetes master is running at https://kbp.centralus.azmk8s.io:443

將此 URL 儲存在 kubernetes_server secret 中。

建立 Service Account 與 ClusterRoleBinding

接下來,建立一個 Service Account 供 Drone 使用:

kubectl create serviceaccount drone

然後,建立 ClusterRoleBinding:

kubectl create clusterrolebinding drone-admin \
--clusterrole=cluster-admin \
--serviceaccount=default:drone

取得 Service Account Token 與憑證

取得 Service Account Token:

TOKENNAME=`kubectl -n default get serviceaccount/drone -o jsonpath='{.secrets[0].name}'`
TOKEN=`kubectl -n default get secret $TOKENNAME -o jsonpath='{.data.token}' | base64 -d`
echo $TOKEN

將輸出的 Token 儲存在 kubernetes_token secret 中。

取得使用者憑證:

kubectl get secret $TOKENNAME -o yaml | grep 'ca.crt:'

將輸出的 ca.crt 儲存在 kubernetes_cert secret 中。

內容解密:

  1. kubectl create serviceaccount drone:建立一個名為 drone 的 Service Account。
  2. kubectl create clusterrolebinding drone-admin:將 drone Service Account 繫結到 cluster-admin 角色,使其具有管理叢集的許可權。
  3. TOKENNAME=TOKEN= 指令:取得 drone Service Account 的 Token 名稱與實際值。
  4. kubectl get secret $TOKENNAME -o yaml | grep 'ca.crt:':取得叢集的 CA 憑證。

建立 Drone Pipeline

.drone.yml 檔案中定義 Pipeline,包括建置、測試、發布和佈署步驟。

建置步驟

pipeline:
  build:
    image: node
    commands:
      - cd frontend
      - npm i redis --save

測試步驟

  test:
    image: node
    commands:
      - cd frontend
      - npm i redis --save
      - npm test

發布步驟

  publish:
    image: plugins/docker
    dockerfile: ./frontend/Dockerfile
    context: ./frontend
    repo: dstrebel/frontend
    tags: [latest, v2]
    secrets: [docker_username, docker_password]

佈署步驟

  kubectl:
    image: dstrebel/drone-kubectl-helm
    secrets: [kubernetes_server, kubernetes_cert, kubernetes_token]
    kubectl: "apply -f ./frontend/deployment.yaml"

內容解密:

  1. image: node:使用官方 Node.js 映像執行建置和測試命令。
  2. commands:在容器內執行的命令,例如安裝依賴和執行測試。
  3. image: plugins/docker:使用 Docker 映像建置和發布 Docker Image。
  4. secrets: [docker_username, docker_password]:使用儲存在 Drone 中的 Secret 來驗證 Docker Hub。
  5. kubectl: "apply -f ./frontend/deployment.yaml":使用 kubectl 將應用程式佈署到 Kubernetes 叢集。

進行滾動更新

修改前端程式碼後提交變更,觸發 Drone 自動建置和佈署新的版本。

簡單的混沌實驗

使用 Chaos Toolkit 自動終止 Pod,驗證應用的韌性。

  1. 安裝 Chaos Toolkit:
    pip install -U chaostoolkit
    pip install chaostoolkit-kubernetes
    
  2. 設定前端服務 URL:
    export FRONTEND_URL="http://$(kubectl get svc frontend -o jsonpath="{.status.loadBalancer.ingress[*].ip}"):8080/api/"
    
  3. 執行混沌實驗:
    chaos run experiment.json
    

內容解密:

  1. pip install -U chaostoolkit:安裝 Chaos Toolkit。
  2. export FRONTEND_URL:設定前端服務的 URL,用於混沌實驗的驗證。
  3. chaos run experiment.json:執行定義好的混沌實驗。

CI/CD 最佳實踐

  1. 自動化與快速建置:最佳化建置速度,提供開發者快速的回饋。
  2. 可靠的測試:確保測試的可靠性,提供開發者即時的錯誤回饋。
  3. Pipeline as Code:將 Pipeline 定義為程式碼,與應用程式碼一同版本控制。
  4. 最佳化 Docker Image:減少 Image 大小,降低生產環境中的攻擊面。
  5. 避免使用 “latest” Tag:使用可追溯的 Tag,如建置 ID 或 Git Commit Hash。
  6. 滾動更新與漸進式佈署:從滾動更新開始,逐漸採用藍綠佈署或金絲雀發布等策略。
  7. 生產環境測試:在生產環境中進行小規模測試,確保監控機制健全。

版本控制、釋出與佈署

傳統的單體式應用程式隨著時間的推移會變得過於龐大和難以管理,難以滿足業務需求的升級、版本控制或修改速度。許多人認為這是導致敏捷開發實踐和微服務架構出現的重要因素之一。快速迭代新程式碼、解決新問題或修復隱藏問題,以及實作零停機升級,都是開發團隊努力實作的目標。實際上,這些問題可以透過適當的流程和程式來解決,但通常需要更高的技術和人力成本來維護。

版本控制的重要性

在設計系統時,隔離性和可組合性是重要的變數。採用容器作為應用程式碼的執行環境可以實作這一點,但仍然需要高水準的人工自動化或系統管理來維護大型系統的可靠性。隨著系統的發展,複雜性增加,系統工程師開始建立複雜的自動化流程來實作複雜的釋出、升級和故障檢測機制。像Apache Mesos、HashiCorp Nomad,甚至是專門的容器協調器如Kubernetes和Docker Swarm等服務協調工具已經將這些流程演變成更原始的元件,直接整合到其執行環境中。

版本控制(Versioning)

本文並非旨在介紹軟體版本控制的基礎知識或其歷史背景。有無數的文章和電腦科學課程教材都對此進行了討論。最重要的是選擇一個模式並堅持下去。大多數軟體公司和開發者都同意,某種形式的語意版本控制(semantic versioning)是最有用的,特別是在微服務架構中。

語意版本控制基礎

對於新手來說,語意版本控制遵循一個三部分的版本號模式,即主要版本(major version)、次要版本(minor version)和修補程式(patch),通常以點符號表示,如1(主要).2(次要).3(修補程式)。修補程式表示包含錯誤修復或非常小的變更的增量釋出,且沒有API變更。次要版本表示可能有新的API變更,但與前一版本向後相容。這是開發人員與其他微服務合作時的一個關鍵屬性。


#### 版本號示例
- 1.4.7 -> 1.5.7:次要版本更新,表示新增功能但保持向後相容
- 1.4.7 -> 2.0.0:主要版本更新,表示有重大變更且可能不向後相容

內容解密:

此版本的變更規則表明,在微服務架構中,若某服務依賴其他服務的API,則需關注其版本號。例如,從1.4.7更新到1.5.7表示新增了功能但保持向後相容,而更新到2.0.0則表示有重大變更,可能不向後相容。

釋出(Releases)

事實上,Kubernetes本身並沒有真正的釋出控制器,因此沒有原生釋出的概念。這通常透過在Deployment metadata.labels規範和/或pod.spec.template.metadata.label規範中新增標籤來實作。使用Helm後,其中一個主要概念就是釋出,用於區分叢集中相同Helm圖表的執行例項。這個概念很容易在沒有Helm的情況下重現;然而,Helm原生地跟蹤釋出及其歷史,因此許多持續交付(CD)工具將Helm整合到其Pipeline中,作為實際的釋出服務。

釋出名稱的有用性

釋出名稱可以非常有用,如果機構對某些名稱的定義達成一致。通常,使用諸如stablecanary之類別的標籤,有助於在使用服務網格等工具進行細粒度路由決策時提供一些操作控制。大型的組織會採用環狀架構,可以表示為ring-0ring-1等。

佈署(Rollouts)

在Kubernetes引入Deployment控制器之前,控制應用程式如何由Kubernetes控制器程式佈署的唯一機制是使用命令列介面(CLI)命令kubectl rolling-update對特定的副本控制器進行更新。這對於宣告式的持續交付模型非常困難,因為這不是原始清單狀態的一部分。Deployment控制器新增了使用特定策略自動化此更新過程的功能,然後允許系統讀取根據對spec.template的更改的新宣告狀態。

佈署策略

Kubernetes Deployments支援兩種策略:rollingUpdaterecreate,前者是預設值。如果指定了滾動更新,則Deployment將建立一個新的ReplicaSet以擴充套件到所需的副本數量,而舊的ReplicaSet將根據maxUnavailablemaxSurge的具體值縮小到零。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-deployment
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  selector:
    matchLabels:
      app: example-app
  template:
    metadata:
      labels:
        app: example-app
    spec:
      containers:
      - name: example-container
        image: example-image:latest

內容解密:

此YAML檔案定義了一個名為example-deployment的Deployment,使用滾動更新策略。它指定了3個副本,並且在更新過程中最多允許1個不可用的Pod和1個額外的Pod。此策略確保了在更新過程中服務的連續性。

版本控制、釋出與佈署管理

在 Kubernetes 中,版本控制、釋出與佈署管理是確保應用程式平穩執行的關鍵因素。正確的策略可以實作零停機佈署並提高 DevOps 團隊的效率。

重建策略(Recreate Strategy)與滾動更新(Rolling Update)

重建策略是一種有效的佈署策略,適用於可以容忍短暫服務中斷的工作負載。在此策略下,Deployment 控制器會先建立新的 ReplicaSet,然後刪除舊的 ReplicaSet,最後才將新的 Pod 上線。這種策略適合用於後端有佇列機制的服務,例如訊息佇列系統,當新的 Pod 啟動時,佇列中的訊息會被處理,從而減少服務中斷的影響。

綜合範例分析

以下是一個綜合範例,涵蓋了 Web 佈署、資料函式庫佈署以及資料函式庫備份任務:

Web 佈署

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gb-web-deploy
  labels:
    app: guest-book
    appver: 1.6.9
    environment: production
    release: guest-book-stable
    release-number: 34e57f01
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 3
      maxSurge: 2
  selector:
    matchLabels:
      app: gb-web
      ver: 1.5.8
    matchExpressions:
      - {key: environment, operator: In, values: [production]}
  template:
    metadata:
      labels:
        app: gb-web
        ver: 1.5.8
        environment: production
    spec:
      containers:
      - name: gb-web-cont
        image: evillgenius/gb-web:v1.5.5
        env:
        - name: GB_DB_HOST
          value: gb-mysql
        - name: GB_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password

資料函式庫佈署

apiVersion: apps/v1
kind: Deployment
metadata:
  name: gb-mysql
  labels:
    app: guest-book
    appver: 1.6.9
    environment: production
    release: guest-book-stable
    release-number: 34e57f01
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: gb-db
      tier: backend
  template:
    metadata:
      labels:
        app: gb-db
        tier: backend
        ver: 1.5.9
        environment: production
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password

資料函式庫備份任務

apiVersion: batch/v1
kind: Job
metadata:
  name: db-backup
  labels:
    app: guest-book
    appver: 1.6.9
    environment: production
    release: guest-book-stable
    release-number: 34e57f01
  annotations:
    "helm.sh/hook": pre-upgrade, pre-delete, pre-rollback
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    metadata:
      labels:
        app: gb-db-backup
        tier: backend
        ver: 1.6.1
        environment: production
    spec:
      containers:
      - name: mysqldump
        image: evillgenius/mysqldump:v1
        env:
        - name: DB_NAME
          value: gbdb1
        - name: GB_DB_HOST
          value: gb-mysql
        - name: GB_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password

版本控制與釋出的最佳實踐

為了實作有效的 CI/CD 和零停機佈署,需要遵循以下最佳實踐:

  • 使用語義化版本控制(Semantic Versioning),為應用程式、容器和 Pod 指定不同的版本,以便於追蹤和管理變更。
  • 在 Deployment 中使用 releaserelease-number 標籤來跟蹤 CI/CD 管道的釋出記錄,這有助於追溯變更歷史和回復操作。
  • 如果使用 Helm 封裝服務,請將需要一起升級或回復的服務封裝在同一個 Helm Chart 中,以便於管理和維護。

版本控制邏輯解析

在上述範例中,Web 佈署的容器版本是 v1.5.5,而 Pod 的版本是 1.5.8,應用程式的版本是 1.6.9。這種分層版本控制方式可以清晰地反映出不同層級的變更。

釋出與回復管理

透過在 Deployment 中使用 releaserelease-number 標籤,可以實作對釋出記錄的追蹤和回復操作。這種做法可以提高 DevOps 團隊的工作效率和系統的可靠性。

圖表說明

以下是展示版本控制與釋出流程的

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Drone CI/CD Pipeline 建立與混沌工程實踐

package "Kubernetes Cluster" {
    package "Control Plane" {
        component [API Server] as api
        component [Controller Manager] as cm
        component [Scheduler] as sched
        database [etcd] as etcd
    }

    package "Worker Nodes" {
        component [Kubelet] as kubelet
        component [Kube-proxy] as proxy
        package "Pods" {
            component [Container 1] as c1
            component [Container 2] as c2
        }
    }
}

api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2

note right of api
  核心 API 入口
  所有操作經由此處
end note

@enduml

圖表翻譯: 此圖示展示了版本控制與釋出流程。首先,建立新版本;然後,檢查新版本的相容性。如果相容,則發布新版本;如果不相容,則修復相容性問題後重新檢查。最後,監控系統狀態以確保平穩執行。