Kubernetes 基础体系 · 第 56/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes GitOps:期望状态、调谐、Secret、漂移、晋级和回滚
GitOps 不是“把 Kubernetes YAML 放进 Git”这么简单。它是一种交付控制模型:
Git 中的版本化声明描述期望状态,集群中的控制器持续观察实际状态,并通过调谐把实际状态拉回期望状态。
因此,GitOps 的核心不是某个命令,而是几个相互关联的问题:
- 期望状态和实际状态分别是什么;
- 谁负责比较和调谐;
- Secret 如何进入声明式交付链路;
- 集群被人工修改后如何识别和修复漂移;
- 一个版本如何从开发环境晋级到生产环境;
- 回滚究竟是恢复 Git、恢复 Argo CD 历史,还是回退 Kubernetes 工作负载。
本文示例以 Kubernetes 当前稳定 API 为基础,使用 Argo CD 说明 GitOps 控制器的常见实现。Argo CD 自身的 CRD、命令和行为可能随版本变化,使用前应以所安装版本的文档和 CRD 定义为准。
一、先区分三个“状态”
1. 期望状态
期望状态是系统应该达到的配置集合,例如:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: demo
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: ghcr.io/example/web:1.4.2
ports:
- containerPort: 8080
它表达了几个约束:
demo命名空间中存在名为web的 Deployment;- Deployment 期望有 3 个副本;
- Pod 模板使用
ghcr.io/example/web:1.4.2; - 标签和选择器必须匹配。
这份 YAML 不是“执行三条命令”的脚本,而是一个目标描述。重复应用同一份声明,理论上应得到相同的目标结果,这种性质称为幂等性。
2. 实际状态
实际状态是 Kubernetes API Server 当前记录的对象,以及控制器观察到的运行结果。例如:
kubectl -n demo get deployment web \
-o jsonpath='{.spec.replicas}{" "}{.status.readyReplicas}{" "}{.spec.template.spec.containers[0].image}{"\n"}'
可能输出:
2 2 ghcr.io/example/web:1.4.1
这里至少存在两层实际状态:
- API 对象状态:Deployment 的
spec.replicas是 2,镜像是1.4.1; - 运行时状态:当前有 2 个 Pod Ready。
API 对象的 spec 更接近“当前被声明的目标”,而 status 是 Kubernetes 控制器根据观察结果写入的状态。status 通常不能由用户直接当作期望状态提交。
3. 观察状态
控制器还会看到 API 对象之外的事实,例如:
- Pod 是否已经调度;
- 容器是否启动;
- 镜像是否能拉取;
- 节点是否健康;
- LoadBalancer 是否分配地址;
- Job 是否成功结束。
所以,一个 Deployment 可能在 Git 和 API Server 中都显示 replicas: 3,但实际只有 2 个 Pod Ready。GitOps 控制器通常可以发现对象定义是否同步,却不能仅凭对象定义保证应用健康。
可以用下面的抽象表示:
GitOps 的基础比较主要是 与 ,健康检查则进一步使用 。
二、什么是调谐:从差异到收敛
1. 调谐循环
Kubernetes 控制器普遍采用调谐循环:
- 读取期望或当前对象;
- 读取依赖对象和运行时状态;
- 计算当前状态与目标状态的差异;
- 执行创建、更新、删除或等待;
- 再次观察结果。
Argo CD 在此基础上把 Git 仓库作为期望状态来源。简化的数据流如下:
flowchart LR
G[Git Repository] --> R[Manifest Renderer]
R --> D[Desired Resources]
A[Argo CD Controller] -->|读取| D
A -->|读取| K[Kubernetes API Server]
K --> L[Live Resources]
A --> C{比较与健康评估}
D --> C
L --> C
C -->|差异存在且允许同步| S[Apply / Delete]
S --> K
K --> W[Deployment/Service 等 Kubernetes Controllers]
W --> O[Pods、ReplicaSets、运行时状态]
O --> K
关键点是:Argo CD 通常不是直接管理 Pod,而是把 Deployment、Service、ConfigMap 等声明提交给 API Server;之后由 Kubernetes 自己的控制器创建 ReplicaSet 和 Pod。
2. 一个完整的调谐算例
假设 Git 中的 Deployment 是:
spec:
replicas: 3
template:
spec:
containers:
- name: web
image: ghcr.io/example/web:1.4.2
第一次观察到:
Git desired: replicas=3, image=1.4.2
Cluster live: replicas=2, image=1.4.1
控制器计算出差异:
replicas: 2 -> 3
image: 1.4.1 -> 1.4.2
它向 API Server 提交更新。Deployment 控制器随后不会直接“把旧 Pod 改成新镜像”,而是:
- 发现 Pod 模板哈希改变;
- 创建新 ReplicaSet;
- 按 RollingUpdate 策略逐步创建新 Pod、删除旧 Pod;
- 等待新 Pod Ready;
- 更新 Deployment 的
status。
因此,Argo CD 的同步成功不必然等于应用已经可用。同步成功通常表示资源更新请求已被接受或完成,而健康状态还要看 Deployment、Service、探针和应用自身。
3. 收敛不是无条件保证
调谐要达到稳定状态,至少需要满足以下条件:
- 期望状态可被 Kubernetes API 接受;
- 资源之间没有互相冲突的控制器;
- 控制器拥有所需 RBAC 权限;
- 依赖对象最终可以创建;
- 期望状态本身是可实现的;
- 外部系统不会持续把状态改回去。
例如 Git 要求:
spec:
replicas: 3
但另一个 HPA 根据 CPU 将副本数改为 5。此时两个控制器都在写同一个字段:
GitOps: replicas -> 3
HPA: replicas -> 5
GitOps: replicas -> 3
HPA: replicas -> 5
这不是“调谐速度不够”,而是两个控制回路的目标冲突。通常应让 HPA 管理副本数,并避免 GitOps 强制覆盖该字段;否则系统会振荡。
三、Argo CD Application:把 Git 映射到集群
Argo CD 中最重要的对象之一是 Application。它描述:
- Git 仓库地址;
- 路径或 Helm Chart;
- 目标集群;
- 目标命名空间;
- 是否自动同步;
- 是否允许剪枝和自愈。
示例:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web-demo
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/platform-config.git
targetRevision: main
path: apps/web/overlays/dev
destination:
server: https://kubernetes.default.svc
namespace: demo
syncPolicy:
automated:
prune: false
selfHeal: true
syncOptions:
- CreateNamespace=true
前置条件包括:
- 集群中已经安装 Argo CD;
- Argo CD 能访问该 Git 仓库;
apps/web/overlays/dev能被渲染为合法 Kubernetes 资源;- Argo CD 使用的 ServiceAccount 有权限访问目标命名空间;
demo命名空间不存在时,安装版本支持并允许CreateNamespace=true。
应用创建后可以观察:
kubectl apply -f application.yaml
argocd app get web-demo
常见结果类似:
Name: argocd/web-demo
Project: default
Server: https://kubernetes.default.svc
Namespace: demo
Sync Status: Synced
Health Status: Healthy
Synced 表示 Argo CD 认为期望资源与实际资源一致;Healthy 是健康评估结果。两者不是同一个维度:
Synced、OutOfSync:配置是否一致;Healthy、Progressing、Degraded:运行状态是否健康。
自动同步并不等于自动修复一切
automated.selfHeal: true 通常表示 Argo CD 发现受管理对象被直接修改后,会再次同步,使其回到 Git 版本。
prune: true 则允许删除 Git 中已经不存在的受管理资源。这个选项风险更高:
- 从 Git 删除文件可能触发集群删除;
- 误删路径或错误分支可能造成大范围破坏;
- 某些资源拥有持久化数据,删除资源与删除数据的后果可能不同;
- 自定义资源可能有级联删除行为。
生产环境通常需要明确资源所有权,并通过项目范围、命名空间范围、同步窗口、审批或策略检查限制删除影响。
四、GitOps 中的 Secret:编码不是加密
1. Kubernetes Secret 的实际含义
Kubernetes Secret 使用 data 字段时,值必须是 Base64 编码:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: demo
type: Opaque
data:
username: YXBw
password: c3VwZXItc2VjcmV0
解码后分别是:
username: app
password: super-secret
Base64 只是一种编码,不提供保密性。任何能读取这个 Secret 的主体,或者能读取包含它的 Git 文件的人,都可以解码。
Kubernetes 对 Secret 的保护依赖多个层次:
- API Server 的认证和 RBAC;
- etcd 的访问控制;
- 可选的 etcd 静态加密配置;
- 节点、日志、备份系统和控制器的访问边界。
即使启用了 etcd 加密,也不意味着 Secret 在 API 响应、Pod 环境变量、进程内存或日志中自动安全。
2. 不应把明文 Secret 直接提交 Git
下面这种文件即使 data 使用 Base64,也不应直接提交:
data:
password: c3VwZXItc2VjcmV0
因为 Git 历史会保留旧提交。删除当前文件并不能消除已经泄露的密码。发生泄露时,正确动作是:
- 立即吊销或轮换凭据;
- 检查 Git 历史、CI 日志和构建产物;
- 删除或限制暴露源;
- 重新发布使用新凭据的应用;
- 检查访问审计记录。
3. 三种常见 Secret 方案
方案 A:Sealed Secrets
Sealed Secrets 的思路是:
- 使用集群控制器的公钥在离线环境加密 Secret;
- 把加密后的自定义资源提交 Git;
- 控制器在集群内解密并生成普通 Secret。
Git 中看到的是类似:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: demo
spec:
encryptedData:
password: Ag...
优点是适合 GitOps,开发者不需要把明文提交到 Git。边界是:
- 加密私钥必须被妥善备份和保护;
- 丢失控制器密钥可能导致历史密文无法解密;
- 能在集群中读取普通 Secret 的主体仍然可以读取凭据;
- 密文虽然不能直接解密,但删除、替换和回滚仍会影响真实 Secret。
方案 B:SOPS 加密文件
SOPS 可以使用云 KMS、GPG 或 age 加密 YAML、JSON 等文件。Git 中提交密文,部署前由受信任的渲染或同步组件解密。
这种模式的安全边界取决于“谁在什么位置解密”:
Git 密文
-> CI 或 Argo CD 插件解密
-> 生成 Kubernetes Secret
-> API Server
如果 CI 日志打印了解密内容,或者渲染服务的权限过大,密码仍可能泄露。SOPS 只解决文件静态存储的加密问题,不自动解决运行时权限问题。
方案 C:External Secrets
External Secrets Operator 等方案让 Git 中只保存外部密钥的引用:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: demo
spec:
refreshInterval: 1h
secretStoreRef:
name: cloud-secrets
kind: ClusterSecretStore
target:
name: db-credentials
data:
- secretKey: password
remoteRef:
key: production/database
property: password
该示例依赖具体 Operator 的 CRD;apiVersion 和字段不能脱离安装版本直接假定。它的运行链路是:
- GitOps 应用
ExternalSecret; - Operator 使用云厂商或外部系统身份读取密钥;
- Operator 创建或更新普通 Kubernetes Secret;
- Pod 从普通 Secret 读取配置。
优点是凭据可以在外部系统轮换,不必修改 Git。风险是增加了一个控制器和外部依赖,必须验证:
- Operator 是否在线;
- 工作负载身份是否有最小读取权限;
- 外部密钥删除或改名后的行为;
- Secret 更新能否触发应用重新加载。
4. Secret 更新不一定会让应用立即使用新值
Secret 作为卷挂载时,kubelet 通常会最终更新挂载文件;但应用必须重新读取文件才能使用新值。
Secret 作为环境变量注入时,已经启动的进程不会自动获得新环境变量。常见做法是用 Secret 的哈希生成 Pod 模板注解:
spec:
template:
metadata:
annotations:
config-hash: "由渲染工具根据 Secret 内容计算的哈希"
哈希变化会改变 Pod 模板,Deployment 创建新 ReplicaSet,Pod 重新启动。
这里不能把“Secret 内容变化”与“Deployment 自动滚动更新”混为一谈。是否发生滚动更新取决于 Pod 模板是否改变,以及具体控制器是否负责重新加载。
五、漂移:集群为什么会偏离 Git
漂移是期望状态与实际状态不一致:
当 时,系统处于 OutOfSync 或等价状态。
1. 常见漂移来源
人工修改
kubectl -n demo scale deployment web --replicas=5
如果 Git 仍然要求 3 个副本,Argo CD 会看到:
desired replicas: 3
live replicas: 5
启用自愈后,Argo CD 可能把它改回 3;未启用自愈时,差异会持续存在。
HPA 修改
HPA 通过 scaleTargetRef 修改 Deployment 的副本数。这是合法的控制行为,不应简单视为错误漂移。若副本数由 HPA 管理,就不应同时让 GitOps 强制管理该字段。
Mutating Admission Webhook 修改
服务网格、Pod 安全组件或平台 Webhook 可能注入:
- sidecar 容器;
- 环境变量;
- 卷;
- 标签;
- 注解。
如果 Git 期望对象没有这些字段,比较结果可能长期 OutOfSync。
Kubernetes 控制器填充字段
API Server 或控制器可能添加默认值、状态字段和 managedFields。严谨的 GitOps 实现不会把所有服务端生成字段都当作用户声明差异,但不同工具的比较规则存在差异。
2. 漂移的诊断路径
先看 Argo CD:
argocd app diff web-demo
argocd app get web-demo
argocd app history web-demo
再看实际对象:
kubectl -n demo get deployment web -o yaml
查看谁修改了字段:
kubectl -n demo get deployment web -o json \
| jq '.metadata.managedFields[] |
{manager,operation,fieldsType,time}'
使用 managedFields 时要注意,它是服务端字段管理信息,不等价于完整审计记录;要追踪“谁在什么时候执行了什么请求”,仍应查看 API Server 审计日志或平台审计系统。
然后比较渲染结果,而不是只比较仓库中的模板:
helm template web ./charts/web \
-f ./env/dev-values.yaml > /tmp/desired.yaml
kubectl -n demo get deployment web -o yaml > /tmp/live.yaml
diff -u /tmp/desired.yaml /tmp/live.yaml
如果使用 Kustomize,则应使用对应的 kustomize build。只看 Helm 模板而不带实际 Values,是常见误判来源。
3. 不要随意忽略差异
Argo CD 可以配置 ignoreDifferences,例如忽略 HPA 管理的副本数字段。但忽略差异意味着:
控制器不再把这部分差异当作需要修复的错误。
这不是“隐藏界面噪声”那么简单。若错误地忽略镜像、权限、容器命令等字段,Git 可能看起来 Synced,实际却运行着未经审查的配置。
正确原则是:
- 先确定字段的真实所有者;
- 只忽略由明确控制器生成的字段;
- 记录忽略原因;
- 定期验证忽略规则没有扩大范围。
六、Helm 在 GitOps 中负责什么
Helm 是 Kubernetes 的打包和模板工具;Argo CD 是持续比较和同步的控制器。两者职责不同。
1. Helm 渲染链路
一个 Chart 可能包含:
Chart.yaml
values.yaml
templates/
deployment.yaml
service.yaml
渲染命令:
helm template web ./charts/web \
--namespace demo \
-f ./env/dev-values.yaml \
--set image.tag=1.4.2
Helm 将 Chart、Values 和命令行覆盖合并,渲染成 Kubernetes 清单。Argo CD 可以在集群内调用 Helm 渲染,然后比较渲染结果与集群对象。
在这种模式中,Git 通常保存:
- Chart;
- Values;
- 环境覆盖;
- 版本引用。
GitOps 控制器保存并执行同步,但不一定创建 Helm 的传统 Release 秘密。也就是说,Argo CD 使用 Helm 渲染,不等于 helm install 创建了一个由 Helm CLI 管理的 Release。具体行为取决于 Argo CD 的 Helm 集成方式。
2. Helm Release 回滚与 GitOps 回滚不是同一件事
传统 Helm 流程:
helm upgrade web ./charts/web -n demo
helm history web -n demo
helm rollback web 3 -n demo
这里 Helm Release 记录了历史修订。
GitOps 流程通常是:
Git 提交
-> Argo CD 渲染
-> API Server
回滚一般应通过 Git 恢复到旧提交或旧版本引用:
git revert <bad-commit>
git push
然后 Argo CD 重新渲染并同步。这样 Git 仍然记录了生产状态,其他环境和审计流程也能理解这次回退。
直接执行:
argocd app rollback web-demo <revision>
是否可用、具体语义和限制取决于 Argo CD 版本及应用历史。即使命令成功,Git 仍可能保留错误版本;后续自动同步还可能再次把 Git 版本应用回去。因此临时回滚后通常必须把 Git 修正到目标版本。
七、晋级:从环境差异转为版本流动
晋级是把已经在低风险环境验证过的版本,推进到更高环境,例如:
开发 -> 集成 -> 预发布 -> 生产
GitOps 的关键是晋级提交应改变“版本引用”,而不是在生产集群中手工修改对象。
1. 一个清晰的仓库结构
platform-config/
├── apps/
│ └── web/
│ ├── base/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ └── overlays/
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── patch.yaml
│ ├── staging/
│ └── prod/
└── argocd/
├── web-dev.yaml
├── web-staging.yaml
└── web-prod.yaml
base 描述共用结构,环境 Overlay 描述副本数、域名、资源限制和镜像版本等差异。
例如生产 Overlay 可以通过镜像替换:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
images:
- name: ghcr.io/example/web
newTag: 1.4.2
开发环境可能先使用 1.4.2-rc.1,测试通过后,生产 Overlay 通过一个独立提交改为不可变的 1.4.2。
2. 为什么推荐不可变版本
如果 Deployment 使用:
image: ghcr.io/example/web:latest
即使镜像仓库中的 latest 被重新推送,Git 中的 YAML 也没有变化,Argo CD 可能不会触发配置同步;节点上的重新拉取行为还受 imagePullPolicy 和缓存影响。
更可追踪的做法是:
image: ghcr.io/example/web:1.4.2
或者使用镜像 digest:
image: ghcr.io/example/web@sha256:abcdef...
Tag 便于阅读,digest 更接近不可变身份。实际晋级流程应同时记录:
- 应用版本;
- 镜像 digest;
- Chart 版本;
- 配置变更;
- 数据库迁移版本;
- 审批和验证结果。
3. 晋级的因果链
一个受控晋级可以是:
- CI 构建镜像;
- 运行单元测试、镜像扫描和部署验证;
- 将镜像推送到仓库;
- 生成版本 Tag 或 digest;
- 更新开发 Overlay;
- Argo CD 同步开发环境;
- 运行集成测试和验收;
- 通过 Pull Request 将同一版本引用复制到预发布;
- 预发布验证通过后,再更新生产 Overlay;
- 生产 Argo CD 在审批或同步窗口允许时部署。
“同一版本”非常重要。如果重新构建一次再推送,虽然源码 Tag 相同,也可能得到不同镜像。晋级应尽量推广已经构建并验证过的镜像身份,而不是重新构建。
八、回滚:回到哪个状态、回滚什么对象
回滚不是单一动作。至少要区分三类。
1. 配置回滚
如果错误来自 Deployment、Service 或 ConfigMap 的 Git 提交,使用 Git revert:
git revert <bad-commit>
git push origin main
Argo CD 发现新提交后,重新渲染旧配置并同步。
优点是:
- 目标状态重新回到 Git;
- 审计记录连续;
- 其他集群可以复用同一恢复提交。
2. 工作负载版本回滚
Deployment 自身记录 ReplicaSet 修订,可以查看:
kubectl -n demo rollout history deployment/web
kubectl -n demo rollout undo deployment/web
kubectl -n demo rollout status deployment/web
但在启用 GitOps 自动同步和自愈时,直接 kubectl rollout undo 可能只产生临时状态:
Deployment 回到旧 ReplicaSet
Git 仍然要求新镜像
Argo CD 发现漂移
Argo CD 再次应用新镜像
因此该方法适合紧急止血,但之后仍应提交 Git 回滚或暂停自动同步并完成正式修复。
3. 数据库回滚
应用代码可以从 1.4.2 回到 1.4.1,数据库却未必能安全回退。尤其是破坏性迁移:
版本 1.4.2:
添加新列,并删除旧列
回滚到 1.4.1:
旧代码仍依赖已删除的旧列
这时 Kubernetes Deployment 回滚成功,应用仍可能启动失败或数据访问失败。
更安全的迁移通常遵循扩展/收缩模式:
- 先添加新结构,不删除旧结构;
- 发布同时兼容新旧结构的应用;
- 回填数据;
- 切换读写逻辑;
- 确认旧版本不再需要后再删除旧结构。
所以一次生产回滚必须回答:
- 只回滚应用镜像,还是同时回滚配置;
- 数据库迁移是否可逆;
- 消息格式是否兼容;
- 缓存是否需要清理;
- 外部 API 是否已经产生不可逆副作用;
- 回滚后探针和业务指标是否恢复。
九、Kubernetes RollingUpdate 与 GitOps 回滚的关系
Deployment 默认使用 RollingUpdate。可以明确限制滚动过程:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
含义是:
maxUnavailable: 0:更新时不主动减少可用副本;maxSurge: 1:最多额外创建一个 Pod。
假设原来有 3 个副本:
开始: old=3, new=0
步骤1: old=3, new=1,等待 new Ready
步骤2: old=2, new=2,等待 new Ready
步骤3: old=1, new=3,删除最后一个旧 Pod
结束: old=0, new=3
这并不保证零停机,因为还存在:
- 应用启动时间;
- readinessProbe 配置错误;
- 节点资源不足;
- 新版本启动后立即崩溃;
- Service 端点更新延迟;
- 应用自身无法处理并发版本。
若新 Pod 永远不 Ready,Deployment 会停留在 Progressing 或最终表现为失败。诊断命令:
kubectl -n demo rollout status deployment/web
kubectl -n demo describe deployment/web
kubectl -n demo get pods -l app=web
kubectl -n demo describe pod <pod-name>
kubectl -n demo logs <pod-name> -c web
命令结果需要区分:
ImagePullBackOff:镜像地址、凭据或网络问题;CrashLoopBackOff:进程启动后退出,需看日志和配置;Pending:调度、资源、亲和性或污点问题;Readiness probe failed:容器可能运行,但未达到流量接收条件。
GitOps 只能把声明提交给 Kubernetes。它不能替应用修复错误的端口、数据库连接或启动逻辑。
十、失败路径与恢复操作
1. Git 提交无法渲染
表现可能是:
ComparisonError
Manifest generation error
常见原因:
- Helm Values 类型错误;
- Kustomize 路径错误;
- 模板引用不存在;
- 使用了集群未安装的 CRD;
- 清单使用了目标集群不支持的 API 版本。
先在本地复现:
helm template web ./charts/web -f env/prod-values.yaml
kustomize build apps/web/overlays/prod
kubectl apply --dry-run=server -f rendered.yaml
kubectl apply --dry-run=server 需要访问目标 API Server,可以检查资源结构和字段,但不等价于完整运行时验证。
2. 同步失败
表现可能是:
PermissionDenied
the server doesn't have a resource type
admission webhook denied the request
诊断顺序应包括:
kubectl auth can-i create deployment \
--as=system:serviceaccount:argocd:argocd-application-controller \
-n demo
kubectl api-resources
kubectl get crd
kubectl get events -n demo --sort-by=.lastTimestamp
需要分别判断:
- Argo CD 是否有 RBAC 权限;
- API 版本是否存在;
- CRD 是否先安装;
- Admission Webhook 是否拒绝;
- 目标命名空间是否正确;
- 资源是否被另一个控制器占用。
3. 同步成功但应用不可用
这通常是“配置一致、运行不健康”:
Sync Status: Synced
Health Status: Degraded
可能原因包括:
- Deployment 已更新,但 Pod 没有 Ready;
- Service selector 与 Pod 标签不匹配;
- Ingress 后端端口错误;
- Secret 键名不一致;
- 应用依赖数据库或外部 API 失败;
- 新版本有逻辑错误。
不能通过反复点击 Sync 解决。应从工作负载、事件、探针、应用日志和业务指标建立故障链。
十一、并发、所有权和“谁可以改生产”
GitOps 的一致性依赖明确的资源所有权。以下对象不应同时被多个独立系统无约束地管理:
- Deployment 的
spec.replicas; - HPA 关联的 scale 子资源;
- Service 的
spec.ports; - Helm、Operator 和 Argo CD 同时管理的同一 CR;
- Secret 的全部字段;
- Admission Webhook 自动注入的字段。
可以把字段所有权表示为:
镜像版本 -> GitOps
副本数 -> HPA
Pod 注入标签 -> Webhook
Secret 内容 -> 外部密钥系统
状态字段 -> Kubernetes Controller
这比“整个 YAML 归一个工具”更准确,因为现代 Kubernetes 使用 Server-Side Apply 的字段管理能力,可以在字段级别表达管理者。但字段管理仍不能消除语义冲突:两个控制器即使没有立刻触发冲突,也可能不断写入不同目标。
生产环境还应限制直接修改:
开发人员 -> 提交 Git PR
CI -> 验证和更新版本
Argo CD -> 同步集群
紧急运维 -> 受审计的临时操作
紧急 kubectl edit 不是绝对禁止,而是必须理解其后果:如果它没有进入 Git,下一次调谐可能覆盖它;如果启用了自愈,覆盖可能很快发生。
十二、GitOps 不等于完整的发布策略
GitOps 负责把版本和配置可靠地送到集群,但流量切换策略仍由 Kubernetes 资源和流量组件实现。
Rolling
Deployment 逐步替换 ReplicaSet,适合大多数无状态服务。它控制 Pod 替换速度,但不天然提供按用户、地域或百分比的业务流量分配。
Canary
Canary 需要额外的流量控制能力,例如 Ingress、Service Mesh 或网关。典型过程是:
版本 A 接收 100%
版本 B 接收 0%
版本 B 接收 5%
观察错误率和延迟
版本 B 接收 25%
继续观察
版本 B 接收 100%
GitOps 可以管理 Canary 对象、权重和分析规则,但指标系统、流量代理和分析控制器必须正确协作。
Blue-Green
Blue 和 Green 是两套并行工作负载,Service 或网关决定流量指向哪一套。切换通常很快,但需要承担双份资源、数据库兼容和切换后验证成本。
回滚与流量策略
流量切回旧版本不一定等于数据库和消息回滚。若新版本已经写入新格式,旧版本是否仍能读取,必须在发布设计中验证。发布策略解决“如何暴露版本”,GitOps 解决“哪个声明版本应被系统持续维持”,两者不是替代关系。
十三、一个可验证的最小流程
下面流程适用于已经安装 Argo CD、拥有可访问镜像仓库的测试集群。
首先提交一个 Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: demo
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
path: /
port: http
创建命名空间并应用:
kubectl create namespace demo
kubectl apply -f application.yaml
检查同步和运行状态:
argocd app get web-demo
kubectl -n demo get deployment,replicaset,pod
kubectl -n demo rollout status deployment/web
然后模拟漂移:
kubectl -n demo scale deployment web --replicas=4
kubectl -n demo get deployment web -o jsonpath='{.spec.replicas}{"\n"}'
若 selfHeal 生效,经过一次调谐后副本数应回到 Git 中的 2;验证:
kubectl -n demo get deployment web \
-o jsonpath='{.spec.replicas}{"\n"}'
再修改 Git 中的镜像版本,提交并推送:
git add .
git commit -m "promote web to 1.27"
git push
检查:
argocd app sync web-demo
kubectl -n demo rollout status deployment/web
kubectl -n demo get pods -l app=web \
-o jsonpath='{range .items[*]}{.metadata.name}{" "}{.spec.containers[0].image}{"\n"}'
最后执行正式回滚:
git revert <promote-commit>
git push
argocd app sync web-demo
kubectl -n demo rollout status deployment/web
这个流程验证了四件不同的事:
- Git 清单能否渲染和同步;
- Kubernetes Deployment 能否完成滚动更新;
- 人工修改是否形成可观察漂移;
- Git 回退是否能驱动工作负载回到旧版本。
十四、需要明确的生产边界
GitOps 提供的是声明、审计和持续调谐,不会自动解决以下问题:
- Secret 的运行时泄露;
- 数据库不可逆迁移;
- 错误版本的业务逻辑;
- 外部依赖不可用;
- 节点资源不足;
- 流量网关配置错误;
- 破坏性删除;
- 多控制器之间的写入冲突。
一个可靠的回滚条件可以形式化为:
其中:
ArtifactAvailable:旧镜像和 Chart 仍可获取;ConfigCompatible:旧版本能使用当前配置;DataCompatible:旧版本能读写当前数据;TrafficSwitchable:流量可以切回旧版本;DependencyRecoverable:外部依赖状态允许恢复。
只满足“旧镜像还在”,并不能推出系统可安全回滚。
GitOps 最终建立的是一个持续运行的控制回路:
Git 版本
-> 清单渲染
-> 期望状态
-> API Server
-> Kubernetes 控制器
-> 运行时状态
-> 差异与健康观测
-> 再次调谐
理解这条回路后,Secret、漂移、晋级和回滚就不再是孤立功能,而是同一个问题的不同阶段:如何让可审计的期望状态,在真实、有并发、有故障的集群中逐步收敛,并在收敛失败时保留可验证的恢复路径。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kustomize 配置管理:Base、Overlay、Patch、Generator 和边界
- 下一篇:Kubernetes 发布策略:Rolling、Canary、Blue-Green、流量和回滚
- 延伸:Helm 完整指南:Chart、Template、Values、Release、Hook 和回滚
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论