本指南將引導您在 Kubernetes 集羣上設置漸進式交付 GitOps 管道。html
GitOps 是一種進行持續交付的方法,它經過將 Git 用做聲明性基礎結構和工做負載的真實來源來工做。對於 Kubernetes,這意味着使用 git push
代替 kubectl create/apply
或者 kubectl create/apply
。linux
GitOps vs CiOps 在傳統的 CI/CD 管道中,CD 是由持續集成工具支持的實現擴展,用於將構建工件升級到生產環境。在 GitOps 管道模型中,對生產的任何更改必須先在源代碼管理中提交(最好經過拉取請求),而後再應用於集羣。若是整個生產狀態受版本控制並在單個Git 存儲庫中進行描述,則在災難發生時,能夠快速恢復整個基礎架構,而無需從新運行 CI 管道。nginx
Kubernetes 反模式:讓咱們作 GitOps,而不是 CIOps!git
爲了將 GitOps 模型應用到 Kubernetes 上,你須要作三件事:github
在本研討會中,您將使用 GitHub 託管配置存儲庫,使用 Docker Hub 做爲容器註冊中心,使用 Flux 做爲 GitOps 控制器,並使用 Helm Operator 進行應用程序生命週期管理。web
漸進式交付是高級部署模式(如金絲雀,功能標記和 A/B 測試)的總稱。
經過給予應用程序開發人員和 SRE 團隊對爆炸半徑的細粒度控制,漸進交付技術被用來下降在生產中引入新軟件版本的風險。json
使用金絲雀的好處是可以在生產環境中使用發現問題的安全回滾策略對新版本進行容量測試。經過緩慢增長負載,您能夠監視和捕獲有關新版本如何影響生產環境的指標。windows
在本研討會中,您將使用
Flagger,
Linkerd 和
Prometheus
來自動化金絲雀分佈 Helm charts。瀏覽器
爲了安裝研討會的前提條件,您須要一個 Kubernetes 集羣(1.13 或更新版本),並支持 負載平衡器 和 RBAC。
確保您已經在本地安裝瞭如下工具:
在 macOS 上安裝 Helm v3 CLI:
brew install helm
在 Linux 或 Windows 上,您能夠從官方發佈頁面下載二進制文件。
Fork workshop 倉庫並克隆它到本地(使用你的 GitHub 用戶名替換 GHUSER
):
export GHUSER=stefanprodan git clone https://github.com/${GHUSER}/gitops-helm-workshop
設置您的 GitHub 用戶名和電子郵件:
cd gitops-helm-workshop git config user.name "${GHUSER}" git config user.email "your@main.address"
集羣狀態目錄結構:
├── cluster ├── canaries ├── charts │ └── podinfo ├── namespaces └── releases
將 Flux 存儲庫添加到 Helm 存儲庫:
helm repo add fluxcd https://charts.fluxcd.io
建立 fluxcd namespace:
kubectl create ns fluxcd
經過提供您的 GitHub 存儲庫 URL 安裝 Flux:
helm upgrade -i flux fluxcd/flux --wait \ --namespace fluxcd \ --set registry.pollInterval=1m \ --set git.pollInterval=1m \ --set git.url=git@github.com:${GHUSER}/gitops-helm-workshop
安裝 fluxctl:
# macOS and linux curl -sL https://fluxcd.io/install | sh export PATH=$PATH:$HOME/.fluxcd/bin # windows https://github.com/fluxcd/flux/releases
找到 Git SSH 公鑰:
export FLUX_FORWARD_NAMESPACE=fluxcd fluxctl identity
複製公鑰並在 GitHub 存儲庫上建立具備寫訪問權的部署密鑰。轉到 Settings > Deploy keys
,單擊 Add deploy key
,選中 Allow write access
,粘貼 Flux 公鑰並單擊 Add key
。
在 fluxcd
命名空間中安裝 Flux Helm Operator:
helm upgrade -i helm-operator fluxcd/helm-operator --wait \ --namespace fluxcd \ --set git.ssh.secretName=flux-git-deploy \ --set git.pollInterval=1m \ --set chartsSyncInterval=1m \ --set helm.versions=v3
下載 Linkerd v2 CLI:
# macOS and linux curl -sL https://run.linkerd.io/install | sh export PATH=$PATH:$HOME/.linkerd2/bin # windows https://github.com/linkerd/linkerd2/releases
在 linkerd
名稱空間中安裝 Linkerd 控制平面:
linkerd install | kubectl apply -f -
使用如下命令驗證安裝:
linkerd check
添加 Flagger Helm 倉庫:
helm repo add flagger https://flagger.app
安裝 Flagger 的金絲雀 CRD:
kubectl apply -f https://raw.githubusercontent.com/weaveworks/flagger/master/artifacts/flagger/crd.yaml
在 linkerd
命名空間中安裝 Flagger:
helm upgrade -i flagger flagger/flagger --wait \ --namespace linkerd \ --set crd.create=false \ --set metricsServer=http://linkerd-prometheus:9090 \ --set meshProvider=linkerd
一個 chart 發佈是經過 Kubernetes 自定義資源 HelmRelease 進行描述的。
一個 Helm release 能夠引用的 chart,以下:
爲了將應用程序暴露在集羣以外,您將使用 NGINX ingress 控制器。控制器將在 Linkerd 網格內運行。
建立一個啓用 linkerd 注入的名稱空間:
apiVersion: v1 kind: Namespace metadata: annotations: fluxcd.io/ignore: "false" linkerd.io/inject: enabled name: ingress-nginx
建立一個 Helm release 來安裝 NGINX ingress 控制器:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: nginx-ingress namespace: ingress-nginx annotations: fluxcd.io/ignore: "false" spec: releaseName: nginx-ingress chart: repository: https://kubernetes-charts.storage.googleapis.com/ name: nginx-ingress version: 1.33.4 values: controller: service: type: LoadBalancer
應用更改:
git add -A && \ git commit -m "install ingress" && \ git push origin master && \ fluxctl sync
驗證 Helm operator 是否已安裝 release:
kubectl -n ingress-nginx get hr
查找 ingress 控制器的公共 IP:
kubectl -n ingress-nginx get svc
Podinfo 是一個很小的 Go Web 應用程序。您將使用存儲 cluster/charts/podinfo
的 git 倉庫中的 Helm chart 安裝 podinfo。
建立啓用 linkerd 注入的 prod
命名空間:
apiVersion: v1 kind: Namespace metadata: annotations: fluxcd.io/ignore: "false" linkerd.io/inject: enabled name: prod
建立一個 Helm release 以安裝 podinfo chart(替換 GHUSER
爲你的 GitHub 用戶名和使用你的 ingress IP 替換 LB-PUBLIC-IP
):
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: podinfo namespace: prod annotations: fluxcd.io/ignore: "false" spec: releaseName: podinfo chart: git: git@github.com:GHUSER/gitops-helm-workshop ref: master path: cluster/charts/podinfo values: image: repository: stefanprodan/podinfo tag: 3.1.0 service: enabled: true type: ClusterIP ingress: enabled: true annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/configuration-snippet: | proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:9898; proxy_hide_header l5d-remote-ip; proxy_hide_header l5d-server-id; path: / hosts: - LB-PUBLIC-IP.nip.io
請注意,若是您使用的是 EKS,則主機應設置爲 elb.amazonaws.com
地址:
kubectl -n ingress-nginx get svc | grep Ingress
應用更改:
git add -A && \ git commit -m "install podinfo" && \ git push origin master && \ fluxctl sync
驗證 Helm operator 是否已安裝 podinfo:
kubectl -n prod get hr
打開瀏覽器並導航到 http://LB-PUBLIC-IP.nip.io/
,您應該看到 podinfo v3.1.0 UI
。
Flux 能夠用於自動化集羣中的容器映像更新。
您能夠經過註釋 Helm release 對象來啓用自動化 image 標記更新。
您還能夠經過使用 glob、regex 或語義版本表達式來控制更新應該考慮哪些標記。
編輯 podinfo Helm release 並啓用 Flux 自動 Image 更新:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: annotations: fluxcd.io/automated: "true" fluxcd.io/tag.chart-image: semver:~3.1
應用更改:
git add -A && \ git commit -m "automate podinfo" && \ git push origin master && \ fluxctl sync
驗證 Helm operator 是否已升級 podinfo:
kubectl -n prod get hr
在本地拉取 Flux 所作的更改:
git pull origin master
打開瀏覽器並導航到 http://LB-PUBLIC-IP.nip.io/
,您應該看到 podinfo v3.1.5 UI。
爲了將 secrets 安全地存儲在公共 Git 存儲庫中,您能夠使用 Sealed Secrets 控制器 並將您的 Kubernetes Secrets 加密爲 SealedSecrets。只能經過在集羣中運行的控制器來解密密封的 secrets。
建立 Sealed Secrets Helm release:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: sealed-secrets namespace: fluxcd annotations: fluxcd.io/ignore: "false" spec: releaseName: sealed-secrets chart: repository: https://kubernetes-charts.storage.googleapis.com/ name: sealed-secrets version: 1.8.0
應用更改:
git add -A && \ git commit -m "install sealed-secrets" && \ git push origin master && \ fluxctl sync
安裝 kubeseal CLI:
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v1.8.0/kubeseal-darwin-amd64 sudo install -m 755 kubeseal-darwin-amd64 /usr/local/bin/kubeseal
在啓動時,sealed-secrets 控制器生成一個 RSA key 並記錄公鑰。使用 kubeseal,您能夠將您的公鑰保存爲 pub-cert.pem,公鑰可安全存儲在 Git 中,可用於加密 secrets,無需直接訪問 Kubernetes 集羣:
kubeseal --fetch-cert \ --controller-namespace=fluxcd \ --controller-name=sealed-secrets \ > pub-cert.pem
您能夠使用 kubectl 在本地生成 Kubernetes secret,並使用 kubeseal 對其進行加密:
kubectl -n prod create secret generic basic-auth \ --from-literal=user=admin \ --from-literal=password=admin \ --dry-run \ -o json > basic-auth.json kubeseal --format=yaml --cert=pub-cert.pem < basic-auth.json > basic-auth.yaml
這將生成一個類型爲 SealedSecret 的自定義資源,其中包含加密的憑據。
Flux 將在您的集羣上應用 sealed secret,而後 sealed-secrets 的控制器將其解密爲 Kubernetes secret。
爲了準備進行災難恢復,您應該使用如下命令備份 Sealed Secrets 控制器的私鑰:
kubectl get secret -n fluxcd sealed-secrets-key -o yaml \ --export > sealed-secrets-key.yaml
要在災難後從備份中恢復,請替換新建立的密鑰並從新啓動控制器:
kubectl replace secret -n fluxcd sealed-secrets-key -f sealed-secrets-key.yaml kubectl delete pod -n fluxcd -l app=sealed-secrets
一個金絲雀發佈是用名爲 Canary 的 Kubernetes 自定義資源描述的。
編輯 podinfo Helm release 和禁用 image 更新和 ClusterIP 服務:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: podinfo namespace: prod annotations: fluxcd.io/automated: "false" spec: releaseName: podinfo values: image: repository: stefanprodan/podinfo tag: 3.1.0 service: enabled: false type: ClusterIP
應用更改:
git add -A && \ git commit -m "prep canary" && \ git push origin master && \ fluxctl sync
建立一個針對 podinfo 的金絲雀發佈:
apiVersion: flagger.app/v1beta1 kind: Canary metadata: name: podinfo namespace: prod annotations: fluxcd.io/ignore: "false" spec: targetRef: apiVersion: apps/v1 kind: Deployment name: podinfo service: port: 9898 analysis: interval: 10s maxWeight: 100 stepWeight: 5 threshold: 5 metrics: - name: request-success-rate thresholdRange: min: 99 interval: 1m - name: request-duration thresholdRange: max: 500 interval: 1m webhooks: - name: acceptance-test type: pre-rollout url: http://flagger-loadtester.prod/ timeout: 30s metadata: type: bash cmd: "curl -sd 'test' http://podinfo-canary.prod:9898/token | grep token" - name: load-test type: rollout url: http://flagger-loadtester.prod/ metadata: cmd: "hey -z 2m -q 10 -c 2 http://podinfo-canary.prod:9898/"
應用更改:
git add -A && \ git commit -m "add canary" && \ git push origin master && \ fluxctl sync
驗證 Flagger 已經初始化金絲雀:
kubectl -n prod get canary
安裝負載測試服務以在金絲雀分析期間生成流量:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: load-tester namespace: prod annotations: fluxcd.io/ignore: "false" spec: releaseName: load-tester chart: git: https://github.com/weaveworks/flagger ref: 1.0.0-rc.1 path: charts/loadtester values: fullnameOverride: load-tester
當您部署新的 podinfo 版本時,Flagger 逐漸將流量轉移到金絲雀,同時測量請求的成功率以及平均響應持續時間。
基於對這些Linkerd提供的指標的分析,金絲雀部署要麼提高要麼回滾。
經過更新容器映像來觸發金絲雀部署:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease spec: releaseName: podinfo values: image: tag: 3.1.1
應用更改:
git add -A && \ git commit -m "update podinfo" && \ git push origin master && \ fluxctl sync
當 Flagger 檢測到部署修訂版本已更改時,它將開始新的部署。您能夠使用如下方法監視流量的變化:
watch kubectl -n prod get canaries
在金絲雀分析期間,您可能會生成 HTTP 500 錯誤和高延遲,以測試 Flagger 是否暫停並回滾有故障的版本。
觸發另外一隻金絲雀的發佈:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease spec: releaseName: podinfo values: image: tag: 3.1.2
應用更改:
git add -A && \ git commit -m "update podinfo" && \ git push origin master && \ fluxctl sync
執行到測試 pod 和產生 HTTP 500 錯誤:
kubectl -n prod exec -it $(kubectl -n prod get pods -o name | grep -m1 load-tester | cut -d'/' -f 2) bash $ hey -z 1m -c 5 -q 5 http://podinfo-canary:9898/status/500 $ hey -z 1m -c 5 -q 5 http://podinfo-canary:9898/delay/1
當檢查失敗的數量達到金絲雀分析閾值時,流量將路由回主要服務器,而且金絲雀將比例縮放爲零。
觀看Flagger日誌:
$ kubectl -n linkerd logs deployment/flagger -f | jq .msg Starting canary analysis for podinfo.prod Advance podinfo.test canary weight 5 Advance podinfo.test canary weight 10 Advance podinfo.test canary weight 15 Halt podinfo.test advancement success rate 69.17% < 99% Halt podinfo.test advancement success rate 61.39% < 99% Halt podinfo.test advancement success rate 55.06% < 99% Halt podinfo.test advancement request duration 1.20s > 0.5s Halt podinfo.test advancement request duration 1.45s > 0.5s Rolling back podinfo.prod failed checks threshold reached 5 Canary failed! Scaling down podinfo.test
Linkerd 儀表板可實時提供有關服務狀況的高級視圖。
它可用於可視化服務依賴關係,流量拆分和了解特定服務路由的運行情況。
經過運行如下命令打開儀表板:
linkerd dashboard --port=50750
在金絲雀分析期間,導航至:
http://127.0.0.1:50750/namespaces/ingress-nginx/deployments/nginx-ingress-controller
您能夠使用如下命令從命令行監視生產名稱空間的實時流量:
linkerd -n prod top deploy
您能夠使用如下命令查看 podinfo 公開的全部路由:
linkerd -n prod routes service/podinfo
以上路由是從 podinfo swagger 規範生成的,並做爲 Linkerd 服務配置文件導出。
Flagger 附帶有一個測試服務,該服務在配置爲 Webhook 時能夠運行 Helm 測試。
爲 podinfo 令牌 API 建立一個測試:
apiVersion: v1 kind: Pod metadata: name: {{ template "podinfo.fullname" . }}-jwt-test-{{ randAlphaNum 5 | lower }} labels: heritage: {{ .Release.Service }} release: {{ .Release.Name }} chart: {{ .Chart.Name }}-{{ .Chart.Version }} app: {{ template "podinfo.name" . }} annotations: linkerd.io/inject: disabled "helm.sh/hook": test-success spec: containers: - name: tools image: giantswarm/tiny-tools command: - sh - -c - | TOKEN=$(curl -sd 'test' ${PODINFO_SVC}/token | jq -r .token) && curl -H "Authorization: Bearer ${TOKEN}" ${PODINFO_SVC}/token/validate | grep test env: - name: PODINFO_SVC value: {{ template "podinfo.fullname" . }}:{{ .Values.service.externalPort }} restartPolicy: Never
將以上文件保存在 cluster/charts/podinfo/tests
中。
在 prod
名稱空間中部署 Helm 測試運行器:
apiVersion: helm.fluxcd.io/v1 kind: HelmRelease metadata: name: helm-tester namespace: prod annotations: fluxcd.io/ignore: "false" spec: releaseName: helm-tester chart: git: https://github.com/weaveworks/flagger ref: 1.0.0-rc.1 path: charts/loadtester values: fullnameOverride: helm-tester serviceAccountName: helm-tester
應用更改:
git add -A && \ git commit -m "install helm-tester" && \ git push origin master && \ fluxctl sync
將 helm 測試添加爲預發佈 webhook:
apiVersion: flagger.app/v1beta1 kind: Canary metadata: name: podinfo namespace: prod spec: analysis: webhooks: - name: "helm test" type: pre-rollout url: http://helm-tester.prod/ timeout: 2m metadata: type: "helmv3" cmd: "test podinfo" - name: load-test url: http://load-tester.prod/ metadata: cmd: "hey -z 2m -q 10 -c 2 http://podinfo-canary.prod:9898/"
應用更改:
git add -A && \ git commit -m "update podinfo" && \ git push origin master && \ fluxctl sync
當金絲雀分析開始時,Flagger 將在將流量路由到金絲雀以前調用預發佈 Webhooks。 若是 helm 測試失敗,Flagger 將重試,直到達到分析閾值而且金絲雀回退爲止。