在工程世界裡,抽象不是哲學上的空談,而是日常中無處不在的工具。它就像一層「隱形的結構」,幫助我們面對複雜的系統時,仍能保持清晰的操作與判斷。今天,我想透過幾個實際案例,來說明抽象如何在工程實務裡發揮價值。
Helm Chart 提供了 values.yaml 作為「使用者介面」。對開發者而言,它隱藏了繁雜的 Kubernetes manifest 細節,讓我們只需要修改少量的參數,就能完成部署。
沒有抽象時,你可能需要在 Deployment 裡修改許多地方,例如調整 image、資源限制或環境變數,這樣的操作不僅容易出錯,也難以維護:
apiVersion: apps/v1
kind: Deployment
metadata:
name: kafka
spec:
template:
spec:
containers:
- name: kafka
image: kafka:0.47.0-kafka-3.9.0
env:
- name: ENV
value: "staging"
resources:
limits:
cpu: "4"
memory: 16Gi
requests:
cpu: 250m
memory: 2Gi
抽象後,我們只需調整 values.yaml
裡的幾個簡單參數,整個部署行為就能跟著更新,語意更直接、簡潔:
kafka:
image: kafka:0.47.0-kafka-3.9.0
limit: cpu-4,mem-16G
requests: cpu-250,mem-2G
這種方式讓配置更直覺,降低因人工作業導致錯誤的風險。當我們把多層次的細節收斂為一個清晰的結構時,保留了靈活性,同時維持了一致性,讓複雜度不至於無限制膨脹。
Terraform 的 module 本身就是一種抽象實作,能將複雜的資源設定封裝起來,讓基礎架構能夠重複利用。更進一步的做法,是使用 locals 定義變數,再搭配 YAML 作為外部輸入,讓 Terraform 程式碼與環境配置分離,實現更高的可讀性與維護性。
最初版本是直接撰寫資源,每個細節都必須逐一描述,雖然清楚,但重複度高、修改麻煩:
resource "google_storage_bucket" "raw" {
name = "my-raw-data"
location = "ASIA"
force_destroy = true
versioning {
enabled = true
}
lifecycle_rule {
action {
type = "Delete"
}
condition {
age = 30
}
}
}
當我們將邏輯封裝進 module 之後,呼叫就變得簡單許多,只需傳入必要參數,所有細節都隱藏在 module 內:
module "gcs_bucket" {
source = "./modules/gcs"
name = "my-raw-data"
region = "ASIA"
logs = true
lifecycle_days = 30
}
如果再進一步抽象,讓 Terraform 只解析 YAML 檔案,維護起來就更加清晰,且更適合大型專案:
buckets:
my-raw-data:
region: "ASIA"
lifecycle_days: 30
這樣的分層設計,讓工程師不用頻繁修改 Terraform 程式碼,只需專注在 YAML 檔案中定義需求即可。抽象在這裡扮演的是「隔離細節」的角色,幫助團隊降低心智負擔,並確保設定可以持續擴展。從一開始直接寫資源,到使用 module,再到 YAML 配置,層層抽象讓基礎架構管理變得更加高效,並且降低了錯誤發生的機會。
抽象在很多時候是需要取捨及平衡的。以 GitLab CI 為例,我們在設計 pipeline 時,必須在「明確性」與「通用性」之間找到平衡。
第一種寫法是將所有參數明確列出,像是將服務設定一一標示清楚:
variables:
TF_ROOT: prod/terraform
TF_Backend: "prod.config"
TF_CLI_ARGS_plan: "-var env_yml=prod"
terraform-plan:
stage: build
image:
name: hashicorp/terraform:1.9.1
entrypoint: [""]
script:
- cd $TF_ROOT
- terraform init --backend-config=$TF_Backend
- terraform plan -out plan.tfplan
- terraform show -json plan.tfplan > plan.json
這樣的優點是透明易讀,每個變數都清楚呈現,像是「所見即所得」。但當專案規模擴大、環境增多時,這種明確的寫法會迅速膨脹,設定檔變得冗長且難以維護。
第二種寫法則對通用性參數進行抽象,減少了對於stage所作行為的關心,將管理聚焦在參數的注入:
variables:
ENV: prod
terraform-plan:
stage: build
image:
name: hashicorp/terraform:1.9.1
entrypoint: [""]
script:
- cd $ENV/terraform
- export TF_VAR_env=$ENV
- terraform init --backend-config=$ENV.config
這樣的方式更加簡潔,也能適應多環境專案,因為只需改動一個變數即可。然而,對新進成員來說,這種隱藏邏輯的抽象會降低直覺理解的便利性。從抽象的角度看,它將共通模式提取出來,降低重複操作的負擔。但也可能增加新成員的學習成本。
從這個案例可以看出,抽象的目的會深刻影響設計,而在設計過程中,必須平衡簡潔、效率與可讀性。除了保持配置的簡潔與高效外,也需要考量團隊成員的理解成本以及後期維護成本。工程師應依據專案規模、使用者熟悉度以及維護需求,判斷哪些部分適合抽象,哪些部分應保留明確性,才能同時達到最佳的工程效能與可維護性。
從這個章節可以看到,抽象設計在 DevOps 工程中具有許多值得深思的環節。除了模組化帶來的重用性,以及共通性萃取的便利,抽象的核心目的始終不離「服務使用者」。儘管 DevOps 工程不像程式開發有明確的邏輯堆疊(程式中的抽象更多為模組化與通用性),也不同於前端設計的「所見即所得」,但 DevOps 工程師仍能遵循相同的開發典範:將設定檔視為使用者介面,將參數設計成模組,藉此達成更高效率的管理。
抽象讓工程師專注於「我要什麼」,而非「該怎麼實作」,同時兼顧可讀性與維護性。正如抽象與具體之間的衡量,需要工程師在實務中判斷與設計,這也是我希望在這一系列文章中探討的核心:如何透過抽象提升工程體驗,讓系統既簡潔、高效,又貼近使用者的需求。