前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kubebuilder 学习笔记

Kubebuilder 学习笔记

原创
作者头像
blazehu
修改2022-08-12 17:24:03
4900
修改2022-08-12 17:24:03
举报
文章被收录于专栏:小千世界

Kubebuilder is a framework for building Kubernetes APIs using custom resource definitions (CRDs).

Note: kubebuilder can save us a lot of work and make developing CRDs and adminsion webhooks incredibly easy.

Installation


代码语言:shell
复制
# download kubebuilder and install locally.
curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)
chmod +x kubebuilder && mv kubebuilder /usr/local/bin/

Create a Project


Create a directory, and then run the init command inside of it to initialize a new project. Follows an example.

代码语言:shell
复制
[blazehu@MacBook ~]$ mkdir ~/Project/workspace-go/example
[blazehu@MacBook ~]$ cd ~/Project/workspace-go/example
[blazehu@MacBook ~]$ kubebuilder init --domain blazehu.com --owner "blazehu" --repo blazehu.com/example
Writing kustomize manifests for you to edit...
Writing scaffold for you to edit...
Get controller runtime:
$ go get sigs.k8s.io/controller-runtime@v0.10.0
Update dependencies:
$ go mod tidy
Next: define a resource with:
$ kubebuilder create api

If your project is initialized within GOPATH, the implicitly called go mod init will interpolate the module path for you. Otherwise --repo=<module path> must be set.

Adding a new API


代码语言:shell
复制
[blazehu@MacBook ~]$ kubebuilder create api --group cos --version v1 --kind Bucket
Create Resource [y/n]
y
Create Controller [y/n]
y
Writing kustomize manifests for you to edit...
Writing scaffold for you to edit...
api/v1/bucket_types.go
controllers/bucket_controller.go
Update dependencies:
$ go mod tidy
Running make:
$ make generate
go: creating new go.mod: module tmp
Downloading sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0
go get: added sigs.k8s.io/controller-tools v0.7.0
/Users/huyuhan/Project/workspace-go/example/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
Next: implement your new API and generate the manifests (e.g. CRDs,CRs) with:
$ make manifests

Designing an API


api/v1/bucket_types.go

代码语言:go
复制
// BucketSpec defines the desired state of Bucket
type BucketSpec struct {
	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
	// Important: Run "make" to regenerate code after modifying this file

	// Foo is an example field of Bucket. Edit bucket_types.go to remove/update
	Name   string `json:"name,omitempty"`
	Region string `json:"region,omitempty"`
	ACL    string `json:"acl,omitempty"`
}

Implementing a controller


controllers/cos.go

代码语言:go
复制
package controllers

import (
	"context"
	"fmt"
	"github.com/tencentyun/cos-go-sdk-v5"
	"net/http"
	"net/url"
)

type CosStorage struct {
	client          *cos.Client
	accessKeyId     string
	accessKeySecret string
	bucket          string
	region          string
}

// NewCosStorage endpoint: https://cloud.tencent.com/document/product/436/6224
func NewCosStorage(region, bucketName string) *CosStorage {
	url, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucketName, region))
	accessKeyId := ""
	accessKeySecret := ""
	b := &cos.BaseURL{BucketURL: url}
	client := cos.NewClient(b, &http.Client{
		Transport: &cos.AuthorizationTransport{
			SecretID:  accessKeyId,
			SecretKey: accessKeySecret,
		},
	})
	return &CosStorage{
		client:          client,
		accessKeyId:     accessKeyId,
		accessKeySecret: accessKeySecret,
		region:          region,
		bucket:          bucketName,
	}
}

func (c *CosStorage) Put(acl string) error {
	opt := &cos.BucketPutOptions{
		XCosACL: acl,
	}
	_, err := c.client.Bucket.Put(context.Background(), opt)
	return err
}

func (c *CosStorage) Delete() error {
	_, err := c.client.Bucket.Delete(context.Background())
	return err
}

controllers/bucket_controller.go

tips: Finalizers allow controllers to implement asynchronous pre-delete hooks. Let’s say you create an external resource (such as a storage bucket) for each object of your API type, and you want to delete the associated external resource on object’s deletion from Kubernetes, you can use a finalizer to do that.

代码语言:go
复制
/*
Copyright 2022 blazehu.

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"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
	"sigs.k8s.io/controller-runtime/pkg/log"

	cosv1 "blazehu.com/example/api/v1"
)

// BucketReconciler reconciles a Bucket object
type BucketReconciler struct {
	client.Client
	Scheme *runtime.Scheme
}

const (
	bucketFinalizerName = "bucket.cos.blazehu.com/finalizer"
)

//+kubebuilder:rbac:groups=cos.blazehu.com,resources=buckets,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=cos.blazehu.com,resources=buckets/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=cos.blazehu.com,resources=buckets/finalizers,verbs=update

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Bucket object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *BucketReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	logger := log.FromContext(ctx)

	bucket := &cosv1.Bucket{}
	if err := r.Get(ctx, req.NamespacedName, bucket); err != nil {
		return ctrl.Result{}, client.IgnoreNotFound(err)
	}

	// examine DeletionTimestamp to determine if object is under deletion
	if bucket.ObjectMeta.DeletionTimestamp.IsZero() {
		// The object is not being deleted, so if it does not have our finalizer,
		// then lets add the finalizer and update the object. This is equivalent
		// registering our finalizer.
		if !controllerutil.ContainsFinalizer(bucket, bucketFinalizerName) {
			controllerutil.AddFinalizer(bucket, bucketFinalizerName)
			if err := r.Update(ctx, bucket); err != nil {
				return ctrl.Result{}, err
			}
		} else {
			if err := r.updateExternalResources(bucket); err != nil {
				logger.Error(err, "unable to create Bucket")
				return ctrl.Result{}, err
			}
			logger.Info("create Bucket succeed")
		}
	} else {
		// The object is being deleted
		if controllerutil.ContainsFinalizer(bucket, bucketFinalizerName) {
			// our finalizer is present, so lets handle any external dependency
			if err := r.deleteExternalResources(bucket); err != nil {
				// if fail to delete the external dependency here, return with error
				// so that it can be retried
				logger.Error(err, "unable to delete Bucket")
				return ctrl.Result{}, err
			}

			// remove our finalizer from the list and update it.
			controllerutil.RemoveFinalizer(bucket, bucketFinalizerName)
			if err := r.Update(ctx, bucket); err != nil {
				return ctrl.Result{}, err
			}
			logger.Info("delete Bucket succeed")
		}

		// Stop reconciliation as the item is being deleted
		return ctrl.Result{}, nil
	}

	// bucket reconcile logic
	return ctrl.Result{}, nil
}

func (r *BucketReconciler) updateExternalResources(bucket *cosv1.Bucket) error {
	cosClient := NewCosStorage(bucket.Spec.Region, bucket.Spec.Name)
	return cosClient.Put(bucket.Spec.ACL)
}

func (r *BucketReconciler) deleteExternalResources(bucket *cosv1.Bucket) error {
	cosClient := NewCosStorage(bucket.Spec.Region, bucket.Spec.Name)
	return cosClient.Delete()
}

// SetupWithManager sets up the controller with the Manager.
func (r *BucketReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&cosv1.Bucket{}).
		Complete(r)
}

Test It Out


Install the CRDs into the cluster (make install)

代码语言:shell
复制
[blazehu@MacBook ~]$ make install
/Users/huyuhan/Project/workspace-go/example/bin/controller-gen rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
/Users/huyuhan/Project/workspace-go/example/bin/kustomize build config/crd | kubectl apply -f -
customresourcedefinition.apiextensions.k8s.io/buckets.cos.blazehu.com created

Run your controller (this will run in the foreground, so switch to a new terminal if you want to leave it running) (make run)

代码语言:shell
复制
[blazehu@MacBook ~]$ make run
/Users/huyuhan/Project/workspace-go/example/bin/controller-gen rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
/Users/huyuhan/Project/workspace-go/example/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go run ./main.go
2022-01-27T22:05:30.207+0800	INFO	controller-runtime.metrics	metrics server is starting to listen	{"addr": ":8080"}
2022-01-27T22:05:30.207+0800	INFO	setup	starting manager
2022-01-27T22:05:30.208+0800	INFO	starting metrics server	{"path": "/metrics"}
2022-01-27T22:05:30.208+0800	INFO	controller.bucket	Starting EventSource	{"reconciler group": "cos.blazehu.com", "reconciler kind": "Bucket", "source": "kind source: /, Kind="}
2022-01-27T22:05:30.208+0800	INFO	controller.bucket	Starting Controller	{"reconciler group": "cos.blazehu.com", "reconciler kind": "Bucket"}
2022-01-27T22:05:30.309+0800	INFO	controller.bucket	Starting workers	{"reconciler group": "cos.blazehu.com", "reconciler kind": "Bucket", "worker count": 1}

Create Custom Resources (create bucket.cos.blazehu.com/bucket-sample) (cos_v1_bucket.yaml)

代码语言:yaml
复制
apiVersion: cos.blazehu.com/v1
kind: Bucket
metadata:
  name: bucket-sample
  namespace: blazehu
spec:
  # TODO(user): Add fields here
  name: example-1251762279
  region: ap-shanghai
  acl: private

kubectl apply -f cos_v1_bucket.yaml

代码语言:shell
复制
[blazehu@MacBook ~]$ kubectl apply -f cos_v1_bucket.yaml
bucket.cos.blazehu.com/bucket-sample created
[blazehu@MacBook ~]$ kubectl get bucket.cos.blazehu.com  -n blazehu
NAME            AGE
bucket-sample   17s

Tencent cloud console view found that the bucket was created normally.

image.png
image.png

Delete Instances of Custom Resources (delete bucket.cos.blazehu.com/bucket-sample)

代码语言:shell
复制
[blazehu@MacBook ~]$ kubectl delete -f cos_v1_bucket.yaml
bucket.cos.blazehu.com "bucket-sample" deleted

Tencent Cloud Console view found that the bucket has been deleted.

Run It On the Cluster


Deploy the controller to the cluster with image specified by IMG

代码语言:shell
复制
make docker-build docker-push IMG=<some-registry>/<project-name>:tag
make deploy IMG=<some-registry>/<project-name>:tag

Uninstall CRDs


To delete your CRDs from the cluster, run make uninstall

Undeploy controller


UnDeploy the controller to the cluster, run make undeploy

Reference documentation

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Installation
  • Create a Project
  • Adding a new API
  • Designing an API
  • Implementing a controller
  • Test It Out
  • Run It On the Cluster
  • Uninstall CRDs
  • Undeploy controller
  • Reference documentation
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档