昨天把 Todo App 包成 Helm Chart,今天用這個 Chart 部署到 dev 和 staging 兩個環境,實際體驗 Helm 怎麼讓多環境部署變得容易 !
| 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 檔案覆寫需要的部分。
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。
my-todo 和它的 PVC 要已經移除:helm list -A
kubectl get pvc -A
還看得到 my-todo 或 mysql-data-my-todo-mysql-0,就回到 Day 23 Step12 清除。
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不存在,直接跳下一點即可。
kubectl top nodes
kubectl get pods -n ingress-nginx
沒有的話先安裝(minikube):
minikube addons enable metrics-server
minikube addons enable ingress
在 helm/ 這一層建立 values-dev.yaml 和 values-staging.yaml,內容就是上面兩段:
helm/
├── todo-app/
├── values-dev.yaml
└── values-staging.yaml
⚠️ 不要建在
todo-app/裡。今天的指令都在helm/執行,放錯位置會出現cannot find the file specified。
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。
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

MySQL 初始化期間 API 會重啟幾次,等 MySQL 1/1 Running 後就會恢復。
⚠️ 裝壞想重來時,
helm uninstall後要一併刪掉 PVCmysql-data-todo-dev-mysql-0,否則換了密碼會出現Access denied。
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

staging 的 API 由 HPA 維持最少 2 個,frontend 也是 2 個;dev 各只有 1 個。
Pod 卡在
Pending時,用kubectl describe pod看 Events,Insufficient memory/cpu代表本機資源不夠。
helm list -A

helm get values todo-dev -n dev
helm get values todo-staging -n staging
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。
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 }} 自動對上。
kubectl get hpa -A
PowerShell 把
grep -E、grep換成Select-String。
請求會先進 Ingress Controller,再依 host 分到 dev 或 staging:
瀏覽器 → 本機 8080 → Ingress Controller → dev / staging
HOSTS 是 dev.todo.localhost 和 staging.todo.localhost:kubectl get ingress -A
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 的ADDRESSIP 本機連不到,tunnel 在 Windows 又常綁不了 80 port。port-forward 用 8080,Docker Desktop、minikube 都能用。
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。
用一個沒推過的 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

新 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}"
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

rollback 不會刪掉 REVISION 2,而是新增一筆內容等同 REVISION 1 的 REVISION 3,歷史完整保留。
練習結束後,到 Step7 的終端機按 Ctrl + C 停掉 port-forward。
今天用 Helm Chart 實現了多環境部署:
values-dev.yaml 和 values-staging.yaml 管理各環境的差異,密碼不寫進檔案,安裝時用 --set 傳入dev.todo.localhost、staging.todo.localhost 區分--create-namespace:安裝時順便建立 Namespace--reuse-values:升級時沿用上次的值,不用重傳密碼今天的 dev 和 staging 先保留著,明天學 CI/CD,把部署流程自動化——Push 到 GitHub 就自動 build image 並更新 K8s 的 Release。