# Argo CD GitOps 轨道 改 GitOps 仓库里的 Application / 清单 → 开 PR/MR → **用户合并** → Argo CD 同步到集群。 Agent 不直接 `kubectl apply` 工作负载,也不合并 MR。 Compose 的 `_config.yaml` / `sync.py` / `remote.py` 不用于本轨道。 ## 项目配置 `argocd.yaml` 只做指针:GitOps 仓库的 Git 地址,以及可选的本地目录。Application 名、镜像、namespace、域名、Secret 名的权威在 GitOps 清单里,不要再抄一份。 不要嵌进 `_config.yaml`(脚本解析器不支持嵌套映射)。 两种接法(人类说明见 skill `README.md`): ```yaml # 1)只写地址:部署时浅 clone 到临时目录,开完 MR 删掉 repo: git@git.example.com:org/infra-gitops.git # 2)已有 checkout:不 clone,直接在目录里改 # repo: git@git.example.com:org/infra-gitops.git # 可选;缺省用该目录 origin repo_dir: ../infra-gitops # 相对路径相对项目根 # path: argocd/applications/other-name # 仅当目录不是 argocd/applications/<源项目名> ``` | 字段 | 必填 | 说明 | |------|------|------| | `repo` | 与 `repo_dir` 至少一个 | Git 远程地址(`git@host:group/name.git` 或 `https://...`) | | `repo_dir` | 与 `repo` 至少一个 | 本地 checkout;相对**项目根**或绝对路径 | | `path` | 否 | 默认 `argocd/applications/<源项目 git 仓库名>` | `repo` 写成本地路径(`../`、`/`、`~/`)时,当作 `repo_dir`(旧写法兼容)。 有 `repo_dir` 且目录是 git 仓库 → **不 clone**,在里面改。`repo` 同时存在且与 `origin` 指向不同仓库时停下问用户。 只有 `repo` 地址 → `git clone --depth 1` 到 `mktemp -d`;开完 MR(或确认失败)后删掉临时目录。不要 clone 进源项目。 无此文件:只问 Git 地址,写成 `repo:` 再动手。 其余一律推导,不要写进这个文件: | 需要的信息 | 从哪来 | |------------|--------| | 推送 remote | `repo` 地址,或 `git -C remote get-url origin` | | Application 名 | `path` 末级,或 `application.yaml` 的 `metadata.name` | | 镜像(无 tag) | 清单里的 `image:`;首次无清单则项目 Makefile / `.env` 的 registry + name | | 升 tag 改哪个文件 | 在 `path` 下搜该镜像 | | namespace / host / imagePullSecret / TLS Secret | `application.yaml` 与 Ingress/Deployment | 默认 `path` 不存在:再按 `metadata.name == 源项目名` 搜;还没有则走「新增」,不要把 namespace/host 写回 `argocd.yaml`。 ## 摸底 1. 读 `argocd.yaml`,按上一节得到工作副本(浅 clone 或 `repo_dir`)。`repo_dir` 先 `git fetch`;工作区有无关改动则 `git worktree add` 隔离。 2. 打开 `path/`:有 `application.yaml` 视为已接入;只有惯例目录、没有本应用则走「新增」。 3. **清单写法跟目标 GitOps 仓库已有应用走**,不要另起一套: - 自建镜像:`application.yaml` + `manifests/`(Deployment/Service/Ingress) - 上游 Helm:`sources[]` 里 chart + 本仓库 `values.yaml` 4. 从兄弟应用抄:`spec.project`、`destination.server`、`syncPolicy`、IngressClass、TLS 引用方式。 5. 仓库 README 写明「无 app-of-apps / 需逐个创建 Application」时,合入 main **不会**自动注册新 Application。 ## 新增 Application 向用户确认清单里写不出的项:namespace、对外 host(或仅 ClusterIP)、是否要 `imagePullSecrets`、TLS Secret 名、镜像 tag。 然后在工作副本里: 1. 从最新默认分支拉出 `feat/`(浅 clone 已在默认分支;`repo_dir` 从 `origin/` 拉)。 2. 建 `path/application.yaml` + 清单(或 values),字段对齐兄弟应用。 3. 镜像用固定 tag,不用 `:latest`。 4. Secret、dockerconfigjson、口令 **不入库**;Deployment 只引用 Secret 名。 ## 升级 / 改清单 已有 Application 时: 1. 在 `path` 下搜到的镜像行,把 `image: :` 改成新 tag。 2. 只改本次要求的清单;不要顺手改无关 values。 3. tag 必须已经推进镜像仓库(本轨道不负责 `docker push`,那是 builder)。 ## 提交 MR 1. 分支从最新 `origin/` 拉出,名称如 `feat/` 或 `chore/-`。 2. 只提交 GitOps 仓库内本次文件。 3. `git push -u` 到 `origin`;不 `--force`、不硬 reset。 4. 按 remote 选 CLI:**GitLab `glab mr create`**、**GitHub `gh pr create`**、**Gitea/Forgejo `tea pulls create`**。 创建前读该子命令 `--help`;指定 title、body、base、head。 5. CLI 对项目 404(token 看不到仓库)而 SSH push 已成功:把 `git push` 打印的「create merge request」网页链接交给用户,不要读 token 配置、不要改用 curl 带 token。 6. **停在 MR**。不合并、不替用户点 Sync。浅 clone 的临时目录在得到 MR 链接(或确认失败)后删除。 MR 正文写清:改了什么、镜像引用、合入后用户还要做的事(首次 apply Application、拷 Secret、验 healthz/域名)。 ## 合入后交给用户 无 app-of-apps 时,**第一次**注册 Application(之后改 manifests 合入即可): ```bash kubectl apply -f /application.yaml ``` TLS / 拉镜像 Secret 必须已在**工作负载所在 ns** 存在。没有则从已有 ns 拷贝(见下),不要新建一份写进 Git。 ## Secret(ns 级,不入库) `imagePullSecrets` 与 Ingress `tls.secretName` 都只在 Pod/Ingress 所在 namespace 生效。 不是集群每个 ns 都要建,只给**实际跑这个负载的 ns** 准备。 列出已有拉镜像 Secret(名字以 Deployment 的 `imagePullSecrets` 为准): ```bash kubectl get secret -A ``` 拷到目标 ns(类型须为 `kubernetes.io/dockerconfigjson`): ```bash kubectl get secret -n SOURCE -o json \ | jq 'del( .metadata.uid, .metadata.resourceVersion, .metadata.creationTimestamp, .metadata.selfLink, .metadata.ownerReferences, .metadata.managedFields, .metadata.annotations, .status ) | .metadata.namespace = "DEST"' \ | kubectl apply -f - ``` 没有 `jq`: ```bash kubectl get secret -n SOURCE \ -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d > /tmp/dockerconfig.json kubectl -n DEST create secret generic \ --type=kubernetes.io/dockerconfigjson \ --from-file=.dockerconfigjson=/tmp/dockerconfig.json rm /tmp/dockerconfig.json ``` 本环境没有 kubeconfig 时,把命令交给用户执行,不要假装已经建好。 ## 验证 - MR URL 可打开,head 含本次清单,base 为约定默认分支 - 用户合并后:Argo CD 中该 Application Synced(automated 关闭时用户需手动 Sync) - 升级:集群里容器镜像 tag 与清单一致 - 有 Ingress:curl 域名 healthz / 业务路径;HTTPS 失败先查 TLS Secret 是否在该 ns ## 不要做 - 把 Compose 的 rsync/`remote.py` 套到集群 - 常规发布用 `kubectl apply` 工作负载绕过 GitOps - 提交 Harbor 密码、`dockerconfigjson`、TLS 私钥 - 在未获「合并」授权时 `glab mr merge` / `gh pr merge`