Skip to content

Latest commit

 

History

23 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

从提交到外网:一份可开关的 Helmfile 清单

这是一条给开发和运维共用的链路:从 git push 到客户能打开 HTTPS,同时能管集群、看指标、日志、链路和剖析。不必在每台机器上散落 helm install。本仓用 Helmfile 做成开关:每个应用独立 release、独立 namespace,关掉一个不会绑死其它。

git push
  → GitLab(代码仓、MR)
    → CI(GitLab CI + Runner,或 Jenkins):构建镜像
      → 镜像仓库(GitLab Registry 或 Harbor)
        → CD(Argo CD 或 Flux):同步到集群
          → 应用运行(PVC、数据库、队列、对象存储按需)
            → APISIX 或 Envoy Gateway(Gateway API)按域名对外
              → 可选 ExternalDNS 写公网记录
              → 外网 HTTPS
          → 集群管理(CiliKube / Headlamp)
          → 可观测(Prometheus / Grafana / VictoriaLogs / Tempo / Pyroscope / OTel)
          → 服务网格(Istio,按需)

Chart 本体不进 Git(charts/platform-config 和 charts/flux-gitops 除外)。其余部署时从官方 Helm / OCI 仓库和 charts.cillian.website(CiliKube)拉取。

开关在 values/defaults.yaml 的 apps.<id>.enabled。默认装入口、存储类、集群管理和可观测;代码、CI、镜像仓库、CD 和中间件按环境打开。

GitOps 管的应用不走 Helmfile。apps.flux2 只装控制器,装完它还不知道看哪个仓;fluxGitops 补上这一步,渲染一个 GitRepository 和若干 Kustomization。仓库地址、分支、目录是每个集群自己的事,写在 values/private.yaml(不进 Git),格式见 values/private.yaml.example:

fluxGitops:
  gitUrl: "https://gitlab.example.com/you/gitops.git"
  deployToken: ""          # 只读 token,公开仓可留空
  kustomizations:
    - name: demo
      path: ./apps/demo
      namespace: demo
      createNamespace: true

fluxGitops.enabled 需要同时开 apps.flux2.enabled,且 gitUrl 和 kustomizations 都不能空,否则 helmfile 直接 fail——装个只拉代码不 apply 的 Flux 没有意义。

组件

分类 组件 作用 默认
代码 GitLab 代码仓、MR 关
CI GitLab CI 随 GitLab,构建并推镜像 随 GitLab
CI GitLab Runner 在集群里跑 GitLab CI job;需 gitlabUrl + runnerToken 开
CI Jenkins 独立 CI,与 GitLab CI 二选一即可 开
镜像仓库 GitLab Registry 随 GitLab,CI 推镜像 随 GitLab
镜像仓库 Harbor 独立仓库;与 GitLab Registry 二选一 关
CD Argo CD 把应用同步到集群 开
CD Flux(community chart) 默认走 flux bootstrap,不从这里装 关
CD Flux Operator 官方 bootstrap 方式之一,与上一行互斥 关
流量入口 MetalLB 裸金属 L2 VIP,给网关 LoadBalancer 开
流量入口 cert-manager 发证书;ClusterIssuer selfsigned,可选 local-ca、letsencrypt-prod 开
流量入口 APISIX HTTP/HTTPS 网关与 Ingress Controller 开
流量入口 Gateway API Kubernetes Gateway / HTTPRoute CRD 关
流量入口 Envoy Gateway Gateway API 的一种实现(GatewayClass eg) 关
流量入口 ExternalDNS 把 Ingress / Service(开 Gateway API 时含 HTTPRoute)写成 DNS 关
存储 local-path-provisioner 动态 PVC,设为 default StorageClass 开
存储 RustFS S3 对象存储(standalone,首选) 关
存储 MinIO S3 对象存储,与 RustFS 互斥 关
存储 JuiceFS CSI POSIX 文件系统,依赖 Redis + RustFS 或 MinIO 关
中间件 PostgreSQL 关系数据库 关
中间件 Redis 缓存;开 JuiceFS 时必须开 关
中间件 NATS 消息队列 关
可观测 kube-prometheus-stack Prometheus、Grafana、Alertmanager 开
可观测 告警转发 Alertmanager → 飞书机器人;填了 alerting.lark.webhookUrl 才渲染 随地址
可观测 VictoriaLogs 日志存储 开
可观测 VictoriaLogs Collector 采集节点上的容器日志 开
可观测 VictoriaMetrics 指标备选,不要与 Prometheus 同时当主刮取 关
可观测 OpenTelemetry Collector OTLP 采集(deployment 模式,带 k8sattributes) 开
可观测 Tempo 链路 开
可观测 Pyroscope 持续剖析 开
可观测 Loki 日志备选,与 VictoriaLogs 互斥 关
可观测 Beyla eBPF 自动剖析(特权 DaemonSet) 关
服务网格 Istio sidecar 网格(base + istiod);南北流量仍走 APISIX 关
集群管理 metrics-server kubectl top / HPA;kubeadm 与 Kind 用 --kubelet-insecure-tls 开
集群管理 CiliKube 看节点和工作负载 开
集群管理 Headlamp 看节点和工作负载 开

业务入口默认用 Ingress,class 取 private.yaml 的 ingressClass(默认 apisix),证书注解 cert-manager.io/cluster-issuer: selfsigned(公网改 letsencrypt-prod)。

域名解析到内网地址时,ACME 签不出证书:Let's Encrypt 要从公网访问 http://<域名>/.well-known/... 做 http01 校验,指向 192.168.x.x 的记录它连不上;dns01 则要求 DNS 托管商有对应的 cert-manager webhook。这种情况打开 localCa.enabled:platform-config 会用 selfsigned 签一张自签根证书,再建一个从它签发的 local-ca ClusterIssuer。Ingress 注解写 cert-manager.io/cluster-issuer: local-ca,客户端导入一次根证书,之后这个 issuer 签的所有域名都受信任。导出根证书:

kubectl get secret local-ca-tls -n cert-manager \
  -o jsonpath='{.data.ca\.crt}' | base64 -d > local-ca.crt

网关要自己打开 HTTPS 监听:apisix chart 的 apisix.ssl.enabled 默认是 false,此时 Ingress 里的 tls: 块会被正常接受、证书也会签出来,但 443 根本没人监听。本仓 values/apps/apisix.yaml 已经打开它并把 Service 的 tls.servicePort 设成 443。备选是 Gateway API:先开 gatewayApi(只装 CRD)或直接开 envoyGateway(CRD + 控制器)。HTTPRoute 用 GatewayClass eg。南北流量选一个当主(APISIX 或 Envoy Gateway),不要两个一起当主入口。Istio 只做 sidecar,不提供对外 Ingress。

APISIX ingress controller 2.x 还需要一个 GatewayProxy:控制器自己不知道该把路由写给哪个数据面,缺了它 Ingress 会被 reconcile 然后丢掉,网关对所有域名回 404 Route Not Found,日志里只有一句 no GatewayProxy configs provided。charts/platform-config 的 apisixGatewayProxy(默认关)负责建这个对象;apisix chart 建出来的 IngressClass 没有 parameters,还要按模板注释里的 kubectl patch 指到该 GatewayProxy。

Envoy Gateway 不处理 Ingress 对象,它只实现 Gateway API。所以关掉 APISIX 时,示例 Ingress 和 Let's Encrypt 的 http01 挑战都会指向一个没人接管的 class;这种组合 helmfile 直接 fail,要么开着 APISIX,要么把 ingressClass 改成实际在跑的控制器。

