【kubebuilder2.0】安裝、源碼分析


什么是Kubebuilder

Kubebuilder是一個用於在Go中快速構建和發布Kubernetes API的SDK。它建立在用於構建核心Kubernetes API的規范技術之上,以提供簡化的抽象來減少開發工作。

與Web開發框架(如Ruby on Rails和SpringBoot)類似,Kubebuilder提高了速度並降低了開發人員管理的復雜性。

包含在Kubebuilder中:

1、使用包括基本結構的項目初始化

  • 在規范版本中獲取包依賴性。
  • 主程序入口點
  • 用於格式化,生成,測試和構建的Makefile
  • 用於構建容器映像的Dockerfile

2、腳手架API

  • 資源(模型)定義
  • 控制器實現
  • 資源和控制器的集成測試
  • CRD定義

3、用於實現API的簡單抽象

  • Controllers
  • Resource Schema Validation
  • Validating Webhooks

4、用於發布API以安裝到集群中的工件

  • Namespace
  • CRDs
  • RBAC Roles and RoleBindings
  • Controller StatefulSet + Service

API參考文檔和示例
Kubebuilder是在控制器運行時和控制器工具庫之上開發的。

 

環境准備

Requirements

除了上面的工具和環境以外,需要有一套可連接的kubernetes環境,要求配置好kubectl config,以便能直連進行調試。

由於Feature gates的 --CustomResourceWebhookConversion參數是在v1.15及以上的版本k8s才默認為true,為了避免版本導致的額外問題,如果是新部署,建議安裝v1.15.4 以上的版本,原有的集群版本較低的話請升級。

參考這里:

Feature gates

安裝

go

參考這里:Install Go

docker

mac安裝包:

wget https://download.docker.com/mac/stable/Docker.dmg

linux根據不同的發行版來安裝,網絡資料很多,不再贅述

kubebuilder

二進制(推薦):

 

os=$(go env GOOS)
arch=$(go env GOARCH)
curl
-L https://go.kubebuilder.io/dl/2.3.1/${os}/${arch} | tar -xz -C /tmp/

sudo mv /tmp/kubebuilder_2.3.1_${os}_${arch} /usr/local/kubebuilder export PATH=$PATH:/usr/local/kubebuilder/bin

 

通過源碼安裝:

git clone https://github.com/kubernetes-sigs/kubebuilder
cd kubebuilder
make build
cp bin/kubebuilder $GOPATH/bin

kustomize

curl -s "https://raw.githubusercontent.com/\
kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"  | bash

 

$ cat install_kustomize.sh

#!/bin/bash # Downloads the most recently released kustomize binary # to your current working directory. # # Fails if the file already exists. where=$PWD if [ -f $where/kustomize ]; then echo "A file named kustomize already exists (remove it first)." exit 1 fi tmpDir=`mktemp -d` if [[ ! "$tmpDir" || ! -d "$tmpDir" ]]; then echo "Could not create temp dir." exit 1 fi function cleanup { rm -rf "$tmpDir" } trap cleanup EXIT pushd $tmpDir >& /dev/null opsys=windows if [[ "$OSTYPE" == linux* ]]; then opsys=linux elif [[ "$OSTYPE" == darwin* ]]; then opsys=darwin fi curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases |\ grep browser_download |\ grep $opsys |\ cut -d '"' -f 4 |\ grep /kustomize/v |\ sort | tail -n 1 |\ xargs curl -s -O -L tar xzf ./kustomize_v*_${opsys}_amd64.tar.gz cp ./kustomize $where popd >& /dev/null ./kustomize version echo kustomize installed to current directory.

kubernetes

安裝方式眾多,文檔豐富,不再贅述。

可參考:https://www.cnblogs.com/lizhewei/p/13366172.html

 

 

創建項目

查看現有的所有resource:

kubectl api-resources -o wide

 

查看現有的api groupVersion:

kubectl api-versions

Step 1: 初始化

新建一個 gitlab 項目,運行

mkdir $GOPATH/src/crd-demo
cd $GOPATH/src/crd-demo
export GO111MODULE=on # 如果路徑位於GOPATH/src下,go mod這一步可省略 go mod init ${CRD}

在$GOPATH/src/crd-demo項目目錄下執行
kubebuilder init --domain=kruise.io

參數解讀:domain 指定了后續注冊 CRD 對象的 Group 域名

Step 2: 創建 API

實際上不僅會創建 API,也就是 CRD,還會生成 Controller 的框架

kubebuilder create api --group apps --version v1alpha1 --kind SidecarSet 

 

參數解讀:

  • group 加上之前的 domian 即此 CRD 的 Group: apps.kruise.io;

  • version 一般分三種,按社區標准;

    • v1alpha1: 此 api 不穩定,CRD 可能廢棄、字段可能隨時調整,不要依賴;

    • v1beta1: api 已穩定,會保證向后兼容,特性可能會調整;

    • v1: api 和特性都已穩定;

  • kind: 此 CRD 的類型,類似於社區原生的 Service 的概念;

  • namespaced: 此 CRD 是全局唯一還是 namespace 唯一,類似 node 和 Pod。

 目錄結構

 

 

 

Step 3:定義 CRD

在圖 2 中對應的文件定義 Spec 和 Status。

 

Step 4:編寫 Controller 邏輯

在圖 3 中對應的文件實現 Reconcile 邏輯。

 

Step 5: 測試發布

本地測試完之后使用 Kubebuilder 的 Makefile 構建鏡像,部署我們的 CRDs 和 Controller 即可。

 

源碼閱讀

從 main.go 開始

var (
    scheme   = runtime.NewScheme()
    setupLog = ctrl.Log.WithName("setup")
)

func init() {
    _ = clientgoscheme.AddToScheme(scheme)

    _ = extensionsv1alpha1.AddToScheme(scheme)
    // +kubebuilder:scaffold:scheme
}

func main() {
    var metricsAddr string
    var enableLeaderElection bool
    flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
    flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
        "Enable leader election for controller manager. "+
            "Enabling this will ensure there is only one active controller manager.")
    flag.Parse()

    ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
    
    // 1.init Manager
    mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
        Scheme:             scheme,
        MetricsBindAddress: metricsAddr,
        Port:               9443,
        LeaderElection:     enableLeaderElection,
        LeaderElectionID:   "f5e19998.sncloud.com",
    })
    if err != nil {
        setupLog.Error(err, "unable to start manager")
        os.Exit(1)
    }
    
    // 2.init Reconciler
    if err = (&controllers.SidecarSetReconciler{
        Client: mgr.GetClient(),
        Log:    ctrl.Log.WithName("controllers").WithName("SidecarSet"),
        Scheme: mgr.GetScheme(),
    }).SetupWithManager(mgr); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "SidecarSet")
        os.Exit(1)
    }
    // +kubebuilder:scaffold:builder
    
    setupLog.Info("starting manager")
    // 3.start Manager
    if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
        setupLog.Error(err, "problem running manager")
        os.Exit(1)
    }
}

可以看到在 init 方法里面我們將 appsv1alpha1 注冊到 Scheme 里面去了,這樣一來 Cache 就知道 watch 誰了,main 方法里面的邏輯基本都是 Manager 的:

  1. 初始化了一個 Manager;
  2. 將 Manager 的 Client 傳給 Controller,並且調用 SetupWithManager 方法傳入 Manager 進行 Controller 的初始化;
  3. 啟動 Manager。

我們的核心就是看這 3 個流程。

Manager 初始化

Manager 初始化代碼如下

// New returns a new Manager for creating Controllers.
func New(config *rest.Config, options Options) (Manager, error) {
    
...

     // 創建Cache對象,用做client的讀請求,以及生成informer
cache, err := options.NewCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod, Namespace: options.Namespace}) if err != nil { return nil, err }

  // 創建讀請求的client,即apiReader,讀請求走的Cache
   apiReader, err := client.New(config, client.Options{Scheme: options.Scheme, Mapper: mapper})
    if err != nil {
        return nil, err
    }
  
  // 創建寫請求的client,寫請求直連APIServer writeObj, err :
= options.NewClient(cache, config, client.Options{Scheme: options.Scheme, Mapper: mapper}) if err != nil { return nil, err }
...
return &controllerManager{ config: config, scheme: options.Scheme, cache: cache, fieldIndexes: cache, client: writeObj, apiReader: apiReader, recorderProvider: recorderProvider, resourceLock: resourceLock, mapper: mapper, metricsListener: metricsListener, internalStop: stop, internalStopper: stop, port: options.Port, host: options.Host, certDir: options.CertDir, leaseDuration: *options.LeaseDuration, renewDeadline: *options.RenewDeadline, retryPeriod: *options.RetryPeriod, healthProbeListener: healthProbeListener, readinessEndpointName: options.ReadinessEndpointName, livenessEndpointName: options.LivenessEndpointName, }, nil }

可以看到主要是創建 Cache 與 Clients。

 

 

創建Clients

==> D:\code\go\pkg\mod\sigs.k8s.io\controller-runtime@v0.5.0\pkg\manager\manager.go

// defaultNewClient creates the default caching client
func defaultNewClient(cache cache.Cache, config *rest.Config, options client.Options) (client.Client, error) {
    // Create the Client for Write operations.
    c, err := client.New(config, options)
    if err != nil {
        return nil, err
    }

    return &client.DelegatingClient{
        Reader: &client.DelegatingReader{
            CacheReader:  cache,
            ClientReader: c,
        },
        Writer:       c,
        StatusClient: c,
    }, nil
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM