作为 10 年运维专家,先把核心逻辑捋清楚,再给落地步骤和可直接跑的案例 —— 全程说人话,不堆砌术语,适配 K8s 1.33 的特性(比如废弃的 API 都规避掉)。

一、核心技术逻辑:先搞懂 Helm 到底在干嘛

1. Helm 的本质

Helm 是 K8s 的包管理器,把 K8s 的 YAML 配置(Deployment、Service、Ingress 等)打包成一个「Chart」(相当于 Linux 的 rpm/deb 包),解决:

  • 零散 YAML 不好管理的问题;
  • 环境差异化配置(开发 / 测试 / 生产);
  • 版本追溯、回滚、升级的管控;
  • 一键部署 / 卸载,不用手动改 YAML。

2. Chart 的核心逻辑

一个 Chart 就是一个目录结构,核心是「模板 + 配置」:

  • 模板(templates):带变量的 K8s YAML 文件(比如镜像版本、副本数用变量 {{.Values.xxx}} 代替);
  • 配置(values.yaml):模板变量的默认值,部署时可通过-f--set覆盖;
  • 元数据(Chart.yaml):Chart 的版本、名称、依赖、适配 K8s 版本等核心信息(关键:要适配 1.33)。

3. 版本管控的核心逻辑

Helm 的版本管控分两层:

  • Chart 版本:Chart.yaml 里的version(语义化版本,比如 1.0.0、1.0.1),对应 Chart 包的迭代;
  • Release 版本:Helm 部署 Chart 后生成的 Release(比如部署 myapp-1.0.0 生成 release myapp-v1),Helm 会记录每个 Release 的配置,支持回滚(helm rollback);
  • 配合仓库(Chart Repository):把打好的 Chart 包推到仓库(比如 Harbor、ChartMuseum),实现版本归档和团队共享。

4. K8s 1.33 适配要点

1.33 版本主要废弃 / 变更的 API 要规避:

  • Ingress:必须用networking.k8s.io/v1(v1beta1 早已废弃);
  • StatefulSet/Deployment:用apps/v1(没问题,1.33 仍支持);
  • PodSecurityPolicy(PSP)已完全移除,改用 Pod Security Standards(PSS)+ Pod Security Admission(PSA);
  • 其他核心 API(Service、ConfigMap、Secret)无变更,正常用v1

二、自定义 Chart 开发:详细操作步骤

步骤 1:环境准备(适配 1.33)

先确认 Helm 版本(建议 3.14+,适配 1.33):

# 安装Helm 3.14+(Linux示例)
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# 验证版本
helm version # 输出v3.14.x即可
# 验证K8s集群(1.33)
kubectl version --short # 确保Server Version是v1.33.x

步骤 2:初始化 Chart 骨架

Helm 提供create命令生成标准目录结构,不用手动建:

# 创建名为myapp的Chart(自定义Chart的核心目录)
helm create myapp

生成的目录结构(重点标★):

myapp/
├── Chart.yaml       ★ Chart元数据(版本、名称、K8s适配)
├── values.yaml      ★ 模板默认配置(镜像、副本数、端口等)
├── charts/          ★ 依赖的其他Chart(比如redis、mysql)
├── templates/       ★ 核心模板目录(K8s资源YAML)
│   ├── deployment.yaml  ★ 部署模板
│   ├── service.yaml     ★ 服务模板
│   ├── ingress.yaml     ★ 入口模板(适配1.33)
│   ├── _helpers.tpl     ★ 辅助函数(变量复用)
│   ├── NOTES.txt        ★ 部署后提示信息
└── .helmignore       ★ 忽略文件(类似.gitignore)

步骤 3:定制 Chart 元数据(Chart.yaml)

核心是适配 K8s 1.33,示例如下(注释讲清楚每一项):

apiVersion: v2 # Helm Chart的API版本(v2是通用版,v1是旧版)
name: myapp    # Chart名称(全局唯一)
description: A simple web app Chart for K8s 1.33 # 描述
type: application # 类型(application/库chart)

# Chart版本(语义化:主版本.次版本.补丁,比如1.0.0)
# 迭代规则:主版本(大改)、次版本(新增功能)、补丁(bug修复)
version: 1.0.0

# App版本(对应业务应用的版本,比如v1.0.0)
appVersion: "v1.0.0"

# 适配的K8s版本(1.33.x),避免部署到不兼容的集群
kubeVersion: ">=1.33.0 <1.34.0"

# 可选:依赖(比如需要redis)
dependencies:
  - name: redis
    version: 17.3.1
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled # 条件启用

步骤 4:定制默认配置(values.yaml)

核心是「默认值 + 可覆盖」,适配 1.33 的 Ingress,示例如下:

# 全局配置(可被依赖Chart继承)
global:
  namespace: myapp # 默认命名空间

# 部署配置
deployment:
  replicas: 2 # 默认副本数
  strategy:
    type: RollingUpdate # 滚动更新(1.33支持)
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  pod:
    # 容器配置
    containers:
      name: myapp-web
      image: nginx:1.25-alpine # 基础镜像(1.33兼容)
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
          name: http
      # 资源限制(避免资源抢占)
      resources:
        limits:
          cpu: 500m
          memory: 512Mi
        requests:
          cpu: 100m
          memory: 256Mi
      # 健康检查(1.33推荐配置)
      livenessProbe:
        httpGet:
          path: /
          port: http
        initialDelaySeconds: 10
        periodSeconds: 10
      readinessProbe:
        httpGet:
          path: /
          port: http
        initialDelaySeconds: 5
        periodSeconds: 5
    # PSS适配(替代PSP,1.33必用)
    securityContext:
      runAsUser: 1000
      runAsGroup: 1000
      fsGroup: 1000

# Service配置
service:
  type: ClusterIP # 类型(ClusterIP/NodePort/LoadBalancer)
  port: 80
  targetPort: http

# Ingress配置(适配1.33的networking.k8s.io/v1)
ingress:
  enabled: true
  className: nginx # Ingress Controller类名(需提前部署nginx-ingress)
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
          pathType: Prefix # 1.33必填(Prefix/Exact/ImplementationSpecific)
  tls: false # 暂时关闭TLS,生产可开启

# 依赖配置(redis)
redis:
  enabled: false # 默认不启用
  auth:
    enabled: true

步骤 5:定制模板(templates/)

模板是「带变量的 K8s YAML」,核心是复用 values.yaml 的配置,适配 1.33:

5.1 Deployment 模板(deployment.yaml)
apiVersion: apps/v1 # 1.33支持
kind: Deployment
metadata:
  name: {{ .Release.Name }}-myapp # Release名称+固定后缀(避免冲突)
  namespace: {{ .Values.global.namespace }}
  labels:
    app: {{ .Chart.Name }}
    version: {{ .Chart.AppVersion }}
spec:
  replicas: {{ .Values.deployment.replicas }}
  strategy:
    {{- toYaml .Values.deployment.strategy | nindent 4 }} # 复用values的更新策略
  selector:
    matchLabels:
      app: {{ .Chart.Name }}
  template:
    metadata:
      labels:
        app: {{ .Chart.Name }}
    spec:
      securityContext:
        {{- toYaml .Values.deployment.pod.securityContext | nindent 8 }}
      containers:
        - name: {{ .Values.deployment.pod.containers.name }}
          image: {{ .Values.deployment.pod.containers.image }}
          imagePullPolicy: {{ .Values.deployment.pod.containers.imagePullPolicy }}
          ports:
            {{- toYaml .Values.deployment.pod.containers.ports | nindent 12 }}
          resources:
            {{- toYaml .Values.deployment.pod.containers.resources | nindent 12 }}
          livenessProbe:
            {{- toYaml .Values.deployment.pod.containers.livenessProbe | nindent 12 }}
          readinessProbe:
            {{- toYaml .Values.deployment.pod.containers.readinessProbe | nindent 12 }}
5.2 Service 模板(service.yaml)
apiVersion: v1 # 1.33支持
kind: Service
metadata:
  name: {{ .Release.Name }}-myapp-service
  namespace: {{ .Values.global.namespace }}
  labels:
    app: {{ .Chart.Name }}
spec:
  type: {{ .Values.service.type }}
  selector:
    app: {{ .Chart.Name }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.targetPort }}
      protocol: TCP
      name: http
5.3 Ingress 模板(ingress.yaml,适配 1.33)

重点:必须用networking.k8s.io/v1,pathType 必填:

{{- if .Values.ingress.enabled }} # 条件渲染(enabled为true才生成)
apiVersion: networking.k8s.io/v1 # 1.33唯一支持的版本
kind: Ingress
metadata:
  name: {{ .Release.Name }}-myapp-ingress
  namespace: {{ .Values.global.namespace }}
  annotations:
    {{- toYaml .Values.ingress.annotations | nindent 4 }}
spec:
  ingressClassName: {{ .Values.ingress.className }}
  rules:
    {{- range .Values.ingress.hosts }} # 循环渲染多个host
    - host: {{ .host }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ .path }}
            pathType: {{ .pathType }} # 1.33必填
            backend:
              service:
                name: {{ $.Release.Name }}-myapp-service # 引用Service名称
                port:
                  number: {{ $.Values.service.port }}
          {{- end }}
    {{- end }}
  {{- if .Values.ingress.tls }} # TLS配置
  tls:
    {{- toYaml .Values.ingress.tls | nindent 4 }}
  {{- end }}
{{- end }}

步骤 6:模板校验(避免语法错误)

开发完模板后,先校验语法和适配性:

# 1. 检查Chart语法(核心)
helm lint myapp/
# 2. 渲染模板(看最终生成的YAML,不部署)
helm template myapp/ --namespace myapp
# 3. 模拟部署(dry-run,检查K8s 1.33兼容性)
helm install myapp-test myapp/ --namespace myapp --create-namespace --dry-run

如果报错,优先看:

  • API 版本是否正确(比如 Ingress 用 networking.k8s.io/v1);
  • 变量是否存在(比如 {{.Values.xxx}} 有没有在 values.yaml 定义);
  • K8s 1.33 的特性是否兼容(比如 pathType 必填)。

三、版本管控:从打包到仓库全流程

步骤 1:Chart 打包(版本固化)

把 Chart 目录打成 tgz 包,版本对应 Chart.yaml 里的 version:

# 打包(生成myapp-1.0.0.tgz)
helm package myapp/
# 查看包信息
helm show chart myapp-1.0.0.tgz

步骤 2:搭建 Chart 仓库(团队共享)

推荐用 Harbor(自带 Chart 仓库)或 ChartMuseum,这里用 ChartMuseum 快速搭建(适配 1.33):

# 1. 部署ChartMuseum(用Helm本身部署)
helm repo add chartmuseum https://chartmuseum.github.io/charts
helm install chartmuseum chartmuseum/chartmuseum \
  --namespace chartmuseum \
  --create-namespace \
  --set env.open.DISABLE_API=false # 开启API(允许推送)

# 2. 获取ChartMuseum地址
kubectl get svc -n chartmuseum chartmuseum-chartmuseum -o jsonpath='{.spec.clusterIP}'
# 假设地址是10.96.0.100:8080

步骤 3:推送 Chart 到仓库(版本归档)

cm-push插件推送(需先安装):

# 安装cm-push插件
helm plugin install https://github.com/chartmuseum/helm-push.git

# 添加仓库到Helm
helm repo add myrepo http://10.96.0.100:8080

# 推送包到仓库(版本1.0.0)
helm cm-push myapp-1.0.0.tgz myrepo

# 刷新仓库
helm repo update myrepo

# 查看仓库里的Chart版本
helm search repo myrepo/myapp

步骤 4:部署指定版本(版本管控核心)

部署时指定版本,避免随意升级:

# 部署1.0.0版本
helm install myapp vmyrepo/myapp --version 1.0.0 \
  --namespace myapp \
  --create-namespace \
  # 覆盖默认配置(比如生产环境副本数3)
  --set deployment.replicas=3 \
  --set ingress.hosts[0].host=myapp.prod.example.com

# 查看Release版本
helm list -n myapp
# 输出示例:NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
#          myapp   myapp           1               2025-12-15 10:00:00                     deployed        myapp-1.0.0     v1.0.0

步骤 5:版本升级 / 回滚(运维核心操作)

5.1 版本升级(比如迭代到 1.0.1)
  1. 修改 Chart.yaml 的 version 为 1.0.1,更新功能(比如增加资源限制);
  2. 重新打包:helm package myapp/
  3. 推送新版本:helm cm-push myapp-1.0.1.tgz myrepo
  4. 升级 Release:
helm upgrade myapp myrepo/myapp --version 1.0.1 -n myapp \
  --set deployment.replicas=4 # 同时覆盖配置
5.2 版本回滚(升级出问题时)

Helm 会记录每个 Release 的版本(REVISION),回滚到上一版本:

# 查看Release历史
helm history myapp -n myapp
# 输出示例:REVISION        UPDATED                         STATUS          CHART           DESCRIPTION
#          1               2025-12-15 10:00:00             superseded      myapp-1.0.0     Install complete
#          2               2025-12-15 10:10:00             deployed        myapp-1.0.1     Upgrade complete

# 回滚到REVISION 1(1.0.0版本)
helm rollback myapp 1 -n myapp

# 验证回滚结果
helm status myapp -n myapp

步骤 6:版本清理(避免冗余)

# 卸载Release(保留Chart版本)
helm uninstall myapp -n myapp

# 从仓库删除指定Chart版本(1.0.0)
curl -X DELETE http://10.96.0.100:8080/api/charts/myapp/1.0.0

# 清理本地Chart缓存
helm repo remove myrepo
rm -rf myapp-1.0.0.tgz myapp-1.0.1.tgz

四、完整案例:部署一个 Nginx Web 应用(适配 1.33)

案例目标

开发一个 myapp Chart,部署 Nginx 应用,支持:

  • 自定义副本数、镜像版本;
  • Ingress 访问(myapp.example.com);
  • 版本升级(1.0.0→1.0.1);
  • 版本回滚。

前置条件

  1. K8s 1.33 集群已部署 nginx-ingress Controller(Ingress 需要);
  2. Helm 3.14 + 已安装;
  3. 本地 hosts 配置:集群节点IP myapp.example.com

步骤 1:创建并定制 Chart

# 1. 创建Chart
helm create myapp

# 2. 修改Chart.yaml(按前面的示例,version=1.0.0,kubeVersion=>=1.33.0)
# 3. 修改values.yaml(按前面的示例,镜像用nginx:1.25-alpine,副本数2)
# 4. 校验模板
helm lint myapp/
helm template myapp/ --namespace myapp

步骤 2:部署 1.0.0 版本

# 1. 创建命名空间
kubectl create namespace myapp

# 2. 部署Release
helm install myapp myapp/ -n myapp \
  --set ingress.hosts[0].host=myapp.example.com \
  --set deployment.replicas=2

# 3. 验证部署
helm status myapp -n myapp
kubectl get pods -n myapp # 应该有2个nginx pod
kubectl get ingress -n myapp # Ingress已创建
curl myapp.example.com # 应该返回Nginx默认页面

步骤 3:升级到 1.0.1 版本

# 1. 修改Chart.yaml,version改为1.0.1,appVersion改为v1.0.1
# 2. 修改values.yaml,镜像改为nginx:1.26-alpine,副本数3
# 3. 打包并推送(这里用本地测试,不用仓库)
helm package myapp/

# 4. 升级Release
helm upgrade myapp myapp-1.0.1.tgz -n myapp \
  --set deployment.replicas=3

# 5. 验证升级
helm status myapp -n myapp # 显示REVISION=2,CHART=myapp-1.0.1
kubectl get pods -n myapp # 3个pod,镜像为1.26-alpine
curl myapp.example.com # 仍返回Nginx页面

步骤 4:回滚到 1.0.0 版本

# 1. 查看Release历史
helm history myapp -n myapp

# 2. 回滚到REVISION=1
helm rollback myapp 1 -n myapp

# 3. 验证回滚
helm status myapp -n myapp # 显示REVISION=3,CHART=myapp-1.0.0
kubectl get pods -n myapp # 2个pod,镜像为1.25-alpine

步骤 5:卸载 Release

helm uninstall myapp -n myapp
kubectl delete namespace myapp

五、核心总结(运维视角)

  1. Chart 开发核心:模板 + 配置分离,适配 K8s 1.33 的 API 版本(尤其是 Ingress);
  2. 版本管控核心:Chart 版本(包迭代)+ Release 版本(部署迭代),通过仓库归档,支持升级 / 回滚;
  3. 避坑点
    • 不要硬编码 K8s 资源名称,用 {{.Release.Name}} 避免冲突;
    • 必须校验模板(helm lint/helm template),避免部署时出错;
    • K8s 1.33 的 Ingress 必须用 networking.k8s.io/v1,pathType 必填;
    • 版本号严格遵循语义化,便于团队协作和追溯。

可以基于这个框架,扩展到更复杂的场景(比如 StatefulSet、ConfigMap/Secret、多环境配置),核心逻辑不变 —— 模板复用、配置可覆盖、版本可管控。

更多推荐