CI 把镜像推到 GitLab Registry 或 Harbor,不要两个都当主仓库。开 Harbor 时,清单会关掉 GitLab 自带 Registry。Harbor 默认 ClusterIP(http://harbor.harbor.svc),需要域名时再改 Ingress。

GitLab 的外部依赖

chart 10.x 不再自带 PostgreSQL、Redis 和对象存储,缺一个就连渲染都过不去。所以 apps.gitlab.enabled: true 之外还要:

  • gitlab.domain:必填,GitLab 的所有 URL 由它拼出来
  • 数据库和缓存:开着本仓的 postgresql / redis 就自动指向 postgresql.<ns>.svc 和 redis-master.<ns>.svc;用外部实例就填 gitlab.psqlHost / gitlab.redisHost
  • 本仓不会替你建这些 Secret,install 前先在 GitLab 的命名空间里建好(名字可在 private.yaml 改):
Secret key 内容
gitlab-psql password 数据库用户 gitlab 的口令
gitlab-redis redis-password Redis 口令
gitlab-object-storage connection Rails 的对象存储连接(YAML)
gitlab-registry-storage config Registry 后端配置(开 Harbor 时用不到)
gitlab-s3cmd config 备份用的 s3cmd 配置

数据库里的 gitlab 用户和 gitlabhq_production 库也要自己建——本仓的 postgresql release 是通用的,不会为 GitLab 预置。

GitLab 自带的 cert-manager 与 issuer 在 values/apps/gitlab.yaml 里关掉了:本仓单独装 cert-manager,ClusterIssuer 由 charts/platform-config 提供,两边都开会变成两个证书来源。

GitLab CI 要在集群里跑 job,还要单独开 gitlabRunner(只开 GitLab 不会装 Runner)。在 private.yaml 填 gitlabUrl(带 https://)、runnerToken 和 tags。token 从 GitLab 里已注册的 runner 拿,不是旧的 registration token。tags 不填时 chart 会把 RUNNER_TAG_LIST 渲成空字符串,下一次 apply 会清掉 Runner 上已有的标签,按标签挑 Runner 的 job 就再也排不上。GitLab 的主机名在集群里解析不了时,在 private.yaml 写 gitlabRunner.hostAliases(ip + hostnames 的列表)。CoreDNS 不读节点 /etc/hosts,而顶层 hostAliases 只作用在 Runner 自己的 Pod,job Pod 是另一批 Pod,要在 runners.config 里重复一遍 [[runners.kubernetes.host_aliases]],否则 DinD 解析不了那个主机名——helmfile 会按这份列表把两处一起渲染出来。DinD 还必须在同一段里写 privileged = true(chart 0.89 不再认 runners.privileged,见 values/apps/gitlab-runner.yaml)。

ExternalDNS 默认关。打开后按 Ingress / Service 写 DNS;同时开了 Gateway API 或 Envoy Gateway 时会多 watch HTTPRoute。策略是 upsert-only(不删你在 DNS 控制台手工建的记录)。必须在 private.yaml 写 provider(不要用 chart 默认的 aws);Cloudflare 还要 cloudflareApiToken。

metrics-server 默认开,给 kubectl top 和 HPA 用。kubeadm / Kind 上 chart overlay 带 --kubelet-insecure-tls。目标集群已经有 metrics-server 时,把 metricsServer.enabled 关掉再 apply。

可观测默认是 指标(Prometheus)+ 日志(VictoriaLogs)+ 链路(Tempo)+ 剖析(Pyroscope)。VictoriaLogs Collector 是 DaemonSet,把容器日志打进 VictoriaLogs(:9428)。链路上,OTel Collector 与 Tempo 都装在 monitoring(和 kube-prometheus-stack 同一个命名空间,Grafana 里指标跳链路更顺);OTel Collector 用 deployment 模式,应用按 Service DNS 把 span 发给 opentelemetry-collector.monitoring.svc:4317(不是 daemonset 的 hostPort,应用因此不依赖本节点的 agent),Collector 再转给 Tempo —— 转发这一段是本仓在 values/apps/opentelemetry-collector.yaml 里显式配的 otlp/tempo exporter;chart 默认的 traces pipeline 只有 debug,不配就等于收到即丢弃。VictoriaMetrics 默认关,当作 Prometheus 的替换/备选。Loki 默认关,当作 VictoriaLogs 的替换(开 Loki 时 Promtail 会一起装,且必须关掉 VictoriaLogs)。Beyla 默认关,需要集群级 eBPF 剖析时再开。

Istio 默认关,只装控制面(istiod),不另开 Istio Ingress;对外入口默认仍是 APISIX,备选 Envoy Gateway。

RustFS 与 MinIO 不要同时开启,helmfile 会失败。JuiceFS 的 StorageClass 名是 juicefs;其余组件的 PVC 都不写 storageClassName,落在集群的 default StorageClass 上。集群里已经有 default StorageClass 时(比如 OpenEBS 的 openebs-hostpath),在 private.yaml 里关掉 localPathProvisioner,否则会多出第二套 hostpath provisioner。

Prometheus 与 Tempo 本仓没有配 persistence,用的是 chart 默认的 emptyDir:Pod 一重建历史数据就没了。要留存得自己加 persistence。

CiliKube 公开 index 上的 chart 版本是 1.0.0,镜像 tag 在 values/apps/cilikube.yaml 为 v1.5.0。

前置

  • Helm 3.16+、Helmfile 1.x(清单文件必须是 helmfile.yaml.gotmpl,否则 {{ }} 不会渲染)
  • 可选:helm plugin install https://github.com/databus23/helm-diff
  • cp values/private.yaml.example values/private.yaml,并填写:
    • metallb.addresses:节点二层网一段空闲地址,例如 192.168.1.240-192.168.1.250。占位 192.168.0.0-192.168.0.0 会让 template/apply 失败
    • CiliKube:openssl rand -hex 16 生成 jwtSecret(≥16)和 encryptionKey(恰好 32 字符)。CiliKube 默认开着,而它启动时会拒绝 chart 自带的占位值,所以这两项不填 helmfile 就 fail
    • 若开启 GitLab:gitlab.domain,以及上面「GitLab 的外部依赖」列的库、缓存和 Secret
    • 若开启 Redis / JuiceFS:redis.password(建议 hex)
    • 若开启 RustFS / MinIO:access/secret,不要用上游默认口令
    • 若开启 Harbor:harbor.adminPassword,不要用 Harbor12345
    • 若开启 GitLab Runner:gitlabRunner.gitlabUrl(带协议)、gitlabRunner.runnerToken(已注册 runner 的 token,不是旧 registration token)和 gitlabRunner.tags(逗号分隔;留空会在下次 apply 清掉 Runner 上已有的标签)
    • 若开启 ExternalDNS:externalDns.provider(例如 cloudflare);Cloudflare 还要填 cloudflareApiToken,可选 domainFilters / txtOwnerId

面向空集群。目标集群里已有同名 Helm release 时,不要用默认开关直接 apply(会升级或冲突);把某项改为 enabled: false 再 apply 会卸载已有 release。

安装

git clone <this-repo>
cd platform-stack
cp values/private.yaml.example values/private.yaml
# 至少填 metallb.addresses 和 CiliKube 的 jwtSecret / encryptionKey,
# 否则 template / apply 会带着具体缺哪一项的提示直接失败
# 需要提交即部署:打开 gitlab(GitLab CI + Registry)+ gitlabRunner,或 jenkins + harbor,以及 argoCd 或 flux2
./scripts/apply.sh

网关地址:

kubectl -n apisix get svc apisix-gateway

EXTERNAL-IP 就是 MetalLB 从 metallb.addresses 里分的 VIP,域名解析到它即可省掉 NodePort 端口号。两点容易卡住:

  • 地址段要在节点二层可达的那张网卡所在子网里,并且从 DHCP 池里排除掉,否则 VIP 分得出来却没人应答 ARP
  • 节点是虚机时,宿主机虚拟交换机的混杂模式 / MAC 变更 / 伪传输策略可能拦掉 L2 广播,表现同样是 VIP 通不了
  • MetalLB 镜像在 quay.io,拉不动就按 values/apps/metallb.yaml 换镜像站

公网 Let's Encrypt(VIP 的 80 端口须从互联网可达,且已填写 letsencrypt.email):

./scripts/apply.sh --state-values-file values/letsencrypt.yaml

把 CiliKube / Grafana 挂到网关:exampleIngress.enabled: true,并在 private.yaml 写入 hosts.cilikube / hosts.grafana。

Grafana、Prometheus、Alertmanager 的入口由 hosts.grafana / hosts.prometheus / hosts.alertmanager 决定,写法和下面两个一致。有两处 chart 自己做得不对,helmfile 里补掉了:

  • Grafana 的密码默认每次 apply 都会变。adminPassword 为空时 chart 用随机串渲染,于是每升级一次登录就失效。填 secrets.grafanaAdminPassword 固定下来;想知道现在在用哪个,kubectl -n <ns> get secret kube-prometheus-stack-grafana -o jsonpath='{.data.admin-password}' | base64 -d。
  • externalUrl 会被推导成 http://,哪怕网关只提供 https。这个地址会出现在告警通知的链接里,所以 helmfile 按 https://<host> 显式写入。

还有一条不是 chart 的问题:Prometheus 和 Alertmanager 自身没有任何认证。给它们配上域名,等于把全部指标对能访问到这个地址的人开放,Alertmanager 的界面还能静默告警。只在可信网络里这么做,或者自己在网关上加一层。

Argo CD 和 Jenkins 的入口同样由 private.yaml 的 hosts.argocd / hosts.jenkins 决定:填了就渲染 Ingress,留空就只有 ClusterIP,需要时 port-forward。证书注解自动取当前可用的 issuer(letsencrypt.enabled → letsencrypt-prod,否则 localCa.enabled → local-ca,再否则 selfsigned)。这两个 chart 各有一处必须配合网关:

  • Argo CD 开了 configs.params.server.insecure。网关终结 TLS 后转发的是明文 HTTP,不关这个开关,argocd-server 会把它重定向回 HTTPS,浏览器一直转圈。
  • Jenkins 的 controller.jenkinsUrl 指向对外域名,否则它生成的绝对链接会用集群内的 Service 名。chart 默认不渲染 Ingress 的 path,这里显式写了 / 与 Prefix。

Jenkins 还开了 controller.initializeOnce。它的 init 容器每次启动都去下载整份插件清单,集群启动早于出网链路时下载失败,init 退出——而插件目录是 emptyDir,重试时上一轮的文件还在,cp -i 于是逐个问"要覆盖吗",从此卡死在那里。打开这个开关后,JENKINS_HOME 里有 initialization-completed 就整段跳过。代价是改 installPlugins 不再自动生效,需要删掉那个标记文件让 init 重跑一次。

没有域名时可以 port-forward:

kubectl -n cilikube port-forward svc/cilikube-frontend 8088:80
kubectl -n cilikube port-forward svc/cilikube-backend 8080:8080
kubectl -n headlamp port-forward svc/headlamp 4466:80

告警发到飞书

kube-prometheus-stack 装完就有几十组告警规则在跑,但 chart 默认的 receivers 列表里只有一个叫 null 的——告警正常产生、正常路由,然后进黑洞。在群里建一个自定义机器人,把地址填进 private.yaml:

alerting:
  lark:
    enabled: true
    webhookUrl: "https://open.feishu.cn/open-apis/bot/v2/hook/..."
    signSecret: ""          # 机器人开了签名校验才填

地址为空时不会给 Alertmanager 配这条路由,免得告警发给一个没人监听的地址。

中间需要一个转换器,因为 Alertmanager 不能给 webhook 套模板:它发出去的 JSON 结构是固定的,而机器人只认自己那套 {"msg_type": ...}。platform-config 会渲染一个很小的转换服务——一段不依赖三方库的 Python 放在 ConfigMap 里,跑在官方 python 镜像上,不引第三方镜像。

路由里 Watchdog 单独留给 null:它按设计永远在 firing,用来证明告警链路本身活着,不该推送。其余全部转发,send_resolved 打开,所以恢复时会收到一张绿色卡片。

验证不需要等真实故障,往 Alertmanager 塞一条就行:

kubectl -n monitoring port-forward svc/kube-prometheus-stack-alertmanager 9093:9093
curl -XPOST http://127.0.0.1:9093/api/v2/alerts -H 'Content-Type: application/json' -d '[{
  "labels":{"alertname":"PipelineTest","severity":"warning"},
  "annotations":{"summary":"告警链路联调"},
  "startsAt":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}]'
