DevOps Bootcamp · 课时

使用 yq 编辑 YAML 配置文件

使用 yq 原地读取和修改 Kubernetes 与 CI YAML,同时保留结构和注释。

第 4 / 4 课13 个步骤

使用 yq 编辑 YAML 配置文件 是 CoddyKit 上的免费 DevOps Bootcamp 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 DevOps Bootcamp 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 DevOps Bootcamp 课程共包含 4 节课。

yq 是什么,以及为什么使用它处理 YAML

yq 是一个可移植的命令行 YAML 处理器,类似于 jq 处理 JSON 的方式。您可以使用它读取、筛选和编辑 YAML 文件,而无需使用 Python 或 Ruby 编写脚本。

有两个常用的工具都名为 yq:

  • mikefarah/yq(Go)— 目前仍在积极维护,支持 YAML、JSON、XML 和 TOML。本课程使用此版本。
  • kislyuk/yq(Python)— 用于 YAML 的 jq 包装器;语法有所不同。

安装 Go 版本:

  • 在 macOS 上运行 brew install yq
  • 在 Linux 上运行 snap install yq
  • 或者下载二进制文件:wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq && chmod +x /usr/local/bin/yq

验证安装:yq --version 应输出 v4.x.x。第 4 版使用的表达式语法不同于 v3,因此版本很重要。

# Install yq (Go version) on Linux
wget -q https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 \
  -O /usr/local/bin/yq
chmod +x /usr/local/bin/yq

# Confirm version
yq --version

从 Kubernetes 部署 YAML 中读取值

在编辑任何内容之前,请先学习如何读取 YAML 字段。对于 Kubernetes 部署,您可以使用点号路径提取任意嵌套值。

示例 deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: production
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: app
          image: my-app:1.0.0

主要读取命令:

  • yq '.metadata.name' deployment.yaml — 输出 my-app
  • yq '.spec.replicas' deployment.yaml — 输出 3
  • yq '.spec.template.spec.containers[0].image' deployment.yaml — 输出 my-app:1.0.0

默认情况下,输出为纯文本(不带引号)。如果需要在脚本中使用原始字符串,请添加 -r 标志,或使用 | yq -r。

# Create a sample deployment YAML
cat > /tmp/deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  namespace: production
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: app
          image: my-app:1.0.0
EOF

# Read individual fields
echo "App name:    $(yq '.metadata.name' /tmp/deployment.yaml)"
echo "Replicas:    $(yq '.spec.replicas' /tmp/deployment.yaml)"
echo "Image:       $(yq '.spec.template.spec.containers[0].image' /tmp/deployment.yaml)"

使用 -i 标志进行原地编辑

实际使用中最重要的标志是-i(原地编辑)。不使用该标志时,yq 会将结果输出到标准输出,而不会修改文件。

语法:

  • 仅读取(标准输出):yq '.spec.replicas' file.yaml
  • 原地编辑:yq -i '.spec.replicas = 5' file.yaml

赋值运算符 = 用于设置值。该表达式是一个完整的 yq 过滤器,因此您可以在一次操作中同时完成读取和写入。

重要提示: yq -i 会完整重写文件。与字段位于同一行的注释通常会保留,但独立的注释块可能会移动。在执行批量原地编辑之前,请务必先将 YAML 提交到版本控制系统。

先不使用 -i 进行测试,确认输出符合预期后再添加它。

# Start with the deployment from the previous scene
echo 'Before:' && yq '.spec.replicas' /tmp/deployment.yaml

# Edit in place: scale to 5 replicas
yq -i '.spec.replicas = 5' /tmp/deployment.yaml

echo 'After:' && yq '.spec.replicas' /tmp/deployment.yaml

更新容器镜像标签

一项非常常见的持续集成任务,是在构建新镜像后更新 Kubernetes 清单中的 Docker 镜像标签。使用 yq 可以通过一行命令完成。

基本模式如下:

  • 使用 select() 按名称定位容器,避免将数组索引 0 硬编码。
  • 使用 |=(更新运算符)或 = 设置新值。

使用数组索引(容器列表发生变化时不可靠):

  • yq -i '.spec.template.spec.containers[0].image = "my-app:2.1.0"' deployment.yaml

使用 select()(更可靠):

  • yq -i '(.spec.template.spec.containers[] | select(.name == "app")).image = "my-app:2.1.0"' deployment.yaml

