共计 5835 个字符,预计需要花费 15 分钟才能阅读完成。
1. 项目介绍
1.1 背景和目标
本教程将指导您使用 Go 语言和 kubebuilder 框架开发一个简单的 Kubernetes Operator。我们将实现一个应用部署控制器,通过自定义资源(CRD)来管理应用的部署。
主要目标:
- 学习使用 kubebuilder 开发 operator 的基本流程
- 实现一个简单但完整的 CRD 和 controller
- 掌握 operator 开发的最佳实践
1.2 功能概述
我们将实现一个 SimpleApp CRD,用于部署简单的应用。它具有以下特点:
- 支持指定容器镜像和副本数
- 自动创建对应的 Deployment 资源
- 支持查看应用部署状态
2. 环境准备
2.1 前置要求
- Go 1.19+
- Docker
- Kubernetes 集群(1.20+)
- kubectl 命令行工具
- kubebuilder 3.0+
2.2 安装 kubebuilder
# 下载 kubebuilder 并添加到 PATH
curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)
chmod +x kubebuilder && mv kubebuilder /usr/local/bin/
3. 项目初始化
3.1 创建项目
# 创建项目目录
mkdir simple-app-operator
cd simple-app-operator
# 初始化项目
kubebuilder init --domain my.domain --repo my.domain/simple-app-operator
# 创建 API
kubebuilder create api --group apps --version v1alpha1 --kind SimpleApp
3.2 项目结构
.
├── Dockerfile
├── Makefile
├── PROJECT
├── api/
│ └── v1alpha1/
│ ├── simpleapp_types.go
│ ├── groupversion_info.go
│ └── zz_generated.deepcopy.go
├── config/
│ ├── default
│ ├── manager
│ ├── rbac
│ └── samples
├── controllers/
│ └── simpleapp_controller.go
└── main.go
4. CRD 开发
4.1 定义 API
编辑 api/v1alpha1/simpleapp_types.go
:
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// SimpleAppSpec 定义了 SimpleApp 的期望状态
type SimpleAppSpec struct {
// 容器镜像
Image string `json:"image"`
// 副本数
Replicas *int32 `json:"replicas"`
}
// SimpleAppStatus 定义了 SimpleApp 的实际状态
type SimpleAppStatus struct {
// 可用副本数
AvailableReplicas int32 `json:"availableReplicas"`
// 应用状态
Status string `json:"status"`
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Replicas",type="integer",JSONPath=".spec.replicas"
//+kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.status"
// SimpleApp 是 apps.my.domain/v1alpha1 的顶层 API 对象
type SimpleApp struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec SimpleAppSpec `json:"spec,omitempty"`
Status SimpleAppStatus `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// SimpleAppList 包含 SimpleApp 列表
type SimpleAppList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []SimpleApp `json:"items"`
}
func init() {
SchemeBuilder.Register(&SimpleApp{}, &SimpleAppList{})
}
4.2 生成代码
make generate
make manifests
5. Controller 实现
5.1 编写控制器逻辑
编辑 controllers/simpleapp_controller.go
:
package controllers
import (
"context"
"fmt"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
appsv1alpha1 "my.domain/simple-app-operator/api/v1alpha1"
)
// SimpleAppReconciler reconciles a SimpleApp object
type SimpleAppReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=apps.my.domain,resources=simpleapps,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps.my.domain,resources=simpleapps/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=apps.my.domain,resources=simpleapps/finalizers,verbs=update
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
func (r *SimpleAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
// 获取 SimpleApp 实例
simpleApp := &appsv1alpha1.SimpleApp{}
err := r.Get(ctx, req.NamespacedName, simpleApp)
if err != nil {
if errors.IsNotFound(err) {
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
// 创建或更新 Deployment
deployment := &appsv1.Deployment{}
err = r.Get(ctx, req.NamespacedName, deployment)
if err != nil && errors.IsNotFound(err) {
// 创建新的 Deployment
deployment = r.deploymentForSimpleApp(simpleApp)
err = r.Create(ctx, deployment)
if err != nil {
log.Error(err, "Failed to create Deployment")
return ctrl.Result{}, err
}
} else if err != nil {
log.Error(err, "Failed to get Deployment")
return ctrl.Result{}, err
} else {
// 更新现有 Deployment
deployment.Spec.Replicas = simpleApp.Spec.Replicas
deployment.Spec.Template.Spec.Containers[0].Image = simpleApp.Spec.Image
err = r.Update(ctx, deployment)
if err != nil {
log.Error(err, "Failed to update Deployment")
return ctrl.Result{}, err
}
}
// 更新状态
simpleApp.Status.AvailableReplicas = deployment.Status.AvailableReplicas
if deployment.Status.AvailableReplicas == *simpleApp.Spec.Replicas {
simpleApp.Status.Status = "Ready"
} else {
simpleApp.Status.Status = "NotReady"
}
err = r.Status().Update(ctx, simpleApp)
if err != nil {
log.Error(err, "Failed to update SimpleApp status")
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
// deploymentForSimpleApp 返回 SimpleApp 对应的 Deployment 对象
func (r *SimpleAppReconciler) deploymentForSimpleApp(app *appsv1alpha1.SimpleApp) *appsv1.Deployment {
labels := map[string]string{
"app": app.Name,
}
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: app.Name,
Namespace: app.Namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: app.Spec.Replicas,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Name: app.Name,
Image: app.Spec.Image,
}},
},
},
},
}
ctrl.SetControllerReference(app, deployment, r.Scheme)
return deployment
}
func (r *SimpleAppReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&appsv1alpha1.SimpleApp{}).
Owns(&appsv1.Deployment{}).
Complete(r)
}
6. 测试和部署
6.1 本地测试
# 安装 CRD
make install
# 运行 controller
make run
6.2 创建 SimpleApp 示例
# config/samples/apps_v1alpha1_simpleapp.yaml
apiVersion: apps.my.domain/v1alpha1
kind: SimpleApp
metadata:
name: simpleapp-sample
spec:
image: nginx:1.19
replicas: 3
# 创建实例
kubectl apply -f config/samples/apps_v1alpha1_simpleapp.yaml
# 查看状态
kubectl get simpleapp
6.3 构建和部署到集群
# 构建镜像
make docker-build docker-push IMG=<your-registry>/simple-app-operator:v1
# 部署到集群
make deploy IMG=<your-registry>/simple-app-operator:v1
7. 清理
# 删除 operator
make undeploy
# 删除 CRD
make uninstall
8. 最佳实践建议
-
错误处理
- 合理处理各种错误情况
- 使用 controller-runtime 的日志记录
-
状态管理
- 及时更新 CR 状态
- 提供有意义的状态信息
-
资源管理
- 使用 owner references 管理资源生命周期
- 正确设置 RBAC 权限
-
测试
- 编写单元测试和集成测试
- 在本地环境充分测试后再部署到生产环境
9. 进阶优化方向
-
添加更多配置选项
- 支持配置资源限制
- 支持配置环境变量
- 支持配置存储卷
-
增强状态监控
- 添加更详细的状态信息
- 支持事件记录
-
实现高级特性
- 滚动更新策略
- 健康检查
- 自动扩缩容
-
提升可靠性
- 添加重试机制
- 实现优雅终止
- 添加监控指标
正文完