在微服务云原生架构的演进中CI持续集成与 CD持续部署的边界划分往往是团队争论的焦点。传统的 CI/CD 流水线习惯于在构建出 Docker 镜像后顺手执行一条kubectl apply或者helm upgrade。这种“一把梭”的模式在单集群、小团队下固然痛快但随着多集群跨云联邦、基础设施即代码IaC的推行它的弊端暴露无遗CI 脚本被迫掌握了生产环境的高权限凭证业务代码仓库被塞满了部署脚本回滚完全依赖重新跑流水线。本文记录了我们将一个 Quarkus 微服务Data Plane 位于腾讯云 K3sControl Plane 位于阿里云 ArgoCD彻底向纯粹的事件驱动 GitOps 架构重构的全过程。一、 架构全景基于 Repository Dispatch 的事件驱动模型我们放弃了在同一个仓库里既写代码又写部署图纸的方案也放弃了让 CI 脚本跨仓库强行提交代码的半吊子解耦。最终落地的是跨仓库事件驱动Webhook架构。1.1 核心设计理念CI 仓库业务研发只负责代码编译、单元测试、打出镜像推送到 Registry。在结束前朝 CD 仓库大吼一声发送 Webhook“新版本造好了Tag 是 XXX”CD 仓库运维大本营存放所有的 ArgoCD Application 和 K8s YAML 图纸。它作为一个“接线员”监听 CI 发来的信号自动修改自己仓库里的镜像版本并提交。ArgoCD部署大脑只认 CD 仓库里的图纸图纸怎么画它就把腾讯云的集群变成什么样。1.2 Mermaid 架构流转图腾讯云 K3s (Spoke)阿里云 ArgoCD (Hub)CD 图纸库 (my-argocd-manifests)镜像仓库 (GHCR)业务代码库 (kong-gitops-experiment)开发者腾讯云 K3s (Spoke)阿里云 ArgoCD (Hub)CD 图纸库 (my-argocd-manifests)镜像仓库 (GHCR)业务代码库 (kong-gitops-experiment)开发者触发 GitHub Actions (ci.yml)触发 GitHub Actions (update-image-tag.yml)1. git push 提交业务代码2. Maven 编译 测试3. docker build push (Tag: SHA)4. Repository Dispatch API (携带 image_tag)5. 提取 Payloadsed 替换 YAML 文件中的 Tag6. 自动 Git Commit Push7. 轮询/Webhook 发现图纸更新8. 下发最新状态 (更新 Deployment)9. 拉取新版本镜像启动 Pod二、 核心实现代码为了实现这种松耦合我们在两个 GitHub 仓库中分别布置了流水线。2.1 业务侧只负责打包和“发信号” (ci.yml)在业务仓库中流水线的核心逻辑在最后一步发生了根本转变。我们不再去 checkout 配置仓库而是使用peter-evans/repository-dispatch触发远端的 API。# kong-gitops-experiment/.github/workflows/ci.ymlname:Quarkus API CI Buildon:push:branches:[main]paths:[apps/quarkus-svc/**,.github/workflows/ci.yml]permissions:contents:readpackages:writejobs:build-and-push:runs-on:ubuntu-lateststeps:-name:Checkout Codeuses:actions/checkoutv3# ... 省略 JDK 安装、Maven 编译和 GHCR docker push 步骤 ...-name:Trigger CD Repo (Repository Dispatch)uses:peter-evans/repository-dispatchv2with:token:${{secrets.CD_GIT_PAT}}repository:nvd11/my-argocd-manifestsevent-type:update-image-tag# 将当前微服务名称和最新的 Commit SHA 作为负载发送出去client-payload:{image_tag: ${{ github.sha }}, svc_name: quarkus-svc}2.2 CD 侧全自动的“配置接线员” (update-image-tag.yml)CD 仓库中的 Action 不受任何 Push 事件触发只监听repository_dispatch。它提取传递过来的参数精确定位到对应的图纸文件进行修改。# my-argocd-manifests/.github/workflows/update-image-tag.ymlname:CD Configuration Updateon:repository_dispatch:types:[update-image-tag]permissions:contents:writejobs:update-tag:runs-on:ubuntu-lateststeps:-name:Checkout Codeuses:actions/checkoutv3-name:Update Image Tag in ArgoCD App Manifestrun:|TAG${{ github.event.client_payload.image_tag }} SVC_NAME${{ github.event.client_payload.svc_name }}echo Updating image tag for $SVC_NAME to $TAG MANIFEST_FILEargocd-apps/${SVC_NAME}-app.yaml# 使用 sed 精准替换 ArgoCD Application 清单中的 tagsed -i -E s/tag:[a-f0-9](.*)/tag:${TAG}\1/g $MANIFEST_FILE git config--global user.name github-actions[bot] git config--global user.email 41898282github-actions[bot]users.noreply.github.com git add $MANIFEST_FILEgit commit -m ci:auto-update $SVC_NAME image tag to ${TAG}[skip ci] git push三、 实践中的深坑与工程化破局在整套架构贯通的过程中并非一帆风顺底层网络、K8s 网关标准以及身份验证等问题接踵而至。以下是极具价值的避坑记录3.1 跨仓库调用的权限壁垒 (GITHUB_TOKEN vs PAT)现象最初在 CI 脚本中调用跨仓库 API 时系统报Input required and not supplied: token或 403 Forbidden。破局GitHub Actions 默认注入的${{ secrets.GITHUB_TOKEN }}权限被严格圈禁在“当前运行的仓库”内。为了调用my-argocd-manifests的 Dispatch API必须生成一个带有repo权限的个人访问令牌 (PAT)并以CD_GIT_PAT的名字存入业务仓库的 Secrets 中。3.2 Kong KIC 引擎与 Gateway API 的“翻译崩溃”现象为了在网关层剥离路径我们在 Gateway API 的HTTPRoute中使用了URLRewrite过滤器结果 Kong 报错KongConfigurationTranslationFailed。破局经过排查开源版 Kong (KIC) 默认使用的是Traditional Router传统路由引擎它根本无法解析 K8s Gateway API 中高级的正则路径重写语法。强行开启未完全成熟的Expression Router风险极高。最终我们选择退回最稳定的注解方式konghq.com/strip-path: true但这又引入了微服务后端“路径迷失”的新问题。3.3 ArgoCD 部署降级 (Degraded) 与探针改造现象为了解决网关路径剥离导致的微服务内部上下文混乱我们在 Quarkus 中配置了quarkus.http.root-path/svc1。结果 ArgoCD 部署后状态变为Degraded旧 Pod 迟迟不肯下线。破局配置全局 Context 后原本的健康检查探针/svc1返回了 404导致 K8s 判定新容器启动失败。为了保持 CD 图纸的通用性不把健康探针硬编码绑定到特定的业务路径/svc1/hello上我们在 Quarkus 代码中设计了一个专用的根路径探针接口完美迎合 K8s 的检查机制Path(/)publicclassGreetingResource{// 专供 K8s Readiness/Liveness 探针使用实际访问路径为 /svc1GETpublicStringhealth(){returnok;}// 实际业务接口真实访问路径为 /svc1/helloGETPath(/hello)Produces(MediaType.APPLICATION_JSON)publicMapString,Stringhello(ContextHttpServerRequestrequest){// ... 业务逻辑}}四、 结语从强耦合的单一 YAML到跨仓库的sed强推再到基于 Webhook Payload 动态渲染文件路径的Repository Dispatch。我们最终建立了一个“只关注产出不关注如何部署”的 CI 研发侧和一个“只接收信号不动业务代码”的 CD 运维侧。这正是云原生架构中最迷人的一面通过精巧的接口与事件边界让庞大的系统像齿轮般严丝合缝地自动运转。
跨仓库 GitOps :基于 GitHub Actions + ArgoCD 的终极解耦
在微服务云原生架构的演进中CI持续集成与 CD持续部署的边界划分往往是团队争论的焦点。传统的 CI/CD 流水线习惯于在构建出 Docker 镜像后顺手执行一条kubectl apply或者helm upgrade。这种“一把梭”的模式在单集群、小团队下固然痛快但随着多集群跨云联邦、基础设施即代码IaC的推行它的弊端暴露无遗CI 脚本被迫掌握了生产环境的高权限凭证业务代码仓库被塞满了部署脚本回滚完全依赖重新跑流水线。本文记录了我们将一个 Quarkus 微服务Data Plane 位于腾讯云 K3sControl Plane 位于阿里云 ArgoCD彻底向纯粹的事件驱动 GitOps 架构重构的全过程。一、 架构全景基于 Repository Dispatch 的事件驱动模型我们放弃了在同一个仓库里既写代码又写部署图纸的方案也放弃了让 CI 脚本跨仓库强行提交代码的半吊子解耦。最终落地的是跨仓库事件驱动Webhook架构。1.1 核心设计理念CI 仓库业务研发只负责代码编译、单元测试、打出镜像推送到 Registry。在结束前朝 CD 仓库大吼一声发送 Webhook“新版本造好了Tag 是 XXX”CD 仓库运维大本营存放所有的 ArgoCD Application 和 K8s YAML 图纸。它作为一个“接线员”监听 CI 发来的信号自动修改自己仓库里的镜像版本并提交。ArgoCD部署大脑只认 CD 仓库里的图纸图纸怎么画它就把腾讯云的集群变成什么样。1.2 Mermaid 架构流转图腾讯云 K3s (Spoke)阿里云 ArgoCD (Hub)CD 图纸库 (my-argocd-manifests)镜像仓库 (GHCR)业务代码库 (kong-gitops-experiment)开发者腾讯云 K3s (Spoke)阿里云 ArgoCD (Hub)CD 图纸库 (my-argocd-manifests)镜像仓库 (GHCR)业务代码库 (kong-gitops-experiment)开发者触发 GitHub Actions (ci.yml)触发 GitHub Actions (update-image-tag.yml)1. git push 提交业务代码2. Maven 编译 测试3. docker build push (Tag: SHA)4. Repository Dispatch API (携带 image_tag)5. 提取 Payloadsed 替换 YAML 文件中的 Tag6. 自动 Git Commit Push7. 轮询/Webhook 发现图纸更新8. 下发最新状态 (更新 Deployment)9. 拉取新版本镜像启动 Pod二、 核心实现代码为了实现这种松耦合我们在两个 GitHub 仓库中分别布置了流水线。2.1 业务侧只负责打包和“发信号” (ci.yml)在业务仓库中流水线的核心逻辑在最后一步发生了根本转变。我们不再去 checkout 配置仓库而是使用peter-evans/repository-dispatch触发远端的 API。# kong-gitops-experiment/.github/workflows/ci.ymlname:Quarkus API CI Buildon:push:branches:[main]paths:[apps/quarkus-svc/**,.github/workflows/ci.yml]permissions:contents:readpackages:writejobs:build-and-push:runs-on:ubuntu-lateststeps:-name:Checkout Codeuses:actions/checkoutv3# ... 省略 JDK 安装、Maven 编译和 GHCR docker push 步骤 ...-name:Trigger CD Repo (Repository Dispatch)uses:peter-evans/repository-dispatchv2with:token:${{secrets.CD_GIT_PAT}}repository:nvd11/my-argocd-manifestsevent-type:update-image-tag# 将当前微服务名称和最新的 Commit SHA 作为负载发送出去client-payload:{image_tag: ${{ github.sha }}, svc_name: quarkus-svc}2.2 CD 侧全自动的“配置接线员” (update-image-tag.yml)CD 仓库中的 Action 不受任何 Push 事件触发只监听repository_dispatch。它提取传递过来的参数精确定位到对应的图纸文件进行修改。# my-argocd-manifests/.github/workflows/update-image-tag.ymlname:CD Configuration Updateon:repository_dispatch:types:[update-image-tag]permissions:contents:writejobs:update-tag:runs-on:ubuntu-lateststeps:-name:Checkout Codeuses:actions/checkoutv3-name:Update Image Tag in ArgoCD App Manifestrun:|TAG${{ github.event.client_payload.image_tag }} SVC_NAME${{ github.event.client_payload.svc_name }}echo Updating image tag for $SVC_NAME to $TAG MANIFEST_FILEargocd-apps/${SVC_NAME}-app.yaml# 使用 sed 精准替换 ArgoCD Application 清单中的 tagsed -i -E s/tag:[a-f0-9](.*)/tag:${TAG}\1/g $MANIFEST_FILE git config--global user.name github-actions[bot] git config--global user.email 41898282github-actions[bot]users.noreply.github.com git add $MANIFEST_FILEgit commit -m ci:auto-update $SVC_NAME image tag to ${TAG}[skip ci] git push三、 实践中的深坑与工程化破局在整套架构贯通的过程中并非一帆风顺底层网络、K8s 网关标准以及身份验证等问题接踵而至。以下是极具价值的避坑记录3.1 跨仓库调用的权限壁垒 (GITHUB_TOKEN vs PAT)现象最初在 CI 脚本中调用跨仓库 API 时系统报Input required and not supplied: token或 403 Forbidden。破局GitHub Actions 默认注入的${{ secrets.GITHUB_TOKEN }}权限被严格圈禁在“当前运行的仓库”内。为了调用my-argocd-manifests的 Dispatch API必须生成一个带有repo权限的个人访问令牌 (PAT)并以CD_GIT_PAT的名字存入业务仓库的 Secrets 中。3.2 Kong KIC 引擎与 Gateway API 的“翻译崩溃”现象为了在网关层剥离路径我们在 Gateway API 的HTTPRoute中使用了URLRewrite过滤器结果 Kong 报错KongConfigurationTranslationFailed。破局经过排查开源版 Kong (KIC) 默认使用的是Traditional Router传统路由引擎它根本无法解析 K8s Gateway API 中高级的正则路径重写语法。强行开启未完全成熟的Expression Router风险极高。最终我们选择退回最稳定的注解方式konghq.com/strip-path: true但这又引入了微服务后端“路径迷失”的新问题。3.3 ArgoCD 部署降级 (Degraded) 与探针改造现象为了解决网关路径剥离导致的微服务内部上下文混乱我们在 Quarkus 中配置了quarkus.http.root-path/svc1。结果 ArgoCD 部署后状态变为Degraded旧 Pod 迟迟不肯下线。破局配置全局 Context 后原本的健康检查探针/svc1返回了 404导致 K8s 判定新容器启动失败。为了保持 CD 图纸的通用性不把健康探针硬编码绑定到特定的业务路径/svc1/hello上我们在 Quarkus 代码中设计了一个专用的根路径探针接口完美迎合 K8s 的检查机制Path(/)publicclassGreetingResource{// 专供 K8s Readiness/Liveness 探针使用实际访问路径为 /svc1GETpublicStringhealth(){returnok;}// 实际业务接口真实访问路径为 /svc1/helloGETPath(/hello)Produces(MediaType.APPLICATION_JSON)publicMapString,Stringhello(ContextHttpServerRequestrequest){// ... 业务逻辑}}四、 结语从强耦合的单一 YAML到跨仓库的sed强推再到基于 Webhook Payload 动态渲染文件路径的Repository Dispatch。我们最终建立了一个“只关注产出不关注如何部署”的 CI 研发侧和一个“只接收信号不动业务代码”的 CD 运维侧。这正是云原生架构中最迷人的一面通过精巧的接口与事件边界让庞大的系统像齿轮般严丝合缝地自动运转。