美文网首页
operator-sdk 简单入门

operator-sdk 简单入门

作者: wwq2020 | 来源:发表于2020-07-12 18:14 被阅读0次

    创建目录

    operator-sdk init operator-sdk init --project-version="2" --domain=example.com --repo=github.com/wwq-2020/crd-demo
    

    创建模板

    operator-sdk create api --group=cache --version=v1alpha1 --kind=Memcached
    

    创建 api

    operator-sdk create api --group=cache --version=v1alpha1 --kind=Memcached
    

    修改 api/v1alpha1/memcached_types.go 中 内容,如下

    // MemcachedSpec defines the desired state of Memcached
    type MemcachedSpec struct {
        // +kubebuilder:validation:Minimum=0
        // Size is the size of the memcached deployment
        Size int32 `json:"size"`
    }
    
    // MemcachedStatus defines the observed state of Memcached
    type MemcachedStatus struct {
        // Nodes are the names of the memcached pods
        Nodes []string `json:"nodes"`
    }
    
    // Memcached is the Schema for the memcacheds API
    // +kubebuilder:subresource:status
    type Memcached struct {
        metav1.TypeMeta   `json:",inline"`
        metav1.ObjectMeta `json:"metadata,omitempty"`
    
        Spec   MemcachedSpec   `json:"spec,omitempty"`
        Status MemcachedStatus `json:"status,omitempty"`
    }
    

    生成这个资源的代码

    make generate
    

    生成 manifests

    make manifests
    

    修改 controllers/memcached_controller.go,内容如下

    /*
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    
        http://www.apache.org/licenses/LICENSE-2.0
    
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
    */
    
    package controllers
    
    import (
        "context"
        "reflect"
    
        "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"
    
        cachev1alpha1 "github.com/example-inc/memcached-operator/api/v1alpha1"
    )
    
    // MemcachedReconciler reconciles a Memcached object
    type MemcachedReconciler struct {
        client.Client
        Log    logr.Logger
        Scheme *runtime.Scheme
    }
    
    // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds,verbs=get;list;watch;create;update;patch;delete
    // +kubebuilder:rbac:groups=cache.example.com,resources=memcacheds/status,verbs=get;update;patch
    // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
    // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;
    
    func (r *MemcachedReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
        ctx := context.Background()
        log := r.Log.WithValues("memcached", req.NamespacedName)
    
        // Fetch the Memcached instance
        memcached := &cachev1alpha1.Memcached{}
        err := r.Get(ctx, req.NamespacedName, memcached)
        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
                log.Info("Memcached resource not found. Ignoring since object must be deleted")
                return ctrl.Result{}, nil
            }
            // Error reading the object - requeue the request.
            log.Error(err, "Failed to get Memcached")
            return ctrl.Result{}, err
        }
    
        // Check if the deployment already exists, if not create a new one
        found := &appsv1.Deployment{}
        err = r.Get(ctx, types.NamespacedName{Name: memcached.Name, Namespace: memcached.Namespace}, found)
        if err != nil && errors.IsNotFound(err) {
            // Define a new deployment
            dep := r.deploymentForMemcached(memcached)
            log.Info("Creating a new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
            err = r.Create(ctx, dep)
            if err != nil {
                log.Error(err, "Failed to create new Deployment", "Deployment.Namespace", dep.Namespace, "Deployment.Name", dep.Name)
                return ctrl.Result{}, err
            }
            // Deployment created successfully - return and requeue
            return ctrl.Result{Requeue: true}, nil
        } else if err != nil {
            log.Error(err, "Failed to get Deployment")
            return ctrl.Result{}, err
        }
    
        // Ensure the deployment size is the same as the spec
        size := memcached.Spec.Size
        if *found.Spec.Replicas != size {
            found.Spec.Replicas = &size
            err = r.Update(ctx, found)
            if err != nil {
                log.Error(err, "Failed to update Deployment", "Deployment.Namespace", found.Namespace, "Deployment.Name", found.Name)
                return ctrl.Result{}, err
            }
            // Spec updated - return and requeue
            return ctrl.Result{Requeue: true}, nil
        }
    
        // Update the Memcached status with the pod names
        // List the pods for this memcached's deployment
        podList := &corev1.PodList{}
        listOpts := []client.ListOption{
            client.InNamespace(memcached.Namespace),
            client.MatchingLabels(labelsForMemcached(memcached.Name)),
        }
        if err = r.List(ctx, podList, listOpts...); err != nil {
            log.Error(err, "Failed to list pods", "Memcached.Namespace", memcached.Namespace, "Memcached.Name", memcached.Name)
            return ctrl.Result{}, err
        }
        podNames := getPodNames(podList.Items)
    
        // Update status.Nodes if needed
        if !reflect.DeepEqual(podNames, memcached.Status.Nodes) {
            memcached.Status.Nodes = podNames
            err := r.Status().Update(ctx, memcached)
            if err != nil {
                log.Error(err, "Failed to update Memcached status")
                return ctrl.Result{}, err
            }
        }
    
        return ctrl.Result{}, nil
    }
    
    // deploymentForMemcached returns a memcached Deployment object
    func (r *MemcachedReconciler) deploymentForMemcached(m *cachev1alpha1.Memcached) *appsv1.Deployment {
        ls := labelsForMemcached(m.Name)
        replicas := m.Spec.Size
    
        dep := &appsv1.Deployment{
            ObjectMeta: metav1.ObjectMeta{
                Name:      m.Name,
                Namespace: m.Namespace,
            },
            Spec: appsv1.DeploymentSpec{
                Replicas: &replicas,
                Selector: &metav1.LabelSelector{
                    MatchLabels: ls,
                },
                Template: corev1.PodTemplateSpec{
                    ObjectMeta: metav1.ObjectMeta{
                        Labels: ls,
                    },
                    Spec: corev1.PodSpec{
                        Containers: []corev1.Container{{
                            Image:   "memcached:1.4.36-alpine",
                            Name:    "memcached",
                            Command: []string{"memcached", "-m=64", "-o", "modern", "-v"},
                            Ports: []corev1.ContainerPort{{
                                ContainerPort: 11211,
                                Name:          "memcached",
                            }},
                        }},
                    },
                },
            },
        }
        // Set Memcached instance as the owner and controller
        ctrl.SetControllerReference(m, dep, r.Scheme)
        return dep
    }
    
    // labelsForMemcached returns the labels for selecting the resources
    // belonging to the given memcached CR name.
    func labelsForMemcached(name string) map[string]string {
        return map[string]string{"app": "memcached", "memcached_cr": name}
    }
    
    // getPodNames returns the pod names of the array of pods passed in
    func getPodNames(pods []corev1.Pod) []string {
        var podNames []string
        for _, pod := range pods {
            podNames = append(podNames, pod.Name)
        }
        return podNames
    }
    
    func (r *MemcachedReconciler) SetupWithManager(mgr ctrl.Manager) error {
        return ctrl.NewControllerManagedBy(mgr).
            For(&cachev1alpha1.Memcached{}).
            Owns(&appsv1.Deployment{}).
            Complete(r)
    }
    

    生成 manifests

    make manifests
    

    注册到 apiserver

    make install
    

    在 k8s 外部运行

    make run ENABLE_WEBHOOKS=false
    

    在 k8s 内运行

    make docker-build IMG=memcached-operator:v0.0.1
    cd config/default/ && kustomize edit set namespace "default" && cd ../..
    make deploy IMG=memcached-operator:v0.0.1
    

    修改 config/samples/cache_v1alpha1_memcached.yaml 中内容

    spec:
      # Add fields here
      size: 3
    

    相关文章

      网友评论

          本文标题:operator-sdk 简单入门

          本文链接:https://www.haomeiwen.com/subject/uolncktx.html