如何編寫Kubernetes(K8s) operator,你學(xué)會了嗎?
編寫 Kubernetes(K8s) operator 的意圖在我心中不斷增長。我開始閱讀文章、探索 GitHub 存儲庫,并就此咨詢我的同事。雖然我不能說它完全成功,但這個(gè)意圖仍然存在。
譯自How to Write a Kubernetes Operator,作者 Payam Qorbanpour。
作為一名每天都與 Kubernetes 打交道的后端開發(fā)人員,我一直希望編寫一個(gè) operator 來擴(kuò)展我的知識邊界。然而,障礙出現(xiàn)了,阻礙了我實(shí)現(xiàn)這一目標(biāo)。
這就是我在服兵役期間編寫gobackup-operator的故事。tl;dr:直接跳到“深入項(xiàng)目”部分
磨刀不誤砍柴工
編寫 Kubernetes(K8s) operator 的意圖在我心中不斷增長。我開始閱讀文章、探索 GitHub 存儲庫,并就此咨詢我的同事。雖然我不能說它完全成功,但這個(gè)意圖仍然存在。
所有這些努力的結(jié)果是我GitHub 帳戶中存儲的一系列教程項(xiàng)目。
我應(yīng)該提到,大約一年前,當(dāng)我第一次接觸 Kubernetes 時(shí),練習(xí)過程就開始了。我首先觀看了Guru 的教程以了解 CKAD,然后觀看了Nana 的 YouTube 教程。
化為灰燼
我被派去服兵役。
那里沒有互聯(lián)網(wǎng)連接,甚至沒有一個(gè)電子設(shè)備。相反,我們只有精裝書、排球以及迷人的日出和日落美景來娛樂我們。
在這種情況下,創(chuàng)建 operator 的想法正在逐漸消失。我所關(guān)心的一切就是吃飯、看書和享受偶爾的自由(假期)。然而,有時(shí)這種自由是短暫的,正如指揮官曾經(jīng)評論的那樣:
假期的快樂在你離開營房的那一刻就結(jié)束了。
訓(xùn)練課程結(jié)束了,我開始在辦公室擔(dān)任一名雇員,但那里也感受到了互聯(lián)網(wǎng)連接的缺乏!在晚上,我離開辦公室,從事我熱愛的工作。有時(shí),你在有限的時(shí)間內(nèi)會有更好的表現(xiàn)。因此,從下午 4 點(diǎn)到晚上 9 點(diǎn),我必須創(chuàng)造一些特別的東西。對我來說,它確實(shí)很特別!
不鳴則已
畢竟,在此系列的幫助下,我設(shè)法從教程中編寫了另一個(gè) Kubernetes operator但這一次,它有所不同。
我的同事已經(jīng)開發(fā)了一個(gè)備份系統(tǒng),但它似乎運(yùn)行得不太好。因此,他們探索了另一種解決方案,并遇到了一個(gè)名為gobackup的項(xiàng)目,該項(xiàng)目旨在定期備份數(shù)據(jù)庫并將它們推送到存儲中。問題是該項(xiàng)目不包括對 etcd 數(shù)據(jù)庫的支持。因此,他們決定通過添加 etcd 支持來滿足要求,從而為該項(xiàng)目做出貢獻(xiàn)。這最終導(dǎo)致了一個(gè)新的版本。
在我缺席期間,他們決定在此基礎(chǔ)上開發(fā)一個(gè) Kubernetes operator 。這對我是重要的一步。當(dāng)他們與我分享時(shí),我急切地檢查了該項(xiàng)目,并想,“終于,就是它了。operator 即將創(chuàng)建。耶!”
在閱讀該項(xiàng)目時(shí),我注意到該項(xiàng)目的自述文件中存在一個(gè)問題。其中一個(gè)鏈接指向 404 頁面。我主動(dòng)修復(fù)了這個(gè)問題并提交了一個(gè)拉取請求。
所有者欣然接受了它。:)
遇到如此開放的態(tài)度后,我的一個(gè)同事建議我們可以將此 operator 放在gobackup 組織下,以便更多的人可以為其開發(fā)做出貢獻(xiàn)。
我打開了一個(gè)問題并提出了gobackup 組織下的一個(gè)存儲庫,并且仍然存在合作的開放性。
白天,我在軍隊(duì)服役,晚上,我致力于 gobackup-operator 項(xiàng)目。
深入項(xiàng)目
我首先設(shè)置我的環(huán)境。
幸運(yùn)的是,我已經(jīng)在計(jì)算機(jī)上安裝了 Golang、Docker 和 kubectl。通過之前的實(shí)踐,我已熟悉本地機(jī)器 Kubernetes 集群(如 Kind)和用于創(chuàng)建 operator 的工具(如 kubebuilder)。
因此,我啟動(dòng)了 operator 代碼。
$ kubebuilder init --domain gobackup.io --repo github.com/gobackup/gobackup-operator
然后我繼續(xù)為 operator 創(chuàng)建 API:
$ kubebuilder create api --group gobackup --version v1 --kind Backup
Create Resource [y/n]
y
Create Controller [y/n]
y
數(shù)據(jù)庫和存儲也是如此:
$ kubebuilder create api --group database.gobackup --version v1 --kind PostgreSQL
Create Resource [y/n]
y
Create Controller [y/n]
y
$ kubebuilder create api --group storage.gobackup --version v1 --kind S3
Create Resource [y/n]
y
Create Controller [y/n]
y
修改 API
我根據(jù)項(xiàng)目的具體要求修改了 API:
// Backup is the Schema for the backups API
type Backup struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec BackupSpec `json:"spec,omitempty"`
Status BackupStatus `json:"status,omitempty"`
BackupModelRef BackupModelRef `json:"backupModelRef,omitempty"`
StorageRefs []StorageRef `json:"storageRefs,omitempty"`
DatabaseRefs []DatabaseRef `json:"databaseRefs,omitempty"`
}
然后修改 Reconcile 方法
//+kubebuilder:rbac:groups=gobackup.io,resources=backups,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=gobackup.io,resources=backups/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=gobackup.io,resources=backups/finalizers,verbs=update
func (r *BackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
// reconcile implementation
}
測試
在對其進(jìn)行測試之前,你需要準(zhǔn)備一個(gè)可供備份的測試數(shù)據(jù)庫。因此,使用 gobackup-operator-postgres-deployment.yaml 文件創(chuàng)建 PostgreSQL 部署:
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-deployment
spec:
selector:
matchLabels:
app: postgres
replicas: 1
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:14.11
env:
- name: POSTGRES_USER
value: ""
- name: POSTGRES_PASSWORD
value: ""
- name: PGDATA
value: "/var/lib/postgresql/data/pgdata"
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: postgredb
volumes:
- name: postgredb
persistentVolumeClaim:
claimName: postgres-pvc
請記住在清單中修改POSTGRES_USER和POSTGRES_PASSWORD并應(yīng)用它:
kubectl apply -f example/gobackup-opetator-postgres-deployment.yaml,
example/gobackup-opetator-postgres-service.yaml
此外,我還添加了一些資源在 Kubernetes 集群中進(jìn)行測試,包括部署、角色、集群角色、服務(wù)帳戶等,所有這些都可以在 gobackup-operator/example/ 目錄中找到。
因此,應(yīng)用這些清單以添加基本資源:
kubectl apply -f example/gobackup-opetator-serviceaccount.yaml,
gobackup-opetator-pvc.yaml,
gobackup-opetator-namespace.yaml,
gobackup-opetator-clusterrolebinding.yaml,
gobackup-opetator-clusterrole.yaml
然后是存儲和數(shù)據(jù)庫清單:
kubectl apply -f example/gobackup-opetator-storage/*
kubectl apply -f example/gobackup-opetator-database/*
使用以下清單,我能夠在我的本地機(jī)器上運(yùn)行該 operator :
kubectl apply -f example/gobackup-opetator-deployment.yaml
因此,每當(dāng)創(chuàng)建或更改 Backup 或 CronBackup 對象時(shí), operator 都會執(zhí)行必要的任務(wù)。
要?jiǎng)?chuàng)建備份模型以設(shè)置備份配置:
kubectl apply -f example/gobackup-opetator/gobackup-opetator-backupmodel.yaml
應(yīng)用 gobackup-operator/example/gobackup-operator 目錄中的清單之一(備份或 cronbackup)將觸發(fā) operator 運(yùn)行備份:
kubectl apply -f example/gobackup-opetator/gobackup-opetator-cronbackup.yaml
結(jié)論
起初,我對在自述文件中做出如此小的更改感到尷尬。感覺就像你為了參與 Hacktoberfest 提交而做出的那些 PR 之一。
但后來我考慮到了它的有效性。即使是那些單行提交也產(chǎn)生了影響。誰知道呢,如果我沒有對 README 文件進(jìn)行更改,我可能就不會創(chuàng)建這個(gè) operator 。