單元 8 · 實作與最佳實踐

完整範例、常見錯誤、工具鏈、風格指南

8.1 完整範例 · Docker Compose

綜合運用單元 2–7:縮排、序列、環境變數、錨點合併與多行字串。以下是一次完整的實作,右欄逐段拆解:

compose.yaml / FULL EXAMPLE

x-app: &app
  restart: unless-stopped
  networks: [appnet]

services:
  api:
    <<: *app
    build: .
    ports:
      - "8080:8080"
    environment:
      DB_URL: "postgres://db:5432/app"
      LOG_LEVEL: info
  db:
    <<: *app
    image: postgres:16
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s

volumes:
  db-data:

networks:
  appnet:

解說 · EXPLAINED

x-app:用 x- 前綴定義「不是服務」的延伸節點,當錨點用。

<<: *app:合併錨點,兩個服務共用 restartnetworks

序列portsnetworksvolumes 都是序列。

環境變數DB_URL 加引號(內含冒號),LOG_LEVEL plain 即可。

多行健康檢查:用 flow style 一行寫完 test

volumes 空節點db-data: 留空 = 空對映。

8.2 常見錯誤 · Common Mistakes

錯誤後果修正
用 Tab 縮排解析錯誤一律用空格
同層縮排不一致結構錯亂或報錯對齊同層級
冒號後沒空格被當成一個字串key: value 加空格
值開頭是 # / -被當註解或序列加引號
yes / on 當字串變成布林加引號,或寫 true
「序列裡對映」未對齊欄位落到錯誤層級- name 與後續鍵對齊
多行區塊縮排不足解析失敗內容縮排比鍵多
重複鍵最後一個贏,藏 bug用 yamllint / 解析器偵測

8.3 安全性 · Security

讀 YAML 時,不要用「不安全」的 load。這非常重要:

Python · 對比
# ❌ 危險:可執行任意程式碼(object constructor)
yaml.load(data)

# ✅ 安全:只載入純資料
yaml.safe_load(data)

# 其他語言的對應作法:
# Go    → gopkg.in/yaml.v3 預設就安全
# Ruby  → YAML.safe_load
# Node  → js-yaml safeLoad / 新版的 load
風險:不安全的 load 可能被「billion laughs」類型的錨點展開攻擊——用錨點產生天文數字層級的巢狀結構,造成記憶體爆掉(DoS)。解析不可信的 YAML 時:用 safe_load、並限制大小

8.4 工具鏈 · Tooling

工具用途
yamllintCLI 檢查縮排、行長、重複鍵——接 CI 最佳
prettier自動排版、統一縮排
yq命令列讀取/修改 YAML(類似 jq)
VS Code YAML 擴充即時辨色、結構錯誤提示、schema 驗證(K8s / Compose 有官方 schema)
onlineyamltools線上轉 JSON、格式化
yaml-language-server編輯器共用的語言伺服器(LSP)
最值得投資:VS Code 的 YAML 擴充搭配 schema 驗證——寫 K8s 或 Compose 時,打錯欄位會即時標紅,等於把「型別與結構檢查」搬進編輯器。

8.5 風格指南 · Style Guide

8.6 Worked Example:交付前檢查清單

檢查清單 · CHECKLIST
- [ ] 縮排全部用空格(兩個空格)
- [ ] 所有冒號後都有空格
- [ ] 布林寫 true / false
- [ ] 電話/代碼等已加引號
- [ ] 「序列裡的對映」有對齊
- [ ] 多行區塊縮排正確
- [ ] 沒有重複鍵
- [ ] 跑過 yamllint 無 error
- [ ] 用解析器讀回並印出驗證
驗收 · ACCEPT

✅ 全部勾選 → 可以交付
❌ 任一未勾 → 修正後再提交

下一步:找一個你手上的 repo,翻開它的 .yml / .yaml,用這 8 個單元的眼光讀一遍;再試著用 yamllint 檢查並修正它。
看完這單元你應該能說出:
  • 從零寫出一份完整的 Docker Compose 設定檔。
  • 避開至少六個常見 YAML 錯誤與其修正。
  • 說明為什麼要用 safe_load、錨點展開攻擊的風險。
  • 用 yamllint / yq / 編輯器 schema 檢查設定品質。

延伸閱讀


進階真實情境 Worked Example:完整的 Production Docker Compose

以下是一份整合了單元 1–8 所有概念的生產級 compose.yaml,包含錨點、多行字串、型別安全、flow/block 混用與安全性考量:

原始碼 · compose.yaml
# Production Compose — 綜合運用
x-logging: &log
  logging:
    driver: json-file
    options:
      max-size: "10m"
      max-file: "3"

x-healthcheck: &hc
  healthcheck:
    interval: "30s
      |
      timeout: 5s
      |
      retries: 3

services:
  api:
    <<: *log
    build: .
    ports: ["8080:8080"]
    environment:
      NODE_ENV: production
      DB_HOST: "db"
    depends_on:
      db: {condition: service_healthy}

  db:
    <<: *log
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: "${DB_PASS}"  # 從 .env 讀入

volumes:
  pgdata:
拆解 · ANATOMY

x-logging + <<:*log:錨點共用 logging 設定
ports: ["8080:8080"]:flow 序列,一行搞定
"${DB_PASS}":加引號防止空值問題
depends_on: {db: {condition:...}}:flow 對映嵌套
pgdata::留空 = 空對映(volumes 宣告)

關鍵設計決策:敏感值 DB_PASS 不寫死在 YAML 中,而是從 .env 檔注入("${DB_PASS}"),這符合安全性最佳實踐。ports 加引號是因為 "8080:8080" 內含冒號,不加引號會被 YAML 誤判。

深入原理擴充

Production YAML 的隱性成本

大型 YAML 設定檔(如 Helm chart values.yaml、K8s operator CRD)的隱性成本包括:

大家以為是這樣但其實不是……

誤解:「yamllint 通過就代表 YAML 沒問題」——yamllint 檢查的是語法與風格(縮排、行長、重複鍵),但不檢查語意正確性(值的型別是否符合預期、鍵名是否正確、是否缺欄位)。要檢查語意需要搭配 JSON Schema 或工具(如 kubeval、checkov)。

診斷式疑難排解表

症狀可能原因解決方案
Docker Compose 讀不到 .env 變數變數未在 .env 檔中定義,或 docker compose 版本不同docker compose config 預覽展開結果
Git merge 衝突不斷YAML 空白敏感,自動格式化後差異大統一格式(prettier)+ 限制 PR 大小
K8s kubectl apply 報隱性欄位錯誤欄位名打錯但 YAML 語法合法用 kubeval / kubeconform + schema 驗證
Ansible playbook 執行時變數為空YAML null vs 空字串 "" 行為不同明確區分 null(無值)與 ""(空字串)
CI 中 yamllint 與本地結果不同yamllint 版本或 config 不同鎖定 yamllint 版本 + commit .yamllint 配置

進階挑戰題

  1. Production 設定檔審計:找一個公開的 GitHub repo(例如 Hashicorp、Prometheus),找到它的 Docker Compose 或 K8s manifest,用本單元的檢查清單逐一審計:縮排、型別安全、錨點使用、敏感值處理。列出至少 3 個你會改的地方。
  2. 全棧 YAML 驗證流程設計:設計一套 CI pipeline:(a) yamllint 檢查風格、(b) JSON Schema 檢查語意、(c) kustomize build 驗證 K8s manifest、(d) docker compose config 驗證 Compose 檔。畫出流程圖並說明每一步的目的。
  3. Migration 計劃:你手上有 50 個 YAML 設定檔,需要從 YAML 1.1 遷移到 1.2。制定一個安全的遷移計劃:哪些值需要改?怎麼自動化偵測?怎麼驗證遷移後的結果?

① 專案級端到端 Worked Example:Monorepo 設定檔治理專案

最後一單元,把全部工具串成一套設定檔治理系統:一個 monorepo 內的所有 YAML,靠 pre-commit + CI + schema 三層自動把關。這是「最佳實踐」從個人習慣升級為團隊工程的完整示範:

產出檔案樹

platform-config/
├── .pre-commit-config.yaml   # pre-commit 掛鉤(本地門檻)
├── .yamllint                 # yamllint 設定
├── .editorconfig             # 縮排/換行統一
├── configs/
│   ├── app.yaml              # 應用設定
│   ├── compose.yml           # Docker Compose
│   └── k8s/
│       ├── deployment.yaml   # K8s manifest
│       └── service.yaml
├── schemas/
│   └── app.schema.json       # 型別契約
└── Makefile                  # 統一指令入口

關鍵檔案內容

.pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
  - repo: https://github.com/adrienverge/yamllint.git
    rev: v1.35.1
    hooks:
      - id: yamllint
        args: [--strict]
  - repo: https://github.com/koalaman/shellcheck-precommit
    rev: v0.10.0
    hooks:
      - id: shellcheck
.yamllint
extends: default
rules:
  indentation: {spaces: 2, indent-sequences: true}
  line-length: {max: 100}
  comments: {min-spaces-from-content: 2}
  key-duplicates: enable
  truthy: {allowed-values: [true, false]}
驗證命令與輸出
$ pre-commit run --all-files
trailing-whitespace................Passed
end-of-file-fixer...................Passed
yamllint............................Passed
shellcheck..........................Passed

$ make lint
yamllint -c .yamllint .
(無輸出 = 通過)

$ make validate
check-jsonschema --schemafile schemas/app.schema.json configs/app.yaml
PASS: configs/app.yaml
治理層級拆解

本地:pre-commit 在 commit 前擋下壞格式
CImake lint + make validate 在 PR 強制重跑
Schema:型別與結構契約(app.schema.json)
統一入口:Makefile 讓所有檢查一條指令
一致性:.editorconfig 統一縮排/換行
版本鎖定:.yamllint + hook rev 都進版控

關鍵洞察:最佳實踐的價值在可重現性——本地與 CI 跑同一套檢查、同一版本、同一規則,才能保證「壞 YAML 進不了 main」。這比任何個人記憶可靠。

② 效能/品質/安全深度

品質:三層品質模型的落地

層級工具抓什麼成本
風格層yamllint / prettier縮排、行長、重複鍵毫秒級
結構層JSON Schema / kubeconform欄位、型別、必填毫秒–秒級
行為層解析 + assert / 乾跑「跑起來對不對」秒–分級

分配原則:越便宜的層級越要頻繁跑(pre-commit 每 commit),越貴的層級放 CI 的關鍵 PR 才跑。

安全:YAML 的機密洩漏面

安全提醒:YAML 設定檔是 secret 洩漏的常見現場——POSTGRES_PASSWORD、API key、token 常被順手寫進 compose.yml。工具防線:gitleaks / trufflehog 掃描 repo 歷史、pre-commit 加 secret 掃描 hook、git-secrets 擋 commit。原則:YAML 永遠不放 secret,一律走環境變數 / secret 管理工具。

效能:pre-commit 與 CI 的取捨

pre-commit 有快取(repo 與環境快取),重複執行很快;CI 每次從零建環境。最佳化:① hook 只跑變更檔案(pre-commit 預設);② CI 用 paths 過濾;③ schema 驗證用 Python 快 (check-jsonschema) 而非起 container;④ 把 lint 放最便宜的 runner。

③ 站際比較對照表

面向yamlmarkdownjsongithubgitlab
風格檢查yamllintmarkdownlint / prettierprettier / eslint 設定actionlint(workflow)gitlab-ci-lint
結構驗證JSON Schema / kubeconformremark / 自訂規則JSON Schema 原生無(平台語法檢查)pipeline 編譯即驗證
CI 整合pre-commit + Makefile + CIdocumentation lint 工作JSON Schema CI 工作workflow 內建pipeline 內建
secret 防護gitleaks / git-secrets無(純文件)同左secret scanning(內建)secret detection(內建)
自動排版prettier / yamlfmtprettierprettier無(手動)無(手動)

④ 互動式檢核清單