https://www.qikqiak.com/post/k8s-operator-101/html
在 Kubernetes 的監控方案中咱們常常會使用到一個Promethues Operator的項目,該項目可讓咱們更加方便的去使用 Prometheus,而不須要直接去使用最原始的一些資源對象,好比 Pod、Deployment,隨着 Prometheus Operator 項目的成功,CoreOS 公司開源了一個比較厲害的工具:Operator Framework,該工具可讓開發人員更加容易的開發 Operator 應用。node
在本篇文章中咱們會爲你們介紹一個簡單示例來演示如何使用 Operator Framework 框架來開發一個 Operator 應用。nginx
Kubernetes Operator
Operator 是由 CoreOS 開發的,用來擴展 Kubernetes API,特定的應用程序控制器,它用來建立、配置和管理複雜的有狀態應用,如數據庫、緩存和監控系統。Operator 基於 Kubernetes 的資源和控制器概念之上構建,但同時又包含了應用程序特定的領域知識。建立Operator 的關鍵是CRD(自定義資源)的設計。git
Kubernetes 1.7 版本以來就引入了自定義控制器的概念,該功能可讓開發人員擴展添加新功能,更新現有的功能,而且能夠自動執行一些管理任務,這些自定義的控制器就像 Kubernetes 原生的組件同樣,Operator 直接使用 Kubernetes API進行開發,也就是說他們能夠根據這些控制器內部編寫的自定義規則來監控集羣、更改 Pods/Services、對正在運行的應用進行擴縮容。github
Operator Framework
Operator Framework 一樣也是 CoreOS 開源的一個用於快速開發 Operator 的工具包,該框架包含兩個主要的部分:golang
Operator SDK: 無需瞭解複雜的 Kubernetes API 特性,便可讓你根據你本身的專業知識構建一個 Operator 應用。
Operator Lifecycle Manager OLM: 幫助你安裝、更新和管理跨集羣的運行中的全部 Operator(以及他們的相關服務)
operator sdk
operator sdk
Workflow
Operator SDK 提供如下工做流來開發一個新的 Operator:docker
這裏咱們將建立一個名爲 AppService 的 CRD 資源對象,而後定義以下的資源清單進行應用部署:數據庫
apiVersion: app.example.com/v1
kind: AppService
metadata:
name: nginx-app
spec:
size: 2
image: nginx:1.7.9
ports:
- port: 80
targetPort: 80
nodePort: 30002
經過這裏的自定義的 AppService 資源對象去建立副本數爲2的 Pod,而後經過 nodePort=30002 的端口去暴露服務,接下來咱們就來一步一步的實現咱們這裏的這個簡單的 Operator 應用。json
開發環境
環境需求
要開發 Operator 天然 Kubernetes 集羣是少不了的,還須要 Golang 的環境,這裏的安裝就很少說了,而後還須要一個 Go 語言的依賴管理工具包:dep,因爲 Operator SDK 是使用的 dep 該工具包,因此須要咱們提早安裝好,能夠查看資料:https://github.com/golang/dep,另一個須要說明的是,因爲 dep 去安裝的時候須要去谷歌的網站拉取不少代碼,因此正常狀況下的話是會失敗的,須要作什麼工做你們應該清楚吧?要科學。api
安裝 operator-sdk
operator sdk 安裝方法很是多,咱們能夠直接在 github 上面下載須要使用的版本,而後放置到 PATH 環境下面便可,固然也能夠將源碼 clone 到本地手動編譯安裝便可,若是你是 Mac,固然還可使用經常使用的 brew 工具進行安裝:
$ brew install operator-sdk
......
$ operator-sdk version
operator-sdk version: v0.7.0
$ go version
go version go1.11.4 darwin/amd64
咱們這裏使用的 sdk 版本是v0.7.0,其餘安裝方法能夠參考文檔:https://github.com/operator-framework/operator-sdk/blob/master/doc/user/install-operator-sdk.md
演示
建立新項目
環境準備好了,接下來就可使用 operator-sdk 直接建立一個新的項目了,命令格式爲: operator-sdk new
按照上面咱們預先定義的 CRD 資源清單,咱們這裏能夠這樣建立:
$ mkdir -p operator-learning
$ cd operator-learning && export GOPATH=$PWD
$ mkdir -p $GOPATH/src/github.com/cnych
$ cd $GOPATH/src/github.com/cnych
$ operator-sdk new opdemo
......
......
$ cd opdemo && tree -L 2
.
├── Gopkg.lock
├── Gopkg.toml
├── build
│ ├── Dockerfile
│ ├── _output
│ └── bin
├── cmd
│ └── manager
├── deploy
│ ├── crds
│ ├── operator.yaml
│ ├── role.yaml
│ ├── role_binding.yaml
│ └── service_account.yaml
├── pkg
│ ├── apis
│ └── controller
├── vendor
│ ├── cloud.google.com
│ ├── contrib.go.opencensus.io
│ ├── github.com
│ ├── go.opencensus.io
│ ├── go.uber.org
│ ├── golang.org
│ ├── google.golang.org
│ ├── gopkg.in
│ ├── k8s.io
│ └── sigs.k8s.io
└── version
└── version.go
23 directories, 8 files
到這裏一個全新的 Operator 項目就新建完成了。
項目結構
使用operator-sdk new命令建立新的 Operator 項目後,項目目錄就包含了不少生成的文件夾和文件。
Gopkg.toml Gopkg.lock — Go Dep 清單,用來描述當前 Operator 的依賴包。
cmd - 包含 main.go 文件,使用 operator-sdk API 初始化和啓動當前 Operator 的入口。
deploy - 包含一組用於在 Kubernetes 集羣上進行部署的通用的 Kubernetes 資源清單文件。
pkg/apis - 包含定義的 API 和自定義資源(CRD)的目錄樹,這些文件容許 sdk 爲 CRD 生成代碼並註冊對應的類型,以便正確解碼自定義資源對象。
pkg/controller - 用於編寫全部的操做業務邏輯的地方
vendor - golang vendor 文件夾,其中包含知足當前項目的全部外部依賴包,經過 go dep 管理該目錄。
咱們主要須要編寫的是pkg目錄下面的 api 定義以及對應的 controller 實現。
添加 API
接下來爲咱們的自定義資源添加一個新的 API,按照上面咱們預約義的資源清單文件,在 Operator 相關根目錄下面執行以下命令:
$ operator-sdk add api --api-version=app.example.com/v1 --kind=AppService
添加完成後,咱們能夠看到相似於下面的這樣項目結構: operator project layout
添加控制器
上面咱們添加自定義的 API,接下來能夠添加對應的自定義 API 的具體實現 Controller,一樣在項目根目錄下面執行以下命令:
$ operator-sdk add controller --api-version=app.example.com/v1 --kind=AppService
這樣整個 Operator 項目的腳手架就已經搭建完成了,接下來就是具體的實現了。
自定義 API
打開源文件pkg/apis/app/v1/appservice_types.go,須要咱們根據咱們的需求去自定義結構體 AppServiceSpec,咱們最上面預約義的資源清單中就有 size、image、ports 這些屬性,全部咱們須要用到的屬性都須要在這個結構體中進行定義:
type AppServiceSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html
Size *int32 json:"size"
Image string json:"image"
Resources corev1.ResourceRequirements json:"resources,omitempty"
Envs []corev1.EnvVar json:"envs,omitempty"
Ports []corev1.ServicePort json:"ports,omitempty"
}
代碼中會涉及到一些包名的導入,因爲包名較多,因此咱們會使用一些別名進行區分,主要的包含下面幾個:
import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
appv1 "github.com/cnych/opdemo/pkg/apis/app/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
這裏的 resources、envs、ports 的定義都是直接引用的"k8s.io/api/core/v1"中定義的結構體,並且須要注意的是咱們這裏使用的是ServicePort,而不是像傳統的 Pod 中定義的 ContanerPort,這是由於咱們的資源清單中不只要描述容器的 Port,還要描述 Service 的 Port。
而後一個比較重要的結構體AppServiceStatus用來描述資源的狀態,固然咱們能夠根據須要去自定義狀態的描述,我這裏就偷懶直接使用 Deployment 的狀態了:
type AppServiceStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book.kubebuilder.io/beyond_basics/generating_crd.html
appsv1.DeploymentStatus json:",inline"
}
定義完成後,在項目根目錄下面執行以下命令:
$ operator-sdk generate k8s
改命令是用來根據咱們自定義的 API 描述來自動生成一些代碼,目錄pkg/apis/app/v1/下面以zz_generated開頭的文件就是自動生成的代碼,裏面的內容並不須要咱們去手動編寫。
這樣咱們就算完成了對自定義資源對象的 API 的聲明。
實現業務邏輯
上面 API 描述聲明完成了,接下來就須要咱們來進行具體的業務邏輯實現了,編寫具體的 controller 實現,打開源文件pkg/controller/appservice/appservice_controller.go,須要咱們去更改的地方也不是不少,核心的就是Reconcile方法,該方法就是去不斷的 watch 資源的狀態,而後根據狀態的不一樣去實現各類操做邏輯,核心代碼以下:
func (r *ReconcileAppService) Reconcile(request reconcile.Request) (reconcile.Result, error) {
reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
reqLogger.Info("Reconciling AppService")
// Fetch the AppService instance instance := &appv1.AppService{} err := r.client.Get(context.TODO(), request.NamespacedName, instance) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers. // Return and don't requeue return reconcile.Result{}, nil } // Error reading the object - requeue the request. return reconcile.Result{}, err } if instance.DeletionTimestamp != nil { return reconcile.Result{}, err } // 若是不存在,則建立關聯資源 // 若是存在,判斷是否須要更新 // 若是須要更新,則直接更新 // 若是不須要更新,則正常返回 deploy := &appsv1.Deployment{} if err := r.client.Get(context.TODO(), request.NamespacedName, deploy); err != nil && errors.IsNotFound(err) { // 建立關聯資源 // 1. 建立 Deploy deploy := resources.NewDeploy(instance) if err := r.client.Create(context.TODO(), deploy); err != nil { return reconcile.Result{}, err } // 2. 建立 Service service := resources.NewService(instance) if err := r.client.Create(context.TODO(), service); err != nil { return reconcile.Result{}, err } // 3. 關聯 Annotations data, _ := json.Marshal(instance.Spec) if instance.Annotations != nil { instance.Annotations["spec"] = string(data) } else { instance.Annotations = map[string]string{"spec": string(data)} } if err := r.client.Update(context.TODO(), instance); err != nil { return reconcile.Result{}, nil } return reconcile.Result{}, nil } oldspec := appv1.AppServiceSpec{} if err := json.Unmarshal([]byte(instance.Annotations["spec"]), oldspec); err != nil { return reconcile.Result{}, err } if !reflect.DeepEqual(instance.Spec, oldspec) { // 更新關聯資源 newDeploy := resources.NewDeploy(instance) oldDeploy := &appsv1.Deployment{} if err := r.client.Get(context.TODO(), request.NamespacedName, oldDeploy); err != nil { return reconcile.Result{}, err } oldDeploy.Spec = newDeploy.Spec if err := r.client.Update(context.TODO(), oldDeploy); err != nil { return reconcile.Result{}, err } newService := resources.NewService(instance) oldService := &corev1.Service{} if err := r.client.Get(context.TODO(), request.NamespacedName, oldService); err != nil { return reconcile.Result{}, err } oldService.Spec = newService.Spec if err := r.client.Update(context.TODO(), oldService); err != nil { return reconcile.Result{}, err } return reconcile.Result{}, nil } return reconcile.Result{}, nil
}
上面就是業務邏輯實現的核心代碼,邏輯很簡單,就是去判斷資源是否存在,不存在,則直接建立新的資源,建立新的資源除了須要建立 Deployment 資源外,還須要建立 Service 資源對象,由於這就是咱們的需求,固然你還能夠本身去擴展,好比在建立一個 Ingress 對象。更新也是同樣的,去對比新舊對象的聲明是否一致,不一致則須要更新,一樣的,兩種資源都須要更新的。
另外兩個核心的方法就是上面的resources.NewDeploy(instance)和resources.NewService(instance)方法,這兩個方法實現邏輯也很簡單,就是根據 CRD 中的聲明去填充 Deployment 和 Service 資源對象的 Spec 對象便可。
NewDeploy 方法實現以下:
func NewDeploy(app appv1.AppService) appsv1.Deployment {
labels := map[string]string{"app": app.Name}
selector := &metav1.LabelSelector{MatchLabels: labels}
return &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Name: app.Name,
Namespace: app.Namespace,
OwnerReferences: []metav1.OwnerReference{ *metav1.NewControllerRef(app, schema.GroupVersionKind{ Group: v1.SchemeGroupVersion.Group, Version: v1.SchemeGroupVersion.Version, Kind: "AppService", }), }, }, Spec: appsv1.DeploymentSpec{ Replicas: app.Spec.Size, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, Spec: corev1.PodSpec{ Containers: newContainers(app), }, }, Selector: selector, }, }
}
func newContainers(app *v1.AppService) []corev1.Container {
containerPorts := []corev1.ContainerPort{}
for _, svcPort := range app.Spec.Ports {
cport := corev1.ContainerPort{}
cport.ContainerPort = svcPort.TargetPort.IntVal
containerPorts = append(containerPorts, cport)
}
return []corev1.Container{
{
Name: app.Name,
Image: app.Spec.Image,
Resources: app.Spec.Resources,
Ports: containerPorts,
ImagePullPolicy: corev1.PullIfNotPresent,
Env: app.Spec.Envs,
},
}
}
newService 對應的方法實現以下:
func NewService(app v1.AppService) corev1.Service {
return &corev1.Service {
TypeMeta: metav1.TypeMeta {
Kind: "Service",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: app.Name,
Namespace: app.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(app, schema.GroupVersionKind{
Group: v1.SchemeGroupVersion.Group,
Version: v1.SchemeGroupVersion.Version,
Kind: "AppService",
}),
},
},
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeNodePort,
Ports: app.Spec.Ports,
Selector: map[string]string{
"app": app.Name,
},
},
}
}
這樣咱們就實現了 AppService 這種資源對象的業務邏輯。
調試
若是咱們本地有一個能夠訪問的 Kubernetes 集羣,咱們也能夠直接進行調試,在本地用戶~/.kube/config文件中配置集羣訪問信息,下面的信息代表能夠訪問 Kubernetes 集羣:
$ kubectl cluster-info
Kubernetes master is running at https://ydzs-master:6443
KubeDNS is running at https://ydzs-master:6443/api/v1/namespaces/kube-system/services/kube-dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
首先,在集羣中安裝 CRD 對象:
$ kubectl create -f deploy/crds/app_v1_appservice_crd.yaml
customresourcedefinition "appservices.app.example.com" created
$ kubectl get crd
NAME AGE
appservices.app.example.com
......
當咱們經過kubectl get crd命令獲取到咱們定義的 CRD 資源對象,就證實咱們定義的 CRD 安裝成功了。其實如今只是 CRD 的這個聲明安裝成功了,可是咱們這個 CRD 的具體業務邏輯實現方式還在咱們本地,並無部署到集羣之中,咱們能夠經過下面的命令來在本地項目中啓動 Operator 的調試:
$ operator-sdk up local
INFO[0000] Running the operator locally.
INFO[0000] Using namespace default.
{"level":"info","ts":1559207203.964137,"logger":"cmd","msg":"Go Version: go1.11.4"}
{"level":"info","ts":1559207203.964192,"logger":"cmd","msg":"Go OS/Arch: darwin/amd64"}
{"level":"info","ts":1559207203.9641972,"logger":"cmd","msg":"Version of operator-sdk: v0.7.0"}
{"level":"info","ts":1559207203.965905,"logger":"leader","msg":"Trying to become the leader."}
{"level":"info","ts":1559207203.965945,"logger":"leader","msg":"Skipping leader election; not running in a cluster."}
{"level":"info","ts":1559207206.928867,"logger":"cmd","msg":"Registering Components."}
{"level":"info","ts":1559207206.929077,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"appservice-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1559207206.9292521,"logger":"kubebuilder.controller","msg":"Starting EventSource","controller":"appservice-controller","source":"kind source: /, Kind="}
{"level":"info","ts":1559207209.622659,"logger":"cmd","msg":"failed to initialize service object for metrics: OPERATOR_NAME must be set"}
{"level":"info","ts":1559207209.622693,"logger":"cmd","msg":"Starting the Cmd."}
{"level":"info","ts":1559207209.7236018,"logger":"kubebuilder.controller","msg":"Starting Controller","controller":"appservice-controller"}
{"level":"info","ts":1559207209.8284118,"logger":"kubebuilder.controller","msg":"Starting workers","controller":"appservice-controller","worker count":1}
上面的命令會在本地運行 Operator 應用,經過~/.kube/config去關聯集羣信息,如今咱們去添加一個 AppService 類型的資源而後觀察本地 Operator 的變化狀況,資源清單文件就是咱們上面預約義的(deploy/crds/app_v1_appservice_cr.yaml)
apiVersion: app.example.com/v1
kind: AppService
metadata:
name: nginx-app
spec:
size: 2
image: nginx:1.7.9
ports:
- port: 80
targetPort: 80
nodePort: 30002
直接建立這個資源對象:
$ kubectl create -f deploy/crds/app_v1_appservice_cr.yaml
appservice "nginx-app" created
咱們能夠看到咱們的應用建立成功了,這個時候查看 Operator 的調試窗口會有以下的信息出現:
......
{"level":"info","ts":1559207416.670523,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
{"level":"info","ts":1559207417.004226,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
{"level":"info","ts":1559207417.004331,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
{"level":"info","ts":1559207418.33779,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
{"level":"info","ts":1559207418.951193,"logger":"controller_appservice","msg":"Reconciling AppService","Request.Namespace":"default","Request.Name":"nginx-app"}
......
而後咱們能夠去查看集羣中是否有符合咱們預期的資源出現:
$ kubectl get AppService
NAME AGE
nginx-app
$ kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-app 2 2 2 2
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1
nginx-app NodePort 10.108.227.5
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-app-76b6449498-2j82j 1/1 Running 0
nginx-app-76b6449498-m4h58 1/1 Running 0
看到了吧,咱們定義了兩個副本(size=2),這裏就出現了兩個 Pod,還有一個 NodePort=30002 的 Service 對象,咱們能夠經過該端口去訪問下應用: crd nginx app demo
若是應用在安裝過程當中出現了任何問題,咱們均可以經過本地的 Operator 調試窗口找到有用的信息,而後調試修改便可。
清理:
$ kubectl delete -f deploy/crds/app_v1_appservice_crd.yaml
$ kubectl delete -f deploy/crds/app_v1_appservice_cr.yaml
部署
自定義的資源對象如今測試經過了,可是若是咱們將本地的operator-sdk up local命令終止掉,咱們能夠猜測到就沒辦法處理 AppService 資源對象的一些操做了,因此咱們須要將咱們的業務邏輯實現部署到集羣中去。
執行下面的命令構建 Operator 應用打包成 Docker 鏡像:
$ operator-sdk build cnych/opdemo
INFO[0002] Building Docker image cnych/opdemo
Sending build context to Docker daemon 400.7MB
Step 1/7 : FROM registry.access.redhat.com/ubi7-dev-preview/ubi-minimal:7.6
......
Successfully built a8cde91be6ab
Successfully tagged cnych/opdemo:latest
INFO[0053] Operator build complete.
鏡像構建成功後,推送到 docker hub:
$ docker push cnych/opdemo
鏡像推送成功後,使用上面的鏡像地址更新 Operator 的資源清單:
$ sed -i 's|REPLACE_IMAGE|cnych/opdemo|g' deploy/operator.yaml
$ sed -i "" 's|REPLACE_IMAGE|cnych/opdemo|g' deploy/operator.yaml
如今 Operator 的資源清單文件準備好了,而後建立對應的 RBAC 的對象:
$ kubectl create -f deploy/service_account.yaml
$ kubectl create -f deploy/role.yaml
$ kubectl create -f deploy/role_binding.yaml
權限相關聲明已經完成,接下來安裝 CRD 和 Operator:
$ kubectl apply -f deploy/crds/app_v1_appservice_crd.yaml
$ kubectl get crd
NAME CREATED AT
appservices.app.example.com 2019-05-30T17:03:32Z
......
$ kubectl create -f deploy/operator.yaml
deployment.apps/opdemo created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
opdemo-64db96d575-9vtq6 1/1 Running 0 2m2s
到這裏咱們的 CRD 和 Operator 實現都已經安裝成功了。
如今咱們再來部署咱們的 AppService 資源清單文件,如今的業務邏輯就會在上面的opdemo-64db96d575-9vtq6的 Pod 中去處理了。
$ kubectl create -f deploy/crds/app_v1_appservice_cr.yaml
appservice.app.example.com/nginx-app created
$ kubectl get appservice
NAME AGE
nginx-app 18s
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-app 2/2 2 2 24s
opdemo 1/1 1 1 5m51s
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1
nginx-app NodePort 10.106.129.82
opdemo ClusterIP 10.100.233.51
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-app-76b6449498-ffhgx 1/1 Running 0 32s
nginx-app-76b6449498-wzjq2 1/1 Running 0 32s
opdemo-64db96d575-9vtq6 1/1 Running 0 5m59s
$ kubectl describe appservice nginx-app
Name: nginx-app
Namespace: default
Labels:
Annotations: spec: {"size":2,"image":"nginx:1.7.9","resources":{},"ports":[{"protocol":"TCP","port":80,"targetPort":80,"nodePort":30002}]}
API Version: app.example.com/v1
Kind: AppService
Metadata:
Creation Timestamp: 2019-05-30T17:41:28Z
Generation: 2
Resource Version: 19666617
Self Link: /apis/app.example.com/v1/namespaces/default/appservices/nginx-app
UID: 2756f232-8302-11e9-80ca-525400cc3c00
Spec:
Image: nginx:1.7.9
Ports:
Node Port: 30002
Port: 80
Protocol: TCP
Target Port: 80
Resources:
Size: 2
Events:
而後一樣的能夠經過 30002 這個 NodePort 端口去訪問應用,到這裏應用就部署成功了。
清理
有資源清單文件,直接刪除便可:
$ kubectl delete -f deploy/crds/app_v1_appservice_cr.yaml
$ kubectl delete -f deploy/operator.yaml
$ kubectl delete -f deploy/role.yaml
$ kubectl delete -f deploy/role_binding.yaml
$ kubectl delete -f deploy/service_account.yaml
$ kubectl delete -f deploy/crds/app_v1_appservice_crd.yaml
開發
Operator SDK 爲咱們建立了一個快速啓動的代碼和相關配置,若是咱們要開始處理相關的邏輯,咱們能夠在項目中搜索TODO(user)這個註釋來實現咱們本身的邏輯,好比在個人 VSCode 環境中,看上去是這樣的: operator code todo demo
本篇文章示例代碼地址:https://github.com/cnych/opdemo
參考資料 CLI reference User Guide Developing Kubernetes Operator is now easy with Operator Framework