iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 20
0
DevOps

其實我真的沒想過要利用研替剩餘的 30 天分享那些年 On-premise Container & Kubernetes 經驗系列 第 20

[Day20] 開發自定義控制器前,需要先了解的東西 Part1

前言

由於 Kubernetes 控制器是主動調和(Reconciliation)資源過程的程式,它會透過與 API 伺服器溝通,以監視叢集的資源狀態,並依據 API 物件的當前狀態,嘗試將其推向預期狀態。而本系列文章是說明如何採用官方 API client 函式庫來編寫 Kubernetes 自定義控制器。因此需要在開發之前,先了解會使用到的函式庫與工具等等。

Kubernetes 組織在 GitHub 上,維護了許多可以使用的程式函式庫,如: api、client 與 api-machinery 等等都被用於不同的功能實現。而要使用這些函式庫只需要以k8s.io/..方式,在 Go 語言的專案下導入即可。在接下來個小部分中,我將介紹一些會用於開發自定義控制器的 API 相關函式庫。

這部分包含以下:

  • API Machinery
  • API
  • gengo
  • code-generator

apimachinery

API Machinery 是定義 API 級別的 Scheme、類型(Typing)、編碼(Encoding)、解碼(Decoding)、驗證(Validate)、類型轉換與相關工具等等功能。當我們要實現一個新的 API 資源時,就必須透過 API Machinery 來註冊 Scheme,另外 API Machinery 也定義了 TypeMeta、ObjectMeta、ListMeta、 Labels 與 Selector 等等物件,而這些物件幾乎在每個 Kubernetes API 資源中都會使用到,比如下面 YAML 所示。

apiVersion: v1 # TypeMeta
kind: Pod # TypeMeta
metadata: # ObjectMeta
  name: memory-demo
  namespace: mem-example
  labels: # Labels
    tt: xx 
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

api

API 主要提供 Kubernetes 原生的 API 資源類型的 Scheme,這包含 Namespace、Pod 等等。該函式庫也提供了每個 API 資源類型,當前所支援的版本,如:v1、v1beta1。而每種 API 資源都依功能取向被群組化,如下圖所示。

Kubernetes API Resources

gengo

gengo 主要用於透過 Go 語言檔案產生各種系統與 API 所需的文件,比如說 Protobuf。而該專案也包含了 Set、Deep-copy、Defaulter 等等產生器(Generator),這些會被用於產生客製化 Client 函式庫。

大家在看 Kubernetes 源碼時,一定會看到這樣一段註解// Code generated by xxx. DO NOT EDIT.。事實上 Kubernetes 有許多程式碼是基於該專案產生出來的,因為 Kubernetes 有很多 API 資源類型,若每一種都寫套維護的話,會非常複雜,因此 Kubernetes 定義了一套標準(Interface 與 Scheme 等等)來維護,並透過 Generator 來產生一些程式碼。

code-generator

Code Generator 是基於 gengo 開發的程式碼產生器,主要用來實現產生 Kubernetes-style API types 的 Client、Deep-copy、Informer、Lister 等等功能的程式碼。這是因為 Go 語言中沒有泛型(Generic)概念,因此不同的 API 資源類型,若都要寫一次上述這些功能的話,會有大量重複的程式碼,因此 Kubernetes 採用定義好類型結構後,再透過該專案提供的工具產生相關程式碼。下面舉個例子。

其他語言的 Generator 可以參考 gen

假設要實作一個 LINE Bot 的 API 資源,並產生 Client 程式時,我們必需先定義結構在 Go 檔案中。然後接著用註解方式,在程式碼標示物件結構要產生程式碼。比範例會產生 Bot 物件的 client 程式碼跟 Deep-copy 方法:

package v1alpha1

import (
	"github.com/line/line-bot-sdk-go/linebot"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

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

	Spec   BotSpec   `json:"spec"`
	Status BotStatus `json:"status,omitempty"`
}

type BotExposeType string

const (
	NgrokExpose        BotExposeType = "Ngrok"
	IngressExpose      BotExposeType = "Ingress"
	LoadBalancerExpose BotExposeType = "LoadBalancer"
)

type BotExpose struct {
	Type           BotExposeType `json:"type"`
	DomainName     string        `json:"domainName"`
	LoadBalanceIPs []string      `json:"loadBalanceIPs,omitempty"`
	NgrokToken     string        `json:"ngrokToken"`
}

type BotSpec struct {
	Selector          *metav1.LabelSelector `json:"selector"`
	ChannelSecretName string                `json:"channelSecretName"`
	Expose            BotExpose             `json:"expose"`
	Version           string                `json:"version"`
	LogLevel          int                   `json:"logLevel"`
}

type BotPhase string

const (
	BotPending     BotPhase = "Pending"
	BotActive      BotPhase = "Active"
	BotFailed      BotPhase = "Failed"
	BotTerminating BotPhase = "Terminating"
)

type BotStatus struct {
	Phase          BotPhase    `json:"phase"`
	Reason         string      `json:"reason,omitempty"`
	LastUpdateTime metav1.Time `json:"lastUpdateTime"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type BotList struct {
	metav1.TypeMeta `json:",inline"`
	metav1.ListMeta `json:"metadata"`

	Items []Bot `json:"items"`
}

當完成定義與註解描述後,我們會以這樣的目錄方式放在開發專案中,其中types.go就是上述檔案。

pkg/apis
└── line
    ├── register.go
    └── v1alpha1
        ├── doc.go
        ├── register.go
        ├── types.go
        └── zz_generated.deepcopy.go

其他檔案會在後續開發時,詳細說明。

接著利用 code-generator 工具來指向 API 物件結構位置,以讓 code-generator 解析,並產生對應的程式碼。下面是執行腳本範例:

#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail

SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister" \
  github.com/kairen/line-bot-operator/pkg/generated \
  github.com/kairen/line-bot-operator/pkg/apis \
  "line:v1alpha1" \
  --output-base "$(dirname ${BASH_SOURCE})/../../../../" \
  --go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt

這邊boilerplate.go.txt為 Go 檔案的 License 內容。用於在產生程式碼時,自動塞在檔案內容的頭。

當完成後,我們會在指定輸出的目錄看到產生的程式碼,如

pkg/generated
├── clientset
│   └── versioned
│       ├── clientset.go
│       ├── doc.go
│       ├── fake
│       │   ├── clientset_generated.go
│       │   ├── doc.go
│       │   └── register.go
│       ├── scheme
│       │   ├── doc.go
│       │   └── register.go
│       └── typed
│           └── line
│               └── v1alpha1
│                   ├── bot.go
│                   ├── doc.go
│                   ├── event.go
│                   ├── eventbinding.go
│                   ├── fake
│                   │   ├── doc.go
│                   │   ├── fake_bot.go
│                   │   ├── fake_event.go
│                   │   ├── fake_eventbinding.go
│                   │   └── fake_line_client.go
│                   ├── generated_expansion.go
│                   └── line_client.go
├── informers
│   └── externalversions
│       ├── factory.go
│       ├── generic.go
│       ├── internalinterfaces
│       │   └── factory_interfaces.go
│       └── line
│           ├── interface.go
│           └── v1alpha1
│               ├── bot.go
│               ├── event.go
│               ├── eventbinding.go
│               └── interface.go
└── listers
    └── line
        └── v1alpha1
            ├── bot.go
            ├── event.go
            ├── eventbinding.go
            └── expansion_generated.go

如此一來,我們就能在開發時,使用程式碼來操作自定義資源的 CRUD。

結語

今天主要初步了解 Kubernetes GitHub 組織上關於 API 的函式庫,在開發 Kubernetes 自定義控制器時,有可能因為跟原本 Kubernetes 的功能整合,因此會很頻繁地使用到這些函式庫。然而對這些函式庫有出不了的話,對於後續在自定義資源實作時,也能比較清楚 Kubernetes 的一些設計架構。

Reference


上一篇
[Day19] 淺談 Kubernetes 自定義資源(Custom Resource)與自定義控制器(Custom Controller)
下一篇
[Day21] 開發自定義控制器前,需要先了解的東西 Part2
系列文
其實我真的沒想過要利用研替剩餘的 30 天分享那些年 On-premise Container & Kubernetes 經驗30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言