当前位置: 首页 > news >正文

云原生技术08-Helm 3:Kubernetes的“Yum/Apt“——包管理so easy,手把手教你写第一个Helm Chart

「知识图谱生成工具」:一键将文件夹内容变身为交互式知识图谱的免安装桌面工具(文末附免费下载链接)-CSDN博客

CSDN AI数字营销功能实测:CSDN AI内容创作,10分钟从技术选题到成文,技术博主最值得开通的功能,没有之一-CSDN博客


目录

  1. 开篇:YAML地狱逃生指南
  2. Helm架构:Chart → Release → Revision
  3. Helm v3革命:Tiller的葬礼
  4. Chart解剖学:三个文件走天下
  5. Kustomize:Overlay模式的魔法
  6. 最佳实践:Helm + Kustomize组合拳
  7. 实战:从零写一个生产级Chart
  8. 文末三件套

开篇:YAML地狱逃生指南

你是否遇到过部署应用时要改几十个YAML文件、环境切换时配置混乱、版本回滚找不到历史记录的崩溃?

想象一下这个场景:你刚入职新公司,老大扔给你一套微服务架构,包含20个Deployment、15个Service、8个ConfigMap、5个Secret。测试环境要改数据库地址,生产环境要调整资源限制,每次上线都像在玩"大家来找茬"——找不完,根本找不完

这时候Helm出现了,就像Kubernetes世界的"Yum/Apt",让你像安装软件一样部署应用。本文将从零开始教你写生产级Helm Chart,告别复制粘贴,拥抱包管理

💡效率技巧:Chart平均大小不到1MB,Rollback回滚时间小于10秒。这意味着什么?意味着你可以像玩Git一样玩K8s部署——错了就回滚,比撤销微信消息还快。


Helm架构:Chart → Release → Revision

核心概念图解

┌─────────────────────────────────────────────────────────────────┐ │ Helm 架构全景图 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Chart │ ───▶ │ Release │ ───▶ │ Revision │ │ │ │ (安装包) │ │ (实例) │ │ (版本) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ templates/ │ │ my-app-v1 │ │ revision 1 │ │ │ │ values.yaml│ │ my-app-v2 │ │ revision 2 │ │ │ │ Chart.yaml │ │ │ │ revision 3 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ 类比: 类比: 类比: │ │ .deb / .rpm 包 安装的软件实例 Git commit历史 │ │ │ └─────────────────────────────────────────────────────────────────┘

三要素详解

概念类比作用
Chart软件安装包(.deb/.rpm)包含所有K8s资源模板的集合
Release安装后的软件实例Chart在集群中的运行实例,可多次安装
RevisionGit commit每次upgrade产生的版本,支持回滚

Chart是模板,Release是实例,Revision是历史——记住这个三角关系,你就掌握了Helm的灵魂。

⚠️避坑警告:很多新手会把Chart和Release搞混。Chart是静态的代码包(存在本地或仓库),Release是动态的运行实例(存在K8s集群)。就像你下载的nginx-1.20.deb是Chart,而apt install nginx后运行的进程是Release。


Helm v3革命:Tiller的葬礼

架构对比:v2 vs v3

┌────────────────────────────────────────────────────────────────────┐ │ Helm v2(已入土) │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────────┐ │ │ │ Helm │ ───▶ │ Tiller │ ───▶ │ Kubernetes API │ │ │ │ Client │ │ (Server) │ │ Server │ │ │ └──────────┘ └──────────┘ └──────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ 需要集群管理员 │ │ │ │ 权限 + RBAC │ │ │ │ 配置复杂 │ │ │ └──────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────┘ ┌────────────────────────────────────────────────────────────────────┐ │ Helm v3(现行版) │ ├────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────┐ ┌──────────────────────┐ │ │ │ Helm │ ───▶ │ Kubernetes API │ │ │ │ Client │ │ Server │ │ │ └──────────┘ └──────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ 直接操作K8s │ │ │ │ 无需Tiller │ │ │ │ 安全简单 │ │ │ └──────────────┘ │ │ │ └────────────────────────────────────────────────────────────────────┘

