Operator 与 CRD:扩展 Kubernetes 能力
深入理解 Operator 模式、自定义资源定义(CRD)、以及使用 KubeBuilder 构建生产级 Operator 的完整指南。
概述
Kubernetes 的核心优势之一是可扩展性。Operator 模式允许开发者自定义资源类型和控制器,将复杂的运维逻辑封装到 Kubernetes 原生 API 中。本文将深入探讨:
学习目标:
- 理解 CustomResourceDefinition(CRD)的定义和使用
- 掌握 Operator 模式与控制器工作原理
- 学会使用 KubeBuilder 构建 Operator
- 理解 RBAC、资源清理和错误处理
- 了解社区成熟 Operator 的使用
CRD:自定义资源
什么是 CRD?
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes 资源模型 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 内置资源(Built-in) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Pod, Deployment, Service, ConfigMap, Secret... │ │
│ │ - K8s 官方定义 │ │
│ │ - 控制器内置 │ │
│ │ - API Server 原生支持 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 自定义资源(Custom Resource) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ MySQL, RedisCluster, Prometheus, CertManager... │ │
│ │ - 用户自定义 │ │
│ │ - 通过 CRD 声明 │ │
│ │ - 需要 Operator 控制器管理 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
创建 CRD
# crd-example.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: databases.myapp.example.com
spec:
group: myapp.example.com # API 组
names:
kind: Database # 资源类型名
plural: databases # 复数形式(API 路径)
singular: database # 单数形式
shortNames:
- db # 简称
listKind: DatabaseList # 列表类型名
scope: Namespaced # 作用域:Namespaced/Cluster
versions:
- name: v1
served: true # 是否提供版本
storage: true # 是否为存储版本(只能有一个)
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
engine:
type: string
enum:
- mysql
- postgresql
version:
type: string
replicas:
type: integer
minimum: 1
maximum: 10
status:
type: object
properties:
phase:
type: string
conditions:
type: array
items:
type: object
preserveUnknownFields: false
使用自定义资源
# database.yaml - 创建自定义资源
apiVersion: myapp.example.com/v1
kind: Database
metadata:
name: production-db
spec:
engine: postgresql
version: "15"
replicas: 3
---
# 操作自定义资源
kubectl get databases # 查看所有 Database
kubectl get db # 使用简称
kubectl describe database production-db
kubectl edit database production-db
kubectl delete database production-db
多版本 CRD
# crd-multi-version.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: websites.myapp.example.com
spec:
group: myapp.example.com
names:
kind: Website
plural: websites
versions:
- name: v1alpha1
served: true
storage: false
schema: ...
- name: v1beta1
served: true
storage: false
schema: ...
- name: v1
served: true
storage: true # 唯一存储版本
schema: ...
# 版本转换(Webhook)
conversion:
strategy: Webhook
webhook:
conversionReviewVersions:
- v1
- v1beta1
clientConfig:
url: https://operator.myapp.svc/conversion
Operator 模式
什么是 Operator?
┌─────────────────────────────────────────────────────────────────┐
│ Operator 工作原理 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 用户创建自定义资源 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ apiVersion: myapp.example.com/v1 │ │
│ │ kind: Database │ │
│ │ metadata: │ │
│ │ name: production-db │ │
│ │ spec: │ │
│ │ engine: postgresql │ │
│ │ version: "15" │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ API Server │ │
│ │ - 存储 CR 到 ETCD │ │
│ │ - 通知 Watchers │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Operator Controller │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Reconcile Loop │ │ │
│ │ │ │ │ │
│ │ │ 1. 监听 CR 变化 (Watch) │ │ │
│ │ │ 2. 获取期望状态 (spec) │ │ │
│ │ │ 3. 获取当前状态 (实际资源) │ │ │
│ │ │ 4. 比较差异 (Reconcile) │ │ │
│ │ │ 5. 采取行动达到期望状态 │ │ │
│ │ │ 6. 更新 CR 状态 (status) │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 实际资源 │ │
│ │ Deployment, Service, ConfigMap, Secret... │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Reconcile 循环
// Operator 核心逻辑
func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// 1. 获取 CR 实例
db := &myappv1.Database{}
if err := r.Get(ctx, req.NamespacedName, db); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 2. 定义期望状态
desired := r.buildDeployment(db)
// 3. 检查并创建/更新 Deployment
found := &appsv1.Deployment{}
if err := r.Get(ctx, types.NamespacedName{
Name: db.Name,
Namespace: db.Namespace,
}, found); err != nil {
// 不存在则创建
if errors.IsNotFound(err) {
log.Info("Creating Deployment", "name", db.Name)
return ctrl.Result{}, r.Create(ctx, desired)
}
return ctrl.Result{}, err
}
// 4. 检查副本数是否匹配
if *found.Spec.Replicas != db.Spec.Replicas {
found.Spec.Replicas = &db.Spec.Replicas
return ctrl.Result{}, r.Update(ctx, found)
}
// 5. 更新 CR status
if !r.compareStatus(db, found) {
db.Status.Phase = "Running"
return ctrl.Result{}, r.Status().Update(ctx, db)
}
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
}
控制器模式图
┌─────────────────────────────────────────────────────────────────┐
│ 控制器架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ETCD │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ CRD │ │ CR │ │ CR │ │
│ │ 定义 │ │ 实例 1 │ │ 实例 2 │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │ │ │ │
│ │ ┌───────────┴───────────┐ │ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Informer/Lister │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ CRD │ │ CR │ │ 其他资源 │ │ │
│ │ │ Cache │ │ Cache │ │ Cache │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │ │ │ │
│ │ └────────────┴────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ WorkQueue │ │ │
│ │ │ [db-1] [db-2] [dep-1] [svc-1] │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Reconcile Worker Pool │ │
│ │ │ │
│ │ Worker 1: Reconcile(db-1) │ │
│ │ Worker 2: Reconcile(db-2) │ │
│ │ Worker 3: Reconcile(dep-1) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
KubeBuilder 入门
什么是 KubeBuilder?
┌─────────────────────────────────────────────────────────────────┐
│ KubeBuilder 定位 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ KubeBuilder = Kubebuilder + K8s Controller Runtime │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ KubeBuilder │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────────┐ ┌────────────┐ │ │
│ │ │ 自动生成 │ │ 最佳实践封装 │ │ 单元测试 │ │ │
│ │ │ 代码骨架 │ │ Controller │ │ 框架 │ │ │
│ │ │ │ │ Runtime │ │ │ │ │
│ │ └─────────────┘ └─────────────────┘ └────────────┘ │ │
│ │ │ │ │ │ │
│ │ └─────────────────┴─────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ 生产级 Operator │ │ │
│ │ └─────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
安装 KubeBuilder
# 安装 kubebuilder CLI
os=$(go env GOOS)
arch=$(go env GOARCH)
curl -L -o kubebuilder $(curl -s https://api.github.com/repos/kubernetes-sigs/kubebuilder/releases/latest | grep "kubebuilder-${os}-${arch}" | grep "browser_download" | cut -d '"' -f 4)
chmod +x kubebuilder
sudo mv kubebuilder /usr/local/kubebuilder
# 安装 controller-gen(自动生成代码)
go install sigs.k8s.io/controller-tools/cmd/controller-gen@latest
go install sigs.k8s.io/controller-runtime/cmd/setup-envtest@latest
# 验证安装
kubebuilder version
创建项目
# 创建项目目录
mkdir -p operator && cd operator
# 初始化项目(使用 Go modules)
go mod init github.com/myorg/operator
# 创建 API(CRD + Controller)
kubebuilder init --domain myapp.example.com --repo github.com/myorg/operator
# 创建 CRD 和 Controller
kubebuilder create api --group myapp --version v1 --kind Database
# 查看生成的文件结构
ls -la
# api/
# v1/
# database_types.go # CRD 类型定义
# groupversion_info.go
# zz_generated.deepcopy.go # 自动生成
# config/
# crd/ # CRD YAML
# rbac/ # RBAC 配置
# manager/ # Deployment
# controllers/
# database_controller.go # 控制器逻辑
# main.go # 入口
定义资源类型
// api/v1/database_types.go
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// DatabaseSpec 定义期望状态
type DatabaseSpec struct {
// +kubebuilder:validation:Enum=mysql;postgresql
Engine string `json:"engine"`
Version string `json:"version"`
Replicas int32 `json:"replicas"`
Storage string `json:"storage"`
}
// DatabaseStatus 定义当前状态
type DatabaseStatus struct {
Phase string `json:"phase"`
Replicas int32 `json:"replicas"`
ReadyReplicas int32 `json:"readyReplicas"`
}
// Database 是 Database 自定义资源
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=db
type Database struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec DatabaseSpec `json:"spec,omitempty"`
Status DatabaseStatus `json:"status,omitempty"`
}
// DatabaseList 包含 Database 列表
// +kubebuilder:object:root=true
type DatabaseList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Database `json:"items"`
}
func init() {
SchemeBuilder.Register(&Database{}, &DatabaseList{})
}
生成代码
# 生成 CRD YAML
make manifests
# 生成 DeepCopy 代码
make generate
# 运行测试
make test
# 构建镜像
make docker-build IMG=myregistry/operator:v1.0.0
# 推送到仓库
make docker-push IMG=myregistry/operator:v1.0.0
# 安装到集群
make deploy IMG=myregistry/operator:v1.0.0
控制器开发实战
实现 Reconcile
// controllers/database_controller.go
package controllers
import (
"context"
"fmt"
"reflect"
"time"
"github.com/go-logr/logr"
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"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
myappv1 "github.com/myorg/operator/api/v1"
)
// DatabaseReconciler 重新实现 Reconcile
type DatabaseReconciler struct {
client.Client
Scheme *runtime.Scheme
}
// +kubebuilder:rbac:groups=myapp.example.com,resources=databases,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=myapp.example.com,resources=databases/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
// 1. 获取 CR 实例
db := &myappv1.Database{}
if err := r.Get(ctx, req.NamespacedName, db); err != nil {
if client.IgnoreNotFound(err) == nil {
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
logger.Info("Reconciling Database", "name", db.Name)
// 2. 构建 Deployment
dep := r.buildDeployment(db)
if err != nil {
return ctrl.Result{}, err
}
// 3. 检查 Deployment 是否存在
found := &appsv1.Deployment{}
err := r.Get(ctx, types.NamespacedName{
Name: dep.Name,
Namespace: dep.Namespace,
}, found)
if err != nil && errors.IsNotFound(err) {
logger.Info("Creating Deployment", "name", dep.Name)
if err := r.Create(ctx, dep); err != nil {
return ctrl.Result{}, err
}
} else if err != nil {
return ctrl.Result{}, err
}
// 4. 检查副本数
if *found.Spec.Replicas != db.Spec.Replicas {
logger.Info("Updating Deployment replicas", "current", *found.Spec.Replicas, "desired", db.Spec.Replicas)
found.Spec.Replicas = &db.Spec.Replicas
if err := r.Update(ctx, found); err != nil {
return ctrl.Result{}, err
}
}
// 5. 更新 Status
if !reflect.DeepEqual(db.Status.Replicas, db.Spec.Replicas) {
db.Status.Phase = "Running"
db.Status.Replicas = db.Spec.Replicas
db.Status.ReadyReplicas = found.Status.ReadyReplicas
if err := r.Status().Update(ctx, db); err != nil {
return ctrl.Result{}, err
}
}
// 6. 定期重新调谐(用于监控外部变化)
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
}
func (r *DatabaseReconciler) buildDeployment(db *myappv1.Database) *appsv1.Deployment {
replicas := db.Spec.Replicas
labels := map[string]string{
"app": db.Name,
}
return &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: db.Name,
Namespace: db.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(db, myappv1.GroupVersion.WithKind("Database")),
},
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "database",
Image: fmt.Sprintf("%s:%s", db.Spec.Engine, db.Spec.Version),
Ports: []corev1.ContainerPort{
{ContainerPort: 5432, Name: "db"},
},
},
},
},
},
},
}
}
添加 OwnerReference
// 设置 OwnerReference 实现级联删除
// 确保 CR 删除时,关联的 Deployment 也被清理
func (r *DatabaseReconciler) buildDeployment(db *myappv1.Database) *appsv1.Deployment {
return &appsv1.Deployment{
// ...
ObjectMeta: metav1.ObjectMeta{
Name: db.Name,
Namespace: db.Namespace,
// 关键:设置 OwnerReference
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(db, myappv1.GroupVersion.WithKind("Database")),
},
},
}
}
Finalizer 处理
// 处理资源清理逻辑
func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
db := &myappv1.Database{}
if err := r.Get(ctx, req.NamespacedName, db); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// 检查是否正在删除
if !db.DeletionTimestamp.IsZero() {
if controllerutil.ContainsFinalizer(db, "database.finalizer.myapp.example.com") {
// 执行清理逻辑
if err := r.cleanupResources(ctx, db); err != nil {
return ctrl.Result{}, err
}
// 移除 Finalizer
controllerutil.RemoveFinalizer(db, "database.finalizer.myapp.example.com")
if err := r.Update(ctx, db); err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
// 添加 Finalizer(防止误删)
if !controllerutil.ContainsFinalizer(db, "database.finalizer.myapp.example.com") {
controllerutil.AddFinalizer(db, "database.finalizer.myapp.example.com")
if err := r.Update(ctx, db); err != nil {
return ctrl.Result{}, err
}
}
// 正常业务逻辑...
return r.reconcileDatabase(ctx, db)
}
RBAC 注解
// 在代码中声明需要的权限,KubeBuilder 会自动生成 RBAC 配置
// +kubebuilder:rbac:groups=myapp.example.com,resources=databases,verbs=get;list;watch;create;update;patch;delete
// 创建/更新 CR 的权限
// +kubebuilder:rbac:groups=myapp.example.com,resources=databases/status,verbs=get;update;patch
// 更新 status 子资源的权限
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
// 管理 Deployment 的权限
// +kubebuilder:rbac:groups="",resources=pods;services;configmaps;secrets,verbs=get;list;watch;create;update;patch;delete
// 管理其他资源的权限
Webhook 验证
验证 Webhook
// api/v1/database_webhook.go
package v1
import (
"context"
"fmt"
"net/http"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)
var databaselog = logf.Log.WithName("database-resource")
//+kubebuilder:webhook:path=/validate-myapp-example-com-v1-database,mutating=false,failurePolicy=fail,groups=myapp.example.com,resources=databases,verbs=create;update,versions=v1,name=vdatabase.kb.io
var _ webhook.Validator = &Database{}
func (r *Database) ValidateCreate() error {
databaselog.Info("validate create", "name", r.Name)
// 验证创建请求
if r.Spec.Replicas < 1 {
return fmt.Errorf("replicas must be at least 1")
}
if r.Spec.Engine != "mysql" && r.Spec.Engine != "postgresql" {
return fmt.Errorf("engine must be mysql or postgresql")
}
return nil
}
func (r *Database) ValidateUpdate(old runtime.Object) error {
databaselog.Info("validate update", "name", r.Name)
// 验证更新请求
oldDb := old.(*Database)
// 不允许降低版本
if r.Spec.Version < oldDb.Spec.Version {
return fmt.Errorf("cannot downgrade version")
}
return nil
}
func (r *Database) ValidateDelete() error {
databaselog.Info("validate delete", "name", r.Name)
return nil
}
func (r *Database) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
Complete()
}
生成 Webhook 配置
# KubeBuilder 自动生成 webhook 配置
make manifests
# 生成的文件:
# config/
# webhook/
# manifests.yaml # ValidatingWebhookConfiguration
高级主题
Status 子资源
// 启用 status 子资源后,可以独立更新 status 而不更新 spec
// 需要在 CR 定义中添加:+kubebuilder:subresource:status
// api/v1/database_types.go
// +kubebuilder:subresource:status
// 更新 status(不触发 spec 更新)
func (r *DatabaseReconciler) updateStatus(db *myappv1.Database) error {
db.Status.Phase = "Running"
return r.Status().Update(context.TODO(), db)
}
级联删除
# OwnerReference 实现自动垃圾回收
# CR 删除时,所有拥有该 CR OwnerReference 的资源会被自动删除
# Kubernetes 默认行为:
# - 前景删除(Foreground):等待所有拥有 OwnerReference 的资源删除后再删除 CR
# - 后台删除(Background):立即删除 CR,由 GC 在后台清理资源
# 修改删除策略
metadata:
finalizers:
- example.com/protect
# CR 必须移除 finalizer 才能被删除
Leader Election
// 在 main.go 中配置 Leader Election
func main() {
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: fmt.Sprintf(":%d", 8080),
Port: 9443,
LeaderElection: true,
LeaderElectionID: "database-controller-lock",
LeaderElectionNamespace: "system",
})
// ...
}
指标暴露
// 使用 controller-runtime 提供的指标
import (
"sigs.k8s.io/controller-runtime/pkg/metrics"
"github.com/prometheus/client_golang/prometheus"
)
var (
reconciliationTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "database_reconciliation_total",
Help: "Total number of reconciliation attempts",
},
[]string{"result"},
)
reconciliationDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "database_reconciliation_duration_seconds",
Help: "Duration of reconciliation attempts",
Buckets: prometheus.DefBuckets,
},
[]string{"result"},
)
)
func init() {
metrics.Registry.MustRegister(reconciliationTotal)
metrics.Registry.MustRegister(reconciliationDuration)
}
常见 Operator 推荐
┌─────────────────────────────────────────────────────────────────┐
│ 成熟 Operator 推荐 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 数据存储 │
│ ├─ prometheus-operator # Prometheus 监控 │
│ ├─ grafana-operator # Grafana 可视化 │
│ ├─ postgres-operator # PostgreSQL 高可用 │
│ ├─ mysql-operator # MySQL Operator │
│ └─ redis-operator # Redis Cluster │
│ │
│ 服务网格 │
│ ├─ istio-operator # Istio 服务网格 │
│ └─ linkerd-operator # Linkerd 服务网格 │
│ │
│ 证书管理 │
│ ├─ cert-manager # Let's Encrypt 证书 │
│ └─ external-secrets # 外部密钥集成 │
│ │
│ 应用管理 │
│ ├─ argocd-operator # ArgoCD GitOps │
│ ├─ flux-operator # Flux GitOps │
│ └─ sealed-secrets # 加密 Secrets │
│ │
└─────────────────────────────────────────────────────────────────┘
安装 Prometheus Operator
# 使用 Helm 安装
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace
# 访问 Prometheus
kubectl port-forward -n monitoring svc/prometheus-grafana 3000:80
常见问题与避坑指南
Q1:Controller 不生效?
# 排查步骤
# 1. 检查 Controller 是否运行
kubectl get pods -n operator-system
# 2. 查看 Controller 日志
kubectl logs -n operator-system deployment/operator-controller-manager -f
# 3. 检查 RBAC 权限
kubectl auth can-i get databases --as=system:serviceaccount:operator-system:operator-controller-manager
# 4. 检查 CRD 是否存在
kubectl get crd databases.myapp.example.com
# 5. 检查 webhook 是否注册
kubectl get validatingwebhookconfiguration vdatabase.kb.io
Q2:如何调试 Reconcile?
// 添加详细日志
func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
logger.Info("Starting reconcile", "name", req.NamespacedName)
// 获取资源
db := &myappv1.Database{}
if err := r.Get(ctx, req.NamespacedName, db); err != nil {
logger.Error(err, "Failed to get Database")
return ctrl.Result{}, err
}
logger.Info("Got Database", "spec", db.Spec)
// ...
}
Q3:如何处理外部依赖?
// 场景:Database CR 依赖外部云数据库资源
// 方案1:检查依赖状态
func (r *DatabaseReconciler) reconcileCloudDB(ctx context.Context, db *myappv1.Database) error {
// 创建云端数据库
cloudDB, err := r.createCloudDB(db)
if err != nil {
db.Status.Phase = "Provisioning"
db.Status.Message = "Creating cloud database..."
return err
}
// 更新状态
db.Status.Phase = "Running"
db.Status.ConnectionString = cloudDB.ConnectionString
return nil
}
// 方案2:幂等创建
// 使用外部资源的 Name/ID 作为唯一标识,重复调用不会重复创建
Q4:如何处理版本迁移?
// 场景:Operator 升级后需要迁移已有资源
func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
db := &myappv1.Database{}
r.Get(ctx, req.NamespacedName, db)
// 检查是否需要迁移
if db.Status.Version != db.Spec.Version {
if err := r.migrateData(db); err != nil {
db.Status.Phase = "Migrating"
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
}
}
// 继续正常逻辑...
}
总结
┌─────────────────────────────────────────────────────────────────┐
│ 核心要点回顾 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ CRD(自定义资源) │
│ ├── 定义新资源类型 │
│ ├── 多版本支持 │
│ └── Webhook 验证 │
│ │
│ Operator 模式 │
│ ├── Watch CR 变化 │
│ ├── Reconcile 达到期望状态 │
│ ├── Status 反馈当前状态 │
│ └── Finalizer 处理清理 │
│ │
│ KubeBuilder │
│ ├── 自动生成代码骨架 │
│ ├── 简化 CRD 开发 │
│ └── 集成测试框架 │
│ │
│ 最佳实践 │
│ ├── RBAC 权限最小化 │
│ ├── OwnerReference 级联删除 │
│ ├── Finalizer 资源清理 │
│ └── Leader Election 高可用 │
│ │
└─────────────────────────────────────────────────────────────────┘
思考题
- Operator 和 Helm Chart 有什么区别?什么场景下选择哪个?
- 如何设计一个安全的 Operator,避免误操作导致数据丢失?
- 如何测试 Operator 的并发行为和错误恢复能力?
引用与参考
下篇预告
下一篇文章我们将探讨 服务网格 Istio,包括:
- Sidecar 代理与数据平面
- 流量管理(路由、负载均衡)
- mTLS 与服务安全
- 可观测性集成
敬请期待!