單元 4 · 序列與巢狀

序列(陣列)、巢狀、複雜結構、錨點預告

4.1 定義:兩種容器

YAML 的資料只有兩種容器:對映 mapping(鍵值組,即 dict / object)與 序列 sequence(有序清單,即 list / array)。其他一切都是「值」。掌握這兩種容器的組合,就掌握 YAML 的 90%。

一句話:YAML 只有「有名字的一組值」(對映)和「照順序的一串值」(序列),巢狀就是它們互相當對方的值。

4.2 序列 · Sequences

- (橫槓 + 空格)表示清單項,同一層的 - 對齊。

原始碼 · YAML SOURCE
fruits:
  - apple
  - banana
  - cherry
渲染結果 · EQUIVALENT JSON
{"fruits": ["apple",
             "banana",
             "cherry"]}

4.3 巢狀:對映裡的序列

對映的值可以是另一個序列。

原始碼 · YAML SOURCE
server:
  ports:
    - 80
    - 443
  tls: true

4.4 巢狀:序列裡的對映(最易錯)

清單的每個元素可以是一組鍵值——這是設定檔最常見的形狀(服務清單、容器清單)。關鍵在對齊:- 與第一個鍵同層,後續鍵值與第一個鍵同縮排。

原始碼 · YAML SOURCE
services:
  - name: web
    port: 8080
  - name: db
    port: 5432
渲染結果 · EQUIVALENT JSON
{"services": [
  {"name": "web", "port": 8080},
  {"name": "db",  "port": 5432}]}
最常見錯誤:port 縮排到 - 底下(和 name 不同層),結果變成「對映裡包一個序列」,結構整個錯位。Kubernetes 的 containers 正是這個形狀——單元 7 會再驗證一次。

4.5 巢狀:多層混用

三種結構可以任意組合到任意深度——這是 YAML 描述複雜系統的方式。

原始碼 · YAML SOURCE
pipeline:
  - name: build
    steps:
      - checkout
      - test
    env:
      DEBUG: false
渲染結果 · EQUIVALENT JSON
{"pipeline": [{
    "name":  "build",
    "steps": ["checkout","test"],
    "env":   {"DEBUG": false}}]}

4.6 Flow style · 精簡式

想要「一行寫完」時,用 JSON 風格:序列 [ ]、對映 { }

原始碼 · YAML SOURCE
aliases: [prod, production]
limits: {cpu: "1.5", mem: "512Mi"}
渲染結果 · EQUIVALENT JSON
{"aliases": ["prod","production"],
 "limits":  {"cpu": "1.5",
              "mem": "512Mi"}}
建議:block style(縮排式)最常見也最好讀;flow style 適合簡短清單或要節省行數。混用兩者可,但在同一結構內保持一致。

4.7 Worked Example:一次讀懂 CI 片段

原始碼 · YAML SOURCE
jobs:
  - name: lint
    commands:
      - npm run lint
  - name: deploy
    when: main
    env: {URL: https://example.com}
練習 · PRACTICE

jobs 是序列
② 每個 job 是對映(序列裡的對映)
commands 是巢狀序列
env 用 flow 對映
👉 試著把它改寫成「純 block style」,再驗證兩者解析結果相同

練習:設計一個「學生清單」設定:每個學生有姓名、年級與選課序列(至少三個),用序列裡的對映寫出,再用 JSON 對照驗證。

看完這單元你應該能說出:
  • 寫出序列(- item)與對映(key: value)兩種容器。
  • 正確縮排「序列裡的對映」——最常見也最易錯的形狀。
  • 用 flow style([ ] / { })一行寫完精簡結構。
  • 理解對映與序列可任意組合到任意深度。

延伸閱讀


進階真實情境 Worked Example:Terraform + Helm 的 values 混合結構

DevOps 中常見「序列裡有深層巢狀對映、對映裡又包序列」的結構。以下是一份含多層混用的真實場景:

原始碼 · terraform.tfvars.yaml
clusters:
  - name: prod-us
    node_pools:
      - name: general
        autoscaling:
          min: 3
          max: 10
        labels: [web, api]
      - name: gpu
        taints:
          - key: nvidia.com/gpu
            value: "true"
            effect: NoSchedule
  - name: staging-eu
    node_pools:
      - name: general
        autoscaling: {min: 1, max: 5}
渲染結構 · TREE VIEW
clusters (sequence)
 ├─ {name: "prod-us", node_pools:
 │    ├─ {name: "general",
 │    │    autoscaling: {min:3, max:10},
 │    │    labels: ["web","api"]}
 │    └─ {name: "gpu",
 │         taints: [{key:"nvidia.com/gpu",
                    value:"true",
                    effect:"NoSchedule"}]}}
 └─ {name: "staging-eu", node_pools:
      └─ {name: "general",
           autoscaling: {min:1, max:5}}}

為什麼這樣設計而非替代方案:labels 用 flow 序列 [web, api] 是因為它短而扁平;taints 用 block 序列是因為每個元素有三個鍵值對,block 更清晰。autoscaling 在 staging 用 flow 是因為只有兩欄——同一份設定檔中根據內容複雜度選擇最適合的風格是 YAML 的最佳實踐。

深入原理擴充

Block vs Flow 的隱含規則

YAML 規範中,flow style 的對映中,冒號後的空格是可選的(例如 {a:1} 合法),但block style 中冒號後一定要有空格a: 1)。這就是為什麼很多從 flow 風格複製到 block 風格的值會突然報錯——漏加了空格。

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

誤解:「序列裡的對映,所有鍵必須對齊 - 的位置」——其實 - 後面的第一個鍵- 同列(或下一行同縮排),但後續的鍵只需要與第一個鍵同縮排,不需要與 - 對齊。對齊 - 只是風格建議,不是語法要求。

診斷式疑難排解表

症狀可能原因解決方案
sequence entries are not allowed here序列與對映混用時縮排錯位- key- 與上層容器對齊
expected ',' or '}'flow 對映中漏了逗號或鍵值間缺少分隔檢查 {a: 1, b: 2} 格式
解析結果層級與預期不同多層巢狀時某處少了一級縮排yq '.' 印出完整結構比對
mapping keys must be unique同層出現重複鍵搜尋並合併或移除重複鍵
序列元素「跳」到上層- 的縮排比上層少確保 - 縮排 ≥ 父容器的縮排 + 1

進階挑戰題

  1. 三層巢狀挑戰:設計一個結構:序列 → 對映 → 序列 → 對映(共四層),用 block style 寫出。再用 flow style 重寫最內兩層,比較可讀性。
  2. Style 選擇器:同一份資料有 block 與 flow 兩種寫法——寫出 Kubernetes containers spec(含 name、image、ports、env),然後把 env 改成 flow style。哪種更清晰?為什麼?
  3. 錯位偵測遊戲:故意寫錯一個「序列裡的對映」的縮排(讓某個鍵落到錯誤層級),用 yaml.safe_load 解析並觀察結果如何變化。畫出正確 vs 錯誤的結構樹。

① 專案級端到端 Worked Example:Kubernetes 完整部署專案

K8s 是「序列與巢狀」的極致考場——containers(序列裡的對映)、env(對映裡包序列)、strategy(多層巢狀)。配合 Kustomize overlay 做多環境管理:

產出檔案樹

k8s-app/
├── base/
│   ├── namespace.yaml      # 命名空間
│   ├── deployment.yaml     # 深層巢狀主檔
│   ├── service.yaml        # 服務暴露
│   └── kustomization.yaml  # 組合 base 資源
└── overlays/
    ├── dev/
    │   └── kustomization.yaml
    └── prod/
        └── kustomization.yaml   # 覆寫 replicas / 資源

關鍵檔案內容

base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 1
  selector:
    matchLabels: {app: api}
  template:
    metadata:
      labels: {app: api}
    spec:
      containers:
        - name: api
          image: myapi:1.0
          ports:
            - containerPort: 8080
          env:
            - name: LOG_LEVEL
              value: info
          resources:
            limits: {cpu: "100m", memory: "128Mi"}
overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
patches:
  - target: {kind: Deployment, name: api}
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 5
驗證命令與輸出
$ kustomize build overlays/prod | kubectl apply --dry-run=client -f - -o name
deployment.apps/api
namespace/api
service/api

$ kustomize build overlays/prod | kubectl apply --server-side --dry-run=server -f - && echo VALID
deployment.apps/api configured (server dry run)
VALID

$ kubeconform -strict -summary <(kustomize build overlays/prod)
Result: PASS
巢狀結構拆解

containers = 序列,每個元素是對映(name/image/ports/env)
ports = 序列裡的對映,containerPort- 對齊
env = 序列裡的對映,再往下一層
④ 最內層 resources 用 flow,讓深度控制在 5 層內
⑤ Kustomize patch 用 |- 寫 YAML-in-YAML(JSON Patch)

端到端流程:base/ 放標準資源 → overlays/prod 覆寫 replicas: 5kustomize build 產生最終 manifest → kubectl apply --dry-run=server 在 API server 驗證。整個管線的每一步都在處理「序列與巢狀」的正確性。

② 效能/品質/安全深度

品質:深度巢狀的可讀性危機

K8s Deployment 的 spec.template.spec.containers[].env[].valueFrom 可達 6–7 層縮排,這是 YAML 可讀性的極限。品質策略:

效能:深層巢狀的解析深度

解析器對巢狀深度有實作上限(PyYAML 預設 ~1000 層,超過拋 maximum recursion depth exceeded)。實務上沒人會寫到 100 層,但「程式產生的 YAML」可能踩到——生成器要設定合理上限並在輸出前驗證。

安全:巢狀與權限邊界

安全提醒:K8s manifest 的巢狀結構很容易讓「看起來無害的欄位」隱藏權限:securityContextserviceAccountNamehostNetwork 都在深層。用 kube-linter / checkov 掃 manifest,別只靠肉眼讀深度巢狀。Pod 安全標準(PSS)是官方的強制分層。

③ 站際比較對照表

面向yamlmarkdownjsongithubgitlab
序列(清單)- item 縮排式- item / 1. item["a","b"]task checklist(- [ ])job list 以 YAML 序列表達
巢狀深度縮排決定,最高可上百層標題層級(#/##/###)括號遞迴,理論無上限目錄樹(檔案系統)group / subgroup 階層
最易錯形狀「序列裡的對映」對齊巢狀列表縮排逗號遺漏檔案路徑結構pipeline stage 依賴
驗證方式yamllint / schema / dry-runMarkdown lint / 渲染預覽JSON Schema / linterworkflow 語法檢查pipeline lint / dry-run

④ 互動式檢核清單