在持续集成流水线中,您可以将标签作为 Shell 变量传入:

NEW_TAG="my-app:2.1.0"
CONTAINER_NAME="app"

# Robust update: target by container name, not index
yq -i \
  "(.spec.template.spec.containers[] | select(.name == \"${CONTAINER_NAME}\")).image = \"${NEW_TAG}\"" \
  /tmp/deployment.yaml

# Verify
yq '.spec.template.spec.containers[0].image' /tmp/deployment.yaml

添加和删除字段

除了更新现有字段之外,yq 还可以添加新键或删除现有键。

添加字段:

  • 只需为不存在的路径赋值:yq -i '.metadata.labels.version = "v2"' file.yaml
  • 如果父键(labels)不存在,yq 会自动创建它。

删除字段:

  • 使用 del() 函数:yq -i 'del(.metadata.annotations)' file.yaml
  • 按索引删除数组元素:yq -i 'del(.spec.template.spec.containers[1])' file.yaml

向数组添加元素:

  • yq -i '.spec.template.spec.containers += [{"name": "sidecar", "image": "envoy:latest"}]' file.yaml
# Add a label to the deployment
yq -i '.metadata.labels.version = "v2"' /tmp/deployment.yaml
yq -i '.metadata.labels.managed-by = "ci-pipeline"' /tmp/deployment.yaml

echo '--- Labels after adding ---'
yq '.metadata.labels' /tmp/deployment.yaml

# Delete one label
yq -i 'del(.metadata.labels.managed-by)' /tmp/deployment.yaml

echo '--- Labels after delete ---'
yq '.metadata.labels' /tmp/deployment.yaml

处理多文档 YAML 文件

Kubernetes 清单通常会将多个资源放在同一个文件中,并使用 --- 分隔。默认情况下,yq 会处理此类文件中的所有文档。

主要技巧:

  • 列出所有文档类型:yq '.[].kind' multi.yaml — 注意开头的 .[],它用于遍历文档。
  • 按类型定位特定文档:yq 'select(.kind == "Service")' multi.yaml
  • 仅原地编辑匹配的文档:

yq -i 'select(.kind == "Deployment").spec.replicas = 2' multi.yaml

不匹配 select() 谓词的文档会原样传递,因此您的 Service、ConfigMap 和其他资源都会保持不变。

要将多文档文件拆分为单独的文件,您可以遍历 yq 的输出,或者使用:

  • yq -s '.kind' multi.yaml — 为每个文档写入一个文件,并使用其 .kind 值命名。
cat > /tmp/multi.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 1
---
apiVersion: v1
kind: Service
metadata:
  name: web-svc
spec:
  port: 80
EOF

# Scale ONLY the Deployment, leave Service untouched
yq -i 'select(.kind == "Deployment").spec.replicas = 4' /tmp/multi.yaml

echo '--- Deployment replicas ---'
yq 'select(.kind == "Deployment").spec.replicas' /tmp/multi.yaml

echo '--- Service port (unchanged) ---'
yq 'select(.kind == "Service").spec.port' /tmp/multi.yaml

修改 GitHub Actions CI YAML

