基于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數量發現生效了。
完。
智能推薦
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 所寫,首先總結了前端組件化樣式中的最佳實踐原則,然后在此基...
19.vue中封裝echarts組件
19.vue中封裝echarts組件 1.效果圖 2.echarts組件 3.使用組件 按照組件格式整理好數據格式 傳入組件 home.vue 4.接口返回數據格式...