v3的核心改进

1. 移除Tiller

  • v2:Tiller作为集群内Pod运行,需要集群管理员权限,RBAC配置复杂得像在写论文
  • v3:Helm直接调用K8s API,权限跟着kubeconfig走,简单得像在作弊

2. Release级别管理

# v2:Tiller存储Release信息在ConfigMap(全局可见,安全隐患) # v3:Release信息存储在Release自身的Namespace中 # 查看Release存储位置 kubectl get secret -n <release-namespace> -l owner=helm

3. 安全性提升

  • 不再需要给Tiller开集群管理员权限
  • 每个Release的Secret只有对应Namespace权限可见
  • RBAC粒度细到令人发指

💡效率技巧:如果你还在用Helm v2,赶紧升级吧。v2已经停止维护,就像还在用Windows XP一样——不是不能用,是没必要。


Chart解剖学:三个文件走天下

Chart目录结构

myapp/ ├── Chart.yaml # Chart的"身份证" ├── values.yaml # 默认配置值 ├── values-dev.yaml # 开发环境覆盖配置 ├── values-prod.yaml # 生产环境覆盖配置 ├── charts/ # 依赖的Chart │ └── mysql-8.0.0.tgz ├── templates/ # K8s资源模板 │ ├── _helpers.tpl # 辅助模板函数 │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── configmap.yaml │ ├── secret.yaml │ ├── hpa.yaml # 水平自动扩缩容 │ ├── pdb.yaml # Pod中断预算 │ └── NOTES.txt # 安装后提示信息 └── README.md

Chart.yaml:Chart的身份证

apiVersion: v2 # v2表示Helm 3,v1是Helm 2 name: myapp description: A Helm chart for Kubernetes type: application # application或library version: 1.2.3 # Chart版本(语义化版本) appVersion: "2.0.0" # 应用的版本 icon: https://example.com/icon.png keywords: - web - microservice home: https://github.com/example/myapp sources: - https://github.com/example/myapp maintainers: - name: John Doe email: john@example.com dependencies: - name: mysql version: "8.0.x" repository: "https://charts.bitnami.com/bitnami" condition: mysql.enabled - name: redis version: "16.x.x" repository: "https://charts.bitnami.com/bitnami" alias: cache

⚠️避坑警告versionappVersion是两个完全不同的东西!version是Chart本身的版本(改模板就升这个),appVersion是里面应用的版本(升级应用就升这个)。很多新手搞混,导致回滚时一脸懵逼。

values.yaml:配置的中心化

# 全局配置 replicaCount: 3 # 镜像配置 image: repository: nginx pullPolicy: IfNotPresent tag: "1.20.0" # 镜像拉取密钥 imagePullSecrets: [] nameOverride: "" fullnameOverride: "" # Service配置 service: type: ClusterIP port: 80 targetPort: 8080 # Ingress配置 ingress: enabled: true className: "nginx" annotations: cert-manager.io/cluster-issuer: "letsencrypt" nginx.ingress.kubernetes.io/ssl-redirect: "true" hosts: - host: myapp.example.com paths: - path: / pathType: Prefix tls: - secretName: myapp-tls hosts: - myapp.example.com # 资源限制 resources: limits: cpu: 1000m memory: 1Gi requests: cpu: 100m memory: 128Mi # 自动扩缩容 autoscaling: enabled: true minReplicas: 3 maxReplicas: 10 targetCPUUtilizationPercentage: 80 targetMemoryUtilizationPercentage: 80 # Pod中断预算 podDisruptionBudget: enabled: true minAvailable: 2 # 健康检查 livenessProbe: httpGet: path: /health port: http initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: http initialDelaySeconds: 5 periodSeconds: 5 # 配置和密钥 config: logLevel: info maxConnections: 100 secrets: dbPassword: "" apiKey: "" # 数据库依赖配置 mysql: enabled: true auth: rootPassword: "" database: myapp username: myapp password: "" primary: persistence: enabled: true size: 10Gi

