我如何將部落格遷移到 Kubernetes(下)

Wi1dcard發表於2019-09-12

(接上文)

前面的部分介紹瞭如何為我的部落格打包 Docker 映象,接下來就是重頭戲 —— 部署到 Kubernetes。

Google Kubernetes Engine

沒錯,我現在自用的 Kubernetes 叢集就是 Google 家的 GKE。暫時沒有遇到什麼太大的坑,各方面整合做得不錯,適合我這種小白上手。

Helm

有 Kubernetes 當然少不了 Helm。在我的理解中,Helm 除了是一個模板引擎之外,還能把一整個「應用」所需要的 YAML 檔案組合為一個整體(Chart),多個 Chart 之間可以互相依賴,組合成為一整個系統;搭配 helm-diffhelm-secret 等外掛還可以展示 YAML 檔案變更、管理敏感金鑰資訊等。

首先,我使用 helm create 建立了一個 Chart,並將它直接放在了 deploy/chart 目錄。

預設 Chart 內的 YAML 模板基本沒有修改,只有 values.yaml 改了一些值:

image:
  repository: wi1dcard/blog

正如你看到的,映象被修改成了 wi1dcard/blog(上文中推送到 Docker Hub 的映象)。同時,我啟用了 Ingress,還給 Ingress 新增了一些註解:

ingress:
  enabled: true
  annotations:
    kubernetes.io/tls-acme: "true"
    ingress.kubernetes.io/ssl-redirect: "true"
    ingress.kubernetes.io/browser-xss-filter: "true" # X-XSS-Protection: 1; mode=block
    ingress.kubernetes.io/content-type-nosniff: "true" # X-Content-Type-Options: nosniff
    ingress.kubernetes.io/referrer-policy: strict-origin-when-cross-origin # Referrer-Policy: same-origin
    ingress.kubernetes.io/custom-frame-options-value: SAMEORIGIN # X-Frame-Options: SAMEORIGIN
    ingress.kubernetes.io/custom-response-headers: X-Powered-By:Wi1dcard Kubernetes Engine

我使用的是 Traefik Ingress Controller,相比於 Nginx Ingress Controller 給 Nginx 加個中間層來說,Traefik 本身既是 Ingress Controller 又是 Ingress 的具體實現;而且我沒有四層協議的需求,效能更是完全無所謂(當然 Traefik 不一定比 Nginx 差),所以直接選擇了為「雲原生」設計的 Traefik。

這些 Annotations 多數都是我從 Traefik Kubernetes Ingress Docs 抄過來的?,除了強制跳轉 HTTPS 之外,它們還可以讓 Traefik Ingress 返回的 HTTP 響應包含特定的「安全頭資訊(Security Headers)」,對限制瀏覽器許可權、保障使用者瀏覽站點時的安全性有一定作用。

更多有關 Security Headers 的說明,可以參考 https://securityheaders.com/

另外,值得注意的是 kubernetes.io/tls-acme: "true",這條註解是為了配置自動申請證照使用,我使用的是 cert-manager,它能夠幫我解決「老大難」的 Let's Encrypt 證照申請、續期問題。在此不再展開,待有空時另開部落格詳細說明。

Helmfile

不少人說 Helm 用於管理 Kubernetes 的 YAML,而 Helmfile 就是用來管理 Helm 的 Helm... ?

算是個比較形象,帶有玩笑成分的比喻吧。Helmfile 的功能很多,以我現在的知識量無法一一列出;至少在部署我的部落格的過程中,它可以提供以下幫助:

  1. Helm v2 Tillerless(此處註明版本是因為 Helm v3 已經進入 Alpha 階段,升級後完全取消 Tiller 概念,因此 Tillerless 暫不詳述,有興趣可自行 Google 瞭解)。
  2. 輸出 YAML 檔案變更(通過 helm-diff 外掛)。
  3. 管理 Helm Chart 倉庫,自動保持更新。
  4. 最重要的一點 —— 通過環境變數覆蓋特定 Values 的值。

helmfile.yaml 內可以檢視到我的 Helmfile 配置,最主要的部分就是 releases 小節:

releases:
  - name: blog # Helm Release 名稱
    namespace: blog # 部署到的 Kubernetes 名稱空間
    chart: ./chart # Helm Chart 的名稱或本地路徑
    values: # 覆蓋 Chart 的預設 Values
      - image:
          tag: {{ requiredEnv "DOCKER_TAG" }}
        ingress:
          hosts:
            - host: {{ requiredEnv "INGRESS_HOST" }}
              paths: [ / ]
            - host: wi1dcard.dev
              paths: [ / ]
          tls:
            - secretName: ingress-tls
              hosts: [ {{ requiredEnv "INGRESS_HOST" }} ]

與 Helm 類似,Helmfile 會將 helmfile.yaml 作為 Go Template 渲染。如你所見,在以上片段中,我使用了 {{ ... }} 語法和 requiredEnv 函式。

requiredEnv 是 Helmfile 提供的功能之一,能夠獲取指定環境變數的值,並參與模板渲染,例如 {{ requiredEnv "INGRESS_HOST" }} 即為讀取 INGRESS_HOST 變數。

配合覆蓋 Chart 預設的 Values,便能夠實現上文中提到的第「4」點。

持續交付(CD)

這樣一來,藉助於 Helmfile,我可以在 CI/CD 過程中靈活地控制部署的映象($DOCKER_TAG)、隱藏 CDN 背後的裸域名($INGRESS_HOST)...

最後一步,CD。在 deploy/deploy.sh 中,我使用了 Helmfile 的官方映象:

echo "Applying helm releases..."
docker run --rm -v "$PWD/deploy:/deploy" \
    -e HELM_TILLER_STORAGE=configmap \
    -e KUBECONFIG_BASE64="$KUBECONFIG_BASE64" \
    -e DOCKER_TAG="$DOCKER_TAG" \
    -e INGRESS_HOST="$INGRESS_HOST" \
    quay.io/roboll/helmfile:v0.82.0 /deploy/helmfile.sh

這不是規範的做法!若是後期遷移到類似 GitLab CI 等 Dockerized CI,那麼只需要宣告另一個 Job 即可,而不必自行啟動容器。

在容器內,我使用了 deploy/helmfile.sh 指令碼;它除了幫我解碼 $KUBECONFIG_BASE64 並填到 ~/.kube/config 之外,只有兩條命令與真正意義上的「部署」有關:

# 初始化 Helm,但不安裝 Tiller。
helm init --client-only
# 部署。
helmfile -f deploy/helmfile.yaml apply --suppress-secrets

沒錯,到最後部署只剩下一行命令。其中:

  • -f deploy/helmfile.yaml 用於指定 helmfile.yaml 的路徑。
  • apply 是 Helmfile 的子命令,直譯為「應用」,你可以理解為「部署」。
  • --suppress-secrets 表示隱藏 Secrets,避免金鑰內容被直接輸出到 CI 日誌中。

最終效果

剩下的都交給 Helmfile、Helm、Kubernetes 就好。它們緊密協作,根據你的配置,最終將應用部署到叢集中:

$ kubectl get pods -n blog

NAME                    READY   STATUS    RESTARTS   AGE
blog-74d758cb84-w5z9k   1/1     Running   0          21h

你可以在 這裡 檢視完整的構建和部署日誌,以及 歷史

以及我的部落格:

$ curl -I https://wi1dcard.dev/

HTTP/2 200
date: Thu, 12 Sep 2019 13:05:37 GMT
content-type: text/html
...
x-powered-by: Wi1dcard Kubernetes Engine

結語

首先感謝 Xiangxuan Liu(@nauxliu)在我學習 Kubernetes 過程中的幫助和指導,節省了大量試錯成本,少繞了很多彎路,算得上是受益匪淺吧。

文中涉及的技術棧均取自(或衍生自)我司現有的 Kubernetes 相關技術,感興趣的話,簡歷丟來吧:https://join.rightcapital.com/(雖然暫時不招 DevOps 2333,不過前後端都在招呀)。

最後,Kubernetes 是個非常繁複精美的專案,我最近剛剛入門;本文的目的並不是為大家提供詳盡的參考,而是在我漫漫學習長路中的一些隨筆記錄吧。

當然,新手 ≠ 不追求專業,如文中有任何錯誤敬請指出,我會盡快修改。

我感謝自己平凡,敢愛敢恨沒負擔。
我感謝自己不凡,可愛可恨都包攬。

相關文章