使用 Golang 構建你的第一個 k8s Operator
本文將展示如何使用 Operator SDK[1] 搭建一個基本的 k8s Operator。在本文中,您將瞭解如何創建一個新項目,並通過創建自定義資源定義 (CRD) 和基本控制器來添加 API。
我們將在 CRD 中添加字段,以包含一些有關期望狀態和實際狀態的信息,修改控制器以調和新資源的實例,然後將 operator 部署到 Kubernetes 集羣。
Prerequisites
-
安裝 Operator-sdk v1.5.0+
-
安裝 Kubectl v1.17.0+
-
一個 Kubernetes 集羣以及其管理訪問權限
-
安裝 Docker v3.2.2+ 版本
-
安裝 Golang v1.16.0+ 版本
Step 1: Create a project
建一個目錄,並初始化一個 operator 項目:
$ mkdir memcached-operator
$ cd memcached-operator
$ operator-sdk init --domain=example.com --repo=github.com/example/memcached-operator
Writing scaffold for you to edit...
Get controller runtime:
$ go get sigs.k8s.io/controller-runtime@v0.7.0
Update go.mod:
$ go mod tidy
Running make:
$ make
go: creating new go.mod: module tmp
Downloading sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1
go: found sigs.k8s.io/controller-tools/cmd/controller-gen in sigs.k8s.io/controller-tools v0.4.1
/Users/username/workspace/projects/memcached-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go build -o bin/manager main.go
operator-sdk init — domain=example.com — repo=github.com/example/memcached-operator
- domin 用於 operator 創建的任何 API Group,因此這裏的 API Group 是 *.example.com。
大家可能熟悉的一個 API Group 是 rbac.authorization.k8s.io,創建 RBAC 資源(如 ClusterRoles 和 ClusterBindings)的功能通常就設置在 Kubernetes 集羣上。operator-sdk 允許您指定一個自定義域,將其附加到您定義的任何 API 組,以幫助避免名稱衝突。
這裏使用的 --repo 值只是一個示例。如果你想提交項目並保留它,可以將其設置爲你可以訪問的 Git 倉庫。
項目初始化後會生生一個 Operator 項目的殼子,我們剩下的工作就是在這個框架之上,實現 operator 的功能。
Step 2: Create an API
使用 create
命令生成 CRD 和控制器:
注意:--version 標誌針對的是操作符的 Kubernetes API 版本,而不是語義版本。因此,請勿在 --version 中使用
$ operator-sdk create api --group cache --version v1alpha1 --kind Memcached --resource=true --controller=true
Writing scaffold for you to edit...
api/v1alpha1/memcached_types.go
controllers/memcached_controller.go
Running make:
$ make
/Users/username/workspace/projects/memcached-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
go build -o bin/manager main.go
--group
是自定義資源所在的組,因此它最終會出現在 API Group "cache.example.com" 中。
--version
決定 API Group 的版本。可以使用不同的版本連續升級自定義資源。
-resource
和 --controller
標誌設置爲 "true",以便爲這兩樣東西生成腳手架。
現在我們已經有了組件的輪廓,讓我們開始用實際功能來填充它們。首先是 CRD。在 api/v1alpha1/memcached_types.go
中,您應該能看到一些定義自定義資源類型規格和狀態的結構:
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec 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 Memcached. Edit Memcached_types.go to remove/update
Foo string `json:"foo,omitempty"`
}
// MemcachedStatus defines the observed state of Memcached
type MemcachedStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
請注意文件頂部的信息。該文件是由 Operator-SDK 搭建的腳手架,我們可以根據自己的項目需求去修改。Spec
包含指定資源所需狀態的信息,而 Status 則包含系統的可觀測狀態,尤其是其他資源可能想要使用的信息。這些 Golang 結構與 Kubernetes 用戶爲創建自定義資源類型實例而編寫的 YAML 直接對應。
讓我們爲類型添加一些基本字段。
// MemcachedSpec defines the desired state of Memcached
type MemcachedSpec struct {
// +kubebuilder:validation:Minimum=0
// Size is the size of the memcached deployment
Size int32 `json:"size"`
}
// MemcachedStatus defines the observed state of Memcached
type MemcachedStatus struct {
// Nodes are the names of the memcached pods
Nodes []string `json:"nodes"`
}
Size
是一個整數,用於確定 Memcached 集羣中的節點數量。我們在 Status 中添加了一個字符串數組,用於存儲集羣中包含的節點的 IP 地址。需要注意的是,這個特定的實現方式只是一個示例。注意父 Memcached 結構 Status 字段的 Kubebuilder 子資源標記。這將在生成的 CRD 清單中添加 Kubernetes 狀態子資源。這樣,控制器就可以只更新狀態字段,而無需更新整個對象,從而提高性能。
// Memcached is the Schema for the memcacheds API
// +kubebuilder:subresource:status
type Memcached struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec MemcachedSpec `json:"spec,omitempty"`
Status MemcachedStatus `json:"status,omitempty"`
}
更改 types.go 文件後,應始終在項目根目錄下運行以下命令:
make generate
此 make target 會調用 controller-gen 更新 api/v1alpha1/zz_generated.deepcopy.go,使其包含您剛剛添加的字段的必要實現。完成更新後,我們應運行以下命令爲 CRD 生成 YAML 清單:
$ make manifests
會生成以下文件:
New:
config/crd/bases/cache.example.com_memcacheds.yaml
config/rbac/role.yaml
config/crd/bases/cache.example.com_memcacheds.yaml 是 memcached CRD 的清單。config/rbac/role.yaml 是 RBAC 清單,其中包含控制器所需的管理 memcached 類型的權限。
Step 3: Create a controller
讓我們看看 controllers/memcached_controller.go 中目前包含的內容:
// 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 Memcached 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.7.0/pkg/reconcile
func (r *MemcachedReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = r.Log.WithValues("memcached", req.NamespacedName)
// your logic here
return ctrl.Result{}, nil
}
Reconcile
方法負責將自定義資源狀態中包含的期望狀態與系統上運行的實際狀態進行覈對,也是實現控制器邏輯的主要部分。實現調和循環的具體細節超出了本教程的範圍,將在進階的文章中介紹。現在,請用此參考實現替換 controllers/memcached_controller.go。注意,如果你在初始化項目時指定了不同的 repo,可能需要更改 github.com/example/memcached-operator/api/v1alpha1 的導入路徑,以便正確指向你定義 memcached_types.goin 的目錄。粘貼後,確保重新生成清單:
make manifests
Step 4: Build and deploy your operator
現在您已經填寫了所有需要的組件,是時候部署 operator 了!一般來說,有三種不同的方法來部署:
-
作爲在 Kubernetes 集羣外執行的程序。這樣做可能是出於開發目的,也可能是出於集羣中數據的安全考慮。Makefile 包含目標 make install run,用於在本地運行運算符,但這種方法不是這裏的重點。
-
作爲 Kubernetes 集羣內的部署運行。這就是我現在要向你展示的內容。
-
由 operator 生命週期管理器部署和管理。建議在生產中使用,因爲 OLM 具有管理和升級運行中的 operator 的附加功能。
現在,先構建並推送控制器的 Docker image。本示例使用了基礎 Dockerfile,但你也可以根據自己的需要進行修改。我使用 Docker Hub 作爲鏡像倉庫,但你能推 / 拉訪問的任何倉庫都可以。
$ export USERNAME=<docker-username>
$ make docker-build docker-push IMG=docker.io/$USERNAME/memcached-operator:v1.0.0
如果出現如下錯誤,則可能需要獲取其他依賴項。運行建議的命令下載依賴項。
/controllers: package k8s.io/api/apps/v1 imported from implicitly required module; to add missing requirements, run:
go get k8s.io/api/apps/v1@v0.19.2
我們還可以在 Makefile 中設置鏡像的默認名稱和標籤。鏡像構建完成後,就可以部署 operator 了:
$ make deploy IMG=docker.io/$USERNAME/memcached-operator:v1.0.0
它使用 config/ 中的清單創建 CRD,在 Pod 中部署控制器,創建控制器管理 CRD 所需的 RBAC 角色,並將其分配給控制器。讓我們來看看。我們的 CRD 類型爲 memcacheds.cache.example.com:
$ kubectl get crds
NAME CREATED AT
catalogsources.operators.coreos.com 2021-01-22T00:13:22Z
clusterserviceversions.operators.coreos.com 2021-01-22T00:13:22Z
installplans.operators.coreos.com 2021-01-22T00:13:22Z
memcacheds.cache.example.com 2021-02-06T00:52:38Z
operatorgroups.operators.coreos.com 2021-01-22T00:13:22Z
rbacsyncs.ibm.com 2021-01-22T00:08:59Z
subscriptions.operators.coreos.com 2021-01-22T00:13:22Z
運行控制器的部署和 Pod:
$ kubectl --namespace memcached-operator-system get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
memcached-operator-controller-manager 1/1 1 1 2m18s
$ kubectl --namespace memcached-operator-system get pods
NAME READY STATUS RESTARTS AGE
memcached-operator-controller-manager-76b588bbb5-wvl7b 2/2 Running 0 2m44s
當 pod 開始運行,我們的 Operator 就可以使用了。編輯 config/samples/cache_v1alpha1_memcached.yaml 中的示例 YAML,加入一個大小整數,就像在自定義資源規範中定義的那樣:
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
name: memcached-sample
spec:
size: 1
然後創建一個自定義資源的新實例:
$ kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
memcached.cache.example.com/memcached-sample created
再看看新的自定義資源和控制器在後臺創建的對象:
$ kubectl get memcached
NAME AGE
memcached-sample 18s
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
memcached-sample 1/1 1 1 33s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
memcached-sample-9b765dfc8-2hvf8 1/1 Running 0 44s
如果查看一下 Memcached 對象,就會發現狀態已用運行節點的名稱更新:
$ kubectl get memcached memcached-sample -o yaml
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"cache.example.com/v1alpha1","kind":"Memcached","metadata":{"annotations":{},"name":"memcached-sample","namespace":"default"},"spec":{"size":1}}
creationTimestamp: "2021-03-29T19:22:53Z"
generation: 1
managedFields:
- apiVersion: cache.example.com/v1alpha1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
f:spec:
.: {}
f:size: {}
manager: kubectl
operation: Update
time: "2021-03-29T19:22:53Z"
- apiVersion: cache.example.com/v1alpha1
fieldsType: FieldsV1
fieldsV1:
f:status:
.: {}
f:nodes: {}
manager: manager
operation: Update
time: "2021-03-29T19:22:58Z"
name: memcached-sample
namespace: default
resourceVersion: "1374"
uid: 63c7b1b1-1a75-49e6-8132-2164807a1b78
spec:
size: 1
status:
nodes:
- memcached-sample-9b765dfc8-2hvf8
要查看控制器的運行情況,可以在集羣中添加另一個節點。將 config/samples/cache_v1alpha1_memcached.yaml 中的大小改爲 2,然後運行:
$ kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
memcached.cache.example.com/memcached-sample configured
查看創建的新 pod:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
memcached-sample-9b765dfc8-2hvf8 1/1 Running 0 50s
memcached-sample-9b765dfc8-jdhlq 1/1 Running 0 3s
然後看到 Memcached 對象再次更新爲新 pod 的名稱:
$ kubectl get memcached memcached-sample -o yaml
apiVersion: cache.example.com/v1alpha1
kind: Memcached
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"cache.example.com/v1alpha1","kind":"Memcached","metadata":{"annotations":{},"name":"memcached-sample","namespace":"default"},"spec":{"size":1}}
creationTimestamp: "2021-03-29T19:22:53Z"
generation: 2
managedFields:
- apiVersion: cache.example.com/v1alpha1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
f:spec:
.: {}
f:size: {}
manager: kubectl
operation: Update
time: "2021-03-29T19:22:53Z"
- apiVersion: cache.example.com/v1alpha1
fieldsType: FieldsV1
fieldsV1:
f:status:
.: {}
f:nodes: {}
manager: manager
operation: Update
time: "2021-03-29T19:22:58Z"
name: memcached-sample
namespace: default
resourceVersion: "1712"
uid: 63c7b1b1-1a75-49e6-8132-2164807a1b78
spec:
size: 2
status:
nodes:
- memcached-sample-9b765dfc8-2hvf8
- memcached-sample-9b765dfc8-jdhlq
Step 5: Cleanup
完成後,可以通過運行這些命令來清理已部署的 operator:
$ kubectl delete memcached memcached-sample
$ make undeploy
Debugging
查看 operator 管理器日誌:
$ kubectl logs deployment.apps/memcached-operator-controller-manager -n memcached-operator-system -c manager
參考資料
[1]
operator sdk: https://github.com/operator-framework/operator-sdk
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/kSxetwU8oSgWeyCmCR6Dpw