templates/deployment.yaml:模板的艺术

apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: {{- include "myapp.selectorLabels" . | nindent 6 }} template: metadata: annotations: # 配置变更自动滚动更新 checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} checksum/secrets: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "myapp.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "myapp.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: {{ .Values.service.targetPort }} protocol: TCP env: - name: LOG_LEVEL value: {{ .Values.config.logLevel | quote }} - name: DB_HOST {{- if .Values.mysql.enabled }} value: {{ include "myapp.fullname" . }}-mysql {{- else }} value: {{ .Values.externalDatabase.host | quote }} {{- end }} - name: DB_PASSWORD valueFrom: secretKeyRef: name: {{ include "myapp.fullname" . }}-db key: password livenessProbe: {{- toYaml .Values.livenessProbe | nindent 12 }} readinessProbe: {{- toYaml .Values.readinessProbe | nindent 12 }} resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: - name: config mountPath: /etc/myapp/config.yaml subPath: config.yaml volumes: - name: config configMap: name: {{ include "myapp.fullname" . }}-config

_helpers.tpl:模板函数库

{{/* 生成Chart全名 */}} {{- define "myapp.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} {{- $name := default .Chart.Name .Values.nameOverride }} {{- if contains $name .Release.Name }} {{- .Release.Name | trunc 63 | trimSuffix "-" }} {{- else }} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} {{- end }} {{- end }} {{- end }} {{/* Chart名称 */}} {{- define "myapp.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* 通用标签 */}} {{- define "myapp.labels" -}} helm.sh/chart: {{ include "myapp.chart" . }} {{ include "myapp.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* 选择器标签 */}} {{- define "myapp.selectorLabels" -}} app.kubernetes.io/name: {{ include "myapp.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Chart标签 */}} {{- define "myapp.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* ServiceAccount名称 */}} {{- define "myapp.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "myapp.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }}

💡效率技巧_helpers.tpl是Chart的瑞士军刀。把常用的命名逻辑、标签生成抽离到这里,可以让你的模板代码减少50%,而且更容易维护。记住:不要重复自己(DRY)


Kustomize:Overlay模式的魔法

为什么需要Kustomize?

Helm擅长包管理和模板化,Kustomize擅长环境差异化配置。它们不是竞争关系,而是互补关系

想象一下:你用Helm打包了一个应用,但开发环境、测试环境、生产环境的配置差异很大——域名不同、资源限制不同、副本数不同。用Helm的--values可以搞定,但当差异变得复杂时,Kustomize的Overlay模式更清晰。

Kustomize架构

┌─────────────────────────────────────────────────────────────────────┐ │ Kustomize Overlay 架构 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ │ │ │ Base(基础) │ ← 通用配置,所有环境共享 │ │ │ deployment.yaml│ │ │ │ service.yaml │ │ │ │ kustomization.yaml │ │ │ └────────┬────────┘ │ │ │ │ │ ┌──────┴──────┬──────────────┐ │ │ ▼ ▼ ▼ │ │ ┌────────┐ ┌──────────┐ ┌──────────┐ │ │ │ dev │ │ staging │ │ prod │ ← Overlay(覆盖层) │ │ │replica:1│ │ replica:2│ │ replica:5│ │ │ │cpu:100m │ │ cpu:500m │ │ cpu:2000m│ │ │ └────────┘ └──────────┘ └──────────┘ │ │ │ │ 原理:Base + Patch = 最终配置 │ │ │ └─────────────────────────────────────────────────────────────────────┘

Kustomize目录结构

k8s/ ├── base/ # 基础配置 │ ├── kustomization.yaml │ ├── deployment.yaml │ ├── service.yaml │ └── configmap.yaml ├── overlays/ │ ├── dev/ # 开发环境 │ │ ├── kustomization.yaml │ │ ├── replica-patch.yaml │ │ └── resource-patch.yaml │ ├── staging/ # 测试环境 │ │ ├── kustomization.yaml │ │ ├── replica-patch.yaml │ │ └── ingress-patch.yaml │ └── prod/ # 生产环境 │ ├── kustomization.yaml │ ├── replica-patch.yaml │ ├── resource-patch.yaml │ ├── hpa-patch.yaml │ └── pdb-patch.yaml └── components/ # 可复用组件 ├── monitoring/ ├── security/ └── logging/

Base层配置

# base/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - deployment.yaml - service.yaml - configmap.yaml commonLabels: app.kubernetes.io/name: myapp app.kubernetes.io/managed-by: kustomize images: - name: myapp newTag: latest
# base/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: replicas: 1 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp image: myapp:latest ports: - containerPort: 8080 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi

Overlay层配置

# overlays/prod/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: production namePrefix: prod- resources: - ../../base - hpa.yaml - pdb.yaml patchesStrategicMerge: - replica-patch.yaml - resource-patch.yaml - config-patch.yaml configMapGenerator: - name: myapp-config behavior: merge literals: - LOG_LEVEL=warn - MAX_CONNECTIONS=1000 secretGenerator: - name: myapp-secrets literals: - DB_PASSWORD=prod-secret-password replicas: - name: myapp count: 5 images: - name: myapp newTag: v2.0.0
# overlays/prod/resource-patch.yaml apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: template: spec: containers: - name: myapp resources: requests: cpu: 500m memory: 512Mi limits: cpu: 2000m memory: 2Gi
# overlays/prod/hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: myapp-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: prod-myapp minReplicas: 5 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70

💡效率技巧:Kustomize的patchesStrategicMerge是神器。它可以智能合并配置,比如你在Base里定义了环境变量列表,在Overlay里添加新的变量,Kustomize会自动合并而不是覆盖。这比Helm的模板替换更优雅。


最佳实践:Helm + Kustomize组合拳

组合架构图

┌─────────────────────────────────────────────────────────────────────────┐ │ Helm + Kustomize 组合架构 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Helm Chart │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ │ │ Chart.yaml │ │ values.yaml │ │ templates/ │ │ │ │ │ │ (元数据) │ │ (默认值) │ │ ┌─────────────────┐ │ │ │ │ │ └─────────────┘ └─────────────┘ │ │ deployment.yaml │ │ │ │ │ │ │ │ service.yaml │ │ │ │ │ │ 职责:包管理、版本控制、依赖管理 │ │ ... │ │ │ │ │ │ 输出:渲染后的K8s YAML清单 │ └─────────────────┘ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Kustomize Overlay │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ │ │ │ base/ │ │ dev/ │ │ prod/ │ │ │ │ │ │ (基础清单) │ │ (开发覆盖) │ │ (生产覆盖) │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ │ │ │ │ │ │ │ 职责:环境差异化、配置覆盖、安全加固 │ │ │ │ 输出:最终应用到集群的YAML │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Kubernetes Cluster │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ 口诀:Helm管分发,Kustomize管差异 │ │ │ └─────────────────────────────────────────────────────────────────────────┘

实战流程

# 步骤1:用Helm渲染模板(不安装) helm template myapp ./myapp-chart \ --values ./myapp-chart/values.yaml \ --output-dir ./rendered # 步骤2:用Kustomize应用环境覆盖 cd k8s/overlays/prod kustomize build . | kubectl apply -f - # 或者一步到位 helm template myapp ./myapp-chart | kustomize build ./overlays/prod | kubectl apply -f -

项目实战目录结构

project/ ├── helm-charts/ # Helm Charts仓库 │ ├── myapp/ │ │ ├── Chart.yaml │ │ ├── values.yaml │ │ └── templates/ │ └── common/ # 通用Library Chart │ ├── Chart.yaml │ └── templates/ │ ├── _resources.tpl │ ├── _probes.tpl │ └── _labels.tpl ├── k8s-manifests/ # Kustomize配置 │ ├── base/ │ │ ├── kustomization.yaml │ │ └── helm-generated/ # Helm渲染输出 │ └── overlays/ │ ├── dev/ │ ├── staging/ │ └── prod/ ├── helmfile.yaml # Helmfile配置(多环境管理) └── scripts/ ├── deploy.sh # 部署脚本 └── rollback.sh # 回滚脚本

Helmfile:多环境Helm管理

# helmfile.yaml releases: # 开发环境 - name: myapp-dev namespace: dev chart: ./helm-charts/myapp values: - ./helm-charts/myapp/values.yaml - ./values/dev.yaml set: - name: replicaCount value: 1 - name: resources.requests.cpu value: 100m # 测试环境 - name: myapp-staging namespace: staging chart: ./helm-charts/myapp values: - ./helm-charts/myapp/values.yaml - ./values/staging.yaml set: - name: replicaCount value: 2 # 生产环境 - name: myapp-prod namespace: production chart: ./helm-charts/myapp values: - ./helm-charts/myapp/values.yaml - ./values/prod.yaml set: - name: replicaCount value: 5 - name: autoscaling.enabled value: true hooks: - events: ["presync"] showlogs: true command: "/bin/sh" args: ["-c", "echo 'Deploying to PRODUCTION - are you sure?'"] repositories: - name: bitnami url: https://charts.bitnami.com/bitnami helmDefaults: timeout: 600 recreatePods: false force: false
# 部署所有环境 helmfile sync # 仅部署生产环境 helmfile -l name=myapp-prod sync # 查看差异 helmfile diff # 回滚 helmfile rollback

⚠️避坑警告:不要试图用Helm做所有的事情,也不要试图用Kustomize做所有的事情。Helm的模板逻辑太复杂会变得难以维护,Kustomize没有版本控制和依赖管理。记住:Helm管分发,Kustomize管差异


实战:从零写一个生产级Chart

步骤1:创建Chart骨架

# 创建Chart helm create myapp cd myapp # 查看生成的结构 tree .

步骤2:完善Chart.yaml

apiVersion: v2 name: myapp description: Production-ready Helm chart for MyApp type: application version: 1.0.0 appVersion: "2.0.0" kubeVersion: ">=1.19.0-0" keywords: - web - microservice - golang home: https://github.com/example/myapp sources: - https://github.com/example/myapp maintainers: - name: DevOps Team email: devops@example.com dependencies: - name: postgresql version: "12.x.x" repository: "https://charts.bitnami.com/bitnami" condition: postgresql.enabled - name: redis version: "17.x.x" repository: "https://charts.bitnami.com/bitnami" condition: redis.enabled annotations: category: Application

步骤3:配置values.yaml

# 全局配置 replicaCount: 3 # 镜像配置 image: repository: myregistry/myapp pullPolicy: IfNotPresent tag: "" # 镜像拉取密钥 imagePullSecrets: - name: regcred nameOverride: "" fullnameOverride: "" # ServiceAccount serviceAccount: create: true annotations: {} name: "" # Pod安全 podSecurityContext: fsGroup: 2000 runAsNonRoot: true seccompProfile: type: RuntimeDefault securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1000 # Service service: type: ClusterIP port: 80 targetPort: 8080 annotations: {} # Ingress ingress: enabled: true className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/rate-limit: "100" hosts: - host: api.example.com paths: - path: / pathType: Prefix tls: - secretName: api-tls hosts: - api.example.com # 资源限制 resources: limits: cpu: 1000m memory: 1Gi requests: cpu: 100m memory: 128Mi # 自动扩缩容 autoscaling: enabled: true minReplicas: 3 maxReplicas: 20 targetCPUUtilizationPercentage: 70 targetMemoryUtilizationPercentage: 80 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 10 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 0 policies: - type: Percent value: 100 periodSeconds: 15 - type: Pods value: 4 periodSeconds: 15 selectPolicy: Max # Pod中断预算 podDisruptionBudget: enabled: true minAvailable: 2 # 健康检查 livenessProbe: httpGet: path: /health port: http initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: http initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 startupProbe: httpGet: path: /health port: http initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 30 # 配置 config: logLevel: info logFormat: json maxConnections: 1000 timeout: 30s # 数据库配置 postgresql: enabled: true auth: postgresPassword: "" database: myapp username: myapp password: "" primary: persistence: enabled: true size: 10Gi storageClass: fast-ssd redis: enabled: true auth: enabled: true password: "" master: persistence: enabled: true size: 5Gi # 监控 metrics: enabled: true port: 9090 path: /metrics serviceMonitor: enabled: true namespace: monitoring interval: 30s

步骤4:编写Deployment模板

# templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} revisionHistoryLimit: 10 strategy: type: RollingUpdate rollingUpdate: maxSurge: 25% maxUnavailable: 0 selector: matchLabels: {{- include "myapp.selectorLabels" . | nindent 6 }} template: metadata: annotations: # 强制滚动更新 checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} checksum/secrets: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} prometheus.io/scrape: "{{ .Values.metrics.enabled }}" prometheus.io/port: "{{ .Values.metrics.port }}" prometheus.io/path: "{{ .Values.metrics.path }}" {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} labels: {{- include "myapp.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "myapp.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http containerPort: {{ .Values.service.targetPort }} protocol: TCP {{- if .Values.metrics.enabled }} - name: metrics containerPort: {{ .Values.metrics.port }} protocol: TCP {{- end }} env: - name: LOG_LEVEL value: {{ .Values.config.logLevel | quote }} - name: LOG_FORMAT value: {{ .Values.config.logFormat | quote }} - name: MAX_CONNECTIONS value: {{ .Values.config.maxConnections | quote }} - name: TIMEOUT value: {{ .Values.config.timeout | quote }} - name: DB_HOST {{- if .Values.postgresql.enabled }} value: {{ include "myapp.fullname" . }}-postgresql {{- else }} value: {{ .Values.externalDatabase.host | quote }} {{- end }} - name: DB_PORT value: "5432" - name: DB_NAME value: {{ .Values.postgresql.auth.database | quote }} - name: DB_USER value: {{ .Values.postgresql.auth.username | quote }} - name: DB_PASSWORD valueFrom: secretKeyRef: name: {{ include "myapp.fullname" . }}-db key: password - name: REDIS_HOST {{- if .Values.redis.enabled }} value: {{ include "myapp.fullname" . }}-redis-master {{- else }} value: {{ .Values.externalRedis.host | quote }} {{- end }} - name: REDIS_PASSWORD valueFrom: secretKeyRef: name: {{ include "myapp.fullname" . }}-redis key: password livenessProbe: {{- toYaml .Values.livenessProbe | nindent 12 }} readinessProbe: {{- toYaml .Values.readinessProbe | nindent 12 }} startupProbe: {{- toYaml .Values.startupProbe | nindent 12 }} resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: - name: tmp mountPath: /tmp - name: config mountPath: /etc/myapp readOnly: true volumes: - name: tmp emptyDir: {} - name: config configMap: name: {{ include "myapp.fullname" . }}-config topologySpreadConstraints: - maxSkew: 1 topologyKey: topology.kubernetes.io/zone whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: {{- include "myapp.selectorLabels" . | nindent 14 }} - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: {{- include "myapp.selectorLabels" . | nindent 14 }} affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app.kubernetes.io/name operator: In values: - {{ include "myapp.name" . }} topologyKey: kubernetes.io/hostname

步骤5:打包和发布

# 更新依赖 helm dependency update # 打包Chart helm package . # 验证Chart helm lint . # 模板渲染测试 helm template myapp . --debug # 本地安装测试 helm install myapp-test . --dry-run --debug # 推送到Chart仓库 helm push myapp-1.0.0.tgz oci://registry.example.com/charts

💡效率技巧:生产级Chart必须包含PodDisruptionBudget(保证升级时最少可用实例)、HPA(自动扩缩容)、TopologySpreadConstraints(跨可用区/节点分布)、以及合理的健康检查配置。这些不是"锦上添花",是"保命必备"。


文末三件套

1. 【源码获取】

关注此系列获取后续更新,后台回复’helm’获取完整Chart源码和配套Kustomize配置。

2. 【思考题】

你的Chart维护有哪些痛点?是模板逻辑太复杂?还是多环境配置管理混乱?欢迎在评论区分享你的踩坑经历。

3. 【系列预告】

  • K8s平台篇:从零搭建生产级K8s集群
  • 安全扫描篇:镜像安全、合规检查、漏洞管理
  • 可观测性篇:Prometheus + Grafana + ELK全链路监控

总结

工具擅长不擅长
Helm包管理、版本控制、依赖管理、模板化复杂的环境差异化配置
Kustomize环境差异化、配置覆盖、无模板化依赖管理、版本控制
Helm + Kustomize全都要学习曲线稍陡

核心口诀:Helm管分发,Kustomize管差异。两者结合,天下无敌。

最后送大家一句话:YAML写得好,半夜不用搞。祝大家的K8s之路,少踩坑,多上线!


标签:Helm, Kustomize, Chart, 包管理, Kubernetes部署, Helmfile

http://www.gsyq.cn/news/1487622.html

相关文章:

  • 行业优选|2026开料机、封边机、六面钻、包装线主流品牌综合解读 - 深度智识库
  • 【Linux网络】深入理解 HTTP 协议(五):Cookie 与 Session从无状态到会话保持的底层实现
  • Python全栈修炼之路 | 第14篇 :装饰器 —— Python最优雅的语法糖
  • 2026 海南财税新政解读:吃透红利,合规经营避坑指南 - 资讯纵览
  • 从办公室网段隔离到智能家居分组:eNSP模拟VLAN的3个真实应用场景实验
  • 简单三步搞定NCM音乐解密:ncmppGui极速转换工具完整使用指南
  • 如何快速配置风扇控制:Windows平台终极风扇控制软件FanControl完全指南
  • 嵌入式电能计量算法:从功率计算到能量累积的工程实现
  • Ultimate Vocal Remover GUI:零门槛掌握AI音频分离的3大实战技巧
  • 上交大突破:多米诺推理策略实现AI推理速度近6倍能力提升
  • 2026在线答题小程序开发工具有哪些?哪个值得使用
  • 2026 年内蒙古正规旅行社权威评测 - 互联网科技品牌测评
  • 基于NXP EdgeLock A5000的硬件级设备认证:从PKI原理到嵌入式安全实践
  • 三国30位核心人物结构化关系数据(JSON+关系图,开箱即用)
  • 杭州劳力士手表回收 收的顶作为行业标杆实力出圈 - 奢侈品回收评测
  • 基于YOLOv8的智能瞄准系统:5步构建高性能AI游戏辅助
  • 2026年AI营销赛道深度测评:六大主流服务商实力盘点,助力企业精准选型 - 资讯焦点
  • 2026年最新依索维尔玻璃棉合规厂家排行及选型指南 - 奔跑123
  • 腾讯元宝 pdf 办公导出痛点全梳理,借助 AI 导出鸭实测多款导出工具,挑选性价比最优的文档转换办法
  • 本周 GitHub 热门项目推荐:Headroom 和 CC Switch
  • VS2019一键运行的OpenGL 3D交互示例:左键自由旋转+右键XY平移
  • 从无人机照片到三维地图:OpenDroneMap(ODM)完全使用指南
  • 国内塑料板材主流生产企业实测排行盘点 - 奔跑123
  • Pandas 高级技巧与最佳实践
  • 执笔赴盛夏,逐梦向长空|沈阳昊天环宇无人机致全体高考学子
  • 为什么说ArduPilot是开源自动驾驶系统的终极解决方案?
  • 5B参数如何实现720P视频生成?深度解析Wan2.2-TI2V-5B的技术突破与实践应用
  • 从STP到RSTP:一次配置搞定思科交换机多VLAN的根桥选举(附优先级设置避坑指南)
  • SQLite图形化工具选哪个?深度对比SQLite Expert与DB Browser的优缺点和适用场景
  • 百度自然排名靠后怎么用GEO优化补救