Helm Charts
概述
Helm是Kubernetes的包管理器,被称为"Kubernetes的apt/yum"。它将Kubernetes资源打包成Charts,实现应用的版本化管理、分发和部署。Helm简化了Kubernetes应用的部署流程,提供了模板化、参数化配置能力,是云原生应用交付的事实标准工具。
核心概念
1. Chart
Chart是Helm的打包格式,包含一组Kubernetes资源定义文件:
- 类似于Homebrew的formula、apt的dpkg
- 包含应用的所有Kubernetes资源定义
- 支持参数化配置和模板化
2. Release
Release是Chart的运行实例:
- 每次安装Chart都会创建一个Release
- 同一个Chart可以安装多次,每次都是独立的Release
- Release有自己的名称和版本历史
3. Repository
Repository是存储和共享Charts的仓库:
- 类似于Docker Hub、PyPI
- 支持公共仓库和私有仓库
- 可以添加多个仓库源
4. Values
Values是Chart的配置参数:
- 通过values.yaml文件定义默认值
- 安装时可以覆盖默认值
- 支持多层配置合并
Helm架构
┌─────────────────────────────────────────────────────────────┐
│ Helm Client │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 本地Charts │ │ Values配置 │ │ Release管理 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓ ↓ ↓
┌─────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Chart │ │ Kubernetes │ │ Repository │
│ Repository │ │ API Server │ │ (远程仓库) │
└─────────────┘ └──────────────┘ └──────────────────┘
↓ ↓ ↓
┌─────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Chart存储 │ │ Secret存储 │ │ Artifact Hub │
│ (本地/远程) │ │ (Release状态) │ │ (公共仓库) │
└─────────────┘ └──────────────┘ └──────────────────┘Helm安装与配置
安装Helm
Linux/macOS安装
bash
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
brew install helmWindows安装
powershell
choco install kubernetes-helm
scoop install helm验证安装
bash
helm version
helm version --short配置Helm仓库
添加常用仓库
bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add stable https://charts.helm.sh/stable
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm repo list搜索Charts
bash
helm search repo nginx
helm search repo nginx --versions
helm search hub wordpress
helm show chart bitnami/nginx
helm show readme bitnami/nginx
helm show values bitnami/nginx
helm show all bitnami/nginxChart结构
标准Chart目录结构
mychart/
├── Chart.yaml # Chart元数据
├── values.yaml # 默认配置值
├── charts/ # 依赖的Charts
├── templates/ # 模板文件目录
│ ├── NOTES.txt # 安装后的说明信息
│ ├── deployment.yaml # Deployment资源
│ ├── service.yaml # Service资源
│ ├── ingress.yaml # Ingress资源
│ ├── configmap.yaml # ConfigMap资源
│ ├── secret.yaml # Secret资源
│ ├── _helpers.tpl # 模板助手函数
│ └── tests/ # 测试文件
│ └── test-connection.yaml
├── templates/tests/ # 测试Pod定义
├── .helmignore # 打包时忽略的文件
├── LICENSE # 许可证
└── README.md # 说明文档Chart.yaml详解
yaml
apiVersion: v2
name: mychart
description: A Helm chart for Kubernetes
type: application
version: 1.0.0
appVersion: "1.16.0"
kubeVersion: ">=1.20.0-0"
keywords:
- nginx
- web
- http
home: https://example.com
sources:
- https://github.com/example/mychart
maintainers:
- name: John Doe
email: john@example.com
url: https://example.com
icon: https://example.com/icon.png
deprecated: false
annotations:
artifacthub.io/license: Apache-2.0
artifacthub.io/signKey: |
fingerprint: "C874011F0AB405110D02105534365D9472D7468F"
url: https://key.url
dependencies:
- name: postgresql
version: "12.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
tags:
- database
alias: postgresvalues.yaml详解
yaml
replicaCount: 3
image:
repository: nginx
tag: "1.21"
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations: {}
podSecurityContext:
fsGroup: 1000
securityContext:
runAsNonRoot: true
runAsUser: 1000
service:
type: ClusterIP
port: 80
targetPort: 80
nodePort: ""
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
config:
data: {}
secrets:
data: {}模板开发
基础模板语法
Deployment模板示例
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "mychart.fullname" . }}
labels:
{{- include "mychart.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "mychart.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "mychart.selectorLabels" . | nindent 8 }}
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "mychart.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.livenessProbe.enabled }}
livenessProbe:
httpGet:
path: {{ .Values.livenessProbe.path }}
port: http
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
httpGet:
path: {{ .Values.readinessProbe.path }}
port: http
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}_helpers.tpl助手函数
yaml
{{- define "mychart.labels" -}}
helm.sh/chart: {{ include "mychart.chart" . }}
{{ include "mychart.selectorLabels" . }}
{{- if .Chart.AppVersion -}}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end -}}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{- define "mychart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "mychart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- define "mychart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end -}}
{{- define "mychart.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 -}}
{{- define "mychart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end -}}
{{- define "mychart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "mychart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end -}}条件判断与循环
条件判断
yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "mychart.fullname" . }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "mychart.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
{{- end }}循环遍历
yaml
{{- range $key, $value := .Values.config.data }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range .Values.hosts }}
- {{ . }}
{{- end }}
{{- range $index, $host := .Values.hosts }}
- index: {{ $index }}
host: {{ $host }}
{{- end }}Helm操作命令
Release管理
安装Chart
bash
helm install myrelease bitnami/nginx
helm install myrelease bitnami/nginx --namespace web --create-namespace
helm install myrelease bitnami/nginx -f values.yaml
helm install myrelease bitnami/nginx --set replicaCount=3
helm install myrelease bitnami/nginx --set image.tag=1.22
helm install myrelease bitnami/nginx --set service.type=NodePort
helm install myrelease bitnami/nginx --set service.nodePort=30080
helm install myrelease ./mychart
helm install myrelease ./mychart --dry-run --debug
helm install myrelease ./mychart --generate-name
helm install myrelease ./mychart --timeout 10m
helm install myrelease ./mychart --wait
helm install myrelease ./mychart --wait-for-jobs升级Release
bash
helm upgrade myrelease bitnami/nginx
helm upgrade myrelease bitnami/nginx --set replicaCount=5
helm upgrade myrelease bitnami/nginx -f new-values.yaml
helm upgrade myrelease bitnami/nginx --reuse-values
helm upgrade myrelease bitnami/nginx --reset-values
helm upgrade myrelease bitnami/nginx --force
helm upgrade myrelease bitnami/nginx --atomic
helm upgrade --install myrelease bitnami/nginx
helm upgrade myrelease bitnami/nginx --version 15.0.0回滚Release
bash
helm rollback myrelease
helm rollback myrelease 2
helm rollback myrelease 2 --dry-run
helm rollback myrelease 2 --force卸载Release
bash
helm uninstall myrelease
helm uninstall myrelease --namespace web
helm uninstall myrelease --keep-history
helm uninstall myrelease --dry-run查看Release
bash
helm list
helm list --all-namespaces
helm list -n web
helm list --all
helm list --filter 'nginx'
helm list --output json
helm list --output yaml
helm status myrelease
helm status myrelease -n web
helm status myrelease --show-desc
helm history myrelease
helm history myrelease --max 10
helm get values myrelease
helm get values myrelease --revision 2
helm get values myrelease --all
helm get manifest myrelease
helm get manifest myrelease --revision 2
helm get notes myrelease
helm get hooks myrelease
helm get all myreleaseChart管理
创建Chart
bash
helm create mychart
helm create mychart --starter my-starter验证Chart
bash
helm lint mychart
helm lint mychart --strict
helm lint mychart -f values.yaml
helm template myrelease mychart
helm template myrelease mychart --debug
helm template myrelease mychart -f values.yaml
helm template myrelease mychart --set replicaCount=3
helm template myrelease mychart --show-only templates/deployment.yaml
helm template myrelease mychart --output-dir ./output打包Chart
bash
helm package mychart
helm package mychart --version 1.2.3
helm package mychart --app-version 2.0.0
helm package mychart --destination ./charts
helm package mychart --sign --key 'John Doe' --keyring ~/.gnupg/pubring.gpg
helm package mychart --dependency-update依赖管理
bash
helm dependency list mychart
helm dependency update mychart
helm dependency build mychart
helm dependency update mychart --skip-refresh仓库管理
bash
helm repo add myrepo https://charts.example.com
helm repo add myrepo https://charts.example.com --username user --password pass
helm repo update
helm repo remove myrepo
helm repo index ./charts
helm repo index ./charts --url https://charts.example.com
helm push mychart-1.0.0.tgz myrepo
helm pull bitnami/nginx
helm pull bitnami/nginx --version 15.0.0
helm pull bitnami/nginx --destination ./charts
helm pull bitnami/nginx --untar
helm pull bitnami/nginx --untardir ./extracted实践示例
示例1:完整的Web应用Chart
创建Chart结构
bash
helm create webappChart.yaml
yaml
apiVersion: v2
name: webapp
description: A production-ready web application Helm chart
type: application
version: 1.0.0
appVersion: "2.0.0"
kubeVersion: ">=1.20.0-0"
keywords:
- web
- nginx
- frontend
home: https://github.com/example/webapp
sources:
- https://github.com/example/webapp
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.enabledvalues.yaml
yaml
global:
imageRegistry: ""
imagePullSecrets: []
storageClass: ""
replicaCount: 3
image:
repository: nginx
tag: "1.25"
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9113"
podSecurityContext:
runAsNonRoot: true
runAsUser: 101
fsGroup: 101
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
service:
type: ClusterIP
port: 80
targetPort: 8080
annotations: {}
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
hosts:
- host: webapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: webapp-tls
hosts:
- webapp.example.com
resources:
limits:
cpu: 1000m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
livenessProbe:
enabled: true
path: /health
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
enabled: true
path: /ready
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
volumes:
- name: cache
emptyDir: {}
- name: config
configMap:
name: webapp-config
volumeMounts:
- name: cache
mountPath: /var/cache/nginx
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
nodeSelector: {}
tolerations: []
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- webapp
topologyKey: kubernetes.io/hostname
config:
nginx: |
upstream backend {
server backend-service:8080;
}
server {
listen 8080;
location / {
proxy_pass http://backend;
}
}
postgresql:
enabled: true
auth:
postgresPassword: "change-me"
database: webapp
redis:
enabled: true
auth:
password: "change-me"templates/deployment.yaml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "webapp.fullname" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "webapp.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "webapp.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "webapp.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.livenessProbe.enabled }}
livenessProbe:
httpGet:
path: {{ .Values.livenessProbe.path }}
port: http
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
httpGet:
path: {{ .Values.readinessProbe.path }}
port: http
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
{{- toYaml .Values.volumeMounts | nindent 12 }}
volumes:
{{- toYaml .Values.volumes | nindent 8 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}templates/configmap.yaml
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "webapp.fullname" . }}-config
labels:
{{- include "webapp.labels" . | nindent 4 }}
data:
nginx.conf: |
{{- .Values.config.nginx | nindent 4 }}部署命令
bash
helm dependency update ./webapp
helm install webapp ./webapp -n production --create-namespace
helm install webapp ./webapp -n production -f custom-values.yaml
helm upgrade webapp ./webapp -n production --reuse-values --set image.tag=2.1.0
helm rollback webapp -n production
helm uninstall webapp -n production示例2:多环境配置管理
目录结构
webapp/
├── Chart.yaml
├── values.yaml
├── values/
│ ├── dev.yaml
│ ├── staging.yaml
│ └── production.yaml
├── templates/
│ └── ...
└── charts/values/dev.yaml
yaml
replicaCount: 1
image:
tag: "latest"
ingress:
enabled: true
hosts:
- host: webapp.dev.example.com
paths:
- path: /
pathType: Prefix
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: false
postgresql:
enabled: true
auth:
postgresPassword: "dev-password"
redis:
enabled: true
auth:
password: "dev-password"values/staging.yaml
yaml
replicaCount: 2
image:
tag: "v2.0.0"
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
hosts:
- host: webapp.staging.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: webapp-staging-tls
hosts:
- webapp.staging.example.com
resources:
limits:
cpu: 1000m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
postgresql:
enabled: true
auth:
postgresPassword: "staging-password"
redis:
enabled: true
auth:
password: "staging-password"values/production.yaml
yaml
replicaCount: 3
image:
tag: "v2.0.0"
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
hosts:
- host: webapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: webapp-production-tls
hosts:
- webapp.example.com
resources:
limits:
cpu: 2000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 60
targetMemoryUtilizationPercentage: 70
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- webapp
topologyKey: kubernetes.io/hostname
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/worker
operator: Exists
postgresql:
enabled: false
redis:
enabled: false部署脚本
bash
helm upgrade --install webapp ./webapp \
-n dev \
--create-namespace \
-f values/dev.yaml
helm upgrade --install webapp ./webapp \
-n staging \
--create-namespace \
-f values/staging.yaml
helm upgrade --install webapp ./webapp \
-n production \
--create-namespace \
-f values/production.yaml \
--atomic \
--timeout 10m示例3:Chart依赖管理
主Chart配置
yaml
apiVersion: v2
name: fullstack-app
description: Full stack application with frontend, backend, and database
version: 1.0.0
appVersion: "3.0.0"
dependencies:
- name: frontend
version: "1.x.x"
repository: file://charts/frontend
condition: frontend.enabled
tags:
- frontend
- name: backend
version: "1.x.x"
repository: file://charts/backend
condition: backend.enabled
tags:
- backend
- name: postgresql
version: "12.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
alias: database
tags:
- database
- name: redis
version: "17.x.x"
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
tags:
- cache
- name: elasticsearch
version: "19.x.x"
repository: https://charts.bitnami.com/bitnami
condition: elasticsearch.enabled
tags:
- searchvalues.yaml
yaml
global:
environment: production
imageRegistry: registry.example.com
imagePullSecrets:
- name: registry-secret
frontend:
enabled: true
replicaCount: 3
image:
repository: frontend
tag: "v2.0.0"
ingress:
enabled: true
hosts:
- host: app.example.com
backend:
enabled: true
replicaCount: 5
image:
repository: backend
tag: "v2.0.0"
config:
database:
host: "{{ .Release.Name }}-database"
port: 5432
redis:
host: "{{ .Release.Name }}-redis-master"
port: 6379
postgresql:
enabled: true
auth:
postgresPassword: "change-me"
database: appdb
primary:
persistence:
size: 50Gi
storageClass: fast-ssd
redis:
enabled: true
auth:
password: "change-me"
master:
persistence:
size: 10Gi
elasticsearch:
enabled: false部署命令
bash
helm dependency update ./fullstack-app
helm dependency build ./fullstack-app
helm install myapp ./fullstack-app \
--set global.environment=production \
--set frontend.replicaCount=5 \
--set backend.replicaCount=10
helm install myapp ./fullstack-app \
--set frontend.enabled=true \
--set backend.enabled=true \
--set postgresql.enabled=true \
--set redis.enabled=true \
--set elasticsearch.enabled=false
helm install myapp ./fullstack-app --tags frontend,backend
helm install myapp ./fullstack-app --tags databaseChart仓库管理
搭建私有仓库
使用GitHub Pages
bash
mkdir -p docs
helm package mychart --destination docs/
helm repo index docs/ --url https://username.github.io/charts
git add docs/
git commit -m "Add chart package"
git push origin main使用对象存储(S3)
bash
helm plugin install https://github.com/hypnoglow/helm-s3.git
helm s3 init s3://my-bucket/charts
helm repo add my-s3-repo s3://my-bucket/charts
helm s3 push mychart-1.0.0.tgz my-s3-repo使用Harbor
bash
helm repo add harbor https://harbor.example.com/chartrepo/library
helm push mychart-1.0.0.tgz harbor使用ChartMuseum
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: chartmuseum
namespace: charts
spec:
replicas: 1
selector:
matchLabels:
app: chartmuseum
template:
metadata:
labels:
app: chartmuseum
spec:
containers:
- name: chartmuseum
image: ghcr.io/helm/chartmuseum:v0.15.0
args:
- --port=8080
- --storage=local
- --storage-local-rootdir=/charts
ports:
- containerPort: 8080
volumeMounts:
- name: charts
mountPath: /charts
volumes:
- name: charts
persistentVolumeClaim:
claimName: chartmuseum-pvc
---
apiVersion: v1
kind: Service
metadata:
name: chartmuseum
namespace: charts
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: chartmuseumbash
helm repo add myrepo http://chartmuseum.example.com
curl -L http://chartmuseum.example.com/api/charts --data-binary @mychart-1.0.0.tgz
helm push mychart-1.0.0.tgz myrepo版本控制与CI/CD
Chart版本管理
语义化版本
yaml
version: 1.2.3
MAJOR.MINOR.PATCH
MAJOR: 不兼容的API变更
MINOR: 向后兼容的功能新增
PATCH: 向后兼容的问题修复版本更新策略
bash
helm package mychart --version 1.2.0
helm upgrade myrelease myrepo/mychart --version 1.2.0
helm search repo mychart --versions
helm search repo mychart --version ">=1.0.0,<2.0.0"GitOps集成
ArgoCD集成
yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: webapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/webapp-chart.git
targetRevision: HEAD
path: charts/webapp
helm:
valueFiles:
- values/production.yaml
parameters:
- name: replicaCount
value: "5"
- name: image.tag
value: "v2.1.0"
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueFlux集成
yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: webapp
namespace: flux-system
spec:
interval: 1m
url: https://charts.example.com
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: webapp
namespace: production
spec:
interval: 5m
chart:
spec:
chart: webapp
version: ">=1.0.0"
sourceRef:
kind: HelmRepository
name: webapp
namespace: flux-system
values:
replicaCount: 3
image:
tag: v2.0.0
ingress:
enabled: true
hosts:
- host: webapp.example.com
paths:
- path: /
pathType: PrefixCI/CD流水线示例
GitLab CI
yaml
stages:
- lint
- test
- build
- deploy
lint:
stage: lint
image: alpine/helm:3.12.0
script:
- helm lint charts/webapp
- helm template webapp charts/webapp > /dev/null
test:
stage: test
image: alpine/helm:3.12.0
script:
- helm plugin install https://github.com/quintush/helm-unittest
- helm unittest charts/webapp
build:
stage: build
image: alpine/helm:3.12.0
script:
- helm package charts/webapp --version ${CI_COMMIT_TAG}
- helm repo index ./ --url https://charts.example.com
artifacts:
paths:
- "*.tgz"
- index.yaml
only:
- tags
deploy:
stage: deploy
image: alpine/helm:3.12.0
script:
- helm upgrade --install webapp charts/webapp
--namespace production
--values values/production.yaml
--set image.tag=${CI_COMMIT_TAG}
--atomic
--timeout 10m
only:
- tags
when: manualGitHub Actions
yaml
name: Helm CI/CD
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Helm
uses: azure/setup-helm@v3
with:
version: v3.12.0
- name: Lint Chart
run: |
helm lint charts/webapp
helm template webapp charts/webapp > /dev/null
test:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- name: Set up Helm
uses: azure/setup-helm@v3
- name: Install unittest plugin
run: helm plugin install https://github.com/quintush/helm-unittest
- name: Run tests
run: helm unittest charts/webapp
publish:
runs-on: ubuntu-latest
needs: test
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v4
- name: Set up Helm
uses: azure/setup-helm@v3
- name: Package Chart
run: |
helm package charts/webapp --version ${GITHUB_REF#refs/tags/}
helm repo index ./ --url https://charts.example.com
- name: Push to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./
deploy:
runs-on: ubuntu-latest
needs: publish
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v4
- name: Set up Helm
uses: azure/setup-helm@v3
- name: Set up kubectl
uses: azure/setup-kubectl@v3
- name: Configure kubeconfig
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > ~/.kube/config
- name: Deploy to Production
run: |
helm upgrade --install webapp charts/webapp \
--namespace production \
--values values/production.yaml \
--set image.tag=${GITHUB_REF#refs/tags/} \
--atomic \
--timeout 10m故障排查指南
问题1:Chart安装失败
症状
bash
Error: INSTALLATION FAILED: unable to build kubernetes objects from release manifest排查步骤
bash
helm template myrelease ./mychart --debug
helm template myrelease ./mychart --debug 2>&1 | grep -A 10 "Error"
helm install myrelease ./mychart --dry-run --debug
kubectl apply -f --dry-run=client -o yaml - < <(helm template myrelease ./mychart)解决方案
- 检查模板语法错误
- 验证Kubernetes资源定义
- 检查values参数类型
- 确认API版本兼容性
问题2:Release状态异常
症状
bash
helm list
NAME STATUS
myrelease pending-install排查步骤
bash
helm status myrelease
kubectl get all -l app.kubernetes.io/instance=myrelease
kubectl describe pods -l app.kubernetes.io/instance=myrelease
kubectl logs -l app.kubernetes.io/instance=myrelease --all-containers
kubectl get secret -l owner=helm,name=myrelease解决方案
bash
helm rollback myrelease
helm uninstall myrelease
kubectl delete secret sh.helm.release.v1.myrelease.v1
helm install myrelease ./mychart --force问题3:依赖更新失败
症状
bash
Error: no cached repository for helm-manager found. (try 'helm repo update')排查步骤
bash
helm repo list
helm repo update
helm dependency list ./mychart
ls -la ./mychart/charts/
cat ./mychart/Chart.lock解决方案
bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm dependency update ./mychart
helm dependency build ./mychart问题4:模板渲染错误
症状
bash
Error: INSTALLATION FAILED: render error in "webapp/templates/deployment.yaml": template: webapp/templates/deployment.yaml:15:20: executing "webapp/templates/deployment.yaml" at <.Values.replicaCount>: can't evaluate field replicaCount in type interface {}排查步骤
bash
helm template myrelease ./mychart --debug
helm get values myrelease --all
helm template myrelease ./mychart -f values.yaml --debug
helm lint ./mychart --strict解决方案
- 检查values.yaml缩进
- 验证模板变量路径
- 确认条件判断语法
- 使用default函数提供默认值
问题5:升级回滚失败
症状
bash
Error: UPGRADE FAILED: another operation (install/upgrade/rollback) is in progress排查步骤
bash
helm history myrelease
helm status myrelease
kubectl get secret -l owner=helm,name=myrelease
kubectl get configmap -l owner=helm,name=myrelease解决方案
bash
helm rollback myrelease
helm rollback myrelease --force
kubectl delete secret sh.helm.release.v1.myrelease.v<version>
helm upgrade myrelease ./mychart --force最佳实践
1. Chart设计最佳实践
命名规范
yaml
name: myapp
fullname: "{{ .Release.Name }}-{{ .Chart.Name }}"
labels:
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/component: frontend
app.kubernetes.io/part-of: myapp
app.kubernetes.io/managed-by: {{ .Release.Service }}资源组织
yaml
templates/
├── deployment.yaml
├── service.yaml
├── configmap.yaml
├── secret.yaml
├── ingress.yaml
├── hpa.yaml
├── serviceaccount.yaml
├── _helpers.tpl
└── tests/
└── test-connection.yaml2. Values设计最佳实践
分层配置
yaml
global:
imageRegistry: ""
imagePullSecrets: []
image:
repository: nginx
tag: "1.25"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
ingress:
enabled: false
hosts: []
resources:
limits: {}
requests: {}使用条件开关
yaml
ingress:
enabled: false
autoscaling:
enabled: false
serviceAccount:
create: true
{{- if .Values.ingress.enabled -}}
{{- end -}}
{{- if .Values.autoscaling.enabled -}}
{{- end -}}3. 模板开发最佳实践
使用助手函数
yaml
{{- define "myapp.labels" -}}
app.kubernetes.io/name: {{ include "myapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- define "myapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "myapp.name" . }}
{{- end -}}使用命名模板
yaml
{{- define "myapp.imagePullSecrets" -}}
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 2 }}
{{- else if .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.imagePullSecrets | nindent 2 }}
{{- end }}
{{- end -}}使用checksum触发滚动更新
yaml
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}4. 安全最佳实践
安全上下文
yaml
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true敏感信息管理
yaml
{{- if .Values.secrets.create -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "myapp.fullname" . }}
labels:
{{- include "myapp.labels" . | nindent 4 }}
type: Opaque
data:
{{- range $key, $value := .Values.secrets.data }}
{{ $key }}: {{ $value | b64enc | quote }}
{{- end }}
{{- end }}5. 测试最佳实践
Chart测试
yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "myapp.fullname" . }}-test-connection"
labels:
{{- include "myapp.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "myapp.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never运行测试
bash
helm test myrelease
helm test myrelease --logs6. 文档最佳实践
README.md
markdown
# MyChart
## Prerequisites
- Kubernetes 1.20+
- Helm 3.0+
## Installing the Chart
\`\`\`bash
helm install myrelease ./mychart
\`\`\`
## Uninstalling the Chart
\`\`\`bash
helm uninstall myrelease
\`\`\`
## Configuration
| Parameter | Description | Default |
|-----------|-------------|---------|
| `replicaCount` | Number of replicas | `3` |
| `image.repository` | Image repository | `nginx` |
| `image.tag` | Image tag | `1.25` |NOTES.txt
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "myapp.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "myapp.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "myapp.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "myapp.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}总结
本章详细介绍了Helm Charts的核心概念和实践方法:
- 基础概念: 掌握了Chart、Release、Repository等核心概念
- Chart开发: 学会了Chart结构设计和模板开发
- 操作命令: 掌握了Helm常用命令和最佳实践
- 多环境管理: 理解了多环境配置管理策略
- 仓库管理: 学会了搭建和管理私有Chart仓库
- CI/CD集成: 掌握了与GitOps工具的集成方法
- 故障排查: 掌握了常见问题的诊断和解决方法
Helm是Kubernetes应用交付的核心工具,为后续的Operator开发和高级特性应用奠定了基础。
下一步学习
- Operator模式 - 学习自定义控制器开发
- 自定义资源 - 掌握CRD定义和管理
- 自动扩缩容 - 配置HPA/VPA自动扩缩容
- 服务网格 - 学习Istio服务网格架构