title | weight | catalog | date | subtitle | header-img | tags | catagories | ||
---|---|---|---|---|---|---|---|---|---|
如何开发一个Operator |
2 |
true |
2023-07-23 03:50:57 -0700 |
|
|
开发一个k8s operator组件主要会用到以下几个仓库或工具:
-
kubebuilder/operator-sdk:主要用于创建CRD对象。
-
controller-manager:主要用于实现operator的controller逻辑。
本文以kubebuilder的工具和controller-manager example为例。
安装kubebuilder工具
curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)
chmod +x kubebuilder && mv kubebuilder /usr/local/bin/
创建operator项目目录
$ kubebuilder init --domain example.com --license apache2 --owner "Your Name" --repo github.com/example/my-operator
将 example.com
替换为你的域名,Your Name
替换为你的名字,github.com/example/my-operator
替换为你的 GitHub 仓库。
$ kubebuilder create api --group mygroup --version v1alpha1 --kind MyResource
这将在 api/v1alpha1
文件夹中创建一个名为 myresource_types.go
的文件,其中定义了 MyResource
资源的规范和状态。
编辑 api/v1alpha1/myresource_types.go
文件,添加自定义资源的规范和状态字段。
在 controllers/myresource_controller.go
文件中实现控制器逻辑。该部分是不同的CRD实现自定义逻辑的核心部分。
主要包含以下几个部分:
-
定义ReconcileMyResource结构体。
-
实现
func (r *ReconcileMyResource) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error)
函数接口。 -
定义finalizer逻辑。
-
定义reconcile逻辑。
package controllers
import (
"context"
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/controller/inject"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
myv1alpha1 "github.com/example/my-operator/api/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)
var log = logf.Log.WithName("controller_myresource")
// Add creates a new MyResource Controller and adds it to the Manager. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileMyResource{client: mgr.GetClient(), scheme: mgr.GetScheme()}
}
// blank assignment to verify that ReconcileMyResource implements reconcile.Reconciler
var _ reconcile.Reconciler = &ReconcileMyResource{}
// ReconcileMyResource reconciles a MyResource object
type ReconcileMyResource struct {
client client.Client
scheme *runtime.Scheme
}
// Reconcile reads that state of the cluster for a MyResource object and makes changes based on the state read
// and what is in the MyResource.Spec
func (r *ReconcileMyResource) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
reqLogger := log.WithValues("Namespace", request.Namespace, "Name", request.Name)
reqLogger.Info("Reconciling MyResource")
// Fetch the MyResource instance
myresource := &myv1alpha1.MyResource{}
err := r.client.Get(ctx, request.NamespacedName, myresource)
if err != nil {
if errors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
// Return and don't requeue
return reconcile.Result{}, nil
}
// Error reading the object - requeue the request.
return reconcile.Result{}, err
}
// Add finalizer logic here
// Add reconcile logic here
return reconcile.Result{}, nil
}
在 main.go
文件中注册控制器,主要包含以下几个步骤
-
ctrl.SetLogger(zap.New()):注册日志工具
-
ctrl.NewManager:创建一个manager,必要时可设置选主逻辑。
-
ctrl.NewControllerManagedBy(mgr):预计mgr创建manager controller并注册Reconciler。
-
mgr.Start(ctrl.SetupSignalHandler()): 运行mgr。
func main() {
// set logger
ctrl.SetLogger(zap.New())
// Set up a Manager
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: myv1alpha1.SchemeBuilder.Scheme,
MetricsBindAddress: *metricsHost,
Port: 9443,
LeaderElection: *enableLeaderElection,
LeaderElectionID: "my-operator-lock",
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
// in a real controller, we'd create a new scheme for this
err = api.AddToScheme(mgr.GetScheme())
if err != nil {
setupLog.Error(err, "unable to add scheme")
os.Exit(1)
}
// Create a new Reconciler
r := &ReconcileMyResource{
client: m.GetClient(),
scheme: m.GetScheme(),
}
// Create a new Controller and register the Reconciler
err = ctrl.NewControllerManagedBy(mgr).
For(&myv1alpha1.MyResource{}). // 自定义crd
Complete(r) // 注册Reconciler
if err != nil {
setupLog.Error(err, "unable to create controller")
os.Exit(1)
}
err = ctrl.NewWebhookManagedBy(mgr).
For(&api.ChaosPod{}).
Complete()
if err != nil {
setupLog.Error(err, "unable to create webhook")
os.Exit(1)
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}
参考: