在工程設計中,我們常在「抽象」與「具體」之間徘徊:
module "network" {
source = "git::ssh://git@repo.git//modules/network"
# 什麼都沒有,使用者完全猜
}
對新進團隊而言,這種模組就像魔法箱,無法維護。
# CI/CD pipeline example
deploy:
script:
- kubectl apply -f dev-deployment.yaml
- kubectl apply -f dev-service.yaml
每次切換環境都要改 YAML,缺乏彈性。
核心概念:抽象是壓縮複雜性,具體是落地的橋樑。太極端都不好。
# 過度抽象
module "db" {
source = "./modules/db"
user = var.db_user
pw = var.db_pw
host = var.db_host
port = var.db_port
# 還有十幾個參數...
}
# 過度具體
resource "aws_db_instance" "prod" {
engine = "mysql"
instance_class = "db.t3.medium"
name = "prod_db"
}
平衡點:模組提供必要參數,內部隱藏可復用邏輯,不暴露全部細節。
我幫你把這段文字與 YAML 範例重新整理與優化,使敘述更流暢、結構更清楚,同時強調「抽象化的原則與落地實務」:
以下是一個 過度具體 的 values.yaml 範例,展示三個微服務(auth、user、payment)完全寫死 replicas、資源、環境與 Ingress 配置的情況:
replicaCount:
auth: 3
user: 2
payment: 4
auth:
image:
repository: "mycompany/auth"
tag: "v1.2.3"
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "250m"
memory: "256Mi"
env:
DB_HOST: "postgres-auth"
DB_PORT: "5432"
service:
type: ClusterIP
port: 8080
ingress:
host: "auth.example.com"
user:
image:
repository: "mycompany/user"
tag: "v3.1.0"
resources:
limits:
cpu: "300m"
memory: "256Mi"
requests:
cpu: "150m"
memory: "128Mi"
env:
DB_HOST: "postgres-user"
DB_PORT: "5432"
service:
type: ClusterIP
port: 8081
ingress:
host: "user.example.com"
payment:
image:
repository: "mycompany/payment"
tag: "v2.5.1"
resources:
limits:
cpu: "700m"
memory: "1024Mi"
requests:
cpu: "350m"
memory: "512Mi"
env:
DB_HOST: "postgres-payment"
DB_PORT: "5432"
service:
type: ClusterIP
port: 8082
ingress:
host: "payment.example.com"
對應的 template 也過度依賴 values.yaml:
{{- range $name, $svc := .Values }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
spec:
replicas: {{ $svc.replicaCount }}
template:
spec:
containers:
- name: {{ $name }}
image: "{{ $svc.image.repository }}:{{ $svc.image.tag }}"
resources:
limits:
cpu: {{ $svc.resources.limits.cpu }}
memory: {{ $svc.resources.limits.memory }}
requests:
cpu: {{ $svc.resources.requests.cpu }}
memory: {{ $svc.resources.requests.memory }}
env:
- name: DB_HOST
value: "{{ $svc.env.DB_HOST }}"
- name: DB_PORT
value: "{{ $svc.env.DB_PORT }}"
---
{{- end }}
⚠️ 問題點:
每個服務都完全寫死配置 → 失去共通抽象。
新增服務或切換環境需要複製整份 values.yaml → 高維護成本。
template 幾乎沒有抽象,修改 replicas、image tag 或 ingress host 必須改多個地方。
若要多 Chart 部署,這種過度具體設定會重複出現 → 冗餘、低復用。
Helm Chart 的目的就是 減少管理負擔,因此設計上可以:
將共通管理的屬性集中,例如:
{service}-postgres
自動生成{service}.example.com
自動生成留下必要可變參數,減少管理項目:
changeDBPort: { auth: 5421 }
auth:
replica: 3
image: mycompany/auth-v1.2.3
limit-cpu: 500m
limit-mem: 512Mi
request-cpu: 250m
request-mem: 256Mi
servicePort: 8080
user:
...
payment:
...
保留最重要的可變參數,簡化 values.yaml 結構
共通邏輯隱去或自動生成 → 減少維護成本
易於團隊協作,避免「黑箱化」的抽象,也避免完全寫死的過度具體
今天已進入系列文章的第四天,延續上一章節對抽象價值的探討,我們進一步呈現了 抽象與具體之間的平衡取捨。透過對比案例,更清楚地理解到:抽象不只是隱藏細節,更是一種 高層級的後設視角,讓我們能從全局審視設計。
當然,錯誤的抽象也可能讓觀點模糊、不易落地。這將是後續實作章節中,我們需要關注的重點——如何運用抽象與具體的思維方式,真正 幫助我們在工作中做出可落地、可維護的設計。
很高興在第一部分中,帶領各位重新認識工程中的抽象概念,並循序漸進地展現其在 DevOps 工程中的實務價值。接下來,我們將進入 實戰,希望在分享操作與實作技巧的同時,也能得到各位的回饋與指教。