Flux:Git 配置部署、Helm 管理与镜像自动更新
Flux 将部署分给协作的 Kubernetes 控制器:Source 取得配置,Kustomize controller 将清单应用到集群,Helm controller 管理 chart 的发布生命周期。每个对象都保留自己的状态,来源下载失败、清单应用失败和 Pod 未就绪会出现在不同位置。
镜像更新也可以交给 Flux。两个可选控制器扫描仓库、选择版本并修改 Git 中标记过的字段;部署控制器随后消费新的提交。这样可以把“选择镜像”和“批准上线”分开,先把候选推入评审分支,再合并到环境使用的分支。
让 Git 中的配置成为可访问的应用
安装 CLI 与隔离控制面
主操作环境为 Linux、Bash、Docker Engine 与 Compose V2、kind、kubectl、Git、curl、Python 3。先完成 Docker Engine 和 kind 的基础操作。宿主操作者需要使用 Docker 和管理实验集群的权限;后续业务调谐使用单独的受限 ServiceAccount。
准备约 4 个 CPU、8 GiB 可用内存和镜像磁盘空间,回环端口 18091、18095、18080 应空闲。本地实验包括一个 kind 节点、六个 Flux 控制器,以及独立的 Git、镜像仓库。Git 和 Registry 使用命名卷,应用配置保存在 Git;它们的删除和恢复分开处理。
固定 Flux 2.9.5。以下摘要属于 Linux AMD64 压缩包,其他平台需在同一发行页选择对应资产,不能复用此值。
umask 077
LAB_ROOT=$(mktemp -d /tmp/flux-lab.XXXXXX)
cd "$LAB_ROOT"
mkdir bin downloads
curl -q --fail-with-body --location --retry 3 \
https://github.com/fluxcd/flux2/releases/download/v2.9.5/flux_2.9.5_linux_amd64.tar.gz \
-o downloads/flux.tar.gz || exit 1
printf '%s %s\n' \
b853df82adfd7736f580692f9f734473d571606307139f8fd20c2a80dd1ff473 \
downloads/flux.tar.gz | sha256sum -c - || exit 1
tar -xzf downloads/flux.tar.gz -C bin flux
chmod 700 bin/flux
export PATH="$LAB_ROOT/bin:$PATH"
flux --version
export KUBECONFIG="$LAB_ROOT/lab.kubeconfig"
kind create cluster --name flux-lab --kubeconfig "$KUBECONFIG" \
--image kindest/node:v1.35.5@sha256:ce977ae6d65918d0b58a5f8b5e940429c2ce42fa3a5619ec2bbc60b949c0ac95 \
--wait 120s
test "$(kubectl config current-context)" = kind-flux-lab || exit 1
flux check --pre版本应为 flux version 2.9.5,前置检查应通过。已有同名 kind 集群时,选择新的专用名称并同步修改 context,不删除用途不明的集群。最低 API 要求与 Kubernetes 仍受维护的版本范围应分别核对,安装入口和发行支持说明提供两类信息。
由固定 CLI 导出其内置安装清单,显式保留两个镜像控制器:
flux install --export \
--components-extra=image-reflector-controller,image-automation-controller \
> downloads/flux-system.yaml
kubectl apply --server-side -f downloads/flux-system.yaml
kubectl -n flux-system wait --for=condition=Available deployment --all --timeout=240s
flux check
kubectl -n flux-system get deployment,pods应有 source、kustomize、helm、notification、image-reflector、image-automation 六个 Deployment。source-watcher 是另一个可选组件,这里没有安装。flux install 不创建 Git 仓库,也不建立管理 Flux 自身的 Git 根;团队 bootstrap 稍后单独配置。显式传入 install 的 --version 会从发行端下载指定清单,与使用当前 CLI 内置清单的路径不同,见 install 命令。
镜像拉取失败时检查具体 registry、节点代理与 CA。CLI 下载、节点拉取镜像、Source 访问仓库由不同进程发起,各自需要正确网络配置。内网安装应取得固定发行的全部控制器镜像与安装清单,按离线安装迁入受控仓库,不关闭 TLS 校验。
准备本地 Git 与镜像仓库
下载完整实验包,解压到 $LAB_ROOT,使 compose.yaml、app/、image/ 直接位于当前目录。压缩包不带顶层包装目录。主要文件职责如下:
| 文件 | 作用 |
|---|---|
| compose.yaml | 独立 Gitea 和 Registry、回环端口及持久卷 |
| prepare-git.py、git-askpass.sh | 创建合成仓库身份,以及从私有文件向 Git 提供口令 |
| image/Dockerfile、image/server.py | 返回版本、商品种类和库存总量的 HTTP 服务 |
| app/kustomization.yaml、app/stock.json | 配置生成、镜像字段和库存输入 |
| app/deployment.yaml、app/service.yaml | 应用副本、探针、配置挂载及端口 |
| access.yaml、reconcile.yaml | 业务身份与 Flux Kustomization |
Gitea 使用固定的 rootless 镜像 1.27.3,运行 UID/GID 为 1000:1000,卷路径为 /var/lib/gitea、/etc/gitea;不要与 rootful 镜像的 /data 布局混用。Gitea rootless 安装说明了身份和目录要求。Registry 3.0.0 的默认进程身份为 root;业务镜像构建安装软件时也使用 root,最终业务进程为 UID 10001。这些身份各自独立。
两个服务只向宿主回环地址开放端口,同时接入 kind 的 Docker 网络。此实验内的 Git 和 Registry 使用 HTTP,只承载合成账户和数据;不要复制到共享或公网环境。
export COMPOSE_PROJECT_NAME=$(basename "$LAB_ROOT" | tr '[:upper:].' '[:lower:]-')
export COMPOSE_FILE="$LAB_ROOT/compose.yaml"
docker compose up -d
docker compose ps
GITEA_CONTAINER=$(docker compose ps -q gitea)
REGISTRY_CONTAINER=$(docker compose ps -q registry)
GITEA_IP=$(docker inspect -f '{{.NetworkSettings.Networks.kind.IPAddress}}' "$GITEA_CONTAINER")
REGISTRY_IP=$(docker inspect -f '{{.NetworkSettings.Networks.kind.IPAddress}}' "$REGISTRY_CONTAINER")
REGISTRY_NAME=$(docker inspect -f '{{.Name}}' "$REGISTRY_CONTAINER" | sed 's#^/##')
curl -q --noproxy '*' --fail-with-body http://127.0.0.1:18091/api/healthz
curl -q --noproxy '*' --fail-with-body http://127.0.0.1:18095/v2/两个 HTTP 检查应成功。连接被拒绝时先等待服务启动并看 docker compose logs --tail=80,端口占用则解决冲突后再继续。项目名来自新工作目录,避免换目录后仍接上固定项目名的旧卷;同一个项目重启会继续使用原数据。
初始化程序只接受指定 Compose 项目的 Gitea 服务,检查它没有用户后,创建 flux-owner、flux-reader、flux-bot 三个普通账户和一个私有 warehouse 仓库。owner 管理仓库,reader 只读,bot 是专用写账号;它们都没有站点管理员权限。
python3 prepare-git.py --container "$GITEA_CONTAINER" \
--project "$COMPOSE_PROJECT_NAME"
chmod 700 git-askpass.sh预期输出说明仓库与权限已创建。生成口令仅保存在当前 private/,不打印到共享终端日志;目录不可提交。已有用户、项目不匹配或 private/ 已存在时程序拒绝重新初始化。若中途失败,保留私有文件和卷,在当前实例核对已完成步骤,不删除凭据后强行重跑。main 的分支保护尚未设置,后续镜像回写前再收紧。
让节点从同一个实验 Registry 拉取镜像
宿主把镜像推到 localhost:18095,但 Kubernetes 节点里的 localhost 指节点自身。给本实验节点配置 containerd 的对应 registry 路由,将这个镜像前缀指向 Docker 网络中的 Registry:
cat > private/hosts.toml <<EOF
server = "http://$REGISTRY_NAME:5000"
[host."http://$REGISTRY_NAME:5000"]
capabilities = ["pull", "resolve"]
EOF
for node in $(kind get nodes --name flux-lab); do
docker exec "$node" mkdir -p /etc/containerd/certs.d/localhost:18095 || exit 1
docker cp private/hosts.toml "$node:/etc/containerd/certs.d/localhost:18095/hosts.toml" || exit 1
done
for version in 1.0.0 1.0.1; do
docker build --build-arg "APP_VERSION=$version" \
-t "localhost:18095/warehouse:$version" image || exit 1
docker push "localhost:18095/warehouse:$version" || exit 1
done两个不可变实验 tag 承载相同库存逻辑,但版本字段不同。首次部署明确使用 1.0.0,1.0.1 留作后续自动更新候选。推送后保存各自输出的 digest;不同机器重新构建可能得到不同摘要,不比对其他人的本机散列。
这组 hosts 配置只修改新建的 kind 节点,不改宿主 Docker daemon。镜像反射控制器稍后通过 $REGISTRY_IP:5000 扫描同一个仓库;节点拉取前缀与扫描地址不同,因此自动化分别更新 tag/digest,而不把扫描地址直接覆盖进部署配置。生产环境通常使用共同可解析、具备 TLS 和授权的 registry 域名。
提交应用,并以受限身份调谐
库存输入为 {"A":12,"B":18,"C":12}。HTTP 服务从 ConfigMap 读取它,返回商品种类、总量和镜像版本;输入损坏或不可读时返回 503。Kustomize 给 ConfigMap 名字追加内容摘要,并改写 Deployment 的挂载引用,配置变化会触发 Pod 滚动替换。
先把 app/ 推入新仓库。askpass 从私有文件读取 owner 口令,单次命令禁用其他 credential helper 和 HTTP 重定向;不会改个人 Git 配置。
git init --initial-branch=main
git config user.name 'Flux Lab'
git config user.email 'lab@example.invalid'
git add app
git -c commit.gpgsign=false commit -m '初始化库存应用'
git remote add origin http://127.0.0.1:18091/flux-owner/warehouse.git
export GIT_ASKPASS="$LAB_ROOT/git-askpass.sh" GIT_TERMINAL_PROMPT=0
export FLUX_GIT_USER=flux-owner
export FLUX_GIT_PASSWORD_FILE="$LAB_ROOT/private/owner-password"
git -c credential.helper= -c http.followRedirects=false push -u origin main只有 app/ 进入这次提交,私有目录、kubeconfig、控制面下载清单都留在本机。Git 服务中的 main 更新后,集群才有新输入可拉取。
kubectl create namespace warehouse
kubectl apply -f access.yaml
kubectl -n flux-system create secret generic warehouse-read \
--from-file=username=private/reader-username \
--from-file=password=private/reader-password
flux create source git warehouse \
--url="http://$GITEA_IP:3000/flux-owner/warehouse.git" \
--branch=main --secret-ref=warehouse-read --interval=1m \
--export > source.yaml
kubectl apply -f source.yaml
flux reconcile source git warehouse
kubectl apply -f reconcile.yaml
flux reconcile kustomization warehouse --with-sourceGitRepository 的 URL 必须是 HTTP(S) 或 SSH,不能使用 git:// 或 scp 短地址。仓库读取身份存于 Source 同命名空间的 Secret,见 GitRepository。应用写入身份是 flux-system/warehouse-reconciler:它只在 warehouse 中管理 ConfigMap、Service、Deployment,并读取相关 Pod、ReplicaSet 与事件,不允许创建 ClusterRoleBinding 或写其他命名空间。
reconcile.yaml 的核心配置如下。这里的 Flux Kustomization 是一个持续调谐对象,app/kustomization.yaml 则是被它调用的 Kustomize 配置文件:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: warehouse
namespace: flux-system
spec:
sourceRef: {kind: GitRepository, name: warehouse}
path: ./app
targetNamespace: warehouse
serviceAccountName: warehouse-reconciler
interval: 1m
timeout: 60s
prune: true
deletionPolicy: Orphan
wait: true调谐成功后,Source 的 Artifact revision 与 Kustomization 的 lastAppliedRevision 应包含同一个 Git commit。targetNamespace 不负责创建命名空间,因此前面由管理员预先创建。
flux get sources git
flux get kustomizations
kubectl -n flux-system get kustomization warehouse \
-o jsonpath='{.status.lastAppliedRevision}{"\n"}'
git rev-parse HEAD
kubectl -n warehouse rollout status deployment/warehouse --timeout=120s另开终端使用相同专用 kubeconfig,运行 kubectl -n warehouse port-forward service/warehouse 18080:80 --address 127.0.0.1 并保持进程运行。原终端请求:
curl -q --noproxy '*' --fail-with-body http://127.0.0.1:18080/响应应为 {"items": 3, "quantity": 42, "version": "1.0.0"}。若 Source 已 Ready 而调谐失败,先看 Kustomization 的条件;若 Pod 拉取失败,核对节点 registry 路由和实际镜像引用;若返回 503,查看挂载的 stock.json。port-forward 适合本机应用验证,真实 Ingress、TLS 和外部客户端入口另行检查。
来源、清单与 Helm 发布怎样协作
Source 取得的内容由下游分别消费
Git / OCI / Helm 仓库 / Bucket
│ 拉取、选择版本、可选验签
▼
Source Artifact
revision、digest、内容文件
├─ Kustomization → build / 解密 / SSA → inventory → 健康检查
└─ HelmChart 或 OCI chart → HelmRelease → Helm action / test / release storage
notification-controller:把上述对象事件发送给 Provider,也接受经验证的 Receiver 触发Artifact 是 Source 为消费方准备的文件归档及元数据。GitRepository 的 revision 对应取到的提交,digest 标识归档内容,URL 通常指向集群内的 source-controller 服务。原仓库、读取规则或版本发生变化后,Source 更新 Artifact,下游再执行自己的操作。归档缓存丢失后可以从仍可用的来源重建,但 Git 历史、仓库凭据或解密密钥丢失不能靠缓存补齐。
| 来源对象 | 版本或内容选择 | 常用接法 |
|---|---|---|
| GitRepository | branch、tag、commit;ignore 控制文件集 | 应用清单、Git 中的 chart、集群配置 |
| OCIRepository | tag、SemVer 或 digest;按媒体类型选择 layer | CI 构建的不可变部署归档,或 OCI chart |
| HelmRepository / HelmChart | 仓库索引与 chart 版本,或 Git 内的 chart 路径 | 由 helm-controller 执行发布生命周期 |
| Bucket | S3 等存储中的对象与过滤规则 | 已由外部流程投递的配置归档 |
Git 分支会移动,固定 commit 则停在选定版本;OCI 的 digest 适合锁定归档内容。OCI、Bucket 的身份配置和文件选择分别见 OCIRepository 与 Bucket。私有仓库的读取授权、Git/OCI 来源签名和运行镜像签名各有不同配置,Artifact 的内容摘要本身不替代来源验签。
Source 的 ignore 会改变归档文件集。把大目录排除可以降低传输与渲染开销;误排 app/ 或 chart 则会使下游找不到路径。monorepo 中多个应用共用一个 Source 很方便,但一次无关提交也可能触发多个消费方检查。根据归档大小、变更频率和所有者选择拆分粒度,避免只按目录数量拆成大量 Source。
更新配置,观察调谐和旧对象清理
将 app/stock.json 改为 {"A":12,"B":18,"C":30},总量变为 60,再沿既有 owner 身份提交:
git add app/stock.json
git -c commit.gpgsign=false commit -m '库存配置更新为六十'
git -c credential.helper= -c http.followRedirects=false push origin main
flux reconcile kustomization warehouse --with-source
kubectl -n warehouse get configmaps
kubectl -n warehouse get deployment warehouse新的 ConfigMap 名称与 Pod 模板一起变化;Flux Kustomization 的 prune: true 清理已不在本轮清单里的旧 ConfigMap。稳定后应只有一个 stock- 开头的配置对象。重新建立可能随旧 Pod 退出的端口转发,HTTP 应返回 quantity 60,镜像版本仍为 1.0.0。
再手动把 Deployment 改为三个副本,然后触发同一 Kustomization:
kubectl -n warehouse scale deployment/warehouse --replicas=3
flux reconcile kustomization warehouse
kubectl -n warehouse get deployment warehouse副本数恢复为 Git 中声明的一。Flux 使用 Server-Side Apply 管理字段;现场修改了它拥有的字段,下次调谐就会修正。需要 HPA 或其他控制器管理某个字段时,先确定字段所有权,再按 Kustomization 的 SSA 策略选择处理方式。不能用对整个对象 Ignore 来掩盖所有差异,atomic list 的合并也有约束。
interval 决定定期检查,Source 的新版本事件或手动 reconcile 可以提前触发。retryInterval 处理失败后的重试,timeout 限制这一轮操作等待。默认轮询、自愈和人工触发共同作用,临时维护要使用 flux suspend kustomization warehouse,修改恢复条件后再 resume;若此对象还被上层 Git 根管理,暂停状态也要从其所有者处保持。
从具体条件区分下载失败和发布失败
在合成 Source 上改成不存在的分支,不改实际仓库:
kubectl -n flux-system patch gitrepository warehouse --type=merge -p \
'{"spec":{"ref":{"branch":"missing-lab-branch"}}}'
flux reconcile source git warehouse --timeout=20s
kubectl -n flux-system describe gitrepository warehouse
kubectl -n flux-system get gitrepository warehouse \
-o jsonpath='{.status.artifact.revision}{"\n"}{.status.conditions}{"\n"}'CLI 可能只返回 context deadline exceeded,因为它等待的 Ready 条件一直没有达到。Source 中的具体错误应为 GitOperationFailed,message 指出找不到 refs/heads/missing-lab-branch。原 Artifact 及其 revision 仍可能保留,已经运行的应用继续返回原库存;查看时同时核对条件的 observedGeneration,不把历史 Artifact 当成这次获取成功。
恢复分支后重新调谐:
kubectl -n flux-system patch gitrepository warehouse --type=merge -p \
'{"spec":{"ref":{"branch":"main"}}}'
flux reconcile kustomization warehouse --with-source第二个负例只改变应用镜像:把 app/kustomization.yaml 的 newTag 改成 missing,提交并推送,再运行 flux reconcile kustomization warehouse --with-source --timeout=90s。Source 应能取得这个提交;下游则会遇到新 Pod 的 ErrImagePull 或 ImagePullBackOff,健康等待失败。检查这些位置:
kubectl -n flux-system describe kustomization warehouse
kubectl -n warehouse get pods
kubectl -n warehouse get events --field-selector type=Warning
kubectl -n flux-system get kustomization warehouse \
-o jsonpath='{.status.lastAttemptedRevision}{"\n"}{.status.lastAppliedRevision}{"\n"}'attempted revision 用于定位这次尝试的提交,applied revision 和 Ready 条件帮助判断成功完成到哪一步。旧 Pod 在滚动更新失败时仍可能服务请求,所以还要检查当前新副本、镜像引用和事件中的拉取原因。这里使用不存在的 tag,应看到相应的清单不存在错误;TLS 或授权错误属于另一条故障。
用 git log --oneline 确认错误提交,再 git revert、推送和调谐。镜像选择恢复后,检查 Ready、Deployment rollout 及 quantity 60 的响应;不要靠手动 set image 维持临时修复,否则下一轮仍会应用 Git 中的坏 tag。
inventory、健康和删除有各自的条件
.status.inventory 记录 Kustomization 已管理的对象。它让 Flux 能找到“上轮存在、本轮已从清单移除”的资源。源无法下载、路径无法 build,与成功生成较小或空的清单是不同情况;发生前两类错误时应修复输入,不能把它们当成正常资源下线。
| 设置 | 影响 |
|---|---|
prune: true | 回收本轮期望中已消失的 inventory 对象 |
对象注解 kustomize.toolkit.fluxcd.io/prune: disabled | 让这个资源跳过自动回收,后续需单独管理 |
deletionPolicy: Orphan | 删除 Kustomization 时保留工作负载 |
MirrorPrune | 删除 Kustomization 时按 prune 值选择删除或保留 |
Delete | 执行受管对象删除,不额外等待其最终终止 |
WaitForTermination | 等待受管对象终止,最多等到 spec.timeout |
WaitForTermination 超时后会记录错误并允许 Kustomization 删除,带 finalizer 的对象仍可能留在 Terminating。具体条件见删除策略。共享 Namespace、PVC 和 CRD 的删除需要额外安排,不能沿用无状态实验直接下线。
本实验设置了 Orphan,可以观察保留行为:
kubectl -n flux-system delete kustomization warehouse --wait=true --timeout=90s
kubectl -n warehouse get deployment,service,configmap应用资源应继续存在并可请求。恢复管理时重新应用 reconcile.yaml 并运行 reconcile,核对重新形成的 inventory。保留下来的资源没有消失,但在恢复管理前也不会继续响应 Git 更新。
健康判断采用 wait: true 时,会检查本轮全部资源,并覆盖显式 healthChecks 列表的选择作用。自定义资源可配置 CEL 健康表达式,但表达式必须覆盖缺字段和中间状态。上游 CRD/operator 与下游 CR 需要分层时,使用 dependsOn 等待对应 Kustomization 就绪;它约束开始条件,不提供跨应用事务。循环依赖需要修正依赖图,重试不会解除环。
readyExpr 可以增加版本锁步条件,但是否替代内置就绪检查还取决于相应 feature gate。按照 Kustomization 依赖与健康配置,不只看一个历史 Ready 值。部署顺序与删除顺序分别设计,避免先删 operator 再留下一批无法完成 finalizer 的 CR。
用 HelmRelease 管理安装、测试和失败补救
HelmRelease 把 chart、values 与 Helm 操作策略交给 helm-controller。安装产生的 release 历史保存于 Kubernetes Secret;运行资源和历史可以位于不同命名空间。这里将两者都放进独立的 warehouse-addon,避免 Helm 的存储权限接触 flux-system 中的仓库凭据。
实验包的 chart/ 包含 Chart.yaml、values schema、应用模板和真实 HTTP 测试 Hook。它部署另一个库存服务:默认只有一种商品,总量为 9。测试 Pod 请求 Service 并断言总量;模板中的配置摘要注解使 values 变化触发滚动更新。完整文件可从实验包查阅。
git add chart
git -c commit.gpgsign=false commit -m '加入库存 Helm chart'
git -c credential.helper= -c http.followRedirects=false push origin main
flux reconcile source git warehouse
kubectl create namespace warehouse-addon
kubectl apply -f helm-access.yaml
kubectl apply -f helmrelease.yaml
kubectl -n flux-system wait --for=create helmchart/flux-system-addon --timeout=120s
flux reconcile helmrelease addon --with-source --timeout=180s
flux get helmreleases
kubectl -n flux-system get helmrelease addon \
-o jsonpath='{.status.conditions}{"\n"}{.status.history}{"\n"}'首次创建 HelmRelease 后,控制器还要生成 HelmChart。等待其出现再使用 --with-source,可避免 CLI 查询尚未创建的对象。这里使用 Git 中的 ./chart 和 reconcileStrategy: Revision,Git 提交变化即重新准备 chart;在状态中会看到 0.1.0+提交前缀 一类版本。团队使用已发布 chart 时通常固定仓库版本或 OCI digest,不必让无关 Git 修改触发重新打包。
安装应同时满足 Ready=True、TestSuccess=True,历史中有 deployed release。查询 HTTP,预期 quantity 9、version 1.0.0:
kubectl -n warehouse-addon exec deployment/addon -- python3 -c \
'import json,urllib.request; d=json.load(urllib.request.urlopen("http://addon/",timeout=10)); assert d["quantity"]==9 and d["version"]=="1.0.0",d; print(d)'helm-access.yaml 只授权 addon-reconciler 管理这个命名空间内的业务资源、测试 Pod 与 release Secret。复杂 chart 可能包含 CRD、ClusterRole 或 Namespace;应先渲染并检查资源类型,再由平台管理员拆分集群级安装。不要为修复一次 Forbidden 就赋予业务 HelmRelease 集群管理员权限。
修改 values 并检查升级结果:
kubectl -n flux-system patch helmrelease addon --type=merge \
-p '{"spec":{"values":{"quantity":15}}}'
flux reconcile helmrelease addon --timeout=120s
kubectl -n warehouse-addon exec deployment/addon -- python3 -c \
'import json,urllib.request; d=json.load(urllib.request.urlopen("http://addon/",timeout=10)); assert d["quantity"]==15,d; print(d)'
kubectl -n warehouse-addon scale deployment/addon --replicas=3
flux reconcile helmrelease addon --timeout=120s
kubectl -n warehouse-addon get deployment addon本实验的 HelmRelease 由本机文件直接安装,因而可以用 patch 学习其行为;团队由 Git 中的 Kustomization 管理 HelmRelease 时,应修改 Git 文件。手动扩出的副本应在漂移检查后回到模板中的 1。driftDetection 也会影响 HPA 等控制器管理的字段,按实际所有者配置 ignore,不能让两个控制器持续争写 replicas。
接下来验证失败升级。附件显式选择 RemediateOnFailure,配置一次重试、rollback 补救,以及最后一次失败也执行补救。只设置 remediation 字段而忽略升级策略,可能受控制器默认策略影响。
kubectl -n flux-system patch helmrelease addon --type=merge \
-p '{"spec":{"values":{"image":"localhost:18095/warehouse:missing"}}}'
if flux reconcile helmrelease addon --timeout=150s; then
printf '%s\n' '坏镜像升级意外成功,请停止并核对实际镜像' >&2
exit 1
fi
kubectl -n flux-system get helmrelease addon \
-o jsonpath='{.status.conditions}{"\n"}{.status.upgradeFailures}{"\n"}{.status.history}{"\n"}'
kubectl -n warehouse-addon get pods
kubectl -n warehouse-addon exec deployment/addon -- python3 -c \
'import json,urllib.request; d=json.load(urllib.request.urlopen("http://addon/",timeout=10)); assert d["quantity"]==15 and d["version"]=="1.0.0",d; print(d)'这次输入的 tag 不存在。重试耗尽后应看到两次 upgradeFailures、Ready=False、已执行回退的 Remediated 条件,以及仍能返回总量 15 的旧版本。若 CLI 先超时,继续检查操作条件和 Pod 事件,不能把所有非零退出都归为镜像不存在。重试中间可能处于 Reconciling,不要只截取一个瞬间判断最终结果。
回退恢复了上一个 release,错误 values 仍在 HelmRelease 中。修复期望值后再次调谐:
kubectl -n flux-system patch helmrelease addon --type=merge \
-p '{"spec":{"values":{"image":"localhost:18095/warehouse:1.0.0"}}}'
flux reconcile helmrelease addon --timeout=120s
flux get helmreleasesReady 与测试应恢复成功。这里的 rollback 只涉及无状态应用和配置;数据库迁移、Hook 产生的外部写入需要自己的恢复方案。valuesFrom、测试失败、超时、补救次数、release storage 和漂移忽略条件见 HelmRelease。
从镜像仓库选出版本,再交给 Git 审核
扫描、选择和回写分别由什么控制
Registry 中的 tag / digest
→ ImageRepository:扫描可见 tag
→ ImagePolicy:按筛选和排序规则选择候选
→ ImageUpdateAutomation:修改带 marker 的 Git 字段并提交
→ automation/warehouse:检查差异,批准合并
→ main:Source 与 Kustomization 部署新的期望CI 负责构建和推送镜像,Flux 的镜像控制器不执行 Docker build。扫描成功后可能没有符合策略的版本;选出版本后也可能没有匹配 marker;已经推送候选分支时,环境使用的 main 还可能完全没变。分别查看这三个对象,可以定位更新停在哪一步。
先限制机器人写入,再启用回写
使用前面创建的 flux-owner 登录本地 Gitea。在 warehouse 仓库的 Settings → Branches 中为 main 添加保护规则:启用推送白名单,仅允许 flux-owner,禁止 force push。实验中的人工合并使用 owner;flux-bot 保留普通协作者写权限,只能向未保护的候选分支推送。生产环境还应通过团队评审和状态检查控制主分支合并。
也可以在实验宿主使用 API 设置同一规则。curl 提示输入 owner 口令时,从本机私有文件读取后输入;不要把口令拼入命令行、截图或 Git URL。
curl -q --noproxy '*' --fail-with-body --user flux-owner \
-H 'Content-Type: application/json' -X POST \
http://127.0.0.1:18091/api/v1/repos/flux-owner/warehouse/branch_protections \
--data '{"rule_name":"main","enable_push":true,"enable_push_whitelist":true,"push_whitelist_usernames":["flux-owner"],"enable_force_push":false}'UI 与 API 选择一种执行。已有规则时先读取和检查,不重复创建。字段以该实例的官方 API 说明及 /swagger.v1.json 为准。
用一个独立的空提交检查机器人确实不能写 main:
git switch -c bot-main-denial
git -c commit.gpgsign=false commit --allow-empty -m '检查机器人主分支权限'
export FLUX_GIT_USER=flux-bot
export FLUX_GIT_PASSWORD_FILE="$LAB_ROOT/private/bot-password"
if git -c credential.helper= -c http.followRedirects=false push origin HEAD:main; then
printf '%s\n' '机器人写入 main 成功,停止并修复分支保护' >&2
exit 1
fi
git switch main
export FLUX_GIT_USER=flux-owner
export FLUX_GIT_PASSWORD_FILE="$LAB_ROOT/private/owner-password"应看到服务端关于受保护分支的拒绝,而不是连接或认证失败;后一类错误无法检验分支权限。测试提交只留在本地测试分支,不合并进 main。
让候选版本写入独立分支
配置专用写 Source 和扫描对象。部署仍读取 warehouse Source 的只读凭据;写 Source 使用 bot 凭据。
kubectl -n flux-system create secret generic warehouse-write \
--from-file=username=private/bot-username \
--from-file=password=private/bot-password
flux create source git warehouse-write \
--url="http://$GITEA_IP:3000/flux-owner/warehouse.git" \
--branch=main --secret-ref=warehouse-write --interval=1m
cat > image-repository.yaml <<EOF
apiVersion: image.toolkit.fluxcd.io/v1
kind: ImageRepository
metadata:
name: warehouse
namespace: flux-system
spec:
interval: 1m
image: $REGISTRY_IP:5000/warehouse
insecure: true
EOF
kubectl apply -f image-repository.yaml
flux reconcile image repository warehouse
kubectl apply -f image-policy.yaml
kubectl -n flux-system wait imagepolicy/warehouse --for=condition=Ready --timeout=90s
flux get images allinsecure 只用于这个独立 HTTP Registry。私有 TLS 仓库应配置可信 CA 和最小读取凭据,认证 Secret 与 ImageRepository 同命名空间;云端身份的绑定方式见 ImageRepository。
image-policy.yaml 选择 >=1.0.0 <1.1.0 中最高的 SemVer。本实验扫描到两个 tag,候选应为 1.0.1,并有非空 digest。digestReflectionPolicy: IfNotPresent 在 tag 不变时保留首次取到的摘要,适用于不可变 tag;若要跟踪可变 tag,需使用 Always 并设置其 interval。完整筛选、字母或数值排序和摘要规则见 ImagePolicy。
应用已有两个 setter:
images:
- name: warehouse
newName: localhost:18095/warehouse
newTag: 1.0.0 # {"$imagepolicy": "flux-system:warehouse:tag"}
digest: "" # {"$imagepolicy": "flux-system:warehouse:digest"}它们只更新 tag 和 digest,保持节点使用的仓库前缀。安装回写对象并获取候选:
kubectl apply -f image-update.yaml
flux reconcile image update warehouse --timeout=120s
git -c credential.helper= -c http.followRedirects=false fetch origin
git diff --name-only main...origin/automation/warehouse
git diff main...origin/automation/warehouse -- app/kustomization.yaml差异应仅包含 app/kustomization.yaml,newTag 变成 1.0.1,digest 对应 ImagePolicy 的 latestRef,newName 保持不变。此时再次请求原应用,仍应返回 version 1.0.0。附件的 update.path 将修改范围限制在 ./app,policySelector 只选择带 app=warehouse 标签的策略;两者都不能替代 Git 服务端权限。
image-update.yaml 从 main checkout,推送到 automation/warehouse。默认情况下,控制器可能用 checkout 内容加本轮修改覆盖已有推送分支,因此该分支应专供自动化,不承载人工提交。禁用 GitForcePushBranch 后还需要处理分支落后导致的推送失败。分支、marker、提交与状态字段见 ImageUpdateAutomation。
合并后验证内容,回退时先处理选择策略
确认差异无误后,以 owner 将本地候选快进合并到 main:
git merge --ff-only origin/automation/warehouse
git -c credential.helper= -c http.followRedirects=false push origin main
flux reconcile kustomization warehouse --with-source
kubectl -n warehouse rollout status deployment/warehouse --timeout=120s
kubectl -n warehouse exec deployment/warehouse -- python3 -c \
'import json,urllib.request; d=json.load(urllib.request.urlopen("http://warehouse/",timeout=10)); assert d["quantity"]==60 and d["version"]=="1.0.1",d; print(d)'
kubectl -n warehouse get deployment warehouse \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'响应应同时保留配置总量 60 和新镜像版本 1.0.1,Deployment 镜像引用携带候选 digest。多平台镜像的索引摘要与运行平台 manifest 摘要可以不同,核对 Pod imageID 时要先解析选中的平台,不能直接要求它等于索引摘要。
若新版本业务检查失败,先 suspend ImageUpdateAutomation,修改选择策略排除问题版本,再将 Git 中镜像 tag 和 digest 一起恢复到已验证组合。只改 tag、留下新 digest 会继续使用该摘要指定的内容。旧版本重新部署成功且策略已纠正后再 resume;否则下一轮自动化仍可能把问题版本选回来。
该实验的候选由人工在本机检查并合并,没有配置托管平台的 PR 审批、签名或安全扫描。团队可把这些检查放进受保护分支的合并流程,Flux 继续消费审批后产生的提交。
团队安装、运行诊断与恢复
为每个集群建立明确的管理根
前面的 install 将控制器直接安装进实验集群。团队通常使用 bootstrap:它向 Git 提交 Flux 安装与同步清单,同时在目标集群建立从该路径读取配置的根。之后控制器自身的版本和参数也能接受 Git 管理。
在另一套已获准使用的测试集群上,由平台管理员准备专用 kubeconfig、现有私有 Git 仓库、可信 HTTPS 证书和具有该仓库读写权限的临时引导身份。仓库 URL、用户名、目标 context 由实际环境确定;不要使用个人全仓库管理员 Token。
: "${TEAM_KUBECONFIG:?指定团队测试集群 kubeconfig}"
: "${TEAM_CONTEXT:?指定经过确认的 context}"
: "${TEAM_GIT_URL:?指定 HTTPS 仓库 URL}"
: "${TEAM_GIT_USER:?指定引导身份}"
test "$(kubectl --kubeconfig "$TEAM_KUBECONFIG" config current-context)" = "$TEAM_CONTEXT" || exit 1
flux --kubeconfig "$TEAM_KUBECONFIG" check --pre
read -r -s -p '专用 Git Token: ' GIT_PASSWORD
printf '\n'
export GIT_PASSWORD
flux bootstrap git --kubeconfig "$TEAM_KUBECONFIG" \
--context "$TEAM_CONTEXT" --url "$TEAM_GIT_URL" \
--username "$TEAM_GIT_USER" --token-auth=true \
--branch main --path clusters/staging \
--components-extra=image-reflector-controller,image-automation-controller
bootstrap_status=$?
unset GIT_PASSWORD
test "$bootstrap_status" -eq 0 || exit 1
flux --kubeconfig "$TEAM_KUBECONFIG" check
flux --kubeconfig "$TEAM_KUBECONFIG" get sources git
flux --kubeconfig "$TEAM_KUBECONFIG" get kustomizations预期 Git 中生成 clusters/staging/flux-system,内含 gotk-components.yaml、gotk-sync.yaml 和 kustomization.yaml,根 Source、根 Kustomization 就绪。TLS 私有 CA 通过 --ca-file 提供。bootstrap 同时写 Git 与集群,失败时先检查两边已形成的对象,再修复权限或网络并重试,不能假设失败就没有任何修改。
SSH 路径可使用专用 deploy key;HTTPS token-auth 则将后续仓库访问凭据保存在集群 Secret。引导结束后,若集群只需读取,协调 Git 侧权限和集群 Secret,换成只读凭据并验证新提交可取,再撤销旧身份。镜像回写继续使用独立写 Source。不要先吊销旧凭据再发现新凭据无法读取。认证方式与多集群路径见 Git bootstrap。
不同环境使用独立目录和部署身份,例如 clusters/staging、clusters/production;共用仓库时还要控制谁能修改生产路径。目录隔离不会自动产生 Git 写权限隔离。每个集群各自部署 Flux 可以缩小集群凭据暴露面;集中调谐远程集群则需要额外维护目标 kubeconfig、失效和轮换流程。
已有 Operator 或 Helm 管理 Flux 安装时,保留明确的安装所有者,不让 bootstrap 再争写同一批控制器。采用新的管理方式前,先根据原管理器的迁移文档解除重复调谐,再接管;不能用 force 跳过所有权冲突后即认定迁移完成。
将仓库权限、调谐权限和解密权限分开
实验显式填写 serviceAccountName,并通过 RoleBinding 将这个身份限制在业务命名空间。核对方式是直接向 Kubernetes 查询授权:
kubectl auth can-i create deployments -n warehouse \
--as=system:serviceaccount:flux-system:warehouse-reconciler
kubectl auth can-i create clusterrolebindings \
--as=system:serviceaccount:flux-system:warehouse-reconciler
kubectl auth can-i create deployments -n default \
--as=system:serviceaccount:flux-system:warehouse-reconciler依次应为 yes、no、no。默认 Flux 控制器具有广泛集群权限,租户如果能任意删除 serviceAccountName、修改平台根或读取 flux-system 中的 Secret,单个示例 Role 无法形成完整隔离。
平台管理员应在安装根中设置禁止跨命名空间引用、禁止远程 Kustomize bases,并为省略执行身份的业务对象指定无额外权限的默认 ServiceAccount。平台根本身则保留创建 Namespace、角色与租户入口所需身份。具体控制器参数与根对象补丁见 Flux 多租户配置。同时限制租户创建特权 Pod、挂载宿主目录和访问其他工作负载凭据的能力。
NetworkPolicy 还依赖 CNI 实际执行策略。不能因为安装文件中存在策略,就声称本机 kind 已证明租户网络隔离。生产验收应从实际租户 Pod 测试允许和拒绝的 Git、Registry、Kubernetes API 及业务网络路径。
需要在 Git 保存加密 Secret 时,可让 Kustomization 使用 SOPS。它先取得加密文件,再用 Secret 或外部 KMS 身份解密,最后将明文资源提交给 Kubernetes。Git 中的密文与解密密钥必须分开保管,Kubernetes Secret 仍需 RBAC 和静态加密保护。
完成 SOPS 工具准备后,在独立的密钥工作目录生成 age 密钥;私钥文件放在 Git 工作树之外。使用它的公钥加密示例 Secret 的 data/stringData,再把密文加入应用 Kustomize 资源。对应 Flux 配置为:
spec:
decryption:
provider: sops
secretRef:
name: sops-agesops-age 与 Kustomization 同命名空间,保存私钥的键名需以 .agekey 结尾。业务执行身份还需有写目标 Secret 的权限;前面的 warehouse Role 故意没有授予该权限,不能不改授权就向其追加 Secret。解密失败时检查密文接收者、私钥键名和解密条件,勿把明文输出到公共日志。完整操作和 KMS 分支见 Flux SOPS 接入。备份恢复必须同时覆盖密文历史和仍有效的解密能力。
根据状态定位一次未生效的更新
先确定期待的 Git commit,再顺着对应对象查。下面命令针对本实验:
git rev-parse HEAD
flux get sources git
flux get kustomizations
flux get helmreleases
flux get images all
kubectl -n flux-system describe gitrepository warehouse
kubectl -n flux-system describe kustomization warehouse
kubectl -n warehouse get deployment,pods,events
kubectl -n flux-system logs deployment/kustomize-controller --since=10m --tail=120| 观察结果 | 下一步检查 |
|---|---|
| Git 已更新,Source revision 仍旧 | Source 是否 suspend,分支是否正确,凭据、DNS、CA、代理和拉取条件 |
| Source 已取得新 revision,Kustomization 没开始 | sourceRef、path、dependsOn 和 suspend;确认条件对应当前 generation |
| build 或解密失败 | 缺文件、Kustomize 语法、替换变量、SOPS 密钥;先修输入 |
| apply Forbidden | 实际 ServiceAccount、目标命名空间、Role;确认模板是否新增集群级对象 |
| 新 Deployment 不能就绪 | 镜像事件、探针、调度、资源配额;旧 Pod 的 HTTP 成功不能代替新副本检查 |
| HelmRelease 非 Ready,但旧服务可用 | 当前 Helm 操作、重试次数、Remediated、release history 与旧版本响应 |
| ImagePolicy 有候选,Git 没变化 | marker、update.path、policySelector、写 Source 凭据和服务端拒绝原因 |
| Git 已有候选,线上仍旧版本 | 是否只更新了候选分支、是否完成合并、部署 Source 选择哪条分支 |
前面的缺失分支实验中,CLI 首先给出等待超时,Source 条件才显示远程 ref 不存在;旧 Artifact 仍留在状态里。修复分支后,先确认新的 Artifact revision,再核对下游应用,避免把缓存中的历史内容当作刚刚取到的提交。
另一种常见情况是持续重试造成噪声。先保留条件、revision 和相关日志,再 suspend 具体对象进行修复。恢复前确认新输入能构建、权限合适,之后 resume 并主动 reconcile。只暂停 Source 不能阻止下游继续应用旧 Artifact;只暂停镜像自动化也不能阻止已合并的 Git 配置部署。
调谐耗时、告警与容量
控制器启动探针只反映进程状态。持续运行应观察调谐结果、耗时、队列积压、错误重试和容器资源,以及 Source 请求 Git/Registry、Kubernetes API 的延迟。先在实验中回环转发一个控制器的 metrics 端口:
kubectl -n flux-system port-forward deployment/kustomize-controller 18081:8080 --address 127.0.0.1另一个终端使用 curl -q --noproxy '*' --fail-with-body http://127.0.0.1:18081/metrics 读取指标。团队通过 ServiceMonitor 或自己的抓取配置接入 Prometheus,按实际指标标签区分 kind、namespace、name;指标选择与仪表盘见 Flux 监控指标。不要把 metrics 端口公开到互联网。
若 300 个对象每分钟至少触发一轮周期调谐,粗略平均是每秒 5 轮;每轮的文件解压、构建和 API 请求量各不相同,变更事件和失败重试还会叠加。这个数适合估算任务到达量,不能直接换算 CPU 核数。实际压测应保留仓库大小、对象数、变更比例和错误率,观察高分位耗时是否持续超过 interval。
Source 的内存和磁盘压力常随仓库归档体积与并发拉取上升;Kustomize 的渲染、解密与对象数量影响单轮峰值;Helm 还会等待工作负载和 Hook。先找耗时阶段,再调整 ignore、仓库拆分、interval、并发和资源。盲目增加并发可能先压满 Git 限流或 Kubernetes API。单个控制器的副本数量与有效工作并行度取决于其 leader election 和分片方式,不能只把 Deployment 扩成三副本就宣称吞吐扩大三倍。
通知侧用 Provider 定义目标、Alert 选择关注的 Flux 资源。先订阅少量应用,制造本地坏 Source 或坏镜像,确认告警能指向资源和 revision,修复后能识别恢复。外部平台地址、Token 和渠道权限按各 Provider 的告警接入配置。
Webhook Receiver 可缩短等待 interval 的时间,但仍需验签、TLS 入口和最小资源选择;收到事件只是触发调谐,最终结果仍由 Source 与部署对象状态确认。按Webhook Receiver对接真实 Git 服务,并测试无效签名被拒绝、有效事件能触发更新。未配置外部通知平台时,保持轮询可用,不把缺少 webhook 当成 Flux 无法部署。
从新的控制面恢复 Git 驱动的应用
恢复所需材料至少包括 Git 历史、固定安装清单、调谐对象与权限、仓库访问身份和可拉取的镜像;使用加密内容时还包括解密密钥。Helm release 历史、PVC 和外部数据库另行备份。仅保存 Source 缓存不能重建这些状态。
本机实验先导出已经修改过的期望,尤其是 Helm values 总量 15:
test "$(kubectl config current-context)" = kind-flux-lab || exit 1
mkdir -m 700 backup
flux export source git warehouse > backup/source.yaml
flux export kustomization warehouse > backup/workload.yaml
flux export helmrelease addon > backup/helmrelease.yaml
flux suspend image update warehouse这些 export 文件用于恢复对象配置,不携带业务数据、完整 Helm 历史或 Secret 内容。将私有凭据及安装清单按受控备份规则分别保存;不要用公开附件承载它们。
为模拟控制面停止,确认当前仅为专用实验集群后暂停控制器。工作负载保留:
test "$(kubectl config current-context)" = kind-flux-lab || exit 1
kubectl -n flux-system scale deployment --all --replicas=0
export KUBECONFIG="$LAB_ROOT/recovery.kubeconfig"
kind create cluster --name flux-recovery --kubeconfig "$KUBECONFIG" \
--image kindest/node:v1.35.5@sha256:ce977ae6d65918d0b58a5f8b5e940429c2ce42fa3a5619ec2bbc60b949c0ac95 \
--wait 120s
test "$(kubectl config current-context)" = kind-flux-recovery || exit 1
for node in $(kind get nodes --name flux-recovery); do
docker exec "$node" mkdir -p /etc/containerd/certs.d/localhost:18095 || exit 1
docker cp private/hosts.toml "$node:/etc/containerd/certs.d/localhost:18095/hosts.toml" || exit 1
done
kubectl apply --server-side -f downloads/flux-system.yaml
kubectl -n flux-system wait deployment --all --for=condition=Available --timeout=240s
flux check
kubectl create namespace warehouse
kubectl create namespace warehouse-addon
kubectl apply -f access.yaml -f helm-access.yaml
kubectl -n flux-system create secret generic warehouse-read \
--from-file=username=private/reader-username \
--from-file=password=private/reader-password
kubectl apply -f backup/source.yaml
flux reconcile source git warehouse
kubectl apply -f backup/workload.yaml
flux reconcile kustomization warehouse --with-source
kubectl apply -f backup/helmrelease.yaml
kubectl -n flux-system wait --for=create helmchart/flux-system-addon --timeout=120s
flux reconcile helmrelease addon --with-source --timeout=180s两套 kind 集群通过既有的实验网络访问同一 Git 和 Registry;备份中的地址在当前 Docker 网络内仍有效。真实跨主机恢复必须先恢复仓库、可信域名和网络入口,不能复制本机容器 IP 作为长期地址。
恢复后,warehouse 应为总量 60、version 1.0.1;addon 应为总量 15、version 1.0.0。分别按前面的 HTTP 断言检查,还要确认新集群中创建了实际 Pod。addon 在空存储中产生的是新的 Helm 安装历史,未恢复旧的 release 序号。若必须保留旧历史,需按集群备份方案连同 release Secret 和关联资源恢复,并验证状态匹配。
再将 app/stock.json 的 C 改为 31,提交、推送 main,在新集群调谐后应得到总量 61。这个步骤检查恢复后还能处理新的输入。原集群仍停留在旧配置;确认新控制面的身份和管理范围后,才考虑恢复镜像写 Source 与自动化,避免两套控制器同时写同一候选分支。
连同 CRD 升级,并保留有条件的回退路径
Flux 发行版包含多个控制器,bundle 版本与各控制器版本并不相同。升级应处理固定版本的完整安装清单,不能只 set image 更新某一个 Deployment。旧 API 迁移按升级说明及其链接的版本迁移步骤检查,特别关注 served/storage version、旧对象字段和工具内置的 Helm/Kustomize 变化。
下面在第三个专用 kind 集群演练 2.9.4 到 2.9.5 的补丁升级。准备旧 CLI,不改前面已恢复的集群:
mkdir downloads/old-bin
curl -q --fail-with-body --location --retry 3 \
https://github.com/fluxcd/flux2/releases/download/v2.9.4/flux_2.9.4_linux_amd64.tar.gz \
-o downloads/flux-old.tar.gz || exit 1
printf '%s %s\n' \
c2c397a52930f52d2005c01d276116b059d062de379386d58e98115380a766a2 \
downloads/flux-old.tar.gz | sha256sum -c - || exit 1
tar -xzf downloads/flux-old.tar.gz -C downloads/old-bin flux
downloads/old-bin/flux --version
downloads/old-bin/flux install --export \
--components-extra=image-reflector-controller,image-automation-controller \
> downloads/flux-2.9.4.yaml
export KUBECONFIG="$LAB_ROOT/upgrade.kubeconfig"
kind create cluster --name flux-upgrade --kubeconfig "$KUBECONFIG" \
--image kindest/node:v1.35.5@sha256:ce977ae6d65918d0b58a5f8b5e940429c2ce42fa3a5619ec2bbc60b949c0ac95 \
--wait 120s
test "$(kubectl config current-context)" = kind-flux-upgrade || exit 1
for node in $(kind get nodes --name flux-upgrade); do
docker exec "$node" mkdir -p /etc/containerd/certs.d/localhost:18095 || exit 1
docker cp private/hosts.toml "$node:/etc/containerd/certs.d/localhost:18095/hosts.toml" || exit 1
done
kubectl apply --server-side -f downloads/flux-2.9.4.yaml
kubectl -n flux-system wait deployment --all --for=condition=Available --timeout=240s
flux check接着执行上一小节从“创建 warehouse、warehouse-addon 命名空间”到 HelmRelease 调谐的恢复命令,仍使用这份 upgrade.kubeconfig。确认 warehouse 总量 61、版本 1.0.1,addon 总量 15、版本 1.0.0,形成旧控制器基线;此集群不安装镜像回写对象。
升级窗口内保持应用 Git 输入不变。比较两份清单时将输出保存在私有目录,核对 CRD schema/storage、RBAC、控制器参数与镜像变化。kubectl diff 返回 1 表示存在差异,其他错误需停止:
test "$(kubectl config current-context)" = kind-flux-upgrade || exit 1
diff_status=0
kubectl diff --server-side -f downloads/flux-system.yaml \
> private/flux-upgrade.diff 2> private/flux-upgrade.err || diff_status=$?
case "$diff_status" in
0|1) ;;
*) printf '%s\n' '无法取得有效差异,请先检查错误文件' >&2; exit 1 ;;
esac
less private/flux-upgrade.diff完成差异核对与备份后,应用 2.9.5 的完整清单并等待滚动更新:
kubectl apply --server-side -f downloads/flux-system.yaml || exit 1
for component in source kustomize helm notification image-reflector image-automation; do
kubectl -n flux-system rollout status "deployment/$component-controller" --timeout=240s || exit 1
done
flux check
kubectl -n flux-system get deployment \
-o custom-columns=NAME:.metadata.name,IMAGE:.spec.template.spec.containers[0].image
flux reconcile kustomization warehouse --with-source
flux reconcile helmrelease addon --with-source --timeout=180s核对六个控制器镜像都对应目标清单,再重跑两套应用的精确 HTTP 检查。只有控制器 rollout 成功、Source 和两种调谐对象成功、业务输出保持一致,才继续推进其他集群。
这两个补丁版本在本实验安装的 CRD spec 上相同,但不能据此推断其他版本也可降级。只有确认没有使用旧控制器不支持的新字段、没有不兼容存储迁移或外部副作用,并且旧清单及恢复材料可用,才在该实验集群执行:
test "$(kubectl config current-context)" = kind-flux-upgrade || exit 1
kubectl apply --server-side -f downloads/flux-2.9.4.yaml || exit 1
for component in source kustomize helm notification image-reflector image-automation; do
kubectl -n flux-system rollout status "deployment/$component-controller" --timeout=240s || exit 1
done
flux check
flux reconcile kustomization warehouse --with-source
flux reconcile helmrelease addon --with-source --timeout=180s再确认镜像回到旧清单、两个 HTTP 结果正确。如果升级涉及不兼容 schema 或持久化格式,停止机械降级,按预先验证的集群恢复方案处理。通过 bootstrap 管理自身的环境,应由安装根 Git 变更来完成升级与修复,避免本机 apply 随后又被根 Kustomization 覆盖。
卸载控制器和删除业务分开执行
先决定应用是继续运行、交给另一管理器,还是连同数据下线。移交时冻结原写入者,保存清单与当前镜像,并让新管理器完成一次没有意外删除的接管。不要在两个控制器同时工作时调整 field manager 直到“看起来不报错”。
对于第三个纯实验集群,可预览卸载并保留命名空间:
test "$(kubectl config current-context)" = kind-flux-upgrade || exit 1
flux uninstall --keep-namespace --dry-run核对删除范围后再执行 flux uninstall --keep-namespace,由交互确认继续。该命令处理 Flux 组件、CRD 和自定义对象,与直接 delete 一个带删除策略的 Kustomization 或 HelmRelease 不是同一操作。按照卸载说明确认期望保留的工作负载和 Helm release 存储,然后实际请求两个应用。
保留命名空间意味着其中凭据可能仍在,需要根据移交结果单独撤销 Git 写身份、读取凭据和外部通知凭据。卸载 Flux 不会删除外部 Git 仓库,也不承担数据库备份清理。
完全结束本机实验时,先停止相关 port-forward,检查三个精确名称都是本轮创建的 kind 集群,再逐个执行 kind delete cluster --name flux-lab、kind delete cluster --name flux-recovery、kind delete cluster --name flux-upgrade。这些操作删除相应实验集群中的全部对象。
最后在原 $LAB_ROOT 核对 docker compose ps 和 COMPOSE_PROJECT_NAME,再运行 docker compose down。不加 --volumes,可保留 Git、Registry 命名卷用于恢复;如果确实决定永久删除实验仓库与镜像,先核对精确卷名和备份后另行删除,勿清理整个 Docker 环境。
权威资料与规范地址
安装、版本与实验 Git 服务
Flux 2.9.5 发行说明:https://github.com/fluxcd/flux2/releases/tag/v2.9.5
Flux 2.9.4 发行说明:https://github.com/fluxcd/flux2/releases/tag/v2.9.4
安装要求:https://fluxcd.io/flux/installation/
发行支持周期:https://fluxcd.io/flux/releases/
flux install:https://fluxcd.io/flux/cmd/flux_install/
离线安装:https://fluxcd.io/flux/installation/configuration/air-gapped/
Git bootstrap:https://fluxcd.io/flux/installation/bootstrap/generic-git-server/
Gitea rootless Docker 安装:https://docs.gitea.com/installation/install-with-docker-rootless/
Gitea API 使用:https://docs.gitea.com/development/api-usage
来源、清单与 Helm
GitRepository:https://fluxcd.io/flux/components/source/gitrepositories/
OCIRepository:https://fluxcd.io/flux/components/source/ocirepositories/
Bucket:https://fluxcd.io/flux/components/source/buckets/
Kustomization:https://fluxcd.io/flux/components/kustomize/kustomizations/
Kustomization 删除策略:https://fluxcd.io/flux/components/kustomize/kustomizations/#deletion-policy
HelmRelease:https://fluxcd.io/flux/components/helm/helmreleases/
镜像扫描与回写
ImageRepository:https://fluxcd.io/flux/components/image/imagerepositories/
ImagePolicy:https://fluxcd.io/flux/components/image/imagepolicies/
ImageUpdateAutomation:https://fluxcd.io/flux/components/image/imageupdateautomations/
安全、监控与生命周期
多租户配置:https://fluxcd.io/flux/installation/configuration/multitenancy/
SOPS 接入:https://fluxcd.io/flux/guides/mozilla-sops/
监控指标:https://fluxcd.io/flux/monitoring/metrics/
告警接入:https://fluxcd.io/flux/monitoring/alerts/
Webhook Receiver:https://fluxcd.io/flux/guides/webhook-receivers/
Flux 升级:https://fluxcd.io/flux/installation/upgrade/
版本迁移步骤:https://github.com/fluxcd/flux2/discussions/5572
Flux 卸载:https://fluxcd.io/flux/installation/uninstall/
