問題背景
即使加密後,有些組織仍不希望在Git中儲存任何形式的憑證。那麼,如何在完全遵循GitOps原則的同時,避免在Git中儲存任何形式的憑證呢?
External Secrets解決方案
External Secrets是一個最初由GoDaddy建立的開放原始碼專案,其目標是將秘密儲存在外部服務或金鑰管理系統中,然後只在Git中儲存對這些秘密的參照。
目前,External Secrets支援多種系統,包括AWS Secrets Manager、HashiCorp Vault、Google Secrets Manager、Azure Key Vault等。這種方法為外部API提供了使用者友好的抽象,用於儲存和管理秘密的生命週期。
ExternalSecrets是一個Kubernetes控制器,負責從外部API源協調Secrets到叢集中。這提供了一種安全與符合GitOps原則的方式來管理敏感資料。
整合Argo CD、HashiCorp Vault和External Secret
在下面的部分中,我將展示如何將Argo CD與HashiCorp Vault和External Secret整合,以建立一個完整的GitOps安全管理解決方案。這種整合提供了最高階別的安全性,同時仍然保持GitOps的宣告式方法的優勢。
HashiCorp Vault的角色
HashiCorp Vault是一個專為保護敏感資料而設計的工具。它提供了一個統一的介面來存取秘密,同時提供嚴格的存取控制和詳細的稽核日誌。在我們的架構中,Vault將作為中央秘密儲存,而External Secrets將作為Vault和Kubernetes之間的橋樑。
實作步驟概述
- 在Kubernetes叢集中佈署HashiCorp
GitOps 中的敏感資料管理與多叢集佈署策略
在 Kubernetes 環境中實施 GitOps 時,安全管理敏感資料和實作多叢集佈署是兩個常見的挑戰。本文將探討如何使用 External Secrets Operator 安全管理敏感資料,以及如何利用 Argo CD 的 ApplicationSet 實作多叢集佈署策略。
安全管理敏感資料:External Secrets Operator
在 GitOps 實踐中,我們追求將所有設定儲存在 Git 中,但敏感資料如密碼、API 金鑰等不適合直接儲存在 Git 倉函式庫這時 External Secrets Operator 就派上用場了。
External Secrets 的工作原理
External Secrets Operator 建立了一座橋樑,連線 Kubernetes 與外部金鑰管理系統(如 HashiCorp Vault、AWS Secrets Manager 等)。它透過兩個核心資源實作這一功能:
- SecretStore:定義了外部後端系統的連線設定
- ExternalSecret:指定從外部系統取得哪些敏感資料,以及如何將其轉換為 Kubernetes Secret
這種設計允許我們將敏感資料的參照(而非資料本身)儲存在 Git 中,大提高了安全性。
安裝 External Secrets Operator
使用 Helm 安裝 External Secrets Operator 非常直接:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace
這段命令首先增加 External Secrets 的 Helm 圖表 倉函式庫後安裝 External Secrets Operator。-n external-secrets
指定安裝的名稱空間,--create-namespace
引數確保該名稱空間存在,如果不存在則自動建立。這是一種標準的 Helm 安裝模式,適用於大多數 Kubernetes 元件。
安裝成功後,你會看到類別似下面的輸出:
NAME: external-secrets
LAST DEPLOYED: Fri Sep 2 13:09:53 2022
NAMESPACE: external-secrets
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
external-secrets has been deployed successfully!
值得一提的是,你也可以透過 OperatorHub.io 使用 OLM (Operator Lifecycle Manager) 安裝 External Secrets Operator。
與 HashiCorp Vault 整合範例
讓我們看一個與 HashiCorp Vault 整合的例項。首先,安裝 Vault 並取得 Token:
export VAULT_TOKEN=<YOUR_TOKEN>
kubectl create secret generic vault-token \
--from-literal=token=$VAULT_TOKEN \
-n external-secrets
這裡建立了一個 Kubernetes Secret 來儲存 Vault 的存取令牌。--from-literal=token=$VAULT_TOKEN
從環境變數直接設定 Secret 的值,而不是從檔案讀取,這是在指令碼中建立簡單 Secret 的常用方式。
然後建立一個 SecretStore 資源,建立與 Vault 的連線:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-secretstore
namespace: default
spec:
provider:
vault:
server: "http://vault.local:8200" # Vault 伺服器的位置
path: "secret"
version: "v2"
auth:
tokenSecretRef:
name: "vault-token" # 包含 Vault 令牌的 Kubernetes Secret 名稱
key: "token" # Secret 中儲存令牌的鍵名
namespace: external-secrets
這個 YAML 定義了一個 SecretStore 資源,指定了如何連線到 Vault 伺服器。spec.provider.vault.server
指定了 Vault 伺服器的 URL,path
是 Vault 中的路徑,version
指定了 Vault KV 金鑰引擎的版本。auth.tokenSecretRef
部分則指定了如何取得授權 Token:從 external-secrets
名稱空間的 vault-token
Secret 中讀取 token
鍵的值。
建立這個資源:
kubectl create -f vault-secretstore.yaml
現在,在 Vault 中建立一個金鑰:
vault kv put secret/pacman-secrets pass=pacman
這個命令在 Vault 的 secret/pacman-secrets
路徑下建立了一個鍵值對,其中鍵為 pass
,值為 pacman
。這是 Vault KV 引擎的標準使用方式,用於儲存簡單的鍵值對資料。
接下來,建立一個 ExternalSecret 資源參照這個 Vault 中的金鑰:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: pacman-externalsecrets
namespace: default
spec:
refreshInterval: "15s"
secretStoreRef:
name: vault-secretstore
kind: SecretStore
target:
name: pacman-externalsecrets
data:
- secretKey: token
remoteRef:
key: secret/pacman-secrets
property: pass
這個 ExternalSecret 定義瞭如何從外部金鑰管理系統取得資料並轉換為 Kubernetes Secret。refreshInterval
指定了同步間隔,每 15 秒檢查一次外部系統的變更。secretStoreRef
參照了前面建立的 SecretStore。target.name
指定了要建立的 Kubernetes Secret 的名稱。data
部分定義了對映關係:從 Vault 的 secret/pacman-secrets
路徑取得 pass
屬性的值,並將其儲存在 Kubernetes Secret 的 token
鍵下。
建立這個資源:
kubectl create -f pacman-externalsecrets.yaml
最後,使用 Argo CD 佈署使用這個 Secret 的應用:
argocd app create pacman \
--repo https://github.com/gitops-cookbook/pacman-kikd-manifests.git \
--path 'k8s/externalsecrets' \
--dest-server https://kubernetes.default.svc \
--dest-namespace default \
--sync-policy auto
這個命令建立了一個 Argo CD 應用,從指定的 Git 倉函式庫徑取得 Kubernetes 清單檔案,佈署到當前叢集的 default
名稱空間。--sync-policy auto
表示變更會自動同步,無需手動干預。這是 Argo CD 的標準應用建立流程。
使用 Argo CD Webhooks 自動觸發應用佈署
問題與解決方案
Argo CD 預設每三分鐘輪詢一次 Git 倉函式庫測變更,但在某些情況下,我們希望在 Git 變更發生時立即佈署應用,無需等待輪詢間隔。
Argo CD 支援根據 Webhook 的事件驅動方法,可以接收來自 GitHub、GitLab 或 Bitbucket 等 Git 伺服器的 Webhook 通知。Argo CD 的 Webhook 端點是 /api/webhooks
。
使用 Gitea 測試 Webhooks
為了在本地測試 Webhooks,我們可以使用 Helm 安裝輕量級的 Git 伺服器 Gitea:
helm repo add gitea-charts https://dl.gitea.io/charts/
helm install gitea gitea-charts/gitea
這裡首先增加了 Gitea 的 Helm 圖表 倉函式庫後安裝 Gitea。Gitea 是一個用 Go 編寫的輕量級 Git 伺服器,非常適合這種測試場景。安裝後,你可以使用連線埠轉發存取 Gitea UI:kubectl port-forward svc/gitea-http 3000:3000
。
安裝後,將 Pac-Man 清單倉函式庫 Gitea,然後設定 Argo CD 應用:
argocd app create pacman-webhook \
--repo http://gitea-http.default.svc:3000/gitea_admin/pacman-kikd-manifests.git \
--dest-server https://kubernetes.default.svc \
--dest-namespace default \
--path k8s \
--sync-policy auto
這個命令建立了一個使用 Gitea 倉函式庫Argo CD 應用。注意倉函式庫RL 使用了叢集內的服務名稱,因為 Argo CD 和 Gitea 都在同一個 Kubernetes 叢集中執行。
接下來,在 Gitea 中設定 Webhook:
- 在倉函式庫右上角點選 Settings
- 選擇 Webhooks 標籤
- 設定 Webhook:
- Payload URL:
http://localhost:9090/api/webhooks
- Content type:
application/json
- Payload URL:
每當你推播變更到 Gitea 倉函式庫Argo CD 將立即同步應用,而不是等待下一個輪詢週期。
使用 ApplicationSet 佈署到多個叢集
問題與解決方案
在企業環境中,通常需要將應用佈署到多個 Kubernetes 叢集(如開發、測試、生產環境)。Argo CD 的 ApplicationSet 資源可以簡化這個過程。
ApplicationSet 允許你"範本化"Argo CD 應用資源,主要用於:
- 將應用佈署到多個 Kubernetes 叢集
- 從一個或多個 Git 倉函式庫多個應用
ApplicationSet 使用生成器(generators)來提供範本中的引數值。
建立 ApplicationSet
以下是一個 ApplicationSet 範例,用於將應用佈署到 staging 和 prod 環境:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: bgd-app
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: staging
url: https://kubernetes.default.svc
location: default
- cluster: prod
url: https://kubernetes.default.svc
location: app
template:
metadata:
name: '{{cluster}}-app'
spec:
project: default
source:
repoURL: https://github.com/gitops-cookbook/gitops-cookbook-sc.git
targetRevision: main
path: ch08/bgd-gen/{{cluster}}
destination:
server: '{{url}}'
namespace: '{{location}}'
syncPolicy:
syncOptions:
- CreateNamespace=true
這個 ApplicationSet 使用 list 生成器定義了兩個環境:staging 和 prod。generator 部分定義了將要替換到範本中的引數值。template 部分定義了 Argo CD Application 資源的範本,其中包含佔位符(如 {{cluster}}
、{{url}}
、{{location}}
),這些佔位符將被生成器提供的值替換。
這樣,從一個 ApplicationSet 定義,Argo CD 會生成兩個 Application 資源,分別對應 staging 和 prod 環境。
應用這個 ApplicationSet:
kubectl apply -f bgd-application-set.yaml
這將建立兩個 Argo CD Application 資源:
staging-app
:佈署到 default 名稱空間prod-app
:佈署到 app 名稱空間
我們可以檢查建立的應用:
# 先登入
argocd login --insecure --grpc-web $argoURL --username admin --password $argoPass
argocd app list
輸出應類別似於:
NAME CLUSTER NAMESPACE
prod-app https://kubernetes.default.svc app
staging-app https://kubernetes.default.svc default
如果需要刪除這些應用,只需刪除 ApplicationSet:
kubectl delete -f bgd-application-set.yaml
ApplicationSet 生成器類別
在撰寫本文時,Argo CD 支援八種生成器:
- List:透過固定的叢集列表生成應用定義(上面範例使用的就是這種)
- Cluster:根據 Argo CD 中註冊的叢集動態生成應用
- Git:根據 Git 倉函式庫錄結構或檔案生成應用
- Matrix:組合兩個生成器的輸出,生成笛卡爾積
- Merge:合併多個生成器的輸出
- **SCM
Argo CD ApplicationSet 生成器種類別與應用場景
在 Kubernetes 的 GitOps 實踐中,Argo CD 已成為標準工具之一,而 ApplicationSet 則是其中的核心功能,能夠大幅簡化多環境應用佈署。在我實作大規模自動化佈署時,深入理解各種生成器的特性與適用場景至關重要。
ApplicationSet 支援多種生成器類別,每種都有其獨特的使用場景:
Cluster 生成器
根據 Argo CD 中定義的叢集清單生成應用定義。這適合於管理跨多個叢集的相同應用佈署,尤其在多雲環境中特別有用。
Git 生成器
從 Git 儲存函式庫 JSON/YAML 屬性檔案或根據儲存函式庫錄結構生成應用定義。這是我最常使用的生成器之一,特別適合於結構化的多環境組態管理。
SCM Provider 生成器
從組織內的儲存函式庫應用定義。當您需要自動佈署組織中多個儲存函式庫用時,這非常有效。
Pull Request 生成器
從開放的提取請求生成應用定義。這對於實作預覽環境和程式碼審查流程自動化極為有價值。
Cluster Decision Resource 生成器
使用鴨子類別(duck-typing)技術生成應用定義。適用於高度自定義的佈署邏輯實作。
Matrix 生成器
組合兩個獨立生成器的值。當需要根據多維度條件生成應用時非常有用。
Merge 生成器
合併來自兩個或更多生成器的值。適合於需要組合不同來源設定的場景。
使用 Git 生成器實作目錄結構化佈署
在前面的例子中,我們從固定列表元素建立了應用物件,這在環境數量較少時是可行的。但當面臨多環境佈署時,使用 Git 生成器能夠實作更靈活的動態設定。
讓我們將之前的範例遷移到 Git 生成器。我們的 Git 目錄結構如下:
bgd-gen
├── staging
│ ├── ...yaml
└── prod
├── ...yaml
根據目錄結構的 ApplicationSet 實作
現在建立一個新的 ApplicationSet 定義,為設定的 Git 儲存函式庫每個目錄生成一個應用:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: cluster-addons
namespace: openshift-gitops
spec:
generators:
- git:
repoURL: https://github.com/gitops-cookbook/gitops-cookbook-sc.git
revision: main
directories:
- path: ch08/bgd-gen/*
template:
metadata:
name: '{{path[0]}}{{path[2]}}'
spec:
project: default
source:
repoURL: https://github.com/gitops-cookbook/gitops-cookbook-sc.git
targetRevision: main
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
這個 ApplicationSet 設定中:
git.repoURL
指定要讀取的 Git 倉函式庫directories.path
使用萬用字元ch08/bgd-gen/*
比對所有子目錄- 範本部分定義瞭如何根據找到的目錄生成 Application 物件
{{path}}
代表完整的目錄路徑{{path.basename}}
提取路徑的最右邊部分(目錄名)作為名稱空間
應用此資源後,Argo CD 會為比對的兩個目錄(staging 和 prod)建立兩個應用:
NAME CLUSTER NAMESPACE
ch08prod https://kubernetes.default.svc prod
ch08staging https://kubernetes.default.svc staging
此生成器在應用由多個元件組成時特別有用,例如當佈署檔案按服務、資料函式庫取等分別放在不同目錄中。或者管理叢集中需要安裝的所有營運商,如:
app
├── tekton-operator
│ ├── ...yaml
├── prometheus-operator
│ ├── ...yaml
└── istio-operator
├── ...yaml
根據設定檔的 Git 生成器
Git 生成器不僅可以根據目錄結構,還可以根據 JSON/YAML 設定檔建立 Application 物件。
例如,JSON 設定檔:
{
"cluster": {
"name": "staging",
"address": "https://1.2.3.4"
}
}
對應的 ApplicationSet 定義:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
spec:
generators:
- git:
repoURL: https://github.com/example/app.git
revision: HEAD
files:
- path: "app/**/config.json"
template:
metadata:
name: '{{cluster.name}}-app'
....
files.path
使用app/**/config.json
模式比對所有子目錄中的 config.json 檔案- 範本中可以使用
{{cluster.name}}
這樣的語法注入 JSON 檔案中定義的值 - 這個 ApplicationSet 會為每個比對的 config.json 檔案生成一個 Application
這種方法比根據目錄的方法更靈活,因為可以在設定檔中定義任意結構的引數。
使用 Pull Request 生成器實作預覽環境
在現代 CI/CD 實踐中,為每個提取請求(PR)自動佈署預覽環境是提高開發效率的關鍵做法。ApplicationSet 的 Pull Request 生成器完美解決了這一需求。
建立 PR 預覽環境的 ApplicationSet
讓我們建立一個 ApplicationSet,它會對標記為 “preview” 的 GitHub PR 做出反應:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapps
namespace: openshift-gitops
spec:
generators:
- pullRequest:
github:
owner: gitops-cookbook
repo: gitops-cookbook-sc
labels:
- preview
requeueAfterSeconds: 60
template:
metadata:
name: 'myapp-{{branch}}-{{number}}'
spec:
source:
repoURL: 'https://github.com/gitops-cookbook/gitops-cookbook-sc.git'
targetRevision: '{{head_sha}}'
path: ch08/bgd-pr
project: default
destination:
server: https://kubernetes.default.svc
namespace: '{{branch}}-{{number}}'
pullRequest.github
設定指定了 GitHub 組織/使用者和倉函式庫labels
設定只有帶有 “preview” 標籤的 PR 才會觸釋出署requeueAfterSeconds: 60
設定每 60 秒輪詢一次檢查新的 PR- 範本中使用
{{branch}}
、{{number}}
和{{head_sha}}
變數來參照 PR 的分支名稱、編號和提交 SHA - 每個 PR 會在單獨的名稱空間中佈署,格式為
{{branch}}-{{number}}
應用此設定後,初始狀態下不會建立任何應用,因為還沒有帶 “preview” 標籤的 PR。一旦建立並標記了 PR,大約一分鐘後 ApplicationSet 控制器會檢測到變更並建立相應的 Application 物件。
當 PR 關閉時,對應的 Application 物件會自動移除,從而實作完整的生命週期管理。
支援的 PR 提供者與事件觸發
目前 ApplicationSet 支援以下 PR 提供者:
- GitHub
- Bitbucket
- Gitea
- GitLab
雖然 ApplicationSet 控制器預設透過輪詢來檢測變更,但也支援使用 webhook 事件。設定 webhook 可以讓系統在 PR 建立或更新時立即回應,而不必等待輪詢週期。
整合 Argo Rollouts 實作高階佈署策略
在生產環境中,簡單的滾動更新常不足以滿足零停機佈署的需求。Argo Rollouts 提供了藍綠佈署、金絲雀釋出等高階佈署技術,與 Argo CD 結合使用可以實作強大的漸進式交付流程。
安裝 Argo Rollouts
首先,我們需要在叢集中安裝 Argo Rollouts:
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/download/v1.2.2/install.yaml
雖然不是必須的,但強烈建議安裝 Argo Rollouts Kubectl 外掛來視覺化佈署過程。
Rollout 資源介紹
Argo Rollouts 引入了一個名為 Rollout 的新 Kubernetes 資源,它類別似於 Deployment 物件(支援所有 Deployment 選項),但增加了一些欄位來設定高階佈署策略。
Rollout 資源支援多種佈署策略:
- 藍綠佈署:建立新版本的完整副本,在驗證後切換流量
- 金絲雀發布:逐步將流量從舊版本轉移到新版本
- 流量映象:將流量同時傳送到新舊版本,但只回傳舊版本的回應
- 暗金絲雀:在不影響使用者的情況下測試新版本
Argo Rollouts 還整合了多種流量管理解決方案(如 Istio、Ambassador、Traefik 等)和分析提供者(如 Prometheus、Datadog、New Relic 等),可以根據指標自動推進或回復佈署。
實作金絲雀發布流程
金絲雀發布是一種漸進式佈署策略,先將少量流量導向新版本,在確認無問題後再逐步增加流量比例。這種方法可以在早期發現問題並最小化影響範圍。
一個典型的金絲雀發布流程包括以下步驟:
- 佈署新版本但初始不接收流量
- 將少量流量(如 5%)導向新版本
- 分析新版本的效能和錯誤率
- 如果指標良好,逐步增加流量比例(如 20%、50%、100%)
- 如果發現問題,立即回復到舊版本
Argo Rollouts 可以自動執行這個過程,並根據預定義的指標決定是否繼續推進佈署。
結合 ApplicationSet 與 Rollouts 的最佳實踐
在我的實踐中,結合 ApplicationSet 與 Argo Rollouts 可以實作強大而靈活的佈署流程:
- 使用 ApplicationSet 管理多環境的應用定義,確保設定一致性
- 在每個環境中使用 Rollout 而非 Deployment 資源
- 根據環境類別設定不同的佈署策略:
- 開發環境:簡單的滾動更新
- 測試環境:完整的藍綠佈署以驗證新版本
- 生產環境:謹慎的金絲雀發布,結合自動分析
這種組合方法可以在保持設定簡單性的同時,實作複雜的佈署流程。特別是在微服務架構中,管理數十甚至上百個服務的佈署時,這種自動化程度可以大降低維運負擔。
透過 ApplicationSet 的強大生成器功能和 Argo Rollouts 的高階佈署策略,我們可以實作真正的 GitOps 自動化,讓開發團隊專注於程式碼開發,而不必擔心複雜的佈署細節。隨著應用規模和複雜性的增長,這種自動化程度變得尤為重要,能夠顯著提高團隊的開發效率和系統的可靠性。
Kubernetes 漸進式佈署:掌握 Argo Rollouts 的優雅發布策略
在現代微服務架構中,安全與可控的應用佈署是確保系統穩定性的關鍵。傳統的 Kubernetes Deployment 雖然提供了基本的滾動更新能力,但對於需要精細控制的生產環境來說仍然不夠。當我們需要更精確地控制流量轉移、支援手動批准或自動化進度控制時,Argo Rollouts 便成為一個強大的解決方案。
在使用 Argo Rollouts 的過程中,我發現它不僅擴充套件了 Kubernetes 的佈署能力,更為團隊提供了一個可靠與可預測的發布流程。今天,我想分享如何利用 Argo Rollouts 實作金絲雀發布策略,以及如何將其與 Istio 整合,實作更精確的流量控制。
為什麼選擇漸進式佈署?
在開始深入技術細節前,我們先探討為什麼漸進式佈署對於現代應用至關重要:
- 風險降低:將新版本逐步引入到生產環境,可以將潛在問題的影響範圍限制在一小部分使用者
- 即時監控:在佈署過程中,可以持續監控新版本的效能和穩定性
- 快速回復:發現問題時,能夠迅速回復到穩定版本,最小化停機時間
- 使用者經驗保障:確保大多數使用者始終能夠使用穩定版本的服務
而 Argo Rollouts 正是為解決這些挑戰而設計的工具,它擴充套件了 Kubernetes 原生的佈署能力,提供了更多進階的佈署策略。