什么是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 以上的版本,原有的集群版本較低的話請升級。
參考這里:
安裝
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
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 的:
- 初始化了一個 Manager;
- 將 Manager 的 Client 傳給 Controller,並且調用 SetupWithManager 方法傳入 Manager 進行 Controller 的初始化;
- 啟動 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 }