GitOps CD:用 FluxCD + KubeVela 把镜像自动发布到 K8s
GitOps CD:用 FluxCD + KubeVela 把镜像自动发布到 K8s
这是《Gitea Actions 全攻略:把 GitHub Actions 私有化》的续篇。
前篇我们搭起了一条完全内网的 CI:
git push tag → runner 构建多架构镜像 → 推到 Zot registry。但镜像推上去之后就静静躺在那儿了——谁来把它部署到 K8s?每次都手动kubectl apply、手动改 image tag、手动滚到所有集群?那违背了「内网自动化」的初衷。本篇就补上这一块:让一次 git commit 等于一次发布。读完你能搭出这条链路:
1改 manifests 里的 image tag → git commit → Flux 拉到集群 → KubeVela 渲染 → Pod Ready全程不出内网,全程声明式。
一、先问一个为什么:CI 之后还差什么
回顾前篇结尾的「四块拼图」:Gitea server、gitea-runner、Action mirror、私有 registry。拼完它,你拥有了一套构建链路。但完整的 DevOps 还差最后一步:把构建产物部署到集群。
传统做法(CI 直接部署)有几个毛病:
- CI runner 通常拿不到集群凭证(或者为了拿凭证,把 kubeconfig 塞进 CI secret,安全隐患大)。
- 部署逻辑写在 CI yaml 里,和构建逻辑混在一起,谁部署的、部署了什么、什么时候部署的,审计困难。
- 多集群时,CI 要 ssh 到每台机器执行,扩展性差。
GitOps 给了另一种范式:集群主动去拉 git,而不是 CI 主动推集群。
- git 仓库是唯一的事实来源(single source of truth)。
- 集群里有个控制器,定时 clone git,发现 diff 就 apply。
- 你想发布?改 git 提交就行。想回滚?
git revert。 - 谁发布的、发布了什么、什么时候,全部在 git log 里,天然可审计。
这就是本篇要做的事。技术选型是 FluxCD + KubeVela。
二、选型:为什么 FluxCD + KubeVela,而不是二选一
GitOps 工具两个主流:Argo CD 和 FluxCD。
| Argo CD | FluxCD | |
|---|---|---|
| 风格 | 重 UI、可视化同步状态 | 纯 CLI、声明式 CR 驱动 |
| 心智负担 | 一堆概念(Application/AppProject/Repository) | 就三个 CR:GitRepository / Kustomization / HelmRelease |
| 适合 | 喜欢点按钮、看图的人 | 喜欢 git diff、喜欢把一切写进 yaml 的人 |
我选 FluxCD。它够纯——纯 GitOps,纯声明式,没有 UI 反而强迫你把状态都落到 git 里,这恰恰是 GitOps 的本意。
但纯 Flux 有个痛点:它只负责「把目录里的 yaml 同步到集群」,你得自己写一坨原生 K8s yaml(Deployment + Service + Ingress + Secret…)。对一个简单 web 服务来说太啰嗦。
KubeVela 就是来填这个缺口的。 它是 OAM(Open Application Model)的 K8s 实现,提供一个 Application CRD,让你用「应用中心」的抽象来描述服务:
1 | |
type: webservice 这个组件类型,KubeVela controller 会自动帮你渲染出 Deployment + Service。你只声明「我要一个镜像跑在 8080 端口」,剩下的交给 controller。
所以最终架构是分层的:
1 | |
Flux 负责「同步」,KubeVela 负责「抽象」,各司其职。 一句话总结为什么是这俩组合:Flux 让 git 成为事实来源,KubeVela 让你写在 git 里的东西足够简洁。
💡 为什么不用 KubeVela 自带的 addon fluxcd?
我试过。KubeVela 官方的 fluxcd addon(catalog 3.0.1)有个硬伤:它的
HelmRepository不支持insecure字段。而我的 Zot registry 走纯 http、无证书,Flux source-controller 拉 chart 时会因 https 握手失败。所以我做了个定制版 vela-core chart,把改造后的 FluxCD 控制器直接渲染进 chart 的
templates/,镜像地址全替换成内网。本篇第三节讲 Flux 安装时会用到这个改造的产物。
三、装 FluxCD(内网镜像替换)
FluxCD 的安装哲学是「导出 yaml → 替换镜像 → apply」。一切都在你掌控的 yaml 里,不依赖任何 Helm repo。
3.1 装 flux CLI + 导出 yaml
1 | |
导出来的 fluxcd.yaml 大约 8700 行,包含:
1 | |
3.2 把镜像拷到 Zot
fluxcd.yaml 里的镜像都是 ghcr.io/fluxcd/...,内网拉不到。先用 skopeo 把三个 controller 镜像拷到 Zot:
1 | |
3.3 替换 + apply
sed 一把梭,把 fluxcd.yaml 里的 ghcr.io 全换成内网:
1 | |
确认三个 controller 起来了:
1 | |
这套 fluxcd.yaml 我直接存进了仓库的 gitops/fluxcd.yaml,开箱即用,不用再导出。
四、装 KubeVela(定制 chart)
KubeVela 用 helm 装。我用的是自己定制的 chart(基于官方 1.10.6),它额外内嵌了三样东西:
- FluxCD 控制器 —— 就是第三节那个 insecure 问题的解决方案,渲染产物直接放进 chart 的
templates/。这样 KubeVela 和 FluxCD 一个 helm install 全装好。 - local-path-storage —— rancher 的本地路径 provisioner,给集群一个默认 StorageClass。
- 两个 CUE 定义 ——
helm-labels.cue/helm-release-def.cue,让 KubeVela 能管理 Flux 的 HelmRelease(本篇用不到,但留了扩展口)。
chart 推到 Zot 后,一行 helm install:
1 | |
--set imageRegistry—— 把 chart 里所有镜像加内网前缀。multicluster.enabled=false—— 关掉多集群,单集群够用,省资源。
装完确认 Application CRD 就位:
1 | |
五、核心配置一:delivery.yaml(Flux 同步规则)
这是本篇核心之一。Flux 同步需要三个 CR,我写在一个文件 gitops/delivery.yaml 里,完整代码如下:
1 | |
逐段拆解:
Secret git-credentials —— Gitea 认证
1 | |
password 是一个 access token,不是登录密码。在 Gitea Web UI:头像 → Settings → Applications → Generate New Token,勾选 repository:read(只读就够,Flux 只需要 clone)。生成的 token 长这样一串十六进制。
GitRepository actions-demo —— 告诉 Flux 去哪 clone
1 | |
source-controller 每分钟 clone 一次这个仓库,发现新 commit 就更新内部 artifact。
Kustomization actions-demo —— 告诉 Flux 同步哪个目录
1 | |
prune: true是 GitOps 的核心承诺:集群状态 = git 状态。git 里删了某个资源,集群里也删掉,不会留垃圾。wait: true+timeout: 3m:apply 完等资源 Ready,3 分钟没 Ready 就报错,避免静默故障。
apply 它
1 | |
⚠️ 名字撞车警告:kustomize 有个资源类型叫
Kustomization(kustomize.config.k8s.io/v1beta1),Flux 也有个 CR 叫Kustomization(kustomize.toolkit.fluxcd.io/v1)。两个完全不同的东西:前者是「目录怎么打包」,后者是「什么时候同步」。本节的 delivery.yaml 用的是后者。
六、核心配置二:manifests/(KubeVela 应用定义)
Flux 现在每分钟去拉 actions/demo.git 的 ./manifests 目录。我们得在那个仓库里放点东西——就是 KubeVela 的 Application。两个文件,放在前篇那个 actions 仓库下:
1 | |
6.1 manifests/namespace.yaml
1 | |
Application 要部署到 actions namespace,得先有这个 namespace。
6.2 manifests/application.yaml —— KubeVela 应用定义
完整代码:
1 | |
这一小段 yaml 顶下面这一大坨原生 K8s yaml:
1 | |
逐字段拆 application.yaml:
| 字段 | 作用 |
|---|---|
type: webservice |
KubeVela 内置组件类型,自动渲染 Deployment + Service |
image |
来自前篇 CI 构建出的镜像。发版时只改这一行 |
imagePullPolicy: IfNotPresent |
节点已有就不拉,省内网带宽 |
exposeType: NodePort |
Service 类型。生产里可换 ClusterIP + Ingress |
ports.port: 8080 |
容器监听端口(main.go 里 app.Listen(":8080")) |
ports.expose: true |
这个端口对外暴露(生成 Service) |
ports.nodePort: 30080 |
节点端口,集群外 curl <nodeIP>:30080 访问 |
省心程度立见高下。type: webservice 这一行,KubeVela controller 看到 Application 被 apply,自动把 Deployment + Service 渲染出来。image 来自前篇 CI 构建的 actions/demo:v1.0.3——闭环了。
6.3 不需要 kustomization.yaml
你可能注意到目录里没有 kustomization.yaml。这是有意的——Flux 的 kustomize-controller 遇到没有 kustomization.yaml 的目录,会把目录里所有 .yaml 当作普通资源直接 apply(叫「Kustomize 无构建模式」)。对于几个文件的小项目,省一个文件。如果目录变复杂、需要 patches/transformers,再加 kustomization.yaml。
七、跑起来:从 git push 到 Pod Ready
现在把整条链路走一遍。假设前篇已经构建出 actions/demo:v1.0.3:
① 首次部署 —— 把 manifests 推到 git:
1 | |
Flux 最多 1 分钟内拉到,自动 apply。
② 后续发版 —— 改 image tag 提交:
1 | |
Flux 1 分钟内拉到新 commit → apply 新 Application → KubeVela 重新渲染 Deployment → rolling update → 新 Pod Ready。
③ 访问验证:
1 | |
整个发版过程就是一次 git commit。没有 kubectl,没有「部署」按钮,没有人在集群里手敲命令。git commit 等于发布。这就是 GitOps。
八、排查:出问题时看这几条命令
GitOps 链路长,排查要分层。从 git 到 Pod,每层都有对应的 CR / 资源可看:
1 | |
常见卡点对照表:
| 现象 | 原因 | 排查 |
|---|---|---|
GitRepository 报 401 |
token 过期或没勾 repository:read |
重新生成 token,更新 Secret |
Kustomization 一直 NotReady |
path 路径错,或 yaml 语法错 |
看 kustomization 的 Status 里 error 信息 |
Application 状态 workflowFinished 但没 Deployment |
namespace 不存在 | 确认 namespace.yaml 已 apply |
Pod ImagePullBackOff |
节点没配 insecure registry,或 tag 拼错 | crictl pull <image> 手动验证 |
| 改了 git 但集群没更新 | Flux 还没轮询到(1 分钟周期) | 等,或 flux reconcile 手动触发 |
九、总结:发版闭环
把前篇和本篇拼起来,一条完全私有、声明式、可审计的 CI/CD 链路:
1 | |
涉及的文件清单(都在你的仓库里):
| 文件 | 作用 |
|---|---|
| gitops/fluxcd.yaml | FluxCD 控制器安装 yaml(8700 行,镜像已替换内网) |
| gitops/delivery.yaml | Flux 同步规则(GitRepository + Kustomization + Secret) |
| actions/manifests/application.yaml | KubeVela 应用定义(webservice 抽象) |
| actions/manifests/namespace.yaml | actions namespace |
| charts/vela-core/ | 定制版 KubeVela chart(集成 FluxCD) |
一句话记住核心:CI 把代码变成镜像(前篇),CD 把镜像变成集群里跑的服务(本篇),中间的桥梁是一个存了 image tag 的 git 仓库。
下一站大概是可观测性(监控 + 日志 + 链路追踪),或者镜像签名准入(Cosign + policy-controller),那又是另一个故事。
合集导航
- 《Gitea Actions 全攻略:把 GitHub Actions 私有化》 — CI 部分(101)
- 本文 — GitOps CD(102)