1. 什么是 GitOps不是新名词而是运维思维的“归位”你肯定听过 DevOps——开发和运维拧成一股绳用自动化流水线把代码从 IDE 直接送到用户手机里你也大概率接触过 MLOps它把机器学习模型的训练、验证、上线、监控全链路串起来让 AI 不再是实验室里的“一次性实验”。但当你在技术会议听到有人突然抛出 “GitOps” 这个词或者在 Kubernetes 社区看到大家反复强调 “Git 是唯一真相源”你心里可能闪过一个念头这又是个包装精美的 buzzword 吗还是说它真正在解决某个我们天天踩却习以为常的坑答案是后者。GitOps 不是凭空造出来的概念它是 DevOps 在基础设施层的一次“自然演进”更是对过去十年云原生实践的一次系统性反思与收敛。它的核心不是引入一堆新工具而是把一个早已被开发者奉为圭臬的协作范式——Git 工作流——原封不动、严丝合缝地移植到基础设施管理这个曾经最混乱、最黑盒、最依赖“人肉经验”的领域。我第一次在生产环境落地 GitOps 是在 2021 年当时团队正被 Kubernetes 集群的“配置漂移”折磨得夜不能寐。某天凌晨三点线上服务突然抖动排查发现是某个节点上的 ConfigMap 被手动修改过而 Git 仓库里对应的文件还是三个月前的版本。没人记得是谁改的、为什么改、改对了没有。那次事故后我们花了两周时间把所有集群的 YAML 文件、Helm values、Terraform state backend 全部收口到一个私有 Git 仓库并强制所有变更必须走 Pull Request。结果很直接下一次类似故障发生时我们 30 秒内就定位到是哪个 PR 引入了问题5 分钟内完成回滚。这不是魔法只是把写代码时最基础的纪律——版本控制、代码审查、自动化测试——搬到了运维现场。所以GitOps 的本质是让基础设施管理回归到一种“可读、可审、可测、可逆”的工程状态。它不关心你用的是 Kubernetes 还是 Nomad不挑剔你的 IaC 工具是 Terraform 还是 Pulumi甚至不强制要求你必须用容器。它只坚守一条铁律系统当前运行的真实状态Actual State必须能且只能由 Git 仓库中声明的期望状态Desired State自动、持续地推导和维持出来。这个“自动、持续”的过程就是 GitOps 的灵魂。它解决的从来不是“能不能自动化”的问题而是“自动化是否可信、是否可追溯、是否可审计”的问题。对于正在构建 LLM 应用的你来说这意味着你的模型服务 API、GPU 资源配额、Ingress 路由规则、甚至 Prometheus 的告警阈值都不再是散落在不同人脑中的模糊记忆或藏在某个运维同学笔记本里的临时命令而是一份份清晰、可 diff、可评论、可回溯的文本文件。这才是真正支撑起 AI 工程化落地的底层地基。2. GitOps 的底层逻辑与核心设计思想2.1 为什么是 Git而不是其他任何东西很多人初学 GitOps 会疑惑为什么非得是 Git用 SVN 行不行用数据库存配置行不行甚至用一个 Web UI 管理界面行不行这个问题的答案决定了你能否真正理解 GitOps 的设计哲学。Git 被选中绝非偶然而是因为它天然具备 GitOps 所需的全部“基因”。我们可以拆解为四个不可替代的硬性条件第一强一致性与不可变性。Git 的每一次 commit 都是一个 SHA-256 哈希值它精确地、唯一地标识了仓库在那一刻的完整快照。你无法“编辑”一个已存在的 commit只能基于它创建一个新的 commit。这种不可变性为基础设施状态提供了一个绝对可靠的“锚点”。当 Argo CD 或 Flux 检查到集群状态与某个 commit ID 不符时它知道该向哪个确定的目标去修复而不是在一个模糊的、可能已被覆盖的“最新版”上做猜测。相比之下一个 Web UI 的后台数据库其记录的“最后修改时间”永远无法保证操作的原子性和一致性。第二完备的协作工作流。Pull RequestPR是 Git 的核心协作机制。它天然集成了代码审查Code Review、讨论Comment、批准Approve、合并Merge等环节。将基础设施变更纳入 PR 流程意味着每一次扩容、每一次参数调整、每一次安全策略更新都必须经过至少一位同事的审视。我见过太多因为一个replicas: 3被手误改成replicas: 30导致集群雪崩的案例而一个简单的 PR review 就能拦住它。这种“强制性的多人确认”是任何单点操作界面都无法提供的安全护栏。第三强大的历史追溯能力。git log --oneline -p一条命令就能让你看到过去半年里每一个配置项是如何被谁、在什么背景下、出于什么原因被修改的。你可以轻松地git bisect定位到导致问题的那次提交也可以git revert精准地撤销某一次变更。这种能力在传统运维中是奢侈品。当一个数据库连接池参数被调低后服务开始超时你很难在一堆日志和聊天记录中还原出当时的决策链。而在 GitOps 下那条 commit message 里很可能就写着“#1234 降低连接池以缓解 MySQL 内存压力待观察 24 小时”。第四开箱即用的生态系统。GitHub、GitLab、Bitbucket 等平台早已围绕 Git 构建了完整的 CI/CD、权限管理、审计日志、Webhook 集成生态。你不需要自己造轮子去实现一个“配置变更通知中心”只需要在仓库设置里勾选一个 Webhook就能让 Argo CD 实时感知到推送。这种成熟度是其他任何配置存储方案短期内无法企及的。所以GitOps 选择 Git不是因为它“流行”而是因为它“可靠”、“可协作”、“可追溯”、“可集成”。它把一个已经被数千万开发者验证过的、最健壮的软件协作协议直接复用到了基础设施领域。这是一种极简主义的智慧不发明新轮子而是把最好的轮子装到最需要的地方。2.2 声明式Declarative与命令式Imperative两种截然不同的世界观理解 GitOps绕不开“声明式”与“命令式”这对概念。它们代表了两种完全不同的系统管理哲学其差异之大堪比“告诉导航仪‘我要去北京’”和“告诉司机‘先左转再直行 500 米然后右转’”。命令式Imperative是传统运维的典型方式。你登录到服务器执行kubectl scale deployment my-app --replicas5或者在 Terraform 中运行terraform apply -varenvprod。你下达的是一系列具体的、按顺序执行的操作指令Commands。它的特点是“过程导向”你关心的是“怎么做”。这种方式的问题在于它无法告诉你“现在系统到底是什么状态”。你执行了 10 条命令但其中第 7 条失败了第 9 条执行了两次那么最终状态是啥没人能立刻回答。它像是一本没有目录、没有页码、字迹还可能被涂改的笔记只有写笔记的人才知道故事的全貌。声明式Declarative则是 GitOps 的基石。你不再告诉系统“做什么”而是告诉系统“你想要什么样子”。你写一个 YAML 文件apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: replicas: 5 selector: matchLabels: app: my-app template: spec: containers: - name: app image: my-registry/my-app:v1.2.3这份文件就是一个“声明”Declaration它描述了你期望的 Deployment 应该具备的所有属性副本数是 5镜像版本是 v1.2.3标签是app: my-app。至于如何从当前状态可能是 3 个副本、v1.1.0 镜像变成这个目标状态那是 GitOps 控制器如 Argo CD的工作。它会自动计算出差异Diff并执行最小集的变更操作比如拉取新镜像、滚动更新 Pod。这种方式是“结果导向”你只关心“最终要什么样”。这种范式的转变带来了质的飞跃。首先它实现了幂等性Idempotency无论你对同一个声明执行 1 次还是 100 次最终结果都是一样的。这彻底消除了“重复执行命令导致意外”的风险。其次它天然支持状态比对与自愈。控制器可以 24/7 地运行一个循环读取 Git 中的声明 - 读取集群中的实际状态 - 计算差异 - 执行修复。如果有人绕过 Git直接在集群里执行kubectl delete pod控制器会在几秒内发现缺失了一个 Pod并立即创建一个新的来补足。这就是所谓的“自愈”Self-healing能力它让系统拥有了对抗人为失误和意外故障的免疫力。我在一个金融客户的项目中亲眼见证了这种威力。他们的合规要求极其严格所有生产环境的变更必须有双人审批。以前运维同学 A 审批后自己 SSH 登录执行命令B 同学只能通过日志确认。现在A 提交一个包含所有变更的 PRB 审批合并后Flux 控制器自动完成部署。整个过程A 和 B 都无需碰生产环境所有操作都固化在 Git 的 commit history 里审计报告一键生成。这不再是效率的提升而是安全边界的重构。2.3 拉取Pull与推送Push两种模型的本质区别与适用场景GitOps 的实现目前主要分为两大流派拉取Pull-based和推送Push-based。很多初学者容易陷入“哪个更好”的争论但更务实的视角是它们是同一枚硬币的两面服务于不同阶段、不同规模、不同成熟度的团队。拉取模型是 GitOps 的“纯正血统”也是 Argo CD、Flux 等专业 GitOps 工具所采用的方式。它的核心架构非常简洁一个运行在集群内部的“控制器”Controller它像一个不知疲倦的哨兵持续地、主动地Pull从 Git 仓库中拉取最新的配置文件并将其与集群当前的实际状态进行比对。一旦发现不一致它就立即执行修复操作。这个模型的威力在于其持续性Continuous和自治性Autonomous。它不依赖于任何外部事件触发也不需要你去“启动”一个部署流程。它是一个永不停歇的闭环。因此它天然具备三大核心能力持续校验Continuous Reconciliation每 30 秒可配置检查一次确保集群状态永不偏离 Git。漂移检测Drift Detection任何绕过 Git 的手动变更都会被瞬间捕获。自动修复Auto-Healing检测到漂移后自动执行反向操作将集群“拉回”到 Git 所声明的正确状态。推送模型则更像是 DevOps CI/CD 流水线的自然延伸。它依赖于 GitHub Actions、GitLab CI 等外部 CI/CD 系统。当开发者向 Git 仓库推送Push代码或配置后CI/CD 系统被 Webhook 触发它会执行一系列预定义的步骤构建镜像、运行测试、然后执行kubectl apply -f infra/prod/或helm upgrade命令将新的配置“推送”到集群。它的优势在于简单、直接、易上手。如果你已经熟练使用 GitHub Actions那么只需增加一个部署步骤就能享受到 GitOps 的大部分好处版本控制、PR 审查、自动化部署。它不需要在集群里额外部署和维护一个控制器学习成本极低。然而它的短板也十分明显且是根本性的无持续性CI/CD 只在代码推送时触发一次。它不会在之后的时间里持续关注集群。如果集群在部署后因某种原因发生了漂移比如节点宕机导致 Pod 丢失CI/CD 不会管它。无自愈能力它是一次性的“快照式”部署而非一个“活”的状态同步机制。安全边界更宽为了让 CI/CD 能够执行kubectl命令你必须将集群的访问凭证如 kubeconfig以密钥Secret的形式存储在 CI/CD 平台中。这相当于在 CI/CD 系统里放了一把通往生产环境的万能钥匙一旦 CI/CD 平台被攻破后果不堪设想。我的建议是对于个人项目、POC概念验证、或刚刚起步的小团队推送模型是绝佳的起点。它能让你快速获得 GitOps 的核心价值建立信心。但一旦项目进入关键业务阶段或者团队规模超过 5 人就必须认真评估向拉取模型迁移的必要性。这不是为了追求“高大上”而是为了给你的系统加上一道无法被绕过的、自动化的、全天候的安全与稳定性保障。就像汽车的安全带你平时感觉不到它的存在但当意外发生时它就是那道决定性的防线。3. 核心组件解析与实操要点3.1 Git 仓库不只是代码库更是唯一的“真相源”在 GitOps 中Git 仓库的角色发生了根本性的升维。它不再仅仅是存放应用源代码的地方而是整个系统生命周期的“单一事实来源”Single Source of Truth, SSOT。这意味着所有关于“系统应该是什么样”的信息都必须且只能存在于这个仓库里。任何存在于别处的配置都是潜在的风险点。一个符合 GitOps 最佳实践的仓库结构绝不是随意堆放文件的“大杂烩”。它必须经过精心设计以支撑可维护性、可审计性和环境隔离。以下是我在线上项目中验证过的、最稳健的目录结构my-gitops-repo/ ├── app/ # 应用代码与构建定义 │ ├── llm-gradio/ # 具体的 LLM 应用Gradio 前端 │ │ ├── main.py # 应用主逻辑 │ │ ├── requirements.txt # Python 依赖 │ │ └── Dockerfile # 构建容器镜像的指令 │ └── model-server/ # LLM 模型推理后端 │ ├── server.py │ └── Dockerfile ├── infra/ # 基础设施即代码 (IaC) │ ├── kubernetes/ # Kubernetes 相关配置 │ │ ├── base/ # 公共基础配置不直接部署 │ │ │ ├── kustomization.yaml │ │ │ └── deployment.yaml # 通用 Deployment 模板 │ │ ├── overlays/ # 环境覆盖层关键 │ │ │ ├── dev/ │ │ │ │ ├── kustomization.yaml # 指定 base dev 特有 patch │ │ │ │ └── patch-cpu-limit.yaml │ │ │ ├── staging/ │ │ │ └── production/ │ │ └── helm/ # Helm Chart如果使用 │ └── terraform/ # 云资源VPC, EKS Cluster, S3 Bucket │ ├── modules/ # 可复用的模块 │ └── environments/ │ ├── dev/ │ └── prod/ ├── ci/ # CI/CD 流水线定义 │ ├── build-image.yaml # 构建 Docker 镜像 │ └── test-app.yaml # 运行单元测试、集成测试 └── docs/ # 架构决策记录 (ADR)、部署手册 └── adr-001-use-kustomize.md这个结构的核心思想是“分层”与“隔离”。app/层专注业务逻辑。Dockerfile 在这里意味着镜像的构建过程是应用代码的一部分由应用开发者负责。这保证了“构建什么”和“怎么构建”是内聚的避免了运维团队和开发团队在镜像构建标准上的扯皮。infra/层专注基础设施。它又被细分为kubernetes/和terraform/。前者管集群内的资源Pod, Service, Ingress后者管集群外的云资源网络、存储、计算实例。这种分离遵循了“关注点分离”Separation of Concerns原则让不同领域的专家各司其职。overlays/目录这是 Kustomize 的精髓所在也是环境隔离的关键。base/目录存放所有环境共有的、不变的配置比如 Deployment 的基本结构、容器端口。而dev/,staging/,production/目录则只存放各自环境特有的“补丁”Patch比如dev环境的 CPU 限制是100mproduction环境是2000m。这样当你需要为production环境部署时Kustomize 会自动将base和production的补丁合并生成最终的、完整的 YAML。最大的好处是你永远不会因为复制粘贴而忘记修改某个环境的特定参数。提示切勿在base/中硬编码任何环境相关的值如image: my-app:dev。所有可变的值都应该通过kustomization.yaml中的images:字段或patchesStrategicMerge:来注入。这保证了base的纯粹性和可复用性。另一个至关重要的实操要点是分支策略。我强烈推荐采用main或master作为生产环境的“黄金分支”。所有直接合并到main的 PR都必须经过严格的自动化测试包括安全扫描、合规检查和至少两位核心成员的手动审批。对于开发和测试应使用特性分支Feature Branch或develop分支。staging环境的部署应由develop分支的合并触发。这种策略将“代码质量”与“部署环境”进行了强绑定确保了生产环境的稳定。3.2 GitOps 控制器Argo CD 与 Flux 的选型实战当你决定采用拉取模型时就必须在 Argo CD 和 Flux 这两个主流的 GitOps 控制器之间做出选择。它们都是 CNCF云原生计算基金会的毕业项目功能强大且成熟但设计理念和使用体验有显著差异。我的选型建议不是基于抽象的“谁更好”而是基于你团队的技术栈偏好和运维习惯。Argo CD是一个“开箱即用”的可视化平台。它的核心优势在于用户体验UX和企业级功能。安装后你会得到一个功能完备的 Web UI它能清晰地展示所有被管理的应用Application及其在 Git 中的源路径、目标集群、同步状态。应用的健康状态Healthy, Progressing, Degraded。Git 仓库中声明的期望状态Desired State与集群中实际状态Live State的逐行 Diff。完整的同步历史、日志和事件。这对于一个刚接触 GitOps 的团队或者一个需要向上级汇报、需要审计可视化的组织来说是巨大的福音。它降低了理解门槛让“GitOps 是什么”变得一目了然。此外Argo CD 对多集群管理、RBAC基于角色的访问控制、SSO单点登录的支持非常完善是大型企业落地的首选。Flux则是一个“轻量、原生、Kubernetes-first”的工具。它的设计理念是“尽可能少地引入新概念”一切皆为 Kubernetes 的 Custom Resource DefinitionCRD。当你安装 Flux你实际上是在集群里创建了一系列新的 Kubernetes 资源比如GitRepository指向你的 Git 仓库、Kustomization定义如何从仓库中提取和应用配置、HelmRelease管理 Helm Chart。你用kubectl get kustomization就能看到所有被管理的应用用kubectl describe kustomization my-app就能看到详细的同步状态和事件。Flux 的优势在于深度集成和极简心智模型。如果你的团队已经非常熟悉kubectl和 Kubernetes 的原生 API那么 Flux 的学习曲线几乎是平的。你不需要记住一套新的 CLI 命令或 UI 操作你就在用你每天都在用的工具。它没有 Web UI官方不提供社区有第三方但这恰恰是它的哲学GitOps 的真相在 Git 里不在 UI 里。UI 只是视图而 CRD 才是事实。我自己的实践是在内部 PoC 和小规模项目中我首选 Flux。因为它部署快flux bootstrap github一条命令搞定、调试直观kubectl就是你的调试器、与现有 Kubernetes 工作流无缝衔接。而在客户交付项目尤其是金融、政务等对可视化审计有硬性要求的场景我则坚定选择 Argo CD。它的 UI 是一个强大的沟通工具能让非技术人员如产品经理、合规官也能快速理解系统的当前状态和变更历史。无论选择哪一个有一个共同的、不容忽视的实操要点控制器自身的配置也必须被 GitOps 化。也就是说Argo CD 或 Flux 的安装清单、其管理的Application或Kustomization资源定义本身就应该存放在 Git 仓库中并由一个更高层级的 GitOps 控制器或一个初始的、手动的kubectl apply来管理。这被称为“GitOps 的递归”Recursive GitOps它确保了 GitOps 的根基本身也是可审计、可回滚的。否则你就陷入了“用 GitOps 管理应用但用人工管理 GitOps 工具”的悖论。3.3 CI/CD 流水线GitHub Actions 的精细化配置对于推送模型GitHub Actions 是目前最主流、最易上手的选择。但一个“能跑通”的流水线和一个“生产就绪”的流水线中间隔着无数个细节陷阱。下面是我总结的、在 LLM 项目中必须配置的几个关键环节。1. 镜像构建与安全扫描LLM 应用通常依赖大量 Python 包如transformers,torch这些包的供应链安全至关重要。一个简单的build-and-push步骤是远远不够的。# .github/workflows/ci.yaml name: CI Pipeline on: push: paths: - app/** - Dockerfile jobs: build-and-scan: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 # 使用 docker/build-push-action 进行构建它原生支持 BuildKit - name: Set up Docker Buildx uses: docker/setup-buildx-actionv3 - name: Login to Container Registry uses: docker/login-actionv3 with: username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} # 关键启用 BuildKit它能进行多阶段构建和缓存优化 - name: Build and push uses: docker/build-push-actionv5 with: context: ./app/llm-gradio push: true tags: ${{ secrets.REGISTRY_URL }}/llm-gradio:${{ github.sha }} cache-from: typegha cache-to: typegha,modemax # 关键在推送前进行安全扫描 - name: Scan image for vulnerabilities uses: anchore/scan-actionv4 with: image: ${{ secrets.REGISTRY_URL }}/llm-gradio:${{ github.sha }} fail-on: high # 使用 Trivy 作为备选它更轻量 # uses: aquasecurity/trivy-actionmaster # with: # image-ref: ${{ secrets.REGISTRY_URL }}/llm-gradio:${{ github.sha }} # format: sarif # output: trivy-results.sarif # severity: HIGH,CRITICAL这段配置的亮点在于BuildKit 缓存cache-from和cache-to利用了 GitHub Actions 的缓存机制使得后续构建能复用之前构建的中间层极大加速pip install等耗时步骤。安全门禁fail-on: high意味着如果扫描出任何 High 级别的漏洞整个流水线将失败阻止不安全的镜像被推送到仓库。这是保障 LLM 应用供应链安全的第一道闸门。2. 环境隔离的部署策略cd.yaml的核心任务是根据不同的分支或标签将应用部署到正确的环境。一个常见的错误是所有环境都用同一个kubectl apply命令只是替换了-f参数。这极易出错。# .github/workflows/cd.yaml name: CD Pipeline on: push: branches: - main # main 分支 production tags: - v* # v1.0.0 标签 production jobs: deploy-to-production: if: github.event_name push (github.head_ref main || startsWith(github.event.ref, refs/tags/v)) runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 # 使用 kubectl-action它会自动处理 kubeconfig - name: Deploy to Production uses: azure/k8s-deployv4 with: namespace: llm-prod manifests: | infra/kubernetes/overlays/production/ images: | ${{ secrets.REGISTRY_URL }}/llm-gradio:${{ github.sha }}这里的关键是if条件判断和manifests的路径指定。main分支和v*标签都指向production/目录而develop分支的部署则会指向staging/目录。这种基于 Git 语义的自动化路由是实现“环境即代码”的关键。3. 部署后的健康检查仅仅执行kubectl apply并不意味着部署成功。你需要验证应用是否真的 Ready。- name: Wait for Deployment to be Ready run: | timeout 300s bash -c while [[ \$(kubectl get deploy llm-gradio -n llm-prod -o jsonpath{.status.readyReplicas}) ! 1 ]]; do echo Waiting for deployment to be ready... sleep 10 done echo Deployment is ready! 这个简单的 Bash 循环会等待 Deployment 的readyReplicas达到预期值这里是 1超时时间为 5 分钟。如果超时流水线会失败提醒你去排查问题。这比让一个“看似成功”的部署流入生产环境要安全得多。4. 在 LLM 项目中落地 GitOps从零开始的完整实操4.1 项目初始化搭建一个可工作的 GitOps 基础框架让我们从零开始亲手搭建一个用于部署 LLM Gradio 应用的 GitOps 项目。这个过程我会带你走过每一个关键决策点并解释背后的理由。第一步创建 Git 仓库并初始化目录结构在 GitHub 上创建一个名为llm-gitops-demo的新仓库。然后克隆到本地并按照前文所述的结构创建骨架git clone https://github.com/your-org/llm-gitops-demo.git cd llm-gitops-demo # 创建标准目录 mkdir -p app/llm-gradio infra/kubernetes/{base,overlays/{dev,prod}} ci docs # 初始化 README echo # LLM GitOps Demo README.md echo This repo manages the infrastructure and application for our LLM Gradio demo. README.md git add . git commit -m chore: init project structure git push origin main第二步编写 LLM 应用代码在app/llm-gradio/目录下创建一个极简但功能完整的 Gradio 应用。我们不使用真实的 LLM那会涉及 GPU 和模型下载而是用一个模拟的响应函数重点在于验证 GitOps 流程。# app/llm-gradio/main.py import gradio as gr import time def simulate_llm_response(prompt): 模拟一个 LLM 的响应加入一点延迟以体现真实感 time.sleep(1) # 模拟模型推理延迟 return f Model response to {prompt}: This is a simulated answer from our GitOps-managed LLM service. # 创建 Gradio 接口 iface gr.Interface( fnsimulate_llm_response, inputsgr.Textbox(lines2, placeholderEnter your prompt here...), outputstext, titleGitOps-Managed LLM Demo, descriptionA simple demo showing how GitOps automates LLM application deployment. ) if __name__ __main__: iface.launch(server_name0.0.0.0, server_port7860)同时创建requirements.txt和Dockerfile# app/llm-gradio/requirements.txt gradio4.35.0# app/llm-gradio/Dockerfile FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY main.py . EXPOSE 7860 CMD [python, main.py]第三步定义 Kubernetes 基础配置在infra/kubernetes/base/目录下创建kustomization.yaml和deployment.yaml。这是所有环境共享的“蓝图”。# infra/kubernetes/base/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - deployment.yaml - service.yaml - ingress.yaml # 这里定义了所有环境都使用的公共镜像 images: - name: llm-gradio newName: ghcr.io/your-org/llm-gradio newTag: latest# infra/kubernetes/base/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: llm-gradio spec: replicas: 1 selector: matchLabels: app: llm-gradio template: metadata: labels: app: llm-gradio spec: containers: - name: app image: llm-gradio:latest # 这个会被 kustomization.yaml 中的 images 替换 ports: - containerPort: 7860 resources: requests: memory: 128Mi cpu: 100m limits: memory: 512Mi cpu: 500m第四步为不同环境创建覆盖层在infra/kubernetes/overlays/dev/目录下创建kustomization.yaml它指定了如何基于base构建开发环境的配置# infra/kubernetes/overlays/dev/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization bases: - ../../base # 开发环境使用 NodePort方便本地测试 patchesStrategicMerge: - nodeport-patch.yaml # 开发环境使用不同的镜像标签 images: - name: llm-gradio newTag: dev-${TARGET_COMMIT:-latest}# infra/kubernetes/overlays/dev/nodeport-patch.yaml apiVersion: v1 kind: Service metadata: name: llm-gradio spec: type: NodePort ports: - port: 7860 targetPort: 7860 nodePort: 30080第五步编写 CI/CD 流水线现在我们编写ci.yaml它负责构建、扫描和推送镜像。# .github/workflows/ci.yaml name: CI Pipeline on: push: paths: - app/llm-gradio/** - app/llm-gradio/Dockerfile jobs: build-and-push: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Set up Docker Buildx uses: docker/setup-buildx-actionv3 - name: Login to GitHub Container Registry uses: docker/login-actionv3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-actionv5 with: context: ./app/llm-gradio push: true tags: | ghcr.io/your-org/llm-gradio:dev-${{ github.sha }} ghcr.io/your-org/llm-gradio:dev-latest cache-from: typegha cache-to: typegha,modemax - name: Scan image for vulnerabilities uses: anchore/scan-actionv4 with: image: ghcr.io/your-org/llm-gradio:dev-${{ github.sha }} fail-on: medium # 开发环境可以放宽到 medium这个流水线会在每次向app/llm-gradio/目录推送代码时触发构建一个带有dev-commit-sha标签的镜像并进行安全扫描。整个过程从代码提交到镜像就绪通常在 3-5 分钟内完成。4.2 推送模型的部署GitHub Actions 自动化上线CI 流水线完成后镜像已经躺在 GitHub Container Registry 里了。接下来我们需要一个 CD 流水线将这个镜像部署到 Kubernetes 集群。第六步配置集群访问为了让 GitHub Actions 能够