建清单仓库并抓住漂移
目标
创建包含清单的 git 仓库并应用到集群,检测手工变更造成的漂移,并按仓库状态恢复。
为什么重要
GitOps 的规则只有一句:**仓库是正确来源,集群跟随仓库。**遵守它之后,“生产环境现在运行什么”就能通过 git log 回答,回滚也变成普通的 git revert。反之,只要仍有直接修改集群的习惯,仓库就会沦为无法说明现实的文档,环境也无法复现。本实验反复使用 kubectl diff 正因为如此:diff 以退出码回答“声明与实际相差多少”,它手工完成的判断与 ArgoCD 界面显示 OutOfSync 完全相同。本环境没有运行 ArgoCD 控制器,因此最后一步将亲自编写脚本,完成控制器本应代劳的工作。
步骤
- 创建
/root/gitops/repo并执行git init,在该仓库中配置user.name和user.email。然后创建/root/gitops/repo/README.md并提交首次 commit(至少要有一个 commit)。 - 将
/opt/lab/fixtures/gitops/seed/deployment.yaml和service.yaml复制到/root/gitops/repo/apps/web/。Deployment 的metadata.labels包含app.kubernetes.io/managed-by: gitops,spec.selector.matchLabels的app.kubernetes.io/name为web,并且必须显式指定spec.replicas,不能依赖默认值(此处从2开始)。容器镜像必须像nginx:1.27一样使用固定标签(:latest会失败)。README.md也必须保留。 - 对
apps/web/deployment.yaml、apps/web/service.yaml、README.md三个文件全部执行git add使其被跟踪,并用至少 10 个字符的有意义消息提交。完成后git status --porcelain输出必须为空。 - 使用
kubectl apply -n gitops-lab -f /root/gitops/repo/apps/web/应用,并将完整输出保存到/root/gitops/out/apply.txt。应用后,gitops-lab命名空间中必须存在 Deploymentweb和 Serviceweb,Deployment 具有app.kubernetes.io/managed-by=gitops标签。 - 将仓库
apps/web/deployment.yaml中的spec.replicas改为3,用包含单词replicas的提交消息提交,再次应用到集群。完成后应至少有 2 个 commit,仓库和集群的 replicas 都为3,工作树保持干净。 - 这次不要修改仓库,运行
kubectl scale deploy web -n gitops-lab --replicas=5制造漂移。将kubectl diff -n gitops-lab -f /root/gitops/repo/apps/web/的输出保存到/root/gitops/out/drift-diff.txt,把该命令的退出码保存到/root/gitops/out/drift-exit.txt。再在/root/gitops/out/drift-note.txt中用两三行中文说明手工变更会在下一次应用时被回滚并消失。 - 重新按仓库状态应用,消除漂移。然后再次运行
kubectl diff -n gitops-lab -f /root/gitops/repo/apps/web/,把此时的退出码保存到/root/gitops/out/clean-exit.txt。不得把手工设置的5写回仓库——仓库 replicas 应为3,工作树应干净。 - 创建
/root/gitops/sync.sh并赋予执行权限。脚本必须:(a) 用kubectl diff检查应用前差异;(b) 用kubectl apply应用;(c) 用git rev-parse HEAD记录应用了哪个 commit。执行后,在/root/gitops/out/sync-report.json中写入三个键:repo_commit(当前 HEAD 的完整哈希)、drift(布尔值false)、applied(应用的对象数,不小于2)。
参考
- 同一 commit 无论何时应用都必须得到相同结果,才称得上 GitOps。因此镜像标签要固定,replicas 等存在默认值的字段也要显式写明。未声明的值由应用时集群默认值决定,此时仓库便无法定义状态。
kubectl diff有差异时退出码为 1,无差异时为 0。它不同于kubectl apply --dry-run=server,会比较实际服务端合并结果与实时状态。- 必须在命令后立即用
echo $?读取退出码。用&&连接时,命令失败会导致后续部分不执行,因此使用명령 > 파일; echo $? > 코드파일这样的;更安全。 - 当前 HEAD 哈希可通过
git -C /root/gitops/repo rev-parse HEAD获取,必须使用完整哈希而非短哈希。 - 常见错误 1:在第 6 步把手工设置的
5也写回仓库。这不是解决漂移,而是把事故提升为代码。若该值确实正确,应另行正式提交;本实验的正确做法是回滚。 - 常见错误 2:把
sync.sh或out/放进仓库(/root/gitops/repo)内部。不提交会弄脏工作树,提交则会立刻使报告中的 HEAD 哈希失效。产物应放在/root/gitops/下、仓库外部。
初始化保存声明的仓库
创建 /root/gitops/repo 并执行 git init,在该仓库中配置 user.name 和 user.email。然后创建 /root/gitops/repo/README.md 并提交首次 commit(至少要有一个 commit)。
在 GitOps 中,commit 历史就是审计记录。要让仓库记录谁进行了修改,必须在该仓库配置 user.name 和 user.email,并且至少存在一个 commit。
创建按应用划分的清单目录
将 /opt/lab/fixtures/gitops/seed/deployment.yaml 和 service.yaml 复制到 /root/gitops/repo/apps/web/。Deployment 的 metadata.labels 包含 app.kubernetes.io/managed-by: gitops,spec.selector.matchLabels 的 app.kubernetes.io/name 为 web,并且必须显式指定 spec.replicas,不能依赖默认值(此处从 2 开始)。容器镜像必须像 nginx:1.27 一样使用固定标签(:latest 会失败)。README.md 也必须保留。
复制 fixture 使用,但仓库也需要便于人阅读。不要依赖默认值,应明确数量与镜像标签——若同一 commit 得到不同结果,仓库就没有定义状态。还要确认 selector 与 Pod 标签一致。
用 commit 固化声明
对 apps/web/deployment.yaml、apps/web/service.yaml、README.md 三个文件全部执行 git add 使其被跟踪,并用至少 10 个字符的有意义消息提交。完成后 git status --porcelain 输出必须为空。
若仍有未跟踪文件,仓库就无法完整定义集群。几个月后,人们会只看这一行提交消息来决定是否回滚,因此消息必须有意义。
将仓库声明应用到集群
使用 kubectl apply -n gitops-lab -f /root/gitops/repo/apps/web/ 应用,并将完整输出保存到 /root/gitops/out/apply.txt。应用后,gitops-lab 命名空间中必须存在 Deployment web 和 Service web,Deployment 具有 app.kubernetes.io/managed-by=gitops 标签。
可一次应用整个目录。清单未写命名空间,因此必须在命令中指定,并把应用结果保存为文件。
经由 commit 应用变更
将仓库 apps/web/deployment.yaml 中的 spec.replicas 改为 3,用包含单词 replicas 的提交消息提交,再次应用到集群。完成后应至少有 2 个 commit,仓库和集群的 replicas 都为 3,工作树保持干净。
顺序是关键:先修改仓库并提交,再应用。先修改集群,那不是变更,而是漂移。
手工修改并制造漂移
这次不要修改仓库,运行 kubectl scale deploy web -n gitops-lab --replicas=5 制造漂移。将 kubectl diff -n gitops-lab -f /root/gitops/repo/apps/web/ 的输出保存到 /root/gitops/out/drift-diff.txt,把该命令的退出码保存到 /root/gitops/out/drift-exit.txt。再在 /root/gitops/out/drift-note.txt 中用两三行中文说明手工变更会在下一次应用时被回滚并消失。
这次故意不碰仓库,只修改集群。本步骤的关键是查看差异命令的退出码,而且必须紧接该命令读取。
按仓库状态恢复
重新按仓库状态应用,消除漂移。然后再次运行 kubectl diff -n gitops-lab -f /root/gitops/repo/apps/web/,把此时的退出码保存到 /root/gitops/out/clean-exit.txt。不得把手工设置的 5 写回仓库——仓库 replicas 应为 3,工作树应干净。
把手工值偷偷写入仓库,就是把事故提升为代码。假定仓库正确并让集群与之对齐,再确认没有差异时的退出码。
创建同步脚本与报告
创建 /root/gitops/sync.sh 并赋予执行权限。脚本必须:(a) 用 kubectl diff 检查应用前差异;(b) 用 kubectl apply 应用;(c) 用 git rev-parse HEAD 记录应用了哪个 commit。执行后,在 /root/gitops/out/sync-report.json 中写入三个键:repo_commit(当前 HEAD 的完整哈希)、drift(布尔值 false)、applied(应用的对象数,不小于 2)。
用 shell 模拟控制器的工作:应用前查看将发生什么,执行应用,并记录应用了哪个 commit。若把脚本放进仓库,报告中记录的哈希就会失效。