使用kubebuilder開發(fā)kubernetes CRD

原文地址

擴(kuò)展kubernetes兩個(gè)最常用最需要掌握的東西:自定義資源CRD 和 adminsion webhook, 本文教你如何十分鐘掌握CRD開發(fā).

kubernetes允許用戶自定義自己的資源對象蒿往,就如同deployment statefulset一樣址儒,這個(gè)應(yīng)用非常廣泛,比如prometheus opterator就自定義Prometheus對象失受,再加上一個(gè)自定義的controller監(jiān)聽到kubectl create Prometheus時(shí)就去創(chuàng)建Pod組成一個(gè)pormetheus集群很魂。rook等等同理扎酷。

我需要用kubernetes調(diào)度虛擬機(jī),所以這里自定義一個(gè) VirtualMachine 類型

kubebuilder

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

安裝

通過源碼安裝:

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并啟動controller

make install # 安裝CRD
make run # 啟動controller

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

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

來創(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)聽到創(chuàng)建事件時(shí)啥事也沒干帝蒿。

把controller部署到集群中

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

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

修改Makefile:

# docker-build: test
docker-build: 

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

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

go mod vendor
如果你本地有些代碼拉不下來偏序,可以用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)跑起來了:

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

開發(fā)

增加對象數(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胖替, 那我們來加個(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ī)是啟動狀態(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就會發(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)聽都封裝在這一個(gè)接口里了.你不需要關(guān)心怎么事件監(jiān)聽的.

獲取虛擬機(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,日志里就會輸出CPU內(nèi)存了. List接口同理冲呢,我就不贅述了

r.List(ctx, &vms, client.InNamespace(req.Namespace), client.MatchingField(vmkey, req.Name))

更新狀態(tài)

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

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

controller里去更新狀態(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"`
}

這樣就好了

編譯啟動后再去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不到

其它接口

Reconcile結(jié)構(gòu)體聚合了Client接口招狸,所以client的所有方法都是可以直接調(diào)用敬拓,大部分是對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
}

探討可加QQ群:98488045

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市裙戏,隨后出現(xiàn)的幾起案子乘凸,更是在濱河造成了極大的恐慌,老刑警劉巖累榜,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件营勤,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)葛作,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門醒第,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人进鸠,你說我怎么就攤上這事稠曼。” “怎么了客年?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵霞幅,是天一觀的道長。 經(jīng)常有香客問我量瓜,道長司恳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任绍傲,我火速辦了婚禮扔傅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烫饼。我一直安慰自己猎塞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布杠纵。 她就那樣靜靜地躺著荠耽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪比藻。 梳的紋絲不亂的頭發(fā)上铝量,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機(jī)與錄音银亲,去河邊找鬼慢叨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛务蝠,可吹牛的內(nèi)容都是我干的拍谐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼请梢,長吁一口氣:“原來是場噩夢啊……” “哼赠尾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毅弧,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎当窗,沒想到半個(gè)月后够坐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年元咙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了梯影。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡庶香,死狀恐怖甲棍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赶掖,我是刑警寧澤感猛,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站奢赂,受9級特大地震影響陪白,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜膳灶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一咱士、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轧钓,春花似錦序厉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至霉晕,卻和暖如春庭再,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背牺堰。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工拄轻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伟葫。 一個(gè)月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓恨搓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親筏养。 傳聞我的和親對象是個(gè)殘疾皇子斧抱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容