使用 Flux,Helm v3,Linkerd 和 Flagger 漸進式交付 Kubernetes

為少發表於2021-01-11

介紹

本指南將引導您在 Kubernetes 叢集上設定漸進式交付 GitOps 管道。

GitOps 是什麼?

GitOps 是一種進行持續交付的方法,它通過將 Git 用作宣告性基礎結構和工作負載的真實來源來工作。對於 Kubernetes,這意味著使用 git push 代替 kubectl create/apply 或者 kubectl create/apply

GitOps vs CiOps 在傳統的 CI/CD 管道中,CD 是由持續整合工具支援的實現擴充套件,用於將構建工件升級到生產環境。在 GitOps 管道模型中,對生產的任何更改必須先在原始碼管理中提交(最好通過拉取請求),然後再應用於叢集。如果整個生產狀態受版本控制並在單個Git 儲存庫中進行描述,則在災難發生時,可以快速恢復整個基礎架構,而無需重新執行 CI 管道。

Kubernetes 反模式:讓我們做 GitOps,而不是 CIOps!

為了將 GitOps 模型應用到 Kubernetes 上,你需要做三件事:

  • 一個 Git 儲存庫,其中包含以 YAM 格式定義的工作負載、Helm charts 和定義叢集所需狀態的任何其他 Kubernetes 自定義資源
  • 一個容器註冊中心(registry),CI 系統在其中推送不可變的映象(沒有 latest 標籤,使用 語義版本控制 或 git commit sha
  • 一個進行雙向同步的 Kubernetes 控制器:
    • 監視配置儲存庫中的更改並將其應用於您的叢集
    • 監視容器 registry(註冊中心) 的新映像,並根據部署策略更新工作負載定義。

在本研討會中,您將使用 GitHub 託管配置儲存庫,使用 Docker Hub 作為容器註冊中心,使用 Flux 作為 GitOps 控制器,並使用 Helm Operator 進行應用程式生命週期管理。

什麼是漸進式交付?

漸進式交付是高階部署模式(如金絲雀,功能標記和 A/B 測試)的總稱。
通過給予應用程式開發人員和 SRE 團隊對爆炸半徑的細粒度控制,漸進交付技術被用來降低在生產中引入新軟體版本的風險。

使用金絲雀的好處是能夠在生產環境中使用發現問題的安全回滾策略對新版本進行容量測試。通過緩慢增加負載,您可以監視和捕獲有關新版本如何影響生產環境的指標。

Martin Fowler 部落格

在本研討會中,您將使用
Flagger,
Linkerd
Prometheus
來自動化金絲雀分佈 Helm charts。

前提條件

為了安裝研討會的前提條件,您需要一個 Kubernetes 叢集(1.13 或更新版本),並支援 負載平衡器RBAC
確保您已經在本地安裝了以下工具:

  • kubectl 1.16
  • git 2.20

Helm v3

在 macOS 上安裝 Helm v3 CLI:

brew install helm

在 Linux 或 Windows 上,您可以從官方釋出頁面下載二進位制檔案。

Git

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

將 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

Helm Operator

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

下載 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

新增 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

Helm 釋出

一個 chart 釋出是通過 Kubernetes 自定義資源 HelmRelease 進行描述的。

一個 Helm release 可以引用的 chart,如下:

  • 通過 HTTPS 的公共或私有 Helm 儲存庫
  • 通過 SSH 的公共或私有 Git 儲存庫

安裝 NGINX

為了將應用程式暴露在叢集之外,您將使用 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

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

為了將 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 儀表板可實時提供有關服務情況的高階檢視。
它可用於視覺化服務依賴關係,流量拆分和了解特定服務路由的執行狀況。

通過執行以下命令開啟儀表板:

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 服務配置檔案匯出。

Canary Helm 測試

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 將重試,直到達到分析閾值並且金絲雀回退為止。

相關文章