iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0

昨天把 Todo App 包成 Helm Chart,今天用這個 Chart 部署到 dev 和 staging 兩個環境,實際體驗 Helm 怎麼讓多環境部署變得容易 !


dev 和 staging 環境的差異通常在這幾個地方:

dev staging
replica 數量 固定 1 個,不開 HPA(省資源) HPA 自動調整 2~5 個(接近 production)
image tag latest 或 feature branch v1.x.x 穩定版
資料庫密碼 簡單的測試密碼 較安全的密碼
Resource Limit 設低一點 接近 production
環境設定 APP_ENV=development、LOG_LEVEL=debug APP_ENV=staging、LOG_LEVEL=info
Ingress host dev.todo.localhost staging.todo.localhost

沒有 Helm 之前,這些差異要靠維護多份 YAML 來管理,改一個欄位要改好幾個檔案。有了 Helm,只需要不同的 values 檔案覆寫需要的部分。


用 values 檔案管理不同環境

Helm 在安裝的時候會把 values.yaml(預設值)和指定的 values 檔案合併,values 檔案裡的設定會覆寫預設值,沒有寫到的設定就用預設值。

values-dev.yaml(主要寫跟預設值不同的部分):

api:
  replicas: 1
  autoscaling:
    enabled: false
  image:
    tag: latest
  resources:
    requests:
      cpu: "50m"
      memory: "64Mi"
    limits:
      cpu: "200m"
      memory: "128Mi"

frontend:
  replicas: 1
  image:
    tag: latest

mysql:
  storage: "1Gi"

ingress:
  enabled: true
  host: "dev.todo.localhost"

config:
  logLevel: "debug"

values.yaml 預設開啟 HPA(最少 2 個副本),dev 想省資源只跑 1 個,所以要把 autoscaling.enabled 關掉,replicas: 1 才會生效。只寫 replicas: 1 而沒關 HPA 的話,Deployment 模板不會渲染 replicas,HPA 還是會把 API 維持在 2 個。

values-staging.yaml:

api:
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 5
  image:
    tag: v1.0.0
  resources:
    requests:
      cpu: "100m"
      memory: "128Mi"
    limits:
      cpu: "500m"
      memory: "256Mi"

frontend:
  replicas: 2
  image:
    tag: v1.0.0

mysql:
  storage: "5Gi"

ingress:
  enabled: true
  host: "staging.todo.localhost"

config:
  appEnv: "staging"

staging 的 autoscaling、image tag、resources 其實跟 values.yaml 的預設值一樣,這裡故意寫出來,打開檔案就知道 staging 跑的是什麼設定,不用再回頭對照 values.yaml。之後預設值改了,staging 也不會跟著被改到。ingress.enabled: true 也是同樣的道理。

💡 host 用 .localhost 結尾,是因為瀏覽器會把所有 *.localhost 的網域直接當成本機 127.0.0.1,不用改 hosts 檔,也不會被公司電腦的 proxy 攔走。Step7 會再說明。

兩個檔案都沒有寫密碼,密碼一樣在安裝時用 --set 傳入,理由跟昨天相同:values 檔案會 commit 進 git。


實際操作

今天目標:用同一個 Chart 部署 dev 和 staging,透過 Ingress 連到兩個環境確認設定與資料各自獨立,再模擬 dev 升級失敗並 rollback。

Step1: 確認環境是乾淨的

  1. 昨天的 my-todo 和它的 PVC 要已經移除:
helm list -A
kubectl get pvc -A

還看得到 my-todo 或 mysql-data-my-todo-mysql-0,就回到 Day 23 Step12 清除。

  1. dev 裡不能留著之前手動部署的 Todo App,不然會跟 Helm 裝的搶 Ingress 路徑、多吃資源:
kubectl get deploy,statefulset,svc,ingress,hpa,pvc -n dev

有看到 todo-api、todo-frontend、mysql 等舊資源,到當初放 YAML 的資料夾刪除,舊 MySQL 的 PVC 要另外刪:

