• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 基于operator-sdk對k8s做二次開發

    標簽: 學習記錄  kubernetes

    一、概念

    在了解operator之前,我也是一臉蒙蔽。這玩意是啥,沒見書上提到過。后來知道了 就是一個腳手架,在我們開發自定義資源的時候,operator能夠幫我們把我們需要的一些文件都準備好,包括CRD的yaml文件、ControllerManager的go文件等等。再詳細的原理,我就不貼圖片了,大家自己去找吧,反正網上很多相關資料。

    二、安裝operator

    我的本地環境是windows,一開始是嘗試的clone代碼到本地編譯的,后來試了幾次都有問題,官方也說他們沒有嘗試過windows環境的編譯,所以也不知道出了什么問題。所以跑了一個新的虛擬機。

    https://blog.csdn.net/leehomjan/article/details/90735103

    參考這篇文章吧環境搭好了。文章中的operator-sdk版本是RELEASE_VERSION=v0.8.0。我建議用RELEASE_VERSION=v0.15.0,不要用最新的v1.0也不要用什么v0.19.0之類的。因為這東西一直在更新,差一個版本差別好大。然后開發的流程是參考的這個。

    https://www.bilibili.com/video/BV1zE411j7ky

    這個視頻中就是v0.15.0,如果不是這個版本到時候會昏頭轉向。

    三、創建項目和創建CRD

    創建項目:

    operator-sdk new imoocpod-operator --skip-validation=true --repo=github.com/imooc-com/imoocpod-operator

    新增api

    operator-sdk add api --api-version=k8s.imooc.com/v1alpha1 --kind=ImoocPod

    新增controller

    operator-sdk add controller --api-version=k8s.imooc.com/v1alpha1 --kind=ImoocPod

    完成后代碼如下

    .
    |-- build
    |   |-- bin
    |   |   |-- entrypoint
    |   |   `-- user_setup
    |   |-- Dockerfile
    |   `-- _output
    |       `-- bin
    |           `-- imoocpod-operator
    |-- cmd
    |   `-- manager
    |       `-- main.go
    |-- deploy
    |   |-- crds
    |   |   |-- k8s.imooc.com_imoocpods_crd.yaml
    |   |   `-- k8s.imooc.com_v1alpha1_imoocpod_cr.yaml
    |   |-- operator.yaml
    |   |-- role_binding.yaml
    |   |-- role.yaml
    |   `-- service_account.yaml
    |-- go.mod
    |-- go.sum
    |-- pkg
    |   |-- apis
    |   |   |-- addtoscheme_k8s_v1alpha1.go
    |   |   |-- apis.go
    |   |   `-- k8s
    |   |       |-- group.go
    |   |       `-- v1alpha1
    |   |           |-- doc.go
    |   |           |-- imoocpod_types.go
    |   |           |-- register.go
    |   |           `-- zz_generated.deepcopy.go
    |   `-- controller
    |       |-- add_imoocpod.go
    |       |-- controller.go
    |       `-- imoocpod
    |           `-- imoocpod_controller.go
    |-- tools.go
    `-- version
        `-- version.go
    
    15 directories, 25 files
    

    部署operator

    kubectl apply -f deploy/service_account.yaml

    kubectl apply -f deploy/role.yaml

    kubectl apply -f deploy/role_binding.yaml

    kubectl apply -f deploy/operator.yaml

    (這里需要修改一個operator.yaml里面的鏡像我填的是we8015/imooc-operator,大家可以自己找還有很多。)

     

    部署CRD 部署CR

    kubectl apply -f deploy/crds/k8s.imooc.com_imoocpods_crd.yaml

    (需要先部署crd再部署operator 不然pod起不來)

    kubectl apply -f deploy/crds/k8s.imooc.com_v1alpha1_imoocpod_cr.yaml

    更新imoocpod_type.go文件

     

    然后更新k8s.imooc.com_v1alpha1_imoocpod_cr.yaml

    每次修改都需要更新一下相關的資源 如下

    operator-sdk generate k8s

    operator-sdk generate crds

    然后就可以用新的yaml文件部署到k8s中了,這個時候會發現,crd是部署成功了,但是沒有生效,那是因為咱們還沒有編寫controller。

    四、編寫controller

    只需要編寫imoocpod_controller.go文件

    package imoocpod
    
    import (
    	"context"
    	"k8s.io/apimachinery/pkg/labels"
    	"reflect"
    
    	k8sv1alpha1 "github.com/imooc-com/imoocpod-operator/pkg/apis/k8s/v1alpha1"
    	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"
    	"sigs.k8s.io/controller-runtime/pkg/client"
    	"sigs.k8s.io/controller-runtime/pkg/controller"
    	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    	"sigs.k8s.io/controller-runtime/pkg/handler"
    	logf "sigs.k8s.io/controller-runtime/pkg/log"
    	"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_imoocpod")
    
    /**
    * USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller
    * business logic.  Delete these comments after modifying this file.*
     */
    
    // Add creates a new ImoocPod 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 &ReconcileImoocPod{client: mgr.GetClient(), scheme: mgr.GetScheme()}
    }
    
    // add adds a new Controller to mgr with r as the reconcile.Reconciler
    func add(mgr manager.Manager, r reconcile.Reconciler) error {
    	// Create a new controller
    	c, err := controller.New("imoocpod-controller", mgr, controller.Options{Reconciler: r})
    	if err != nil {
    		return err
    	}
    
    	// Watch for changes to primary resource ImoocPod
    	err = c.Watch(&source.Kind{Type: &k8sv1alpha1.ImoocPod{}}, &handler.EnqueueRequestForObject{})
    	if err != nil {
    		return err
    	}
    
    	// TODO(user): Modify this to be the types you create that are owned by the primary resource
    	// Watch for changes to secondary resource Pods and requeue the owner ImoocPod
    	err = c.Watch(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{
    		IsController: true,
    		OwnerType:    &k8sv1alpha1.ImoocPod{},
    	})
    	if err != nil {
    		return err
    	}
    
    	return nil
    }
    
    // blank assignment to verify that ReconcileImoocPod implements reconcile.Reconciler
    var _ reconcile.Reconciler = &ReconcileImoocPod{}
    
    // ReconcileImoocPod reconciles a ImoocPod object
    type ReconcileImoocPod struct {
    	// This client, initialized using mgr.Client() above, is a split client
    	// that reads objects from the cache and writes to the apiserver
    	client client.Client
    	scheme *runtime.Scheme
    }
    
    // Reconcile reads that state of the cluster for a ImoocPod object and makes changes based on the state read
    // and what is in the ImoocPod.Spec
    // TODO(user): Modify this Reconcile function to implement your Controller logic.  This example creates
    // a Pod as an example
    // Note:
    // The Controller will requeue the Request to be processed again if the returned error is non-nil or
    // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
    func (r *ReconcileImoocPod) Reconcile(request reconcile.Request) (reconcile.Result, error) {
    	reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
    	reqLogger.Info("Reconciling ImoocPod")
    
    	// Fetch the ImoocPod instance
    	instance := &k8sv1alpha1.ImoocPod{}
    	err := r.client.Get(context.TODO(), request.NamespacedName, instance)
    	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
    	}
    
    	lbls := labels.Set{
    		"app": instance.Name,
    	}
    	existingPods := &corev1.PodList{}
    	//1:獲取name對應的所以的pod列表
    	err = r.client.List(context.TODO(), existingPods, &client.ListOptions{
    		Namespace:     request.Namespace,
    		LabelSelector: labels.SelectorFromSet(lbls),
    	})
    	if err != nil {
    		reqLogger.Error(err, "取已經存在的pod失敗")
    		return reconcile.Result{}, err
    	}
    
    	//2:取到pod列表中的pod name
    	var existingPodNames []string
    	for _, pod := range existingPods.Items {
    		if pod.GetObjectMeta().GetDeletionTimestamp() != nil {
    			continue
    		}
    
    		if pod.Status.Phase == corev1.PodPending || pod.Status.Phase == corev1.PodRunning {
    			existingPodNames = append(existingPodNames, pod.GetObjectMeta().GetName())
    		}
    
    	}
    
    	//3:update pod.status !=運行中的status
    	//比較 DeepEqual
    	status := k8sv1alpha1.ImoocPodStatus{ //期望的status
    		PodNames: existingPodNames,
    		Replicas: len(existingPodNames),
    	}
    	if !reflect.DeepEqual(instance.Status, status) {
    		instance.Status = status //把期望狀態給運行態
    		err = r.client.Status().Update(context.TODO(), instance)
    		if err != nil {
    			reqLogger.Error(err, "更新pod的狀態失敗")
    			return reconcile.Result{}, err
    		}
    	}
    
    	//4:len(pod)>運行中的len(pod.replace),期望值小,需要scale down
    	if len(existingPodNames) > instance.Spec.Replicas {
    		//delete
    		reqLogger.Info("正在刪除Pod,當前的podnames和期望的Replicas:", existingPodNames, instance.Spec.Replicas)
    		pod := existingPods.Items[0]
    		err := r.client.Delete(context.TODO(), &pod)
    		if err != nil {
    			reqLogger.Error(err, "刪除pod失敗")
    			return reconcile.Result{}, err
    		}
    	}
    
    	//5:len(pod)<運行中的len(pod.replace),期望值大,需要scale up create
    	if len(existingPodNames) < instance.Spec.Replicas {
    		//create
    		reqLogger.Info("正在創建Pod,當前的podnames和期望的Replicas:", existingPodNames, instance.Spec.Replicas)
    		// Define a new Pod object
    		pod := newPodForCR(instance)
    
    		// Set ImoocPod instance as the owner and controller
    		if err := controllerutil.SetControllerReference(instance, pod, r.scheme); err != nil {
    			return reconcile.Result{}, err
    		}
    
    		err = r.client.Create(context.TODO(), pod)
    		if err != nil {
    			reqLogger.Error(err, "創建pod失敗")
    			return reconcile.Result{}, err
    		}
    	}
    
    	 Define a new Pod object
    	//pod := newPodForCR(instance)
    	//
    	 Set ImoocPod instance as the owner and controller
    	//if err := controllerutil.SetControllerReference(instance, pod, r.scheme); err != nil {
    	//	return reconcile.Result{}, err
    	//}
    	//
    	 Check if this Pod already exists
    	//found := &corev1.Pod{}
    	//err = r.client.Get(context.TODO(), types.NamespacedName{Name: pod.Name, Namespace: pod.Namespace}, found)
    	//if err != nil && errors.IsNotFound(err) {
    	//	reqLogger.Info("Creating a new Pod", "Pod.Namespace", pod.Namespace, "Pod.Name", pod.Name)
    	//	err = r.client.Create(context.TODO(), pod)
    	//	if err != nil {
    	//		return reconcile.Result{}, err
    	//	}
    	//
    	//	// Pod created successfully - don't requeue
    	//	return reconcile.Result{}, nil
    	//} else if err != nil {
    	//	return reconcile.Result{}, err
    	//}
    	//
    	 Pod already exists - don't requeue
    	//reqLogger.Info("Skip reconcile: Pod already exists", "Pod.Namespace", found.Namespace, "Pod.Name", found.Name)
    	//return reconcile.Result{}, nil
    	return reconcile.Result{Requeue: true}, nil
    }
    
    // newPodForCR returns a busybox pod with the same name/namespace as the cr
    func newPodForCR(cr *k8sv1alpha1.ImoocPod) *corev1.Pod {
    	labels := map[string]string{
    		"app": cr.Name,
    	}
    	return &corev1.Pod{
    		ObjectMeta: metav1.ObjectMeta{
    			GenerateName:      cr.Name + "-pod",
    			Namespace: cr.Namespace,
    			Labels:    labels,
    		},
    		Spec: corev1.PodSpec{
    			Containers: []corev1.Container{
    				{
    					Name:    "busybox",
    					Image:   "busybox",
    					Command: []string{"sleep", "3600"},
    				},
    			},
    		},
    	}
    }
    

    同意的需要更新一下相關的配置

    operator-sdk generate k8s

    operator-sdk generate crds

    然后是推送鏡像,operator build we8105/imooc-operator (we8105這里是鏡像的名稱)

    推送到遠端  docker push we8105/imooc-operator

    然后更新一下operator中的鏡像就行。

    最后就是測試了,可以查看日志和get pod查看pod數量發現生效了。

     

    完。

    版權聲明:本文為qq_20365437原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_20365437/article/details/108329489

    智能推薦

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    猜你喜歡

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    styled-components —— React 中的 CSS 最佳實踐

    https://zhuanlan.zhihu.com/p/29344146 Styled-components 是目前 React 樣式方案中最受關注的一種,它既具備了 css-in-js 的模塊化與參數化優點,又完全使用CSS的書寫習慣,不會引起額外的學習成本。本文是 styled-components 作者之一 Max Stoiber 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...

    基于TCP/IP的網絡聊天室用Java來實現

    基于TCP/IP的網絡聊天室實現 開發工具:eclipse 開發環境:jdk1.8 發送端 接收端 工具類 運行截圖...

    19.vue中封裝echarts組件

    19.vue中封裝echarts組件 1.效果圖 2.echarts組件 3.使用組件 按照組件格式整理好數據格式 傳入組件 home.vue 4.接口返回數據格式...

    劍指Offer39-調整數組順序使奇數位于偶數前面

    一開始想著用冒泡排序的方法來做,但是bug還是很多,后來看了評論區答案,發現直接空間換時間是最簡單的,而且和快排的寫法是類似的。...

    精品国产乱码久久久久久蜜桃不卡