kubectl -n monitoring logs -l app=alert-lark --tail=5    # 转换器会打印机器人的原始响应

分组等待 30 秒,约 5 分钟后自动转 resolved 再来一张绿卡。

Flux 怎么装

Flux 要装的是两样东西:控制器,和**「该同步哪个仓库的哪个目录」这个指针**。控制器装好而没有指针,它就坐在那儿什么也不干。本仓库提供三种装法,因为上游推荐的那种和「helmfile 管一切平台组件」这个模型是有张力的。

上游的原话是 “The recommended way of installing Flux on Kubernetes clusters is by using the bootstrap procedure.” 官方认可的 bootstrap 有三种实现:flux bootstrap CLI、Terraform provider、Flux Operator。而 fluxcd-community/flux2 这个 Helm chart 被归在 **Dev install(for testing purposes)**下面,并注明由社区 best effort 维护、不保证发版节奏。

开关 装什么 指针在哪 说明
(都不开) flux bootstrap 自己装 Git 仓库里,Flux 自管 官方首推,本仓库的默认
apps.flux2 community chart 装 6 个控制器 需配合 fluxGitops 开箱最省事,但上游把它算作 dev install
apps.fluxOperator operator 本身 FluxInstance 对象 官方 bootstrap 方式之一;本仓库不渲染 FluxInstance