kubectl delete -f . -n dev
kubectl delete pvc <舊 MySQL 的 PVC 名稱> -n dev

出現 No resources found 或 dev 不存在,直接跳下一點即可。

  1. 確認 metrics-server 和 Ingress Controller 正常:
kubectl top nodes
kubectl get pods -n ingress-nginx

沒有的話先安裝(minikube):

minikube addons enable metrics-server
minikube addons enable ingress

Step2: 建立 values 檔案

在 helm/ 這一層建立 values-dev.yaml 和 values-staging.yaml,內容就是上面兩段:

helm/
├── todo-app/
├── values-dev.yaml
└── values-staging.yaml

⚠️ 不要建在 todo-app/ 裡。今天的指令都在 helm/ 執行,放錯位置會出現 cannot find the file specified。

Step3: 準備 dev 用的 image tag

dev 用 latest tag,Docker Hub 上還沒有,先從 v1.0.0 標一個推上去:

docker tag yourname/todo-app-api:v1.0.0 yourname/todo-app-api:latest
docker push yourname/todo-app-api:latest

docker tag yourname/todo-frontend:v1.0.0 yourname/todo-frontend:latest
docker push yourname/todo-frontend:latest

yourname/... 換成你 values.yaml 裡的 repository。

Step4: 部署到 dev

helm install todo-dev ./todo-app \
  -f values-dev.yaml \
  --set mysql.password=devpass123 \
  --set mysql.rootPassword=devroot123 \
  -n dev --create-namespace
  • -f values-dev.yaml:套用 dev 的設定
  • --set:傳入密碼,少了會被 required 擋下
  • --create-namespace:Namespace 不存在就自動建立

PowerShell 換行要把 \ 改成反引號 `,或寫成一行。

kubectl get pods -n dev

https://ithelp.ithome.com.tw/upload/images/20260929/20183863IPZj1hejYf.png

MySQL 初始化期間 API 會重啟幾次,等 MySQL 1/1 Running 後就會恢復。

⚠️ 裝壞想重來時,helm uninstall 後要一併刪掉 PVC mysql-data-todo-dev-mysql-0,否則換了密碼會出現 Access denied。

Step5: 部署到 staging

helm install todo-staging ./todo-app \
  -f values-staging.yaml \
  --set mysql.password=Stg8xK2mQ9pL \
  --set mysql.rootPassword=StgRoot7vN3wR5 \
  -n staging --create-namespace
kubectl get pods -n staging

https://ithelp.ithome.com.tw/upload/images/20260929/20183863k97IDWtebR.png

staging 的 API 由 HPA 維持最少 2 個,frontend 也是 2 個;dev 各只有 1 個。

Pod 卡在 Pending 時,用 kubectl describe pod 看 Events,Insufficient memory/cpu 代表本機資源不夠。

Step6: 確認兩個環境的設定不同

  1. 同一個 Chart,兩個獨立的 Release:
helm list -A

https://ithelp.ithome.com.tw/upload/images/20260929/201838639pV7iLT58K.png

  1. 各 Release 套用的值(含密碼,輸出別外流):
helm get values todo-dev -n dev
helm get values todo-staging -n staging
  1. API 的環境變數:
kubectl exec -n dev deploy/todo-dev-api -- env | grep -E "APP_ENV|LOG_LEVEL|DATABASE_URL"
kubectl exec -n staging deploy/todo-staging-api -- env | grep -E "APP_ENV|LOG_LEVEL|DATABASE_URL"

dev 是 development / debug,staging 是 staging / info,DATABASE_URL 各自指向 todo-dev-mysql-service 和 todo-staging-mysql-service。

  1. 前端的後端位址:
kubectl exec -n dev deploy/todo-dev-frontend -- env | grep API_HOST
kubectl exec -n staging deploy/todo-staging-frontend -- env | grep API_HOST

分別是 todo-dev-api-service 和 todo-staging-api-service,由 {{ .Release.Name }} 自動對上。

  1. HPA 只在 staging:
kubectl get hpa -A

PowerShell 把 grep -E、grep 換成 Select-String。

Step7: 用瀏覽器連到兩個環境

請求會先進 Ingress Controller,再依 host 分到 dev 或 staging:

瀏覽器 → 本機 8080 → Ingress Controller → dev / staging
  1. 確認 Ingress 的 HOSTS 是 dev.todo.localhost 和 staging.todo.localhost:
kubectl get ingress -A
  1. 把 Ingress Controller 轉發到本機 8080,終端機保持開著:
kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 8080:80

出現 namespaces "ingress-nginx" not found,回到 Step1 安裝 Ingress Controller。

用 port-forward 而不是 minikube tunnel:minikube docker driver 的 ADDRESS IP 本機連不到,tunnel 在 Windows 又常綁不了 80 port。port-forward 用 8080,Docker Desktop、minikube 都能用。

  1. 瀏覽器打開:
http://dev.todo.localhost:8080
http://staging.todo.localhost:8080

在 dev 新增 Todo,staging 看不到,兩邊資料完全獨立。

*.localhost 會被瀏覽器直接當成 127.0.0.1,不用改 hosts 檔,也不會被 proxy 攔走。瀏覽器打不開的話,再到 hosts 檔加上這兩個網域指向 127.0.0.1。

Step8: 只升級 dev,並模擬升級失敗

用一個沒推過的 tag v2.0.0-beta 模擬升級出錯:

helm upgrade todo-dev ./todo-app \
  --reuse-values \
  -f values-dev.yaml \
  --set api.image.tag=v2.0.0-beta \
  -n dev

--reuse-values 沿用上次的值(含密碼),再套上這次的 -f 和 --set。

kubectl get pods -n dev

https://ithelp.ithome.com.tw/upload/images/20260930/20183863DVRzJ5ewqL.png

新 Pod 拉不到 image,但滾動更新會保留舊 Pod,dev 網頁仍可正常使用。

staging 不受影響,image 還是 v1.0.0:

kubectl get deployment todo-staging-api -n staging -o jsonpath="{.spec.template.spec.containers[0].image}"

Step9: rollback

helm history todo-dev -n dev
helm rollback todo-dev 1 -n dev

rollback 直接用 REVISION 1 存下的值,不用再傳密碼。ImagePullBackOff 的 Pod 會被移除:

kubectl get pods -n dev
helm history todo-dev -n dev

https://ithelp.ithome.com.tw/upload/images/20260930/20183863Cm61qSffk5.png

rollback 不會刪掉 REVISION 2,而是新增一筆內容等同 REVISION 1 的 REVISION 3,歷史完整保留。

練習結束後,到 Step7 的終端機按 Ctrl + C 停掉 port-forward。


小結

今天用 Helm Chart 實現了多環境部署:

  • values 檔案分離:values-dev.yaml 和 values-staging.yaml 管理各環境的差異,密碼不寫進檔案,安裝時用 --set 傳入
  • 同一個 Chart,多個 Release:dev 和 staging 各有自己的 Namespace、MySQL 和資料,互不影響,前端也會自動連到各自的後端
  • 用 values 開關資源:dev 關閉 HPA 固定 1 個副本,staging 開啟 HPA 自動擴縮
  • Ingress 用 host 分流:兩個環境共用同一個 Ingress Controller,靠 dev.todo.localhost、staging.todo.localhost 區分
  • --create-namespace:安裝時順便建立 Namespace
  • 升級只影響指定的 Release:dev 升級失敗,staging 照常運作
  • --reuse-values:升級時沿用上次的值,不用重傳密碼
  • rollback 有歷史記錄:出問題隨時回到上一個版本

今天的 dev 和 staging 先保留著,明天學 CI/CD,把部署流程自動化——Push 到 GitHub 就自動 build image 並更新 K8s 的 Release。


上一篇
Day 23|Helm (2)
下一篇
Day 25|CI/CD 整合(GitHub Actions)
系列文
從零學 K8s|30 天核心概念 × 實作,新手也能真正掌握 Kubernetes 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言