(接上文)
前面的部分介紹瞭如何為我的部落格打包 Docker 映象,接下來就是重頭戲 —— 部署到 Kubernetes。
Google Kubernetes Engine
沒錯,我現在自用的 Kubernetes 叢集就是 Google 家的 GKE。暫時沒有遇到什麼太大的坑,各方面整合做得不錯,適合我這種小白上手。
Helm
有 Kubernetes 當然少不了 Helm。在我的理解中,Helm 除了是一個模板引擎之外,還能把一整個「應用」所需要的 YAML 檔案組合為一個整體(Chart),多個 Chart 之間可以互相依賴,組合成為一整個系統;搭配 helm-diff
,helm-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 的功能很多,以我現在的知識量無法一一列出;至少在部署我的部落格的過程中,它可以提供以下幫助:
- Helm v2 Tillerless(此處註明版本是因為 Helm v3 已經進入 Alpha 階段,升級後完全取消 Tiller 概念,因此 Tillerless 暫不詳述,有興趣可自行 Google 瞭解)。
- 輸出 YAML 檔案變更(通過
helm-diff
外掛)。 - 管理 Helm Chart 倉庫,自動保持更新。
- 最重要的一點 —— 通過環境變數覆蓋特定 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 是個非常繁複精美的專案,我最近剛剛入門;本文的目的並不是為大家提供詳盡的參考,而是在我漫漫學習長路中的一些隨筆記錄吧。
當然,新手 ≠ 不追求專業,如文中有任何錯誤敬請指出,我會盡快修改。