版本: v0.3.0
Helm Operator 是一个 Kubernetes 控制器,用于管理 Helm 发布的生命周期。它通过 Kubernetes 自定义资源(CRD)提供声明式的 Helm 发布管理功能,具有智能自动化和高级特性。
helm-operator/
├── api/v1alpha1/ # CRD 定义
│ ├── helmrelease_types.go # HelmRelease 资源定义
│ └── helmrepository_types.go # HelmRepository 资源定义
├── internal/
│ ├── controller/ # 控制器逻辑
│ │ ├── helmrelease_controller.go
│ │ └── helmrepository_controller.go
│ ├── helm/ # Helm 客户端封装
│ │ ├── client.go
│ │ ├── release.go
│ │ └── repository.go
│ └── utils/ # 工具函数
├── deploy/ # 部署配置
├── samples/ # 示例资源
└── cmd/main.go # 程序入口- HelmRepository: 定义 Helm 仓库配置
- HelmRelease: 定义 Helm 发布配置
- Go 1.21+
- Docker
- Kubernetes 集群(本地或远程)
- kubectl
- Helm 3.x
- kubebuilder
# 克隆项目
git clone https://github.com/ketches/helm-operator.git
cd helm-operator
# 安装依赖
go mod download# 构建二进制文件
make build
# 构建 Docker 镜像
make docker-build
# 运行测试
make testHelmRepository 定义了 Helm 仓库的配置信息,支持多种类型的仓库:
OCI(Open Container Initiative)仓库因更好的性能、安全性和标准化,是分发 Helm Chart 的推荐方式。
公共 OCI 仓库:
apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: ghcr-charts
namespace: default
spec:
url: "oci://ghcr.io/myorg/charts"
type: "oci"
interval: "1h"
timeout: "10m"
# 优化资源使用
valuesConfigMapPolicy: disabled # 推荐带认证的私有 OCI 仓库:
# 创建 Docker registry secret
apiVersion: v1
kind: Secret
metadata:
name: oci-registry-auth
namespace: default
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-docker-config>
---
apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: acr-private
namespace: default
spec:
url: "oci://myregistry.azurecr.io/helm"
type: "oci"
auth:
secretRef:
name: oci-registry-auth
interval: "1h"
valuesConfigMapPolicy: disabledOCI 仓库示例:
# GitHub Container Registry (GHCR)
spec:
url: "oci://ghcr.io/myorg/charts"
# Azure Container Registry (ACR)
spec:
url: "oci://myregistry.azurecr.io/helm"
# Google Artifact Registry (GAR)
spec:
url: "oci://us-docker.pkg.dev/project-id/helm-charts"
# Amazon Elastic Container Registry (ECR)
spec:
url: "oci://123456789012.dkr.ecr.us-east-1.amazonaws.com/helm-charts"
# Harbor
spec:
url: "oci://harbor.example.com/library"apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: bitnami
namespace: default
spec:
url: "https://charts.bitnami.com/bitnami"
type: "helm"
interval: "30m"
timeout: "5m"
suspend: false
valuesConfigMapPolicy: disabled # 推荐apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: private-repo
namespace: default
spec:
url: "https://private.charts.example.com"
type: "helm"
interval: "1h"
timeout: "10m"
auth:
basic:
secretRef:
name: "private-repo-auth"
namespace: "default"
suspend: falseapiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRepository
metadata:
name: internal-repo
namespace: default
spec:
url: "http://internal-charts.company.local:8080"
type: "helm"
interval: "30m"
timeout: "10m"
auth:
basic:
secretRef:
name: "internal-repo-auth"
namespace: "default"
tls:
insecureSkipVerify: true
suspend: false关键字段说明:
url: Helm 仓库 URL,支持https://、http://协议type: 仓库类型(当前只支持helm,后续可能会添加对oci的支持)interval: 同步间隔timeout: 操作超时时间auth: 认证配置(可选)basic: 基础认证(用户名/密码)tls: TLS 配置
suspend: 是否暂停同步
HelmRelease 定义了 Helm 发布的配置:
apiVersion: helm-operator.ketches.cn/v1alpha1
kind: HelmRelease
metadata:
name: nginx
namespace: default
spec:
chart:
name: "nginx-charts"
version: "0.1.0"
repository:
name: "nginx-charts"
namespace: "default"
release:
name: "nginx"
namespace: "default"
createNamespace: true
values: |
replicaCount: 2
install:
timeout: "10m"
wait: true
upgrade:
timeout: "10m"
wait: true
interval: "1h"关键字段说明:
chart: Chart 配置(名称、版本、仓库)release: 发布配置(名称、命名空间)values: Helm values 配置install/upgrade: 安装/升级选项interval: 协调间隔
-
修改 CRD 定义
# 编辑 API 类型定义 vim api/v1alpha1/helmrelease_types.go # 重新生成代码 make generate # 更新 CRD make manifests
-
更新控制器逻辑
# 编辑控制器 vim internal/controller/helmrelease_controller.go # 添加业务逻辑 # 更新协调循环
-
添加测试
# 添加单元测试 vim internal/controller/helmrelease_controller_test.go # 运行测试 make test
func (r *HelmReleaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 1. 获取资源
release := &helmoperatorv1alpha1.HelmRelease{}
if err := r.Get(ctx, req.NamespacedName, release); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 2. 处理删除逻辑
if !release.DeletionTimestamp.IsZero() {
return r.reconcileDelete(ctx, release)
}
// 3. 添加 Finalizer
if !controllerutil.ContainsFinalizer(release, utils.HelmReleaseFinalizer) {
controllerutil.AddFinalizer(release, utils.HelmReleaseFinalizer)
return ctrl.Result{}, r.Update(ctx, release)
}
// 4. 执行主要逻辑
return r.reconcileNormal(ctx, release)
}// 更新状态条件
condition := utils.NewReleaseReadyCondition(metav1.ConditionTrue, "InstallCompleted", "Release is ready")
meta.SetStatusCondition(&release.Status.Conditions, condition)
// 更新状态
return r.Status().Update(ctx, release)helmClient, err := helm.NewClient("default")
if err != nil {
return fmt.Errorf("failed to create helm client: %w", err)
}installReq := &helm.InstallRequest{
Name: "my-release",
Namespace: "default",
Chart: "bitnami/nginx",
Version: "1.0.0",
Values: "replicaCount: 2",
CreateNamespace: true,
Wait: true,
Timeout: 10 * time.Minute,
}
releaseInfo, err := helmClient.InstallRelease(ctx, installReq)Chart 引用支持多种格式:
-
仓库引用:
repository_name/chart_namechartRef := fmt.Sprintf("%s/%s", repoName, chartName) // 例如: "bitnami/nginx", "private-repo/myapp"
-
直接 URL: 使用
repositoryURL字段spec: chart: name: "nginx" repositoryURL: "https://charts.bitnami.com/bitnami"
-
本地 Chart: 直接使用 chart 名称
spec: chart: name: "./local-chart"
| 仓库类型 | URL 格式 | 示例 | 用途 |
|---|---|---|---|
| 公共 HTTPS | https:// |
https://charts.bitnami.com/bitnami |
公共 Helm 仓库 |
| 私有 HTTPS | https:// |
https://private.charts.example.com |
企业私有仓库 |
| 内网 HTTP | http:// |
http://charts.internal:8080 |
内网私有仓库 |
对于私有仓库,支持多种认证方式:
-
基础认证(用户名/密码)
auth: basic: secretRef: name: "repo-credentials" namespace: "default"
-
TLS 配置
auth: tls: insecureSkipVerify: true # 跳过证书验证(HTTP 仓库) secretRef: name: "tls-config" namespace: "default"
-
认证 Secret 格式
apiVersion: v1 kind: Secret metadata: name: repo-credentials type: Opaque data: username: <base64-encoded-username> password: <base64-encoded-password>
# 运行所有测试
make test
# 运行特定包的测试
go test ./internal/controller/...
# 运行测试并查看覆盖率
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out# 启动测试环境
make test-integration
# 运行端到端测试
make test-e2e-
运行控制器
# 设置 KUBECONFIG export KUBECONFIG=~/.kube/config # 运行控制器 go run cmd/main.go
-
应用测试资源
# 应用 HelmRepository kubectl apply -f samples/helm_repository.yaml # 应用 HelmRelease kubectl apply -f samples/helm_release.yaml
-
查看日志
# 查看控制器日志 kubectl logs -f deployment/helm-operator -n ketches
-
Chart 引用错误
错误: non-absolute URLs should be in form of repo_name/path_to_chart, got: myapp 解决: 确保使用正确的 chart 引用格式 "repository_name/chart_name"
-
Kubernetes 客户端初始化失败
错误: kubernetes client not initialized in Helm configuration 解决: 检查 KUBECONFIG 设置和集群连接
-
权限不足
错误: forbidden: User cannot create resource 解决: 检查 RBAC 配置和服务账户权限
-
启用详细日志
# 设置日志级别 export LOG_LEVEL=debug go run cmd/main.go
-
查看资源状态
# 查看 HelmRelease 状态 kubectl describe helmrelease myapp-sample # 查看事件 kubectl get events --sort-by=.metadata.creationTimestamp
-
使用 Helm CLI 验证
# 列出发布 helm list -A # 查看发布详情 helm get all <release-name> -n <namespace>
-
Go 代码风格
- 遵循
gofmt格式化 - 使用
golint检查代码质量 - 添加适当的注释和文档
- 遵循
-
提交信息格式
<type>(<scope>): <subject> <body> <footer>
例如:
feat(controller): add support for OCI repositories - Add OCI repository type support - Update chart reference handling - Add integration tests Fixes #123
-
测试要求
- 新功能必须包含单元测试
- 测试覆盖率不低于 80%
- 集成测试验证端到端功能
-
版本标记
git tag -a v1.0.0 -m "Release v1.0.0" git push origin v1.0.0 -
构建发布
make release VERSION=v1.0.0
本项目采用 Apache 2.0 许可证。详见 LICENSE 文件。