两个开关默认都关,因为默认路线是 bootstrap。bootstrap 命令长这样:

flux bootstrap git \
  --url=<gitops 仓库> --branch=main --path=clusters/<name> \
  --namespace=flux-system --version=v<版本> --token-auth --username=<user>

明文 HTTP 的仓库还要加 --allow-insecure-http=true,否则 CLI 直接拒绝。密码走 GIT_PASSWORD 环境变量,不要写进命令行。

apps.flux2 和 apps.fluxOperator 同时打开会直接 fail:两者往同一个命名空间装同一批控制器。

走 flux2 时,指针由 flux-gitops 声明,在 private.yaml 里配:

fluxGitops:
  enabled: true
  sourceName: gitops
  gitUrl: "http://gitlab.example.com/OWNER/gitops.git"
  branch: main
  deployToken: "<read_repository 的 token>"   # 公开仓留空则不建 Secret
  kustomizations:
    - name: demo
      path: ./apps/demo

走 operator 或 bootstrap 时不要开 fluxGitops:指针改由 FluxInstance 或 bootstrap 提交的清单描述,两边都声明会抢同一个对象。

不管走哪条,边界都是同一条:helmfile 管平台组件,Flux 管业务应用。业务应用不要写进本仓库的 helmfile,而要写进 Flux 看的那个仓库;反过来那个仓库里也不要再放一份指向自己的 Kustomization。手工 kubectl apply 出 GitRepository 和 Kustomization 当然也能跑,但集群重建时这层接线不会跟着回来——控制器装回来了,却不知道去哪同步。

从 flux2 换到 bootstrap

就地接管,不要先卸载。flux bootstrap 带 --force 可以直接盖在一套已经装好的控制器上——它做的是升级与补齐自管清单,不是重装。

反过来,先 helm uninstall flux2 再 bootstrap 是会出事的:卸载会带走 chart 装的那批 CRD,而删 CRD 等于删掉集群里所有该类型的对象——正在同步的 GitRepository、Kustomization、HelmRelease 全部消失,连带它们 prune 过的工作负载。顺序反了,代价是一次全量重建。

flux bootstrap git \
  --url=<gitops 仓库> --branch=main --path=clusters/<name> \
  --version=v<与集群当前一致> --force

--version 写成集群里正在跑的那个版本,接管这一步就不含版本变更,出问题时只有一个变量。升级留到接管完成之后,改 git 即可。

接管之后 Helm 那边还留着一条 release 记录,它迟早要被处理——apps.flux2 一旦关掉,helmfile apply 就会去执行上面那条 helm uninstall。

**给活对象打 helm.sh/resource-policy: keep 是没用的。**Helm 判断保留与否,读的是它自己存的那份 release manifest,不是集群里对象当前的注解。在外面补注解,Helm 根本看不见,照删不误——37 个对象连 14 个 CRD 一起没了。

真正管用的是下面两条之一:

  • 把 flux2 这一条从 helmfile.yaml.gotmpl 里删掉,而不是设 installed: false。清单不认识它,就永远不会去卸载它。
  • **接受它被删,然后重跑一次 bootstrap 恢复。**这条之所以可行,是因为 bootstrap 已经把全部组件清单提交进了 git;但有一个前提必须先做好——

删 CRD 会触发 CR 上的 finalizers.fluxcd.io,而 Kustomization 的 finalizer 会执行 prune,把它同步过的业务资源一并删掉。所以卸载后要赶在控制器重新起来之前摘掉 finalizer:

kubectl patch kustomization <name> -n flux-system --type=merge -p '{"metadata":{"finalizers":null}}'
kubectl patch gitrepository <name> -n flux-system --type=merge -p '{"metadata":{"finalizers":null}}'

