Next.js 应用的 Helm chart
用于 charts/<应用>/ 以及 Argo CD 如何使用它。它运行的镜像来自 docker-nextjs;发布两者的流水线是 github-actions。
模板的 charts/__APP__/ 是唯一真相来源。所有带注释的值:references/values.md。Argo CD 配置:references/argocd.md。
规则
-
每个应用一个 chart,位于应用仓库根目录的
charts/<应用>/下。 原因:Argo CD 可以从 Git(path: charts/<应用>)直接同步用于开发环境,并从 OCI 注册表同步用于生产环境,文件相同;它位于apps/之外,因此既不在 Docker 构建上下文中,也不是 pnpm workspace 包;chart 与需要它的代码在同一 PR 中变更。该 chart 特意设计为单应用:无 umbrella、无子 chart。仅当单个应用需要时,才使用基于值驱动的通用模板(如后台 umbrella chart):ingress、HPA、PDB、拓扑分布。 -
永远不要手动编辑
version或appVersion。 两者在 Git 中均保持0.0.0。release.yaml从标签vX.Y.Z运行helm package --version X.Y.Z --app-version vX.Y.Z,因此发布的 chart 和镜像标签始终匹配。 -
image.tag默认使用appVersion;仅对 Git 同步环境进行设置。 从 OCI 获取时,appVersion是发布标签,镜像为ghcr.io/nuvraxis/<应用>:vX.Y.Z。从 Git 同步时,appVersion为0.0.0,因此 Argo CD Application 设置image.tag: sha-<短 SHA>(或main)。image.repository始终小写。 -
探针:startup、liveness 和 readiness,均指向
/api/health。 startup 探针(2s × 30)覆盖启动过程;liveness 和 readiness 仅在其之后启动。该路由从不依赖任何依赖项,因此数据库故障不会重启 Pod。不要将探针指向/:它会渲染页面。 -
安全上下文与镜像匹配:uid/gid 1001、
runAsNonRoot、readOnlyRootFilesystem、无权限提升、丢弃所有能力、RuntimeDefaultseccomp。 ServiceAccount 和 Pod 上均设置automountServiceAccountToken: false:应用不调用 Kubernetes API。 -
.next/cache是带有sizeLimit的emptyDir。 它是唯一的可写路径(/app/apps/<应用>/.next/cache)。该限制防止失控的镜像缓存填满节点。 -
通过
envFrom配置:一个来自values.config的 ConfigMap,以及一个已存在的 Secret。config: API_BASE_URL: https://api.nuvraxis.com existingSecret: portal-secretschart 永远不会模板化密文值:无
kind: Secret带来自 values 的data、无--set secret=…。Secret 由 External Secrets、Sealed Secrets 或 operator 创建,且仅在 values 中指定其名称。ConfigMap 变更通过checksum/config注解滚动 Pod。 -
chart 中不包含
NEXT_PUBLIC_*。 它们在镜像构建时被编译进去(参见docker-nextjs);运行时设置它们无效。 -
资源:设置 CPU 和内存的 requests,设置内存 limit,不设置 CPU limit。 CPU limit 会在突发期间限制 Node 服务器并增加尾延迟;内存 limit 保持,因为 Node 的堆会增长到分配的大小。
-
默认高可用:2 个副本、PDB
maxUnavailable: 1、滚动更新maxUnavailable: 0、跨可用区和节点的拓扑分布(ScheduleAnyway)。 当autoscaling.enabled启用时,Deployment 省略replicas,以避免 Argo CD 和 HPA 冲突。 -
Ingress 为可选,通过 cert-manager 注解管理 TLS。
ingress.enabled、className、hosts、带有secretName的tls,以及注解中的cert-manager.io/cluster-issuer。对于 ingress-nginx 的流式响应,添加nginx.ingress.kubernetes.io/proxy-buffering: "off";对于速率限制,添加limit-rps(参见nextjs-security)。 -
无 Helm hooks、无
lookup、无随机值。 Argo CD 使用helm template渲染 chart 并应用结果:hooks 会变成具有不同语义的 Argo hooks,lookup返回空值,randAlphaNum在每次同步时变化并导致应用永久不同步。渲染的所有内容必须是值的纯函数。 -
私有 GHCR 包需要拉取凭据。 GHCR 包首次推送时默认为私有。对于镜像:一个包含具有
read:packages权限的 token 的docker-registrySecret,在imagePullSecrets中引用(生产 values 中引用ghcr-pull)。对于 Argo CD 中的 chart:一个包含enableOCI: "true"的仓库 Secret(参见references/argocd.md)。或者如果代码是公开的,在 GitHub 中将包设为公开。 -
values-production.yaml仅包含生产环境中不同的值。 分层使用:-f values.yaml -f values-production.yaml。不要将默认值复制到其中。 -
使用
helm lint --strict和helm template | kubeconform -strict进行验证。 为什么选择 kubeconform 而不是 helm-unittest:单应用 chart 实际遇到的 bug 是渲染输出中的错误字段名、类型或 API 版本,kubeconform 可以针对真实的 Kubernetes schema 捕获这些问题,且无需编写任何测试代码。helm-unittest 用于断言模板逻辑,而这个 chart 几乎不需要。对两个 values 文件都运行 lint 和 validate,就像pr.yaml所做的那样。
添加内容
| 需求 | 变更 |
|---|---|
| 运行时环境变量 | config: 在 values 中(非秘密)或现有 Secret 中的键 |
| 密文 | 将其添加到外部 Secret;chart 中无需任何更改 |
| 另一个端口或 sidecar | 首先说明理由;一个 Next 应用只需要一个端口 |
| CronJob | 一个单独的 chart 或应用,而非此 Deployment |
| NetworkPolicy | 添加 templates/networkpolicy.yaml,通过 networkPolicy.enabled 开关 |
验证
helm lint --strict charts/<应用>
helm lint --strict charts/<应用> -f charts/<应用>/values-production.yaml
helm template <应用> charts/<应用> -f charts/<应用>/values-production.yaml \
| docker run --rm -i ghcr.io/yannh/kubeconform:v0.8.0 -strict -summary -kubernetes-version 1.34.0 -
来源
- https://helm.sh/docs/topics/charts/
- https://helm.sh/docs/chart_best_practices/
- https://helm.sh/docs/topics/registries/
- https://helm.sh/docs/overview/
- https://github.com/yannh/kubeconform
- https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
- https://kubernetes.io/docs/concepts/security/pod-security-standards/
- https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/
- https://kubernetes.io/docs/concepts/workloads/pods/disruptions/
- https://argo-cd.readthedocs.io/en/stable/user-guide/helm/
- https://cert-manager.io/docs/usage/ingress/
- https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry