第一章:Golang项目GitOps实践全景概览
GitOps为Golang项目提供了以Git仓库为唯一事实源的持续交付范式,将基础设施、应用配置与代码变更统一纳入版本控制。其核心在于通过声明式配置驱动集群状态,并借助自动化工具链(如Argo CD或Flux)实现“Git push → 集群同步”的闭环。
核心组件协同关系
- Git仓库:存放Go应用源码(
main.go,go.mod)、Kubernetes清单(k8s/deployment.yaml,k8s/service.yaml)及CI/CD流水线定义(.github/workflows/ci.yml) - Operator控制器:监听Git仓库变更,比对集群当前状态与目标声明,自动执行差异修复
- Webhook触发器:GitHub/GitLab推送事件通知控制器拉取最新配置,避免轮询开销
典型工作流示例
- 开发者提交新功能至
main分支并打上语义化标签(如v1.2.0) - CI流水线验证Go构建、单元测试与镜像构建,并推送至容器仓库(如
ghcr.io/your-org/app:v1.2.0) - Argo CD检测到
k8s/kustomization.yaml中image.tag字段更新,自动同步Deployment资源
必备声明式配置片段
以下k8s/kustomization.yaml定义了Golang服务的可复用部署基线,支持环境差异化注入:
# k8s/kustomization.yaml —— 声明式部署入口
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
images:
- name: ghcr.io/your-org/app # 镜像名需与CI构建一致
newTag: v1.2.0 # Git tag驱动镜像版本升级
注:
newTag值由CI脚本动态注入(如kustomize edit set image ghcr.io/your-org/app=v${VERSION}),确保Git提交即代表最终运行态。
关键优势对比
| 维度 | 传统CI/CD | GitOps for Go |
|---|---|---|
| 状态可追溯性 | 仅记录构建日志 | 每次部署对应Git commit SHA |
| 回滚操作 | 需手动触发历史流水线 | git revert <commit> + 自动同步 |
| 权限模型 | CI系统需集群写入权限 | 只读Git权限 + Operator专属RBAC |
该模式天然适配Go应用轻量、无状态、容器化特性,使团队聚焦业务逻辑而非运维细节。
第二章:Argo CD在Go微服务中的深度集成与配置治理
2.1 Argo CD核心架构解析与Go项目适配原理
Argo CD 以声明式 GitOps 模式驱动 Kubernetes 应用交付,其核心由三类组件协同构成:
- API Server:提供 gRPC/REST 接口,校验 RBAC 并代理到控制器;
- Application Controller:持续比对 Git 中的期望状态(
ApplicationCR)与集群实际状态; - Repo Server:安全克隆、渲染 Helm/Kustomize 清单,不接触集群凭证。
数据同步机制
Controller 通过 Refresh 和 Sync 两种循环维持一致性:前者每3分钟拉取 Git 最新清单并计算 diff;后者响应手动或自动触发,执行 kubectl apply 级别变更。
// pkg/controller/appcontroller.go 片段
func (a *AppController) processApp(app *appv1.Application) error {
desired, err := a.appStateManager.GetDesiredState(app) // 从RepoServer获取渲染后清单
if err != nil { return err }
actual, _ := a.kubeClient.GetResourceState(app.Spec.Destination, app.Spec.Source) // 获取实时集群状态
diff := cmp.Diff(desired, actual) // 结构化比对(非字符串)
if diff != "" {
a.log.Info("Detected drift", "app", app.Name, "diff", diff)
}
return nil
}
此逻辑体现 Go 项目深度适配:
GetDesiredState封装多源(Helm/Kustomize/Jsonnet)抽象,cmp.Diff使用github.com/google/go-cmp/cmp实现类型安全比对,避免 YAML 字符串误判。
架构交互流程
graph TD
A[Git Repo] -->|Webhook/轮询| B(Repo Server)
B -->|Rendered manifests| C[Application Controller]
C -->|List/Watch| D[Kubernetes API Server]
C -->|Status update| E[API Server → UI/CLI]
| 组件 | 通信协议 | 关键 Go 依赖 |
|---|---|---|
| Repo Server | gRPC | helm.sh/helm/v3, sigs.k8s.io/kustomize/api |
| Application Controller | HTTP/gRPC | k8s.io/client-go, github.com/argoproj/gitops-engine |
2.2 基于Application CRD的Go服务声明式部署实战
Kubernetes 原生资源(如 Deployment + Service)组合繁琐,而 Application CRD 提供了更高阶的抽象,统一描述服务、配置与依赖关系。
定义 Application 资源
apiVersion: app.k8s.io/v1beta1
kind: Application
metadata:
name: go-echo-app
spec:
selector:
matchLabels:
app.kubernetes.io/name: go-echo
componentGroupKind:
group: apps
kind: Deployment
descriptor:
type: "Go HTTP Echo Service"
version: "v1.2.0"
此 CRD 实例将
go-echo应用元信息与底层 Deployment 绑定。componentGroupKind指明管控目标;descriptor字段用于 CI/CD 流水线识别版本与用途,不参与调度但支撑可观测性治理。
部署流程概览
graph TD
A[编写 Application YAML] --> B[kubectl apply -f]
B --> C[Operator 监听创建事件]
C --> D[自动生成 Deployment/Service/ConfigMap]
D --> E[就绪探针验证后标记 Ready]
关键字段对比表
| 字段 | 作用 | 是否必需 |
|---|---|---|
selector |
关联被管工作负载 | ✅ |
componentGroupKind |
指定受控资源类型 | ✅ |
descriptor.version |
支撑灰度与回滚策略 | ❌(推荐填写) |
2.3 多环境同步策略设计:dev/staging/prod的Git分支+标签双轨控制
核心控制模型
采用 branch + tag 双轨机制:
- 分支承载持续集成流(
dev→staging→prod) - 语义化标签(如
v1.2.0-staging,v1.2.0-prod)锚定可部署快照,确保环境间二进制一致性
Git工作流示例
# 合并至staging并打标(CI流水线自动执行)
git checkout staging
git merge --no-ff dev
git tag v1.2.0-staging -m "Promote to staging"
git push origin staging v1.2.0-staging
逻辑说明:
--no-ff强制保留合并历史;标签命名含环境后缀,避免跨环境误用;推送标签触发 staging 部署作业。
环境映射关系
| 环境 | 主干分支 | 部署依据 | 触发条件 |
|---|---|---|---|
| dev | dev |
分支最新 commit | 每次 push 到 dev |
| staging | staging |
vX.Y.Z-staging |
标签推送 |
| prod | prod |
vX.Y.Z-prod |
人工审批后打标 |
发布流程图
graph TD
A[dev 分支提交] --> B{CI 测试通过?}
B -->|是| C[合并至 staging]
C --> D[打 v1.2.0-staging 标签]
D --> E[自动部署 staging]
E --> F[人工验收]
F -->|通过| G[打 v1.2.0-prod 标签]
G --> H[部署 prod]
2.4 Argo CD健康检查插件开发:为Go HTTP/GRPC服务定制liveness探针逻辑
Argo CD 默认的健康状态判定(如 Deployment 的 Available 条件)无法反映业务层可用性。需通过自定义健康检查插件,注入领域感知的 liveness 逻辑。
插件注册机制
在 argocd-cm ConfigMap 中声明插件:
data:
health.lua: |
function check(obj)
if obj.kind == "Service" and obj.spec.type == "ClusterIP" then
return { status = "Healthy", message = "ClusterIP service ready" }
end
return { status = "Progressing" }
end
此 Lua 脚本挂载为
health.lua,由 Argo CD 控制器在资源同步后调用;obj是 Kubernetes 对象的 Lua 表表示,含完整apiVersion、kind、spec字段。
Go 服务探针集成策略
对 Go 编写的 HTTP/GRPC 服务,推荐统一暴露 /healthz 端点,并在插件中发起主动探测:
| 探针类型 | 目标路径 | 超时 | 成功条件 |
|---|---|---|---|
| HTTP | /healthz |
3s | HTTP 200 + JSON {“ok”:true} |
| gRPC | /grpc.health.v1.Health/Check |
5s | gRPC status OK + status: SERVING |
// 在 Go 服务中注册 gRPC Health Check
import "google.golang.org/grpc/health/grpc_health_v1"
...
srv := grpc.NewServer()
grpc_health_v1.RegisterHealthServer(srv, health.NewServer())
grpc_health_v1.RegisterHealthServer将标准健康服务注册到 gRPC Server;Argo CD 插件可通过grpcurl或自定义 Go 客户端调用Check()方法验证服务可服务性。
graph TD A[Argo CD Controller] –>|触发健康检查| B(执行 health.lua) B –> C{对象 Kind == Service?} C –>|是| D[发起 HTTP/gRPC 探针] C –>|否| E[回退默认健康逻辑] D –> F[解析响应状态与 payload] F –> G[返回 Healthy/Progressing/Degraded]
2.5 安全加固实践:RBAC精细化授权、密钥零泄露注入与SOPS集成
RBAC策略最小化示例
以下 ClusterRoleBinding 仅授予 configmap-reader 在 default 命名空间的只读权限:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: configmap-reader-binding
namespace: default
subjects:
- kind: ServiceAccount
name: app-sa
namespace: default
roleRef:
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.io
逻辑分析:
RoleBinding作用域限于单命名空间,避免跨空间越权;subjects明确绑定服务账户而非用户组,杜绝宽泛授权;roleRef指向预定义Role(需单独声明),确保权限原子性。
密钥注入零泄露保障
使用 SOPS + Age 加密 secrets.yaml,通过 kustomize build --enable-alpha-plugins 解密注入:
| 工具 | 用途 | 安全优势 |
|---|---|---|
| SOPS | 加密/解密 Kubernetes 清单 | 支持 Git 友好二进制安全 |
| Age | 非对称密钥加密后端 | 无密钥轮换依赖,私钥离线保管 |
graph TD
A[Git 仓库] -->|加密 secrets.yaml| B(SOPS + Age)
B --> C[CI 流水线]
C -->|运行时解密| D[Kubernetes API Server]
D --> E[Pod 内存中挂载]
第三章:Kustomize驱动的Go项目多环境配置演进体系
3.1 Kustomize v5+ Patch机制与Go二进制构建参数的动态绑定
Kustomize v5 引入 patchesStrategicMerge 的增强语义支持,允许在 patch 中直接引用 Go 构建时注入的变量(如 -ldflags="-X main.Version=${VERSION}")。
动态 Patch 示例
# kustomization.yaml
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
env:
- name: BUILD_VERSION
value: $(BUILD_VERSION) # Kustomize v5+ 支持环境变量插值
此 patch 利用 Kustomize v5 的
vars+replacements机制实现跨层级变量传递;$(BUILD_VERSION)需通过vars:声明并由replacements:从 ConfigMap 或字段中提取,而非传统 shell 替换。
构建时绑定流程
graph TD
A[go build -ldflags “-X main.BuildID=abc123”] --> B[生成 embed.FS 含 buildinfo]
B --> C[Kustomize reads BUILD_ID via replacement from configmap/data]
C --> D[注入 Deployment env]
| 参数 | 来源 | 作用 |
|---|---|---|
BUILD_VERSION |
-ldflags 注入 |
提供给 Go 运行时与 Kustomize 共享 |
replacements |
kustomization.yaml |
建立字段到变量的映射通道 |
3.2 Go module依赖树与kustomization.yaml的版本一致性保障方案
在混合声明式配置(Kustomize)与编译时依赖(Go modules)的现代云原生项目中,go.mod 中的依赖版本与 kustomization.yaml 中引用的镜像/组件版本常因人工同步而脱节。
数据同步机制
采用 go run sigs.k8s.io/kustomize/api/krusty + 自定义校验器,通过解析 go list -m -json all 构建模块版本图谱,并与 kustomization.yaml 中 images: 字段比对:
# 提取 go.mod 中 controller-runtime 版本
go list -m -json sigs.k8s.io/controller-runtime | jq -r '.Version'
# 输出:v0.17.2
该命令返回模块精确语义化版本,供后续断言校验使用。
自动化校验流程
graph TD
A[读取 go.mod] --> B[解析所有 module 版本]
B --> C[提取 kustomization.yaml 中 images]
C --> D[匹配命名与版本前缀]
D --> E[失败则 exit 1]
校验规则表
| 模块路径 | Kustomize image key | 版本映射方式 |
|---|---|---|
sigs.k8s.io/controller-runtime |
controller |
精确匹配 v0.17.2 |
k8s.io/apimachinery |
kube-apiserver |
主版本兼容 v0.29.x |
校验脚本需支持通配符与主版本对齐策略,避免因 patch 版本差异误报。
3.3 面向Go服务特性的ConfigMap/Secret生成器:从go.mod与build flags自动推导配置元数据
传统Kubernetes配置管理常需手动维护 ConfigMap/Secret YAML,易与Go服务实际依赖脱节。本生成器通过静态分析 go.mod 和构建标记(如 -tags=prod,redis),自动提取运行时必需的配置维度。
自动元数据推导逻辑
- 解析
go.mod获取模块名、主版本及间接依赖(如gopkg.in/yaml.v3→ 推导YAML_VERSION) - 提取
build flags中的 tag 组合,映射为环境特征标识(redis→REDIS_ENABLED=true) - 结合
// +k8s:config注释标记的结构体字段,生成类型化键名
# 示例:基于构建上下文生成配置骨架
go run ./cmd/cfggen \
--mod=go.mod \
--tags="prod,postgres" \
--output=config.yaml
该命令解析
go.mod得到module "github.com/example/api",结合prod标签注入APP_ENV=production,postgres标签触发DB_DRIVER=postgres键值对生成。
输出配置映射表
| Go 构建上下文 | 推导配置键 | 值示例 |
|---|---|---|
go.mod module 名 |
APP_MODULE |
github.com/example/api |
-tags=redis |
REDIS_ENABLED |
"true" |
// +k8s:config port=8080 |
SERVER_PORT |
"8080" |
graph TD
A[go.mod] --> B[模块路径/依赖树]
C[build flags] --> D[tag→feature映射]
B & D --> E[配置元数据合成器]
E --> F[ConfigMap/Secret YAML]
第四章:GoReleaser赋能的云原生发布流水线构建
4.1 GoReleaser配置深度定制:支持CGO交叉编译、UPX压缩与SBOM生成
CGO 交叉编译适配
启用 CGO_ENABLED=1 时需显式指定目标平台工具链。在 .goreleaser.yaml 中配置:
builds:
- env:
- CGO_ENABLED=1
goos:
- linux
- windows
goarch:
- amd64
- arm64
ldflags: -s -w
CGO_ENABLED=1允许调用 C 库;goos/goarch组合触发多平台交叉构建;-s -w剥离调试符号以减小体积。
UPX 压缩与 SBOM 生成联动
GoReleaser v1.23+ 原生支持 UPX 和 SPDX SBOM 输出:
| 功能 | 配置字段 | 说明 |
|---|---|---|
| UPX 压缩 | upx: true |
自动检测并压缩二进制文件 |
| SBOM 生成 | sbom: true |
输出 dist/<bin>.spdx.json |
graph TD
A[Build Artifact] --> B{UPX enabled?}
B -->|Yes| C[Apply UPX compression]
B -->|No| D[Skip]
A --> E{SBOM enabled?}
E -->|Yes| F[Generate SPDX JSON]
4.2 语义化版本触发机制:基于git tag规则与go.mod主版本号的自动发布门禁
Go 模块发布门禁依赖双重校验:git tag 格式必须匹配 vMAJOR.MINOR.PATCH(如 v1.2.0),且 go.mod 中模块路径需显式包含主版本后缀(如 example.com/lib/v2)。
触发校验逻辑
# CI 脚本片段:验证 tag 与 go.mod 一致性
TAG=$(git describe --tags --exact-match 2>/dev/null)
MOD_PATH=$(grep '^module ' go.mod | awk '{print $2}')
if [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
MAJOR=$(echo "$TAG" | sed 's/^v\([0-9]\+\)\..*/\1/')
EXPECTED_SUFFIX="/v$MAJOR"
[[ "$MOD_PATH" == *"$EXPECTED_SUFFIX" ]] || exit 1
fi
逻辑分析:脚本提取当前精确 tag,解析主版本号
MAJOR,再检查go.mod模块路径是否以/v{MAJOR}结尾。若v1.5.0tag 对应example.com/pkg(无/v1),则拒绝发布。
版本合规性对照表
| git tag | go.mod module path | 是否允许发布 |
|---|---|---|
v2.0.0 |
example.com/lib/v2 |
✅ |
v2.0.0 |
example.com/lib |
❌(缺失 v2 后缀) |
2.0.0 |
example.com/lib/v2 |
❌(缺少 v 前缀) |
自动化门禁流程
graph TD
A[Push git tag] --> B{Tag 匹配 v\\d+\\.\\d+\\.\\d+?}
B -->|否| C[拒绝发布]
B -->|是| D[解析 MAJOR]
D --> E[读取 go.mod module 行]
E --> F{路径含 /vMAJOR?}
F -->|否| C
F -->|是| G[触发构建与发布]
4.3 Artifacts分发协同:将GoReleaser产物无缝注入Argo CD ApplicationSet与Kustomize remote bases
数据同步机制
GoReleaser 生成的 dist/ 目录(含二进制、checksums、SBOM)需自动同步至 Git 仓库的 charts/ 或 bases/ 路径,作为 Kustomize remote bases 的源。推荐使用 git add && git commit -m "chore(release): v1.2.3" 配合 CI 触发器完成原子更新。
Argo CD ApplicationSet 动态发现
ApplicationSet 通过 generator: git 扫描 releases/ 目录下 YAML 清单,匹配语义化版本标签:
# releases/v1.2.3/appset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapp-set
spec:
generators:
- git:
repoURL: https://github.com/org/repo.git
revision: main
directories:
- path: "bases/v*.*.*" # 匹配所有版本化 base 目录
此配置使 ApplicationSet 自动感知新发布的
bases/v1.2.3/目录,并为每个匹配路径生成独立 Application 实例。path模式支持通配符,无需手动维护清单。
Kustomize remote base 引用示例
# apps/prod/kustomization.yaml
resources:
- https://github.com/org/repo.git//bases/v1.2.3?ref=v1.2.3
Kustomize 支持直接拉取 GitHub 子目录(需
?ref=指定精确 commit/tag),确保环境声明与发布产物强一致。ref必须与 GoReleaser 的--rm-prefix和 tag 严格对齐,避免漂移。
| 组件 | 作用 | 关键约束 |
|---|---|---|
| GoReleaser | 构建+签名+归档 | 输出必须含 kustomization.yaml 至 bases/${version}/ |
| Git Repo | 版本化 artifact 仓库 | main 分支托管所有 bases/,不可变 tag 标记发布点 |
| ApplicationSet | 声明式应用拓扑生成 | 依赖 directories.path 正则匹配,不支持 glob 递归 |
graph TD
A[GoReleaser] -->|Push dist/ & bases/vX.Y.Z/| B[Git Repo]
B -->|Git Generator watches bases/v*| C[ApplicationSet Controller]
C -->|Render Application per version| D[Argo CD]
D -->|Apply kustomize build| E[Kubernetes Cluster]
4.4 可观测性增强:发布事件埋点、Prometheus指标暴露与OpenTelemetry链路追踪注入
可观测性不是日志堆砌,而是事件、指标、链路三者的语义对齐与协同分析。
埋点统一规范
- 事件类型使用
publish.success/publish.timeout等语义化命名 - 携带关键上下文:
topic,partition,message_id,trace_id
Prometheus 指标暴露(Go SDK 示例)
// 注册自定义指标:消息发布成功率与延迟直方图
var publishSuccess = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "kafka_publish_success_total",
Help: "Total number of successful publishes",
},
[]string{"topic", "partition"},
)
var publishLatency = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "kafka_publish_latency_seconds",
Help: "Publish latency in seconds",
Buckets: prometheus.ExponentialBuckets(0.01, 2, 8), // 10ms–2.56s
},
[]string{"topic"},
)
逻辑说明:
CounterVec按 topic/partition 多维计数,支持下钻排查热点分区;HistogramVec使用指数桶覆盖典型延迟分布,避免手工分桶失真。
OpenTelemetry 链路注入
graph TD
A[Producer App] -->|inject trace_id & span_id| B[Kafka Record Headers]
B --> C[Consumer App]
C -->|propagate context| D[Downstream HTTP/DB Calls]
| 组件 | 数据类型 | 采集方式 | 用途 |
|---|---|---|---|
| Kafka Producer | 事件 | 手动埋点 + Header 注入 | 定位发布失败根因 |
| Broker | 指标 | JMX Exporter | 监控吞吐、积压、ISR 状态 |
| Consumer | 链路跨度 | OTel Auto-instrumentation | 追踪端到端消费延迟 |
第五章:23个生产级YAML模板使用指南与演进路线
核心设计原则与约束规范
所有23个模板均遵循“最小权限+显式声明+环境隔离”三原则。例如,ingress-nginx-controller.yaml 中严格禁用 hostNetwork: true,强制通过 Service type=LoadBalancer 暴露;redis-statefulset.yaml 要求 volumeClaimTemplates 必须绑定至 storageClassName: "ssd-prod",且 resources.requests.memory 不得低于 2Gi。每个模板头部均嵌入标准化注释块,包含 # @env: [staging|prod|dr]、# @version: v2.4.1 和 # @last-audit: 2024-06-17 字段,供CI流水线自动校验。
模板版本演进路径(关键里程碑)
| 版本 | 发布时间 | 主要变更 | 影响范围 |
|---|---|---|---|
| v1.0 | 2022-03 | 初始K8s 1.21兼容版,无PodDisruptionBudget | 全部无状态服务 |
| v2.1 | 2023-01 | 引入topologySpreadConstraints与nodeSelector硬约束 |
有状态中间件模板 |
| v2.4 | 2024-05 | 新增securityContext.seccompProfile及sysctls白名单 |
所有v2.x系列模板 |
敏感配置安全注入实践
postgres-deployment.yaml 拒绝在YAML中硬编码密码,而是通过以下方式组合注入:
envFrom:
- secretRef:
name: {{ include "myapp.fullname" . }}-db-secrets
optional: false
配套的Helm values.yaml 定义:
secrets:
db-secrets:
POSTGRES_PASSWORD: "vault:secret/data/db/prod#password"
CI阶段由vault-agent-injector动态挂载Secret,避免Git泄露风险。
多集群差异化部署策略
采用kustomize基线+overlay模式管理23个模板的变体:
base/ # 公共定义(deployment、service等)
├── postgres/
│ ├── kustomization.yaml
│ └── deployment.yaml
overlays/
├── aws-us-east-1/ # 注入EBS CSI driver + IRSA角色
├── gcp-us-central1/ # 启用Cloud SQL Auth Proxy sidecar
└── onprem-ha/ # 替换为Rook Ceph storageClass
模板健康度自动评估流程
graph LR
A[Git Push] --> B[Pre-commit Hook]
B --> C{YAML Lint<br>• yamllint<br>• kubeval v1.28}
C -->|Pass| D[CI Pipeline]
D --> E[Conftest Policy Check<br>• no latest tags<br>• memory limits set]
E --> F[Deploy to staging]
F --> G[Prometheus SLI验证<br>• p95 latency < 200ms<br>• error rate < 0.1%]
G --> H[Auto-promote to prod]
生产故障回滚机制
当canary-service.yaml触发SLO熔断(连续3次/healthz超时),Argo Rollouts自动执行:
- 将
spec.strategy.canary.steps[0].setWeight从10%重置为0 - 从
ConfigMap rollout-history中提取上一稳定版本哈希 - 调用
kubectl patch deployment -p '{"spec":{"template":{"metadata":{"annotations":{"rollout.argoproj.io/revert-to":"v2.3.7"}}}}}'
该机制已在2024年Q2三次数据库连接池耗尽事件中成功启用。
模板依赖关系图谱
23个模板形成强耦合网络:cert-manager-issuer.yaml 是 ingress-tls.yaml 的前置依赖;velero-backup-cron.yaml 必须等待 minio-service.yaml 就绪后启动。依赖链通过helm dependency build生成Chart.lock固化,禁止跨大版本混用(如v2.1模板不可引用v2.4的metrics-server-rbac.yaml)。
