溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

kubernetes CRD開(kāi)發(fā)的示例分析

發(fā)布時(shí)間:2021-12-24 15:35:11 來(lái)源:億速云 閱讀:293 作者:小新 欄目:云計(jì)算

這篇文章主要介紹了kubernetes CRD開(kāi)發(fā)的示例分析,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

kubebuilder

kubebuilder能幫我們節(jié)省大量工作,讓開(kāi)發(fā)CRD和adminsion webhook變得異常簡(jiǎn)單。

安裝

通過(guò)源碼安裝:

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

或者下載二進(jìn)制:

os=$(go env GOOS)
arch=$(go env GOARCH)

# download kubebuilder and extract it to tmp
curl -sL https://go.kubebuilder.io/dl/2.0.0-beta.0/${os}/${arch} | tar -xz -C /tmp/

# move to a long-term location and put it on your path
# (you'll need to set the KUBEBUILDER_ASSETS env var if you put it somewhere else)
sudo mv /tmp/kubebuilder_2.0.0-beta.0_${os}_${arch} /usr/local/kubebuilder
export PATH=$PATH:/usr/local/kubebuilder/bin

還需要裝下kustomize 這可是個(gè)渲染yaml的神器,讓helm顫抖。

go install sigs.k8s.io/kustomize/v3/cmd/kustomize

使用

注意你得先有個(gè)kubernetes集群,一步安裝走你

創(chuàng)建CRD

kubebuilder init --domain sealyun.com --license apache2 --owner "fanux"
kubebuilder create api --group infra --version v1 --kind VirtulMachine

安裝CRD并啟動(dòng)controller

make install # 安裝CRD
make run # 啟動(dòng)controller

然后我們就可以看到創(chuàng)建的CRD了

# kubectl get crd
NAME                                           AGE
virtulmachines.infra.sealyun.com                  52m

來(lái)創(chuàng)建一個(gè)虛擬機(jī):

# kubectl apply -f config/samples/
# kubectl get virtulmachines.infra.sealyun.com 
NAME                   AGE
virtulmachine-sample   49m

看一眼yaml文件:

# cat config/samples/infra_v1_virtulmachine.yaml 
apiVersion: infra.sealyun.com/v1
kind: VirtulMachine
metadata:
  name: virtulmachine-sample
spec:
  # Add fields here
  foo: bar

這里僅僅是把yaml存到etcd里了,我們controller監(jiān)聽(tīng)到創(chuàng)建事件時(shí)啥事也沒(méi)干。

把controller部署到集群中

make docker-build docker-push IMG=fanux/infra-controller
make deploy

我是連的遠(yuǎn)端的kubenetes, make docker-build時(shí)test過(guò)不去,沒(méi)有etcd的bin文件,所以先把test關(guān)了。

修改Makefile:

# docker-build: test
docker-build:

Dockerfile里的gcr.io/distroless/static:latest 這個(gè)鏡像你也可能拉不下來(lái),隨意改改就行,我改成了golang:1.12.7

也有可能構(gòu)建時(shí)有些代碼拉不下來(lái),啟用一下go mod vendor 把依賴(lài)打包進(jìn)去

go mod vendor
如果你本地有些代碼拉不下來(lái),可以用proxy:

export GOPROXY=https://goproxy.io


再改下Dockerfile, 注釋掉download:

修改后:

# Build the manager binary
FROM golang:1.12.7 as builder

WORKDIR /go/src/github.com/fanux/sealvm
# Copy the Go Modules manifests
COPY . . 

# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o manager main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
# FROM gcr.io/distroless/static:latest
FROM golang:1.12.7
WORKDIR /
COPY --from=builder /go/src/github.com/fanux/sealvm/manager .
ENTRYPOINT ["/manager"]

make deploy 時(shí)報(bào)錯(cuò): Error: json: cannot unmarshal string into Go struct field Kustomization.patches of type types.Patch

config/default/kustomization.yaml 中的 patches: 改成 patchesStrategicMerge: 即可

kustomize build config/default 這個(gè)命令就渲染出了controller的yaml文件,可以體驗(yàn)下

看 你的controller已經(jīng)跑起來(lái)了:

kubectl get deploy -n sealvm-system
NAME                        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
sealvm-controller-manager   1         1         1            0           3m
kubectl get svc -n sealvm-system
NAME                                        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
sealvm-controller-manager-metrics-service   ClusterIP   10.98.71.199   <none>        8443/TCP   4m

開(kāi)發(fā)

增加對(duì)象數(shù)據(jù)參數(shù)

看下config/samples下面的yaml文件:

apiVersion: infra.sealyun.com/v1
kind: VirtulMachine
metadata:
  name: virtulmachine-sample
spec:
  # Add fields here
  foo: bar

這里參數(shù)里有foo:bar, 那我們來(lái)加個(gè)虛擬CPU,內(nèi)存信息:

直接api/v1/virtulmachine_types.go即可

// VirtulMachineSpec defines the desired state of VirtulMachine
// 在這里加信息
type VirtulMachineSpec struct {
	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
	// Important: Run "make" to regenerate code after modifying this file
	CPU    string `json:"cpu"`   // 這是我增加的
	Memory string `json:"memory"`
}

// VirtulMachineStatus defines the observed state of VirtulMachine
// 在這里加狀態(tài)信息,比如虛擬機(jī)是啟動(dòng)狀態(tài),停止?fàn)顟B(tài)啥的
type VirtulMachineStatus struct {
	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
	// Important: Run "make" to regenerate code after modifying this file
}

然后make一下:

make && make install && make run

這時(shí)再去渲染一下controller的yaml就會(huì)發(fā)現(xiàn)CRD中已經(jīng)帶上CPU和內(nèi)存信息了:

kustomize build config/default

properties:
  cpu:
    description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
      Important: Run "make" to regenerate code after modifying this file'
    type: string
  memory:
    type: string

修改一下yaml:

apiVersion: infra.sealyun.com/v1
kind: VirtulMachine
metadata:
  name: virtulmachine-sample
spec:
  cpu: "1"
  memory: "2G"
# kubectl apply -f config/samples 
virtulmachine.infra.sealyun.com "virtulmachine-sample" configured
# kubectl get virtulmachines.infra.sealyun.com virtulmachine-sample -o yaml 
apiVersion: infra.sealyun.com/v1
kind: VirtulMachine
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"infra.sealyun.com/v1","kind":"VirtulMachine","metadata":{"annotations":{},"name":"virtulmachine-sample","namespace":"default"},"spec":{"cpu":"1","memory":"2G"}}
  creationTimestamp: 2019-07-26T08:47:34Z
  generation: 2
  name: virtulmachine-sample
  namespace: default
  resourceVersion: "14811698"
  selfLink: /apis/infra.sealyun.com/v1/namespaces/default/virtulmachines/virtulmachine-sample
  uid: 030e2b9a-af82-11e9-b63e-5254bc16e436
spec:      # 新的CRD已生效
  cpu: "1"
  memory: 2G

Status 同理,就不再贅述了,比如我把status里加一個(gè)Create, 表示controller要去創(chuàng)建虛擬機(jī)了(主要一些控制層面的邏輯),創(chuàng)建完了把狀態(tài)改成Running

Reconcile 唯一需要實(shí)現(xiàn)的接口

controller把輪訓(xùn)與事件監(jiān)聽(tīng)都封裝在這一個(gè)接口里了.你不需要關(guān)心怎么事件監(jiān)聽(tīng)的.

獲取虛擬機(jī)信息
func (r *VirtulMachineReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
	ctx = context.Background()
	_ = r.Log.WithValues("virtulmachine", req.NamespacedName)

	vm := &v1.VirtulMachine{}
	if err := r.Get(ctx, req.NamespacedName, vm); err != nil { # 獲取VM信息
		log.Error(err, "unable to fetch vm")
	} else {
        fmt.Println(vm.Spec.CPU, vm.Spec.Memory) # 打印CPU內(nèi)存信息
	}

	return ctrl.Result{}, nil
}

make && make install && make run這個(gè)時(shí)候去創(chuàng)建一個(gè)虛擬機(jī)kubectl apply -f config/samples,日志里就會(huì)輸出CPU內(nèi)存了. List接口同理,我就不贅述了

r.List(ctx, &vms, client.InNamespace(req.Namespace), client.MatchingField(vmkey, req.Name))
更新?tīng)顟B(tài)

在status結(jié)構(gòu)體中加入狀態(tài)字段:

type VirtulMachineStatus struct {
	Status string `json:"status"`
}

controller里去更新?tīng)顟B(tài):

vm.Status.Status = "Running"
if err := r.Status().Update(ctx, vm); err != nil {
	log.Error(err, "unable to update vm status")
}

如果出現(xiàn):the server could not find the requested resource 這個(gè)錯(cuò)誤,那么在CRD結(jié)構(gòu)體上需要加個(gè)注釋 // +kubebuilder:subresource:status

// +kubebuilder:subresource:status
// +kubebuilder:object:root=true

type VirtulMachine struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	Spec   VirtulMachineSpec   `json:"spec,omitempty"`
	Status VirtulMachineStatus `json:"status,omitempty"`
}

這樣就好了

編譯啟動(dòng)后再去apply發(fā)現(xiàn)狀態(tài)已經(jīng)變成running:

# kubectl get virtulmachines.infra.sealyun.com virtulmachine-sample -o yaml
...
status:
  status: Running
刪除
time.Sleep(time.Second * 10)
if err := r.Delete(ctx, vm); err != nil {
	log.Error(err, "unable to delete vm ", "vm", vm)
}

10s之后我們將GET不到

刪除回收器 Finalizers

如果不使用Finalizers,kubectl delete 時(shí)直接就刪了etcd數(shù)據(jù),controller再想去拿CRD時(shí)已經(jīng)拿不到了:

ERRO[0029] VirtulMachine.infra.sealyun.com "virtulmachine-sample" not foundunable to fetch vm  source="virtulmachine_controller.go:48"

所以在創(chuàng)建時(shí)我們需要給CRD加上Finalizer:

vm.ObjectMeta.Finalizers = append(vm.ObjectMeta.Finalizers, "virtulmachine.infra.sealyun.com")

然后刪除時(shí)就只會(huì)給CRD打上一個(gè)刪除時(shí)間戳,供我們做后續(xù)處理, 處理完了我們刪除掉Finalizers:

如果 DeleteionTimestamp不存在
    如果沒(méi)有Finalizers
        加上Finalizers,并更新CRD
要不然,說(shuō)明是要被刪除的
    如果存在Finalizers,刪除Finalizers,并更新CRD

看個(gè)完整的代碼示例:

if cronJob.ObjectMeta.DeletionTimestamp.IsZero() {
        if !containsString(cronJob.ObjectMeta.Finalizers, myFinalizerName) {
            cronJob.ObjectMeta.Finalizers = append(cronJob.ObjectMeta.Finalizers, myFinalizerName)
            if err := r.Update(context.Background(), cronJob); err != nil {
                return ctrl.Result{}, err
            }
        }
    } else {
        if containsString(cronJob.ObjectMeta.Finalizers, myFinalizerName) {
            if err := r.deleteExternalResources(cronJob); err != nil {
                return ctrl.Result{}, err
            }

            cronJob.ObjectMeta.Finalizers = removeString(cronJob.ObjectMeta.Finalizers, myFinalizerName)
            if err := r.Update(context.Background(), cronJob); err != nil {
                return ctrl.Result{}, err
            }
        }
    }

失敗重試

假設(shè)我們A依賴(lài)B而B(niǎo)又后創(chuàng)建,那么在處理A CRD時(shí)直接返回失敗即可,這樣很快會(huì)重試

webhook

kuberentes有三種webhook,admission webhook, authorization webhook and CRD conversion webhook.

這里比如我們要給CRD設(shè)置一些默認(rèn)值,又或者是用戶創(chuàng)建時(shí)少填了一些參數(shù),那么我們得禁止創(chuàng)建等等這些事。

使用webhook也非常的簡(jiǎn)單,只需給定義的結(jié)構(gòu)體實(shí)現(xiàn) DefaulterValidator接口即可.

其它接口

Reconcile結(jié)構(gòu)體聚合了Client接口,所以client的所有方法都是可以直接調(diào)用,大部分是對(duì)CRD object的相關(guān)操作

type Client interface {
	Reader
	Writer
	StatusClient
}
// Reader knows how to read and list Kubernetes objects.
type Reader interface {
	// Get retrieves an obj for the given object key from the Kubernetes Cluster.
	// obj must be a struct pointer so that obj can be updated with the response
	// returned by the Server.
	Get(ctx context.Context, key ObjectKey, obj runtime.Object) error

	// List retrieves list of objects for a given namespace and list options. On a
	// successful call, Items field in the list will be populated with the
	// result returned from the server.
	List(ctx context.Context, list runtime.Object, opts ...ListOptionFunc) error
}

// Writer knows how to create, delete, and update Kubernetes objects.
type Writer interface {
	// Create saves the object obj in the Kubernetes cluster.
	Create(ctx context.Context, obj runtime.Object, opts ...CreateOptionFunc) error

	// Delete deletes the given obj from Kubernetes cluster.
	Delete(ctx context.Context, obj runtime.Object, opts ...DeleteOptionFunc) error

	// Update updates the given obj in the Kubernetes cluster. obj must be a
	// struct pointer so that obj can be updated with the content returned by the Server.
	Update(ctx context.Context, obj runtime.Object, opts ...UpdateOptionFunc) error

	// Patch patches the given obj in the Kubernetes cluster. obj must be a
	// struct pointer so that obj can be updated with the content returned by the Server.
	Patch(ctx context.Context, obj runtime.Object, patch Patch, opts ...PatchOptionFunc) error
}

// StatusClient knows how to create a client which can update status subresource
// for kubernetes objects.
type StatusClient interface {
	Status() StatusWriter
}

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“kubernetes CRD開(kāi)發(fā)的示例分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI