K8s Operator 開發之 kubebuilder 實戰(k8s和docker區別)
一 背景
對于業務應用,需要對其進行先k8s內置資源進行一系列運維操作,因此編寫業務的operator必不可少,在此了解到kubebuilder 是社區認可度很高的一種官方、標準化 Operator 框架,可以利用其非常方便的編寫業務operator,以此來擴展 Kubernetes API
1.1 kubebuilder是什么
Kubebuilder 是一個使用 CRDs 構建 K8s API 的 SDK,主要是:
提供腳手架工具初始化 CRDs 工程,自動生成 boilerplate 代碼和配置;
提供代碼庫封裝底層的 K8s go-client;
方便用戶從零開始開發 CRDs,Controllers 和 Admission Webhooks 來擴展 K8s。
1.2 功能
自定義資源 CRD(Custom Resource Definition)可以擴展 Kubernetes API,掌握 CRD 是成為 Kubernetes 高級玩家的必備技能,本文將介紹 CRD 和 Controller 的概念,并對 CRD 編寫框架 Kubebuilder 進行深入分析,讓您真正理解并能快速開發 CRD。
1.3 基本概念
CRD (Custom Resource Definition): 允許用戶自定義 Kubernetes 資源,是一個類型;
CR (Custom Resourse): CRD 的一個具體實例;
webhook: 它本質上是一種 HTTP 回調,會注冊到 apiserver 上。在 apiserver 特定事件發生時,會查詢已注冊的 webhook,并把相應的消息轉發過去。
按照處理類型的不同,一般可以將其分為兩類:一類可能會修改傳入對象,稱為 mutating webhook;一類則會只讀傳入對象,稱為 validating webhook。
工作隊列: controller 的核心組件。它會監控集群內的資源變化,并把相關的對象,包括它的動作與 key,例如 Pod 的一個 Create 動作,作為一個事件存儲于該隊列中;
controller :它會循環地處理上述工作隊列,按照各自的邏輯把集群狀態向預期狀態推動。不同的 controller 處理的類型不同,比如 replicaset controller 關注的是副本數,會處理一些 Pod 相關的事件;
operator:operator 是描述、部署和管理 kubernetes 應用的一套機制,從實現上來說,可以將其理解為 CRD 配合可選的 webhook 與 controller 來實現用戶業務邏輯,即 operator = CRD + webhook + controller。
二 架構即基本概念
2.1 架構圖
2.2 基礎概念
2.2.1 GVKs&GVRs
GVK = GroupVersionKind,GVR = GroupVersionResource。
API Group & Versions(GV)
API Group 是相關 API 功能的集合,每個 Group 擁有一或多個 Versions,用于接口的演進。
Kinds & Resources(GVR)
每個 GV 都包含多個 API 類型,稱為 Kinds,在不同的 Versions 之間同一個 Kind 定義可能不同, Resource 是 Kind 的對象標識(resource type),一般來說 Kinds 和 Resources 是 1:1 的,比如 pods Resource 對應 Pod Kind,但是有時候相同的 Kind 可能對應多個 Resources,比如 Scale Kind 可能對應很多 Resources:deployments/scale,replicasets/scale,對于 CRD 來說,只會是 1:1 的關系。
每一個 GVK 都關聯著一個 package 中給定的 root Go type,比如 apps/v1/Deployment 就關聯著 K8s 源碼里面 k8s.io/api/apps/v1 package 中的 Deployment struct,我們提交的各類資源定義 YAML 文件都需要寫:
apiVersion:這個就是 GV 。
kind:這個就是 K。
根據 GVK K8s 就能找到你到底要創建什么類型的資源,根據你定義的 Spec 創建好資源之后就成為了 Resource,也就是 GVR。GVK/GVR 就是 K8s 資源的坐標,是我們創建/刪除/修改/讀取資源的基礎。
2.2.3 Scheme
每一組 Controllers 都需要一個 Scheme,提供了 Kinds 與對應 Go types 的映射,也就是說給定 Go type 就知道他的 GVK,給定 GVK 就知道他的 Go type,比如說我們給定一個 Scheme: “tutotial.kubebuilder.io/api/v1”.CronJob{} 這個 Go type 映射到 batch.tutotial.kubebuilder.io/v1 的 CronJob GVK,那么從 Api Server 獲取到下面的 JSON:
{ "kind": "CronJob", "apiVersion": "batch.tutorial.kubebuilder.io/v1", ... }
就能構造出對應的 Go type了,通過這個 Go type 也能正確地獲取 GVR 的一些信息,控制器可以通過該 Go type 獲取到期望狀態以及其他輔助信息進行調諧邏輯。
2.2.4 Manager
Kubebuilder 的核心組件,具有 3 個職責:
負責運行所有的 Controllers;
初始化共享 caches,包含 listAndWatch 功能;
初始化 clients 用于與 Api Server 通信。
2.2.5 Cache
Kubebuilder 的核心組件,負責在 Controller 進程里面根據 Scheme 同步 Api Server 中所有該 Controller 關心 GVKs 的 GVRs,其核心是 GVK -> Informer 的映射,Informer 會負責監聽對應 GVK 的 GVRs 的創建/刪除/更新操作,以觸發 Controller 的 Reconcile 邏輯。
2.2.6 Controller
Kubebuidler 為我們生成的腳手架文件,我們只需要實現 Reconcile 方法即可。
2.2.7 Client
在實現 Controller 的時候不可避免地需要對某些資源類型進行創建/刪除/更新,就是通過該 Clients 實現的,其中查詢功能實際查詢是本地的 Cache,寫操作直接訪問 Api Server。
2.2.8 Index
由于 Controller 經常要對 Cache 進行查詢,Kubebuilder 提供 Index utility 給 Cache 加索引提升查詢效率。
2.2.9 Finalizer
在一般情況下,如果資源被刪除之后,我們雖然能夠被觸發刪除事件,但是這個時候從 Cache 里面無法讀取任何被刪除對象的信息,這樣一來,導致很多垃圾清理工作因為信息不足無法進行,K8s 的 Finalizer 字段用于處理這種情況。在 K8s 中,只要對象 ObjectMeta 里面的 Finalizers 不為空,對該對象的 delete 操作就會轉變為 update 操作,具體說就是 update deletionTimestamp 字段,其意義就是告訴 K8s 的 GC“在deletionTimestamp 這個時刻之后,只要 Finalizers 為空,就立馬刪除掉該對象”。
所以一般的使用姿勢就是在創建對象時把 Finalizers 設置好(任意 string),然后處理 DeletionTimestamp 不為空的 update 操作(實際是 delete),根據 Finalizers 的值執行完所有的 pre-delete hook(此時可以在 Cache 里面讀取到被刪除對象的任何信息)之后將 Finalizers 置為空即可。
2.2.10 OwnerReference
K8s GC 在刪除一個對象時,任何 ownerReference 是該對象的對象都會被清除,與此同時,Kubebuidler 支持所有對象的變更都會觸發 Owner 對象 controller 的 Reconcile 方法。
三 實戰kubebuilder
3.1 需求環境
go version v1.15+.
docker version 17.03+.
kubectl version v1.11.3+.
kustomize v3.1.0+
Kind 本地開發可安裝kind
能夠訪問 Kubernetes v1.11.3+ 集群
環境
創建:k8s.io和sigs.k8s.io兩個目錄
k8s.io直接拉去k8s項目源碼,并將其中kubernetes/staging/src/k8s.io拷貝出來即可
sigs.k8s.io 需要創建目錄,clone k8s-sigs/controller-runtime項目
# 在gopath的src目錄下拉取k8s源碼 $ pwd /Users/xuel/workspace/goworkspace/src $ ls github.com golang.org google.golang.org gopkg.in # 拷貝k8s源碼中的k8s.io到上層目錄 $ cp -R kubernetes/staging/src/k8s.io k8s.io # 在gopath 中創建sigs.k8s.io,并clone controller-runtime $ mkdir /Users/xuel/workspace/goworkspace/src/sigs.k8s.io && cd sigs.k8s.io && git clone https://github.com/kubernetes-sigs/controller-runtime.git $ ls drwxr-xr-x 24 xuel staff 768B Jan 3 18:46 github.com drwxr-xr-x 3 xuel staff 96B Mar 22 2020 golang.org drwxr-xr-x 3 xuel staff 96B May 21 2020 google.golang.org drwxr-xr-x 3 xuel staff 96B May 21 2020 gopkg.in drwxr-xr-x 28 xuel staff 896B Jan 28 19:53 k8s.io drwxr-xr-x 41 xuel staff 1.3K Jan 28 19:52 kubernetes drwxr-xr-x 3 xuel staff 96B Jan 28 19:57 sigs.k8s.io
3.2 創建項目
創建目錄,初始化系統
$ kubebuilder init --domain imoc-operator
目錄結構如下:
3.3 創建API
運行下面的命令,創建一個新的 API(組/版本)為 “webapp/v1”,并在上面創建新的 Kind(CRD) “Guestbook”。
kubebuilder create api --group webapp --version v1 --kind Guestbook
如果你在 Create Resource [y/n] 和 Create Controller [y/n] 中按y,那么這將創建文件 api/v1/guestbook_types.go ,該文件中定義相關 API ,而針對于這一類型 (CRD) 的對賬業務邏輯生成在 controller/guestbook_controller.go 文件中。
3.4 編譯運行controller
kubebuilder自動生成的controller源碼地址是:$GOPATH/src/helloworld/controllers/guestbook_controller.go , 內容如下:
3.5 安裝crd到集群
將 CRD 安裝到集群中
make install
運行控制器(這將在前臺運行,如果你想讓它一直運行,請切換到新的終端)。
make run
此時控制臺輸出以下內容,這里要注意,controller是在kubebuilder電腦上運行的,一旦使用Ctrl+c中斷控制臺,就會導致controller停止:
3.6 創建Guestbook資源的實例
現在kubernetes已經部署了Guestbook類型的CRD,而且對應的controller也已正在運行中,可以嘗試創建Guestbook類型的實例了(相當于有了pod的定義后,才可以創建pod);
kubebuilder已經自動創建了一個類型的部署文件:$GOPATH/src/helloworld/config/samples/webapp_v1_guestbook.yaml ,內容如下,很簡單,接下來咱們就用這個文件來創建Guestbook實例:
apiVersion: webapp.com.bolingcavalry/v1 kind: Guestbook metadata: name: guestbook-sample spec: # Add fields here foo: bar
3.6.2 安裝pod
$ kubectl apply -f config/samples/ $ kubectl get Guestbook
3.7 將controller制作為docker鏡像
至此,咱們已經體驗過了kubebuilder的基本功能,不過實際生產環境中controller一般都會運行在kubernetes環境內,像上面這種運行在kubernetes之外的方式就不合適了,咱們來試試將其做成docker鏡像然后在kubernetes環境運行;
這里有個要求,就是您要有個kubernetes可以訪問的鏡像倉庫,例如局域網內的Harbor,或者公共的hub.docker.com,我這為了操作方便選擇了hub.docker.com,使用它的前提是擁有hub.docker.com的注冊帳號;
在kubebuilder電腦上,打開一個控制臺,執行docker login命令登錄,根據提示輸入hub.docker.com的帳號和密碼,這樣就可以在當前控制臺上執行docker push命令將鏡像推送到hub.docker.com上了(這個網站的網絡很差,可能要登錄好幾次才能成功);
執行以下命令構建docker鏡像并推送到hub.docker.com,鏡像名為bolingcavalry/guestbook:002:
make docker-build docker-push IMG=bolingcavalry/guestbook:002
hub.docker.com的網絡狀況不是一般的差,kubebuilder電腦上的docker一定要設置鏡像加速,上述命令如果遭遇超時失敗,請重試幾次,此外,構建過程中還會下載諸多go模塊的依賴,也需要您耐心等待,也很容易遇到網絡問題,需要多次重試,所以,最好是使用局域網內搭建的Habor服務;
最終,命令執行成功后輸出如下:
[root@kubebuilder helloworld]# make docker-build docker-push IMG=bolingcavalry/guestbook:002
build會鏈接國外網站,需要注意翻墻
鏡像推送上去后,可以查看鏡像信息
$ make docker-build docker-push IMG=127.0.0.1:5000/guesstbook:v1 /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..." go fmt ./... go vet ./... /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases mkdir -p /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin test -f /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin/setup-envtest.sh || curl -sSLo /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.0/hack/setup-envtest.sh source /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin/setup-envtest.sh; fetch_envtest_tools /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin; setup_envtest_env /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin; go test ./... -coverprofile cover.out fetching envtest tools@1.19.2 (into '/Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/testbin') x bin/ x bin/etcd x bin/kubectl x bin/kube-apiserver setting up env vars ? github.com/kaliarch/imoc-operator [no test files] ? github.com/kaliarch/imoc-operator/api/v1 [no test files] ok github.com/kaliarch/imoc-operator/controllers 12.959s coverage: 0.0% of statements docker build -t 127.0.0.1:5000/guesstbook:v1 . Sending build context to Docker daemon 335.5MB Step 1/14 : FROM golang:1.15 as builder 1.15: Pulling from library/golang b9a857cbf04d: Pull complete d557ee20540b: Pull complete 3b9ca4f00c2e: Pull complete 667fd949ed93: Pull complete 547cc43be03d: Pull complete 0977886e8147: Pull complete cceccf7c7738: Pull complete Digest: sha256:de97bab9325c4c3904f8f7fec8eb469169a1d247bdc97dcab38c2c75cf4b4c5d Status: Downloaded newer image for golang:1.15 ---> 5f46b413e8f5 Step 2/14 : WORKDIR /workspace ---> Running in 597efa584096 Removing intermediate container 597efa584096 ---> a21979056316 Step 3/14 : COPY go.mod go.mod ---> b6c4b03d5126 Step 4/14 : COPY go.sum go.sum ---> f1af7c95cdc8 Step 5/14 : RUN go mod download ---> Running in baf57375b805 Removing intermediate container baf57375b805 ---> 62e488ee06f5 Step 6/14 : COPY main.go main.go ---> 72c3d023e770 Step 7/14 : COPY api/ api/ ---> b164eb864a85 Step 8/14 : COPY controllers/ controllers/ ---> 843af6a782ec Step 9/14 : RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go ---> Running in af2881daee7c Removing intermediate container af2881daee7c ---> cf6ef1542da6 Step 10/14 : FROM gcr.io/distroless/static:nonroot nonroot: Pulling from distroless/static 9e4425256ce4: Pull complete Digest: sha256:b89b98ea1f5bc6e0b48c8be6803a155b2a3532ac6f1e9508a8bcbf99885a9152 Status: Downloaded newer image for gcr.io/distroless/static:nonroot ---> 88055b6758df Step 11/14 : WORKDIR / ---> Running in 35900ca6d19f Removing intermediate container 35900ca6d19f ---> 902a3991fa3b Step 12/14 : COPY --from=builder /workspace/manager . ---> 5af066bf1214 Step 13/14 : USER 65532:65532 ---> Running in b44fbfb3c52b Removing intermediate container b44fbfb3c52b ---> 6ca11554d8fa Step 14/14 : ENTRYPOINT ["/manager"] ---> Running in 716538bf799a Removing intermediate container 716538bf799a ---> a98e090c1e68 Successfully built a98e090c1e68 Successfully tagged 127.0.0.1:5000/guesstbook:v1 docker push 127.0.0.1:5000/guesstbook:v1 The push refers to repository [127.0.0.1:5000/guesstbook] babc932481e7: Pushed 8651333b21e7: Pushed v1: digest: sha256:5dbb4e549c0dff1a4edba19ea5a35f9e21deeabe2fcefbc6b6358fb849dd61e2 size: 739 $ curl -XGET http://127.0.0.1:5000/v2/_catalog {"repositories":["guesstbook"]}
部署controller 鏡像到集群內
$ make deploy IMG=127.0.0.1:5000/guesstbook:v1 /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases cd config/manager && /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/kustomize edit set image controller=127.0.0.1:5000/guesstbook:v1 /Users/xuel/workspace/goworkspace/src/github.com/kaliarch/imoc-operator/bin/kustomize build config/default | kubectl apply -f - namespace/imoc-operator-system created customresourcedefinition.apiextensions.k8s.io/guestbooks.webapp.com.bolingcavalry configured role.rbac.authorization.k8s.io/imoc-operator-leader-election-role created clusterrole.rbac.authorization.k8s.io/imoc-operator-manager-role created clusterrole.rbac.authorization.k8s.io/imoc-operator-metrics-reader created clusterrole.rbac.authorization.k8s.io/imoc-operator-proxy-role created rolebinding.rbac.authorization.k8s.io/imoc-operator-leader-election-rolebinding created clusterrolebinding.rbac.authorization.k8s.io/imoc-operator-manager-rolebinding created clusterrolebinding.rbac.authorization.k8s.io/imoc-operator-proxy-rolebinding created configmap/imoc-operator-manager-config created service/imoc-operator-controller-manager-metrics-service created deployment.apps/imoc-operator-controller-manager created
查看部署進集群的控制器
查看鏡像拉取異常,
在此使用阿里云鏡像倉庫
$ sudo docker login --username=1123845260@qq.com registry.cn-shanghai.aliyuncs.com $ sudo docker pull registry.cn-shanghai.aliyuncs.com/kaliarch/slate:[鏡像版本號] $ sudo docker login --username=1123845260@qq.com registry.cn-shanghai.aliyuncs.com $ sudo docker tag [ImageId] registry.cn-shanghai.aliyuncs.com/kaliarch/slate:[鏡像版本號] $ sudo docker push registry.cn-shanghai.aliyuncs.com/kaliarch/slate:[鏡像版本號] 1. 登陸阿里云鏡像倉庫 密碼:阿里控制臺賬號 2. 修改tag docker tag 127.0.0.1:5000/guesstbook:v1 registry.cn-shanghai.aliyuncs.com/kaliarch/guesstbook:v1 3. 推送鏡像 docker push registry.cn-shanghai.aliyuncs.com/kaliarch/guesstbook:v1
重新部署
make deploy IMG=registry.cn-shanghai.aliyuncs.com/kaliarch/guesstbook:v1
查看已經成功運行
查看詳細信息
顯示這個pod實際上有兩個容器,用kubectl describe命令細看,分別是kube-rbac-proxy和manager
查看日志
kubectl logs -f imoc-operator-controller-manager-648b4877c6-4bpp9 -n imoc-operator-system -c manager
四 清理
4.1 清理kubebuilder
make uninstall
4.2 清理kind
kind delete cluster
五 其他
通過了解,我們可以看到 Kubebuilder 提供的功能對于快速編寫 CRD 和 Controller 是十分有幫助的,無論是云原生中知名的開源項目例如 Istio、Knative 等知名項目還是各種自定義 Operators,都大量使用了 CRD,將各種組件抽象為 CRD,據此可以將為自己業務編寫對應的CRD,使得業務更加生于云長于云。
參考鏈接
https://cloudnative.to/kubebuilder/quick-start.html
https://www.cnblogs.com/alisystemsoftware/p/11580202.html
https://blog.csdn.net/boling_cavalry/article/details/113089414
https://zhuanlan.zhihu.com/p/83957726
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。