CI 配置文件(.github/workflows/*.yml、.gitlab-ci.yml)同样是 YAML 文件。相同的 yq 命令也适用,不过路径可能会深度嵌套。

常见的 CI 修改任务:

  • 固定运行器版本:更新所有作业中的 runs-on。
  • 更新操作版本:查找使用特定操作的步骤,并更新其 uses 字段。
  • 切换标志:启用或禁用工作流程级设置。

示例:将所有使用 actions/checkout 的步骤更新为 v4:

yq -i '(.jobs[].steps[] | select(.uses == "actions/checkout@v3")).uses = "actions/checkout@v4"' .github/workflows/ci.yml

这一惯用模式——使用 []遍历、使用 select()缩小范围、使用 =赋值——是编辑任何结构化 YAML 的核心模式。

cat > /tmp/ci.yml << 'EOF'
name: CI
on: [push]
jobs:
  build:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      - run: npm test
  lint:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v3
      - run: npm run lint
EOF

# Bump all checkout steps from v3 → v4
yq -i '(.jobs[].steps[] | select(.uses == "actions/checkout@v3")).uses = "actions/checkout@v4"' \
  /tmp/ci.yml

# Verify both jobs were updated
yq '.jobs[].steps[] | select(.uses | test("checkout")).uses' /tmp/ci.yml

在 yq 表达式中使用环境变量

在 yq 表达式中硬编码值会使脚本变得脆弱。yq 支持使用 env() 函数或strenv() 简写形式注入 Shell 变量。

  • env(VAR_NAME) — 读取环境变量,并将其转换为适当的 YAML 类型(数字仍为数字,字符串仍为字符串)。
  • strenv(VAR_NAME) — 始终返回字符串,适合用于镜像标签。

这样可以避免在包含 YAML 路径的双引号 Shell 字符串中插入变量时产生复杂的引号问题。

模式:

export IMAGE_TAG="my-app:3.0.0"
yq -i '.spec.template.spec.containers[0].image = strenv(IMAGE_TAG)' deployment.yaml

设置 replicas 等数值字段时,请使用 env(),这样可以保留 YAML 类型(整数,而不是带引号的字符串)。

export APP_IMAGE="my-app:3.0.0"
export REPLICA_COUNT=6

# Set image using strenv() — result is a YAML string
yq -i '.spec.template.spec.containers[0].image = strenv(APP_IMAGE)' \
  /tmp/deployment.yaml

# Set replicas using env() — result is a YAML integer
yq -i '.spec.replicas = env(REPLICA_COUNT)' \
  /tmp/deployment.yaml

# Confirm types are correct in the output
yq '.spec.replicas, .spec.template.spec.containers[0].image' /tmp/deployment.yaml

合并两个 YAML 文件

有时您需要将一个补丁文件(一个用于小范围覆盖的 YAML 文件)应用到基础配置上——例如,在类似 Kustomize 的工作流中应用特定环境的覆盖配置。

yq 可以使用*合并运算符合并两个文件:

  • yq '. *= load("patch.yaml")' base.yaml — 将补丁深度合并到基础配置,并写入标准输出。
  • 添加 -i 可就地更新基础文件:yq -i '. *= load("patch.yaml")' base.yaml

合并行为:

  • 补丁中的标量值会覆盖基础配置中的值。
  • 映射会进行深度合并(补丁中不存在的键会保留)。
  • 序列(数组)默认会被替换,而不是追加。请改用 *+ 进行追加。

这种模式可以替代脆弱的 sed 脚本,避免因空白变化而失效。

cat > /tmp/base.yaml << 'EOF'
app:
  name: my-service
  port: 8080
  debug: false
database:
  host: localhost
  port: 5432
EOF

cat > /tmp/patch.yaml << 'EOF'
app:
  port: 9090
  debug: true
database:
  host: db.production.svc
EOF

# Deep-merge patch into base (stdout preview first)
yq '. *= load("/tmp/patch.yaml")' /tmp/base.yaml

# Apply in place
yq -i '. *= load("/tmp/patch.yaml")' /tmp/base.yaml

验证 YAML 并转换为 JSON

在将打过补丁的 YAML 应用到集群之前,最好先对其进行验证,并根据需要将其转换为 JSON,以供其他工具使用。

验证语法:

  • yq '.' file.yaml && echo "Valid" — yq 在解析错误时会以代码 1 退出,因此可以在持续集成门禁中使用。

将 YAML 转换为 JSON:

  • yq -o=json '.' file.yaml — 输出格式化的 JSON。
  • 将结果通过管道传给 jq,以便进一步处理 JSON:yq -o=json '.' file.yaml | jq '.metadata.name'

将 JSON 转换为 YAML:

  • yq -P '.' file.json — 当输入为 JSON 时,-P 标志会强制输出 YAML(格式化后的内容)。

这些转换使 yq 成为 YAML 原生工具(Helm、kubectl)与 JSON 原生工具(Terraform、AWS CLI、jq)之间的桥梁。

# Validate YAML (exits 0 on success, 1 on parse error)
if yq '.' /tmp/deployment.yaml > /dev/null 2>&1; then
  echo "YAML is valid"
else
  echo "YAML parse error!" >&2
  exit 1
fi

# Convert to JSON and query with jq
yq -o=json '.' /tmp/deployment.yaml \
  | jq '{name: .metadata.name, image: .spec.template.spec.containers[0].image}'

# Round-trip: JSON snippet back to YAML
echo '{"replicas": 7, "strategy": "RollingUpdate"}' \
  | yq -P '.'

完整的持续集成部署补丁脚本

将所有技术结合起来:这是一个实际的持续集成脚本,用于在 GitOps 流水线中为 Kubernetes Deployment 清单应用补丁。

该脚本会:

  1. 在处理输入 YAML 之前先验证它。
  2. 使用 env() / strenv() 完成所有变量替换。
  3. 使用基于名称的 select() 更新容器镜像标签。
  4. 增加副本数量。
  5. 使用当前时间戳写入 deploy-time 注解。
  6. 在提交之前再次验证输出结果。

这种模式可以确保即使流水线并发运行,每个步骤仍然是原子的,并且可以审计。

#!/usr/bin/env bash
set -euo pipefail

MANIFEST="/tmp/deployment.yaml"
export NEW_IMAGE="my-app:$(date +%Y%m%d)-abc1234"
export NEW_REPLICAS=3
export DEPLOY_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
export CONTAINER="app"

# 1. Validate before patching
yq '.' "$MANIFEST" > /dev/null

# 2. Update image (by container name)
yq -i \
  '(.spec.template.spec.containers[] | select(.name == strenv(CONTAINER))).image = strenv(NEW_IMAGE)' \
  "$MANIFEST"

# 3. Set replicas
yq -i '.spec.replicas = env(NEW_REPLICAS)' "$MANIFEST"

# 4. Stamp annotation
yq -i '.metadata.annotations."deploy-time" = strenv(DEPLOY_TIME)' "$MANIFEST"

# 5. Validate result
yq '.' "$MANIFEST" > /dev/null && echo "Patch applied successfully"

# 6. Show diff summary
yq '{image: .spec.template.spec.containers[0].image, replicas: .spec.replicas}' "$MANIFEST"

知识检查:安全地编辑多文档文件

请测试您对使用 yq 编辑多文档 Kubernetes YAML 文件的理解。

课程回顾:使用 yq 编辑 YAML

您已经完成了使用 yq 编辑 YAML 配置文件的课程。以下是本课程内容的简要总结:

  • 安装:使用 mikefarah/yq Go 二进制文件(v4)。使用 yq --version 验证安装。
  • 读取:使用类似 .spec.replicas 的点号路径;使用 [0] 访问数组,或使用 [] 进行迭代。
  • 就地编辑:-i 标志会重写文件。请始终先不使用 -i 预览结果。
  • 可靠定位:优先使用 select(.name == "app"),而不是硬编码的数组索引。
  • 添加 / 删除:为新路径赋值即可创建它;使用 del() 删除字段。
  • 多文档文件:使用 select(.kind == "...") 定位一个资源,同时保持其他资源不变。
  • 持续集成变量:字符串使用 strenv(VAR),带类型的值使用 env(VAR),这样可以避免 Shell 引号错误。
  • 合并:. *= load("patch.yaml") 会深度合并覆盖文件,同时不会丢失未打补丁的键。
  • 验证与转换:使用 yq '.' 作为代码检查门禁;使用 -o=json 和 -P 进行格式转换。

在持续集成中为任何 YAML 应用补丁时,核心模式都是:验证 → 选择 → 赋值 → 验证。将此模式与 strenv() 和 select() 结合使用后,您再也不需要依赖脆弱的 sed 单行命令了。

免费开始

用 AI 导师学习 DevOps Bootcamp — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
142
课程
568

常见问题解答

「使用 yq 编辑 YAML 配置文件」课时是免费的吗?

是的 — 「使用 yq 编辑 YAML 配置文件」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 DevOps Bootcamp 课程的其余内容,请升级到 CoddyKit PRO。 DevOps Bootcamp 课程共包含 4 节课。

「使用 yq 编辑 YAML 配置文件」这节课中我会学到什么?

使用 yq 原地读取和修改 Kubernetes 与 CI YAML,同时保留结构和注释。 你通过在浏览器中直接运行的动手代码来练习 DevOps Bootcamp,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 DevOps Bootcamp 需要有经验吗?

无需任何先前经验。CoddyKit 上的 DevOps Bootcamp 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「使用 yq 编辑 YAML 配置文件」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 DevOps Bootcamp 课中编写并运行代码吗?

能。每节 DevOps Bootcamp 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 jq 管道筛选和选择 JSON
  2. 使用 jq 转换和构建 JSON 对象
  3. 结合 curl 与 jq 调用 REST API
  4. 使用 yq 编辑 YAML 配置文件
← 返回 DevOps Bootcamp