摘掉之后 CRD 删除会立刻完成且不做 prune,再跑一遍同样的 flux bootstrap 就能把 CRD、控制器、同步配置全部装回来。

Flux Operator 也支持接管已有安装,上游写了迁移指南,思路相同。

Kind(无 L2 VIP:关闭 MetalLB 和 local-path,APISIX 使用 NodePort):

./scripts/apply.sh -e kind
./scripts/apply.sh -e kind --state-values-set kindMirror=true

升级

# values/defaults.yaml
apps:
  argoCd:
    enabled: true
  rustfs:
    enabled: true
  redis:
    enabled: true
  juicefsCsi:
    enabled: true
  minio:
    enabled: false
./scripts/bump.sh          # 对照上游只打印,不改文件
./scripts/diff.sh         # 或 helmfile template
# 改 version 后再
./scripts/apply.sh

bump.sh 提示 headlamp 可以升到 0.40.x 时跳过这一档:那几个 chart 会给应用传 -session-ttl,而对应的 v0.40.1 二进制不认这个 flag,容器在解析参数时就退出。0.45.0 的二进制认,所以 pin 在 0.45.0。

kube-prometheus-stack 跨大版本升级时,Helm 不会更新 CRD,新 operator 会拿旧 schema 去 reconcile。chart 自带的 crds.upgradeJob 用 pre-upgrade hook 补这一刀,本仓已经打开(还带 forceConflicts,因为集群里的 CRD 归最早创建它的 field manager 所有)。

Argo CD 从 9.x 升到 10.x 会在自己的命名空间里生成一套 NetworkPolicy(每个组件一条)。装了 CNI 策略引擎的集群要留意:argocd-server 那条放行全部入站,网关照常能进;但如果你另外还有拒绝式的命名空间策略,先看一眼两边叠加的结果。

OpenTelemetry Collector 在 chart 0.171 把导出器名从 otlp 改成了 otlp_grpc。老名字暂时还会被 chart 重写,但兼容层会去掉,所以 values/apps/opentelemetry-collector.yaml 里已经写成新名字。

升级中途失败时还有一个坑:Helm 已经把新版本记成一次修订,哪怕状态是 failed。这时 helmfile apply 拿它当基线去比,会得出「没有差异」而跳过,看起来像没执行。用 helmfile sync 跳过比对直接升,镜像拉取慢就把超时放大:

helmfile -l name=argocd sync --args "--timeout 20m"

命名约定

发布名就是应用名,命名空间同名,一个应用一个命名空间。两处例外都有原因:metrics-server 属于集群插件,跟上游一样放 kube-system;jenkins 在 cicd,因为 JENKINS_HOME 是 PVC,换命名空间等于迁数据。

这条约定不只是整洁问题——Helmfile 靠「发布名 + 命名空间」认领 release。名字对不上,它既不会升级你已有的那个,也不会因为开关关着而卸掉它,而是在旁边再装一份;名字对得上而开关是关的,apply 就会把正在跑的卸掉。所以往已有集群接这份清单前,先用 helm list -A 把两边的名字和命名空间对一遍。

看 diff 时的一个坑

helmfile diff 跑的是 dry-run,而 dry-run 里的 lookup 一律返回空。不少 chart 用 lookup 保住自己生成的密码和证书——读得到就复用,读不到才随机生成:

{{- $secret := (lookup "v1" "Secret" ... ) }}
{{- if $secret }}{{ index $secret "data" "admin-password" }}
{{- else }}{{ (randAlphaNum 40) | b64enc | quote }}{{ end }}

于是每次 diff 都会渲染出一个新随机值,连带引用它的 Deployment / StatefulSet 上的 checksum/* 注解一起变,看着像要改,真 apply 时其实原封不动。本仓里 Grafana 的 admin 密码、Jenkins 的 admin 密码、APISIX 内置 etcd 的 jwt-token 都是这个情况。判断方法:看到「Secret 内容变了 + 对应 workload 的 checksum 注解变了」这对组合,先去 chart 里搜一下 lookup,别当成漂移去追。

About

可开关的 Kubernetes Helmfile 清单:按需安装 APISIX、cert-manager、MetalLB、Prometheus、Grafana、OpenTelemetry、Argo CD 与 Flux,从 git push 到 HTTPS。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages