iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0

昨天學了 Helm 的基本概念,今天自己動手把 Todo App 的所有 YAML 包成一個 Helm Chart,包含前端、API 和 MySQL,一個指令就能把整套服務裝起來。


Chart 的目錄結構

今天完成後,Chart 會長這樣:

todo-app/
├── Chart.yaml                    # Chart 的基本資訊(名稱、版本)
├── values.yaml                   # 預設的可設定參數
└── templates/                    # YAML 模板,用 Go template 語法寫
    ├── mysql-secret.yaml
    ├── mysql-statefulset.yaml
    ├── mysql-service.yaml
    ├── api-deployment.yaml
    ├── api-service.yaml
    ├── api-hpa.yaml
    ├── frontend-deployment.yaml
    ├── frontend-service.yaml
    ├── configmap.yaml
    ├── secret.yaml
    └── ingress.yaml
  • Chart.yaml:描述這個 Chart 是什麼、第幾版
  • values.yaml:所有可以調整的參數和預設值,換環境時只要改這裡
  • templates/:原本的 K8s YAML,把會變動的地方換成變數

模板語法

Helm 的模板使用 Go template 語法,用 {{ }} 插入變數。例如原本寫死的:

metadata:
  name: todo-api
spec:
  replicas: 2

改成模板後:

metadata:
  name: {{ .Release.Name }}-api
spec:
  replicas: {{ .Values.api.replicas }}

安裝時 Helm 會把變數換成實際的值,產生最終的 YAML 送進 K8s。

幾個常用的模板變數:

  • {{ .Release.Name }}:安裝時指定的 Release 名稱,例如 helm install my-todo 的 my-todo
  • {{ .Values.xxx }}:values.yaml 裡定義的參數
  • {{ .Chart.Name }}:Chart 名稱
  • {{ .Chart.Version }}:Chart 版本

今天還會用到幾個語法:

  • {{- if .Values.xxx }} ... {{- else }} ... {{- end }}:條件判斷,{{- if not .Values.xxx }} 則是條件不成立時才渲染
  • | quote:幫值加上雙引號,避免 true、8000 這類值被 YAML 當成布林或數字
  • required "訊息" .Values.xxx:必填參數,沒給值時直接報錯並顯示訊息

所有資源名稱都加上 {{ .Release.Name }} 當前綴,這樣同一個 Chart 裝兩次(例如明天的 dev 和 staging)名稱也不會撞在一起。


實際操作

今天目標:把 MySQL、todo-api、todo-frontend、HPA、ConfigMap、Secret、Ingress 包成一個 Helm Chart,用 helm install 一次部署整套服務,並練習升級和查看歷史。

Step1: 建立 Chart 骨架

  1. 在 /Todo-App 底下建立 helm 資料夾並進入:
mkdir helm
cd helm
  1. 用 helm create 建立骨架:
helm create todo-app

這個指令會建立一個預設的 Chart,裡面有範例的 Deployment 和 Service。

  1. 把 templates/ 裡的預設檔案清掉,等等換成我們自己的:
cd todo-app
rm -rf templates/*

Windows PowerShell 的寫法:

cd todo-app
Remove-Item -Path templates/* -Recurse -Force

接下來 Step2~Step8 都是在 todo-app/ 資料夾裡編輯檔案。

Step2: 編輯 Chart.yaml

把 Chart.yaml 的內容改成:

apiVersion: v2
name: todo-app
description: A Helm chart for Todo App
type: application
version: 0.1.0
appVersion: "1.0.0"
  • version:Chart 本身的版本,改了模板就要往上加
  • appVersion:Chart 裡應用程式的版本,只是標示用

Step3: 設定 values.yaml

把 values.yaml 的內容整個換成:

# MySQL 資料庫
mysql:
  image:
    repository: mysql
    tag: "8.0"
  storage: "5Gi"
  database: "tododb"
  user: "todo"            # 不能設成 root,root 帳號由 MySQL 自動建立
  password: ""            # 安裝時用 --set 傳入
  rootPassword: ""        # 安裝時用 --set 傳入
  resources:
    requests:
      cpu: "250m"
      memory: "512Mi"
    limits:
      cpu: "500m"
      memory: "1Gi"

# Todo API 後端
api:
  replicas: 2             # autoscaling.enabled 為 false 時才會用到
  image:
    repository: yourname/todo-app-api
    tag: v1.0.0
  resources:
    requests:
      cpu: "100m"
      memory: "128Mi"
    limits:
      cpu: "500m"
      memory: "256Mi"
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 5
    targetCPUUtilizationPercentage: 70

# Todo Frontend 前端
frontend:
  replicas: 2
  image:
    repository: yourname/todo-frontend
    tag: v1.0.0
  resources:
    requests:
      cpu: "50m"
      memory: "64Mi"
    limits:
      cpu: "200m"
      memory: "128Mi"

# Ingress
ingress:
  enabled: true
  host: ""   # 空白代表用 IP 存取

# 一般設定(對應 Day 10 的 ConfigMap)
config:
  appEnv: "development"
  logLevel: "info"

兩個密碼都留空,不要把真實密碼寫進 values.yaml(這個檔案會 commit 進 git),安裝時再用 --set 傳入。

api.autoscaling 對應之前的 todo-api-hpa.yaml。enabled 為 true 時,API 的副本數交給 HPA 管理,replicas 就不會被使用。

image 的 repository 和 tag 要跟你實際推到 Docker Hub 的名稱一模一樣,寫錯的話 Pod 會卡在 ErrImagePull。不確定的話,可以查之前正常運作的 Deployment 用的是哪個 image:

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

Step4: 建立 MySQL 的模板

MySQL 是有狀態的服務,跟 Day 12 一樣用 StatefulSet 部署。

  1. 建立 templates/mysql-secret.yaml,存放 MySQL 的密碼:
apiVersion: v1
kind: Secret
metadata:
  name: {{ .Release.Name }}-mysql-secret
type: Opaque
stringData:
  MYSQL_ROOT_PASSWORD: {{ required "請用 --set mysql.rootPassword=... 傳入 MySQL root 密碼" .Values.mysql.rootPassword | quote }}
  MYSQL_PASSWORD: {{ required "請用 --set mysql.password=... 傳入 MySQL 密碼" .Values.mysql.password | quote }}
  • stringData:可以直接寫原始值,K8s 會自動做 base64 編碼,不用像 Day 11 那樣手動 echo | base64
  • required:安裝時沒傳入密碼,Helm 會直接報錯並顯示提示訊息
  1. 建立 templates/mysql-statefulset.yaml:
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: {{ .Release.Name }}-mysql
spec:
  serviceName: {{ .Release.Name }}-mysql-service
  replicas: 1
  selector:
    matchLabels:
      app: {{ .Release.Name }}-mysql
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}-mysql
    spec:
      containers:
        - name: mysql
          image: "{{ .Values.mysql.image.repository }}:{{ .Values.mysql.image.tag }}"
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_DATABASE
              value: {{ .Values.mysql.database | quote }}
            - name: MYSQL_USER
              value: {{ .Values.mysql.user | quote }}
          envFrom:
            - secretRef:
                name: {{ .Release.Name }}-mysql-secret   # MYSQL_ROOT_PASSWORD、MYSQL_PASSWORD
          readinessProbe:
            exec:
              command: ["mysqladmin", "ping", "-h", "127.0.0.1"]
            initialDelaySeconds: 10
            periodSeconds: 5
          volumeMounts:
            - name: mysql-data
              mountPath: /var/lib/mysql
          resources:
            requests:
              cpu: {{ .Values.mysql.resources.requests.cpu }}
              memory: {{ .Values.mysql.resources.requests.memory }}
            limits:
              cpu: {{ .Values.mysql.resources.limits.cpu }}
              memory: {{ .Values.mysql.resources.limits.memory }}
  volumeClaimTemplates:
    - metadata:
        name: mysql-data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: {{ .Values.mysql.storage }}
  • MySQL 容器第一次啟動時,會依照 MYSQL_DATABASE、MYSQL_USER、MYSQL_PASSWORD 自動建立資料庫和使用者
  • readinessProbe 用 mysqladmin ping 確認 MySQL 真的可以連線了,才讓 Service 把流量導過來
  • volumeClaimTemplates 會為 Pod 建立專屬的 PVC,資料存在這裡,Pod 重啟資料也不會消失
  1. 建立 templates/mysql-service.yaml:
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-mysql-service
spec:
  clusterIP: None   # Headless Service,StatefulSet 需要
  selector:
    app: {{ .Release.Name }}-mysql
  ports:
    - port: 3306
      targetPort: 3306

API 會透過 {{ .Release.Name }}-mysql-service 這個名稱連到 MySQL。

Step5: 建立 todo-api 的模板

  1. 建立 templates/api-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-api
spec:
  {{- if not .Values.api.autoscaling.enabled }}
  replicas: {{ .Values.api.replicas }}
  {{- end }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}-api
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}-api
    spec:
      containers:
        - name: api
          image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag }}"
          ports:
            - containerPort: 8000
          envFrom:
            - configMapRef:
                name: {{ .Release.Name }}-config
            - secretRef:
                name: {{ .Release.Name }}-secret
          resources:
            requests:
              cpu: {{ .Values.api.resources.requests.cpu }}
              memory: {{ .Values.api.resources.requests.memory }}
            limits:
              cpu: {{ .Values.api.resources.limits.cpu }}
              memory: {{ .Values.api.resources.limits.memory }}

envFrom 引用了 {{ .Release.Name }}-config 和 {{ .Release.Name }}-secret,這兩個會在 Step7 建立。少了它們,Pod 會卡在 CreateContainerConfigError。

replicas 用 if not 包起來:開啟 HPA 時不寫 replicas,副本數完全交給 HPA。如果兩邊都寫,每次 helm upgrade 都會把副本數重設回 values.yaml 的值,蓋掉 HPA 剛擴展出來的數量。

  1. 建立 templates/api-service.yaml:
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-api-service
spec:
  type: ClusterIP
  selector:
    app: {{ .Release.Name }}-api
  ports:
    - port: 80
      targetPort: 8000
  1. 建立 templates/api-hpa.yaml:
{{- if .Values.api.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {{ .Release.Name }}-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ .Release.Name }}-api
  minReplicas: {{ .Values.api.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.api.autoscaling.maxReplicas }}
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: {{ .Values.api.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
  • 內容跟之前的 todo-api-hpa.yaml 一樣,只是 scaleTargetRef.name 改成 {{ .Release.Name }}-api,對到這個 Release 的 API Deployment
  • 最外層的 if:autoscaling.enabled 為 false 時整個 HPA 不會建立
  • HPA 用「實際 CPU 用量 ÷ resources.requests.cpu」算使用率,所以 API 的 requests 一定要設定

Step6: 建立 todo-frontend 的模板

  1. 建立 templates/frontend-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-frontend
spec:
  replicas: {{ .Values.frontend.replicas }}
  selector:
    matchLabels:
      app: {{ .Release.Name }}-frontend
  template:
    metadata:
      labels:
        app: {{ .Release.Name }}-frontend
    spec:
      containers:
        - name: frontend
          image: "{{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }}"
          ports:
            - containerPort: 80
          env:
            - name: API_HOST
              value: {{ .Release.Name }}-api-service
            - name: API_PORT
              value: "80"
          resources:
            requests:
              cpu: {{ .Values.frontend.resources.requests.cpu }}
              memory: {{ .Values.frontend.resources.requests.memory }}
            limits:
              cpu: {{ .Values.frontend.resources.limits.cpu }}
              memory: {{ .Values.frontend.resources.limits.memory }}
  • 前端 image 裡的 nginx 會把 /api/ 的請求轉給後端,後端位址由環境變數 API_HOST、API_PORT 決定。image 內建的預設值是手動部署時的 Service 名稱 todo-api-service
  • 包成 Chart 後,後端 Service 名稱變成 {{ .Release.Name }}-api-service,所以要用環境變數覆蓋預設值。不加的話,nginx 啟動時找不到 todo-api-service 會直接退出,前端 Pod 會一直 Error
  • API_HOST 跟 Step5 的 Service 名稱用同一個寫法,不管 Release 叫什麼名字,前端都會連到同一個 Release 裡的後端
  • API_PORT 填的是 Service 的 port 80,不是容器的 8000。K8s 的環境變數值必須是字串,所以要加引號
  1. 建立 templates/frontend-service.yaml:
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-frontend-service
spec:
  type: ClusterIP
  selector:
    app: {{ .Release.Name }}-frontend
  ports:
    - port: 80
      targetPort: 80

Ingress 的 / 會導到這個 Service,少了它前端就連不到。

Step7: 建立 API 用的 ConfigMap 和 Secret

  1. 建立 templates/configmap.yaml,內容對應 Day 10 的 ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-config
data:
  APP_ENV: {{ .Values.config.appEnv | quote }}
  LOG_LEVEL: {{ .Values.config.logLevel | quote }}
  1. 建立 templates/secret.yaml,內容對應 Day 11 的 Secret:
apiVersion: v1
kind: Secret
metadata:
  name: {{ .Release.Name }}-secret
type: Opaque
stringData:
  DATABASE_URL: "mysql+pymysql://{{ .Values.mysql.user }}:{{ required "請用 --set mysql.password=... 傳入 MySQL 密碼" .Values.mysql.password }}@{{ .Release.Name }}-mysql-service:3306/{{ .Values.mysql.database }}"

Day 11 是手動寫整串 DATABASE_URL,這裡改成由 Chart 自己組出來:帳號、密碼、資料庫名稱從 mysql 的設定拿,主機名稱用 {{ .Release.Name }}-mysql-service。這樣不管 Release 叫什麼名字,API 都會連到同一個 Release 裡的 MySQL,也不用重複輸入密碼。

密碼會直接放進連線字串,建議只用英文和數字,避免 @、:、/ 這類符號讓連線字串解析錯誤。

Step8: 建立 Ingress 的模板

建立 templates/ingress.yaml:

{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: {{ .Release.Name }}-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
    {{- if .Values.ingress.host }}
    - host: {{ .Values.ingress.host | quote }}
      http:
    {{- else }}
    - http:
    {{- end }}
        paths:
          - path: /api(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: {{ .Release.Name }}-api-service
                port:
                  number: 80
          - path: /()(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: {{ .Release.Name }}-frontend-service
                port:
                  number: 80
{{- end }}
  • 最外層的 if:ingress.enabled 設成 false 時,整個 Ingress 不會建立
  • 裡面的 if:ingress.host 有值時才加上 host,沒有值就用 IP 存取。明天部署多個環境時,就是靠不同的 host 區分

前端的 path 寫成 /()(.*) 而不是 /,是因為 rewrite-target: /$2 會套用到同一個 Ingress 的所有路徑。前端的規則也要有第二個捕捉群組,$2 才會是完整的原始路徑,否則前端的 /assets/... 這類請求都會被改寫成 /。

到這裡 templates/ 裡應該有 11 個檔案,可以對照最上面的目錄結構確認。

Step9: 檢查與預覽

  1. 回到 Chart 資料夾的上一層(helm/):
cd ..

接下來的 helm 指令都在這一層執行。如果在 todo-app/ 裡面執行 helm lint ./todo-app,Helm 會去找 todo-app/todo-app/Chart.yaml,就會出現找不到檔案的錯誤。

  1. 用 helm lint 檢查模板有沒有語法錯誤:
helm lint ./todo-app --set mysql.password=test --set mysql.rootPassword=test

看到 1 chart(s) linted, 0 chart(s) failed 就代表沒問題

  1. 用 helm template 預覽渲染後的 YAML,不會實際安裝:
helm template my-todo ./todo-app --set mysql.password=test --set mysql.rootPassword=test

確認輸出裡有這 11 個資源:兩個 Secret、一個 ConfigMap、一個 StatefulSet、兩個 Deployment、三個 Service、一個 HorizontalPodAutoscaler 和 Ingress。也可以找一下 DATABASE_URL,確認主機名稱是 my-todo-mysql-service;再找一下 API_HOST,確認值是 my-todo-api-service。API 的 Deployment 裡不會有 replicas,因為副本數交給 HPA 管理。

Step10: 安裝 Chart

如果同一個 Namespace 裡還留著之前用 kubectl apply 手動部署的 todo-api、todo-frontend、mysql、Ingress 和 HPA,新舊兩個 Ingress 會搶同一組路徑,瀏覽器不一定會連到 Helm 裝的那組。建議安裝前先把舊的部署刪掉,或安裝時加上 --set ingress.enabled=false,確認 Helm 裝的服務都正常後再開啟。

  1. 安裝,兩個密碼都用 --set 傳入:
helm install my-todo ./todo-app --set mysql.password=todopass123 --set mysql.rootPassword=rootpass123
  1. 確認安裝狀態:
helm list
kubectl get pods

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

MySQL 第一次啟動需要初始化,大約要 30 秒到 1 分鐘。這段時間 API 連不到資料庫,可能會重啟幾次(RESTARTS 大於 0),等 MySQL 變成 1/1 Running 後 API 就會恢復正常。

  1. 確認 MySQL 的 PVC 有建立:
kubectl get pvc

https://ithelp.ithome.com.tw/upload/images/20260924/20183863wyUQ0x0Sh6.png

會看到一個叫 mysql-data-my-todo-mysql-0 的 PVC,狀態是 Bound。

  1. 確認 HPA 有建立:
kubectl get hpa

會看到 my-todo-api-hpa,MINPODS 是 2、MAXPODS 是 5。TARGETS 剛建立時可能是 <unknown>,等一兩分鐘 metrics-server 收集到數據後就會顯示實際使用率。如果一直是 <unknown>,確認一下 metrics-server 還在不在:

kubectl get pods -n kube-system | grep metrics-server

PowerShell 把 grep 換成 Select-String。

  1. 進 API 的 Pod 確認環境變數有正確注入。用 deploy/<Deployment 名稱> 就不用先查 Pod 名稱,kubectl 會自動挑一個該 Deployment 的 Pod:
kubectl exec deploy/my-todo-api -- env | grep -E "APP_ENV|LOG_LEVEL|DATABASE_URL"

PowerShell 沒有 grep,改用:

kubectl exec deploy/my-todo-api -- env | Select-String "APP_ENV|LOG_LEVEL|DATABASE_URL"

https://ithelp.ithome.com.tw/upload/images/20260924/201838633evN08jo2e.png

DATABASE_URL 應該會是 mysql+pymysql://todo:todopass123@my-todo-mysql-service:3306/tododb。

順便確認前端拿到的後端位址:

kubectl exec deploy/my-todo-frontend -- env | grep API

應該會看到 API_HOST=my-todo-api-service 和 API_PORT=80。

  1. 也可以直接進 MySQL 確認資料庫有建立:
kubectl exec -it my-todo-mysql-0 -- mysql -u todo -ptodopass123 -e "SHOW DATABASES;"

輸出裡有 tododb 就代表成功了。

Step11: 升級 Chart

  1. 假設要更新 todo-api 的 image tag。這裡沒有真的改程式,先把 v1.0.0 多標一個 v2.0.0 推上去,模擬新版本:
docker tag yourname/todo-app-api:v1.0.0 yourname/todo-app-api:v2.0.0
docker push yourname/todo-app-api:v2.0.0

yourname/todo-app-api 要換成你在 values.yaml 裡設定的 repository。如果本機已經沒有 v1.0.0 的 image,先用 docker pull 拉下來再 tag。

接著升級:

helm upgrade my-todo ./todo-app --reuse-values --set api.image.tag=v2.0.0

helm upgrade 預設不會沿用上次用 --set 傳入的值。如果沒加 --reuse-values,這次升級就沒有密碼,會被 required 擋下來。加上 --reuse-values 就會保留上次的設定,只改這次指定的部分。

  1. 查看 Release 歷史:
helm history my-todo

https://ithelp.ithome.com.tw/upload/images/20260924/20183863owknnm24Ev.png

每次 install 或 upgrade 都會留下一筆 REVISION,之後可以用來回滾。

Step12: 清除練習環境

  1. 明天會用同一個 Chart 重新部署 dev 和 staging 兩個環境,所以先把今天的 Release 刪掉,讓明天從乾淨的環境開始:
helm uninstall my-todo

只會刪除 Helm 裝的資源(包含 HPA),不會動到之前用 kubectl apply 手動部署的東西,也不會刪掉本機的 Chart 檔案。

  1. MySQL 的 PVC 是 StatefulSet 自動建立的,helm uninstall 不會刪掉它,要另外手動刪除:
kubectl delete pvc mysql-data-my-todo-mysql-0
  1. 最後確認都清乾淨了:
helm list -A
kubectl get pvc

helm list -A 裡沒有 my-todo,kubectl get pvc 裡也沒有 mysql-data-my-todo-mysql-0,就完成了。

如果安裝時有用 -n 指定 Namespace,上面的 helm uninstall 和 kubectl delete pvc 也要加上同樣的 -n,例如 -n dev。

今天操作過程中如果裝錯想重來,也可以用這一步清除,再從 Step9 重新開始。


延伸筆記:資料庫一定要包進 Chart 嗎?

今天把 MySQL 跟應用程式包在同一個 Chart 裡,好處是一個 helm install 就能把整套服務開起來,開發和測試環境很方便。

但正式環境通常不會這樣做,資料庫會另外管理:

  • 雲端託管資料庫:例如 AWS RDS、GCP Cloud SQL,Chart 裡只放 API 和前端,連線字串指到外部資料庫
  • 獨立的 Chart:資料庫用自己的 Chart 部署,不跟應用程式綁在一起

原因是資料庫和應用程式的生命週期不一樣。API 可能一天升級好幾次,資料庫卻很少動,也不希望跟著應用程式一起被刪掉或重裝。


小結

今天把 Todo App 包成了 Helm Chart:

  • Chart 結構:Chart.yaml、values.yaml、templates/
  • 模板語法:{{ .Release.Name }}、{{ .Values.xxx }}、| quote
  • 條件渲染:{{- if .Values.xxx }} 讓 Ingress、HPA、host 這類設定可選
  • 服務之間互相引用:前端的 API_HOST、API 的 DATABASE_URL 都用 {{ .Release.Name }} 組出來,換 Release 名稱也連得上
  • MySQL 一起部署:StatefulSet + Headless Service + PVC,DATABASE_URL 由 Chart 自動組出來
  • HPA 一起部署:開啟 autoscaling 時,Deployment 不寫 replicas,副本數交給 HPA
  • 必填參數:required 確保密碼一定要在安裝時傳入,不寫進 values.yaml
  • helm lint / template / install / upgrade / uninstall:完整的開發流程,升級時記得加 --reuse-values

明天把這個 Chart 部署到 dev 和 staging 兩個 Namespace,用不同的 values 管理不同環境的設定。


上一篇
Day 22|Helm (1)
下一篇
Day 24|Helm 多環境部署實戰
系列文
從零學 K8s|30 天核心概念 × 實作,新手也能真正掌握 Kubernetes 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言