本文目录导读:

我来帮你创建一个自动配置和部署 Helm Chart 的脚本,这个脚本将包含完整的部署流程、错误处理和配置管理功能。
完整的 Helm 部署脚本
#!/bin/bash
# =============================================
# 自动配置 Helm Chart 部署脚本
# =============================================
set -euo pipefail
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# =============================================
# 配置变量 - 根据项目修改这些值
# =============================================
# 项目配置
PROJECT_NAME="my-app"
NAMESPACE="default"
CHART_NAME="my-chart"
CHART_VERSION="1.0.0"
RELEASE_NAME="${PROJECT_NAME}-release"
# 环境配置
ENVIRONMENT=${1:-"dev"} # dev, staging, production
ACTION=${2:-"install"} # install, upgrade, uninstall, status, rollback
# Helm 仓库配置
HELM_REPO_URL=""
HELM_REPO_NAME="my-repo"
# 配置文件路径
VALUES_DIR="./values"
VALUES_FILE="${VALUES_DIR}/values-${ENVIRONMENT}.yaml"
COMMON_VALUES="${VALUES_DIR}/values-common.yaml"
SECRETS_FILE="${VALUES_DIR}/secrets-${ENVIRONMENT}.yaml"
# 部署配置
DEPLOY_TIMEOUT="300s"
MAX_HISTORY=5
ROLLBACK_VERSION=0
# =============================================
# 函数定义
# =============================================
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查依赖
check_dependencies() {
log_info "检查依赖..."
# 检查 Helm
if ! command -v helm &> /dev/null; then
log_error "Helm 未安装,请运行: curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"
exit 1
fi
log_success "Helm 版本: $(helm version --short)"
# 检查 kubectl
if ! command -v kubectl &> /dev/null; then
log_error "kubectl 未安装"
exit 1
fi
log_success "kubectl 已安装"
# 检查 yq (用于 YAML 处理)
if ! command -v yq &> /dev/null; then
log_warning "yq 未安装,将使用 grep/sed 进行基本 YAML 处理"
else
log_success "yq 已安装"
fi
}
# 初始化 Helm
initialize_helm() {
log_info "初始化 Helm..."
# 添加 Helm 仓库
if [[ -n "$HELM_REPO_URL" && -n "$HELM_REPO_NAME" ]]; then
if ! helm repo list | grep -q "$HELM_REPO_NAME"; then
helm repo add "$HELM_REPO_NAME" "$HELM_REPO_URL"
log_success "添加 Helm 仓库: $HELM_REPO_NAME"
else
log_info "Helm 仓库已存在: $HELM_REPO_NAME"
fi
fi
# 更新仓库
helm repo update
log_success "Helm 仓库已更新"
}
# 创建命名空间
create_namespace() {
log_info "检查命名空间: $NAMESPACE"
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
kubectl create namespace "$NAMESPACE"
log_success "创建命名空间: $NAMESPACE"
else
log_info "命名空间已存在: $NAMESPACE"
fi
}
# 准备 values 文件
prepare_values() {
log_info "准备配置..."
local temp_values=$(mktemp)
local final_values="${VALUES_DIR}/final-${ENVIRONMENT}.yaml"
# 创建 values 目录
mkdir -p "$VALUES_DIR"
# 合并 values 文件
if [[ -f "$COMMON_VALUES" ]]; then
log_info "使用公共配置: $COMMON_VALUES"
cp "$COMMON_VALUES" "$temp_values"
fi
if [[ -f "$VALUES_FILE" ]]; then
log_info "使用环境配置: $VALUES_FILE"
if [[ -f "$temp_values" ]]; then
yq eval-all '. as $item ireduce ({}; . *+ $item)' "$temp_values" "$VALUES_FILE" > "$final_values" 2>/dev/null || \
cat "$VALUES_FILE" > "$final_values"
else
cp "$VALUES_FILE" "$final_values"
fi
else
log_warning "环境配置文件不存在: $VALUES_FILE"
# 创建默认配置
cat > "$VALUES_FILE" << EOF
# ${ENVIRONMENT} 环境配置
replicaCount: 1
image:
repository: nginx
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
EOF
log_info "创建默认配置: $VALUES_FILE"
cp "$VALUES_FILE" "$final_values"
fi
# 处理 secrets
if [[ -f "$SECRETS_FILE" ]]; then
log_info "使用加密配置: $SECRETS_FILE"
# 如果是加密文件,需要解密
if [[ "$SECRETS_FILE" == *.enc ]]; then
decrypt_secrets "$SECRETS_FILE" "${SECRETS_FILE%.enc}"
yq eval-all '. as $item ireduce ({}; . *+ $item)' "$final_values" "${SECRETS_FILE%.enc}" > "${final_values}.tmp"
mv "${final_values}.tmp" "$final_values"
else
yq eval-all '. as $item ireduce ({}; . *+ $item)' "$final_values" "$SECRETS_FILE" > "${final_values}.tmp" 2>/dev/null || \
cat "$final_values" "$SECRETS_FILE" > "${final_values}.tmp"
mv "${final_values}.tmp" "$final_values"
fi
fi
# 设置环境变量
yq eval ".global.environment = \"${ENVIRONMENT}\"" "$final_values" > "${final_values}.tmp" 2>/dev/null || true
mv "${final_values}.tmp" "$final_values"
echo "$final_values"
}
# 解密 secrets 文件
decrypt_secrets() {
local encrypted_file=$1
local decrypted_file=$2
log_info "解密配置文件..."
# 使用 sops 解密
if command -v sops &> /dev/null; then
sops -d "$encrypted_file" > "$decrypted_file"
log_success "使用 sops 解密成功"
# 使用 helm-secrets 插件
elif helm plugin list | grep -q "secrets"; then
helm secrets decrypt "$encrypted_file" > "$decrypted_file"
log_success "使用 helm-secrets 解密成功"
else
log_warning "未找到解密工具,使用原始文件"
cp "$encrypted_file" "$decrypted_file"
fi
}
# 安装/升级 Helm Chart
install_or_upgrade() {
local values_file=$1
local action=$2
log_info "${action} Helm Chart..."
# 构建 Helm 命令
local helm_cmd="helm ${action} ${RELEASE_NAME} ${CHART_NAME}"
# 添加命名空间
helm_cmd+=" --namespace ${NAMESPACE}"
# 添加 values 文件
if [[ -n "$values_file" && -f "$values_file" ]]; then
helm_cmd+=" --values ${values_file}"
fi
# 添加额外选项
helm_cmd+=" --timeout ${DEPLOY_TIMEOUT}"
helm_cmd+=" --history-max ${MAX_HISTORY}"
# 如果是升级,添加升级选项
if [[ "$action" == "upgrade" ]]; then
helm_cmd+=" --install" # release 不存在则安装
helm_cmd+=" --cleanup-on-fail"
helm_cmd+=" --atomic" # 失败时自动回滚
helm_cmd+=" --wait" # 等待就绪
fi
# 如果是安装,添加安装选项
if [[ "$action" == "install" ]]; then
helm_cmd+=" --create-namespace"
helm_cmd+=" --wait"
fi
# 添加部署标签
helm_cmd+=" --set global.deployedBy=\"helm-auto-deploy\""
helm_cmd+=" --set global.deployedAt=\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
# 执行 Helm 命令
log_info "执行命令: $helm_cmd"
if $helm_cmd; then
log_success "Helm ${action} 成功"
# 检查部署状态
sleep 5
check_deployment_status
else
log_error "Helm ${action} 失败"
return 1
fi
}
# 检查部署状态
check_deployment_status() {
log_info "检查部署状态..."
# 检查 Pod 状态
echo ""
log_info "Pod 状态:"
kubectl get pods -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" --show-labels 2>/dev/null || true
# 检查 Service
echo ""
log_info "Service 状态:"
kubectl get services -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" 2>/dev/null || true
# 检查 Ingress
echo ""
log_info "Ingress 状态:"
kubectl get ingress -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" 2>/dev/null || true
# 检查 HPA
echo ""
log_info "HPA 状态:"
kubectl get hpa -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" 2>/dev/null || true
# 获取访问信息
echo ""
log_info "访问信息:"
local service_name=$(kubectl get svc -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [[ -n "$service_name" ]]; then
local node_port=$(kubectl get svc -n "$NAMESPACE" "$service_name" -o jsonpath='{.spec.ports[0].nodePort}' 2>/dev/null || echo "")
local cluster_ip=$(kubectl get svc -n "$NAMESPACE" "$service_name" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || echo "")
if [[ -n "$node_port" ]]; then
echo " NodePort: ${node_port}"
fi
if [[ -n "$cluster_ip" ]]; then
echo " ClusterIP: ${cluster_ip}"
fi
fi
}
# 卸载 Helm Release
uninstall_release() {
log_info "卸载 Helm Release: $RELEASE_NAME"
read -p "确认卸载 ${RELEASE_NAME}? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
helm uninstall "$RELEASE_NAME" --namespace "$NAMESPACE"
log_success "卸载成功"
else
log_info "取消卸载"
fi
}
# 回滚 Helm Release
rollback_release() {
local version=${1:-0}
log_info "回滚 Helm Release: $RELEASE_NAME"
# 获取历史版本
echo ""
log_info "发布历史:"
helm history "$RELEASE_NAME" --namespace "$NAMESPACE" 2>/dev/null || true
if [[ "$version" -eq 0 ]]; then
# 如果没有指定版本,回滚到上一个版本
local revisions=$(helm history "$RELEASE_NAME" --namespace "$NAMESPACE" -o json 2>/dev/null | jq '. | length' 2>/dev/null || echo "0")
if [[ "$revisions" -gt 1 ]]; then
version=$((revisions - 1))
else
log_error "没有可回滚的版本"
return 1
fi
fi
echo ""
read -p "确认回滚到版本 ${version}? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
helm rollback "$RELEASE_NAME" "$version" --namespace "$NAMESPACE" --wait
log_success "回滚到版本 ${version} 成功"
check_deployment_status
else
log_info "取消回滚"
fi
}
# 查看部署状态
show_status() {
log_info "查看部署状态..."
# 检查 Helm Release 状态
echo ""
log_info "Helm Release 状态:"
helm status "$RELEASE_NAME" --namespace "$NAMESPACE" 2>/dev/null || log_warning "Release 不存在"
# 列出所有资源
echo ""
log_info "所有资源列表:"
kubectl get all -n "$NAMESPACE" -l "app.kubernetes.io/instance=${RELEASE_NAME}" 2>/dev/null || true
# 显示历史版本
echo ""
log_info "发布历史:"
helm history "$RELEASE_NAME" --namespace "$NAMESPACE" 2>/dev/null || true
}
# 创建默认 Chart 结构
create_default_chart() {
if [[ ! -d "$CHART_NAME" ]]; then
log_info "创建默认 Chart 结构..."
mkdir -p "$CHART_NAME/templates"
mkdir -p "$CHART_NAME/charts"
# 创建 Chart.yaml
cat > "$CHART_NAME/Chart.yaml" << EOF
apiVersion: v2
name: ${CHART_NAME}
description: A Helm chart for ${PROJECT_NAME}
type: application
version: ${CHART_VERSION}
appVersion: "1.0.0"
EOF
# 创建默认 values.yaml
cat > "$CHART_NAME/values.yaml" << EOF
# 默认配置
replicaCount: 1
image:
repository: nginx
tag: latest
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations: {}
podSecurityContext: {}
securityContext: {}
service:
type: ClusterIP
port: 80
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: {}
EOF
# 创建默认 Deployment
cat > "$CHART_NAME/templates/deployment.yaml" << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "chart.fullname" . }}
labels:
{{- include "chart.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "chart.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "chart.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "chart.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.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
EOF
# 创建 Service
cat > "$CHART_NAME/templates/service.yaml" << 'EOF'
apiVersion: v1
kind: Service
metadata:
name: {{ include "chart.fullname" . }}
labels:
{{- include "chart.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "chart.selectorLabels" . | nindent 4 }}
EOF
# 创建 _helpers.tpl
cat > "$CHART_NAME/templates/_helpers.tpl" << 'EOF'
{{- define "chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "chart.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 "chart.labels" -}}
helm.sh/chart: {{ include "chart.name" . }}-{{ .Chart.Version | replace "+" "_" }}
{{ include "chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{- define "chart.selectorLabels" -}}
app.kubernetes.io/name: {{ include "chart.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{- define "chart.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "chart.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
EOF
log_success "创建默认 Chart 结构完成"
else
log_info "Chart 目录已存在"
fi
}
# 主函数
main() {
echo ""
echo "=============================================="
echo " Helm Chart 自动化部署脚本"
echo "=============================================="
echo " 环境: ${ENVIRONMENT}"
echo " 操作: ${ACTION}"
echo " 项目: ${PROJECT_NAME}"
echo " Release: ${RELEASE_NAME}"
echo "=============================================="
echo ""
# 检查依赖
check_dependencies
# 初始化 Helm
initialize_helm
# 根据操作执行不同命令
case "$ACTION" in
install)
# 创建默认 Chart
create_default_chart
# 创建命名空间
create_namespace
# 准备配置
local values_file
values_file=$(prepare_values)
# 安装
install_or_upgrade "$values_file" "install"
;;
upgrade)
# 准备配置
local values_file
values_file=$(prepare_values)
# 升级
install_or_upgrade "$values_file" "upgrade"
;;
uninstall)
uninstall_release
;;
rollback)
rollback_release "$ROLLBACK_VERSION"
;;
status)
show_status
;;
*)
echo "用法: $0 <environment> <action>"
echo ""
echo "环境:"
echo " dev 开发环境"
echo " staging 预发布环境"
echo " production 生产环境"
echo ""
echo "操作:"
echo " install 安装新的 release"
echo " upgrade 升级已有的 release"
echo " uninstall 删除 release"
echo " rollback 回滚 release"
echo " status 查看状态"
echo ""
echo "示例:"
echo " $0 dev install"
echo " $0 production upgrade"
echo " $0 staging uninstall"
echo " $0 dev rollback"
echo " $0 production status"
exit 1
;;
esac
echo ""
log_success "操作完成"
}
# 执行主函数
main "$@"
配置文件示例
公共配置 (values/values-common.yaml)
# 公共配置
global:
environment: ""
registry: "registry.example.com"
imagePullSecrets:
- name: registry-credentials
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
开发环境配置 (values/values-dev.yaml)
# 开发环境配置
replicaCount: 1
image:
repository: registry.example.com/my-app
tag: dev-latest
pullPolicy: Always
ingress:
enabled: false
service:
type: NodePort
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: false
生产环境配置 (values/values-production.yaml)
# 生产环境配置
replicaCount: 3
image:
repository: registry.example.com/my-app
tag: stable
pullPolicy: IfNotPresent
ingress:
enabled: true
hosts:
- host: app.example.com
paths:
- path: /
pathType: ImplementationSpecific
tls:
- hosts:
- app.example.com
secretName: app-tls
resources:
limits:
cpu: 1
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
加密 Secrets (values/secrets-production.yaml.enc)
# 加密的敏感信息 env: DATABASE_URL: postgresql://user:password@localhost:5432/dbname REDIS_PASSWORD: your-redis-password API_KEY: your-api-key # 注意:实际使用时,需要使用 sops 或 helm-secrets 进行加密
使用示例
基本使用
# 赋予执行权限 chmod +x deploy.sh # 安装到开发环境 ./deploy.sh dev install # 升级生产环境 ./deploy.sh production upgrade # 查看状态 ./deploy.sh dev status # 回滚到上一个版本 ./deploy.sh staging rollback # 卸载 ./deploy.sh dev uninstall
高级用法
# 使用自定义值文件 VALUES_DIR=custom-values ./deploy.sh dev install # 设置超时时间 DEPLOY_TIMEOUT=600s ./deploy.sh production upgrade # 指定回滚到特定版本 ROLLBACK_VERSION=3 ./deploy.sh production rollback # 使用不同的命名空间 NAMESPACE=my-custom-ns ./deploy.sh dev install
CI/CD 集成 (GitLab CI)
# .gitlab-ci.yml
deploy-dev:
stage: deploy
script:
- chmod +x deploy.sh
- ./deploy.sh dev install
only:
- develop
deploy-production:
stage: deploy
script:
- chmod +x deploy.sh
- ./deploy.sh production upgrade
only:
- tags
environment:
name: production
url: https://app.example.com
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy to Kubernetes
on:
push:
branches: [main]
tags: ['v*']
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Helm
uses: azure/setup-helm@v3
- name: Deploy
run: |
chmod +x deploy.sh
./deploy.sh production upgrade
env:
KUBECONFIG: ${{ secrets.KUBECONFIG }}
注意事项
-
安全性:
- 不要在脚本中硬编码密码
- 使用
sops或helm-secrets加密敏感信息 - 限制脚本执行权限
-
配置管理:
- 维护不同环境的值文件
- 使用版本控制管理配置
- 定期审核配置文件
-
错误处理:
- 脚本包含基本的错误处理
- 生产环境建议添加更严格的验证
- 实现部署前检查
-
监控和日志:
- 记录所有部署操作
- 集成监控告警
- 保存部署历史
-
回滚策略:
- 保留足够的发布历史
- 测试回滚流程
- 文档化回滚步骤
这个脚本提供了一个完整、可扩展的 Helm Chart 部署解决方案,可以根据实际需求进行定制和扩展。