從入門到理解 K8s Operator
【導讀】作者用一個構建流程作爲例子,在這個場景下用 operator 實現了需求,本文比較概括地介紹了這個流程,詳細實現在文末 github 鏈接內。
本文中介紹了創建一個簡單 K8s operator 應用的方法,讀完本文你將會理解有關 Operator 的主要概念和其作用。
首先我會講解基本概念,之後將從最基礎開始講解如何着手編碼。主要包括:
-
Operator 模式介紹
-
Kubernetes controller 簡介
-
使用 Operator-SDK 腳手架工具創建 Operator
-
Demo:實現一個 CICD operator
-
配置准入控制 webhook
-
總結
Operator 模式
1. 概念
kubernetes 官方文檔對 Operator 模式的定義如下:
Operator 模式旨在捕獲(正在管理一個或一組服務的)運維人員的關鍵目標。負責特定應用和 service 的運維人員,在系統應該如何運行、如何部署以及出現問題時如何處理等方面有深入的瞭解。
在 Kubernetes 上運行工作負載的人們都喜歡通過自動化來處理重複的任務。Operator 模式會封裝你編寫的(Kubernetes 本身提供功能以外的)任務自動化代碼。
2. Operator 如何工作?
Kubernetes Operator 一書中的解釋是:
Operator 通過擴展 Kubernetes 控制面和 API 進行工作。最簡單的模式是一個 Operator 給 Kubernetes API 加了一個 endpoint,這個增加的 Encpoint 被稱作用戶定義資源(CR);同時增加一個控制面組件,該組件監控和維護着新資源。
3. Operator 和 Controller 之間有什麼關係?
Operator 包括了一組 controller,這些 controller 會不斷檢查 k8s 上的資源的情況。每個 controller 都會實現一個和解循環(reconciliation loop),在資源被創建、刪除、更新的時候觸發 controller 邏輯。每個 controller 都只負責某一種資源。
4. 創建 Operator 有什麼好工具?
可用於創建 Kubernetes Operator 的開源項目有:
-
Operator SDK
-
Kubebuilder
-
KUDO
-
Metacontroller
Kubernetes controller 簡介
controller 是一個不斷檢查系統狀態的無限循環,controller 會把資源狀態不斷向着 “期望” 的狀態改變(就是和解循環)。
一旦設置了溫度,這個設置的溫度就是期望的狀態。實際室溫是當前狀態。恆溫器的作用是通過打開或關閉設備,使當前狀態更接近理想狀態。
Kubernetes 內有一些內置的 controller,運行在 master 節點的 controller manager 內。
你可以創建自定義的 operator,和上圖中的 controller 一樣,管理有狀態和無狀態的應用資源。
使用 Operator-SDK 腳手架工具創建 Operator
1. Operator-SDK 是什麼
Operator-SDK 是 Operator 框架的腳手架組件,用於編寫 kubernetes 原生應用。
Operator 框架是用高效、自動化、可擴展的方式管理叫做 Operator 的 kubernetes 原生應用的開源工具套件。
Operator 框架由下面幾部分組成:
-
Operator SDK
-
Operator 生命週期管理(OLM)
-
Operator 計量
本文中只講 Operator SDK。
2. 我應該創建什麼 Operator?
Operator SDK 有三種類型:
-
Helm
-
Ansible
-
Go
後面有一節會專門講基於 go 的 Operator 實現 CI/CD。
3. Operator 庫
Operator 會利用一些庫和 kubernetes api 交互。可用的庫有 client-go 和 controller-runtime 等。既然要編程管理資源,這就需要對 k8s 有足夠了解,比如 informer,listener,workQueue,runtime.Object 和 scheme 等。
這裏推薦一篇分享,詳細介紹了這些庫。https://speakerdeck.com/govargo/inside-of-kubernetes-controller
4. Operator SDK 和 kubebuilder 對比
以前有兩個 github 開源項目,他們的功能和 star 數量和開發者數量都不相上下。現在這兩個項目的開發者們決定共同努力爲一個目標做貢獻。以前兩個工具創建都項目目錄結構不同,現在項目目錄結構已經一樣了。
本文中所有的例子都是使用 Operator SDK1.0 版本生成的。
5. Operator 例子
有時最好的學習辦法是看看真實案例,找一些通用範式和最佳實踐,最好能在自己的 operator 裏用一用。
通用的模式顯而易見,Operator 通常對資源(包括 deployment、job、secret、service、pod 等)進行 crud 操作,最後會把這些資源的狀態變更掉。創建或編輯資源既可以程序化也可以是聲明式的,還可以像 Manisfestival(https://github.com/manifestival/manifestival)這種做成第三方倉庫的模式。所謂第三方倉庫模式就是用非結構化數據一次更新多個運行時對象。
既然說到最佳實踐,成功執行了創建、排隊和重試的操作後,進行持續的資源狀態檢查是一個比較建議的模式。有這個概念後再看其他 operator 項目的代碼就可以很容易地理解其中邏輯。
awesome-operators 項目(github.com/operator-framework/awesome-operators)中有很多例子,這裏面的很多項目都是用本文中介紹的腳手架創建的。
6. Operator Hub
Kubernetes 社區有一個集中放 Operator 的地方,叫 Operator Hub,你可以爲你的 Operator 寫一些開始引導和代碼貢獻說明後把它發佈到這個倉庫上。這個倉庫和 Docker、Helm 的 Hub 類似。
7. Helm vs Operator
既然 Operator 管理 kubernetes 資源的生命週期,爲什麼不直接用 Helm?
下面鏈接中的這篇分享中分享者提出了 Helm 是爲了解決 Day 1 問題而 Operator 目標是做 Day 2 的操作。youtube.com/watch?v=N9QVJk6kjwg
Day 0/Day 1/Day 2 是什麼意思?
-
Day 0:軟件開發中的設計階段。在這個階段,我們要收集解決方案的所有需求。
-
Day 1:第一次在基礎架構中部署應用時。
-
Day 2:管理應用的生命週期時。這時還要保證應用可用,需要考慮備份、重啓、故障轉移和回退等。
比如你通過 Helm chart 裝了一個應用,這個應用包含 deployment、service、ingress,然後有人刪掉了 service。這時應用將不可用,但是對於 helm 來說一切正常,因爲 helm 負責管理資源到狀態,直到我們變更了部署信息後 helm 纔會知道 service 不存在。這種情況下需要用 Operator 才能滿足需求。
剛剛的例子中,有人刪掉了的 service 是某個 operator 監控的,這個 operator 會負責把 service 重新創建好,然後應用就恢復正常了。因此 Helm 並不能完全替代 Operator。
Demo:實現一個 CICD operator
下面我們開始着手寫一個 CI/CD operator,代碼託管在 GitHub/Bitbucket,基於 git 倉庫上的代碼進行編譯、構建鏡像和把鏡像推鏡像倉庫,最終部署到 k8s。
1. 動機
當然用 Operator 是要利用自動化的方式降低複雜度啦!
2. 創建 Kubernetes 集羣測試 Operator
首先創建一個 k8s 集羣用來部署和測試自定義 Operator。這裏用基於 docker 容器的 KinD 集羣部署工具快速部署 k8s 集羣。
- 如何用 KinD 創建集羣
創建 KinD 集羣這個任務非常明確。我創建了一個 master 節點、兩個 worker 節點的集羣,還部署了一個 docker registry 用來放我們自己的鏡像。
下面的腳本可以快速啓動 KinD 集羣。運行腳本之前要裝好 docker 和 kubectl。
#!/bin/sh
set -o errexit
# create registry container unless it already exists
reg_name='kind-registry'
reg_port='5000'
running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)"
if [ "${running}" != 'true' ]; then
docker run \
-d --restart=always -p "${reg_port}:5000" --name "${reg_name}" \
registry:2
fi
# create a cluster with the local registry enabled in containerd
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraMounts:
- hostPath: /Users/victorp/projects/kind
containerPath: /data
- role: worker
extraMounts:
- hostPath: /Users/victorp/projects/kind
containerPath: /data
- role: worker
extraMounts:
- hostPath: /Users/victorp/projects/kind
containerPath: /data
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."${reg_name}:${reg_port}"]
endpoint = ["http://${reg_name}:${reg_port}"]
EOF
執行腳本:
# Running the script to create a KinD cluster:
$ chmod +x kind-with-registry.sh
$ ./kind-with-registry.sh
$ kind get nodes
$ export KUBECONFIG=`kind get kubeconfig`
$ kubectl get all --all-namespaces
執行過上面的腳本後 k8s 集羣就部署好了,沒有任何 VM 依賴。此外還留了一個掛載點,今後有需要創建持久化存儲可以掛到這裏。
3. CICD operator 架構
下面的圖展示了 CICD operator 的架構:
a) operator 部署到 kubernetes 集羣后,需要用 kubectl 人工或自動地 創建 CR。
b) operator controller 通過 API server 循環檢查資源情況,觸發 reconcile 方法。
c) CI 控制器會觸發 reconcile 方法,這個方法會創建 Job 編譯代碼。
d) Job 包含了一個裝了 git-sync 的 init container,這個 init container 把代碼拉到一個 init container 和業務容器都掛載了捲上。
e) 裝着 Kaniko 鏡像的業務容器會在 init container 執行完任務後對代碼進行編譯,編譯結果上傳到 docker 倉庫。
f) job 跑完後 CD 控制器就會被觸發,它會負責創建一個 deployment。
g)這個 deployment 裏會從 docker 倉庫拉 e 步驟裏打的鏡像。
目前爲止還沒有給這個應用創建 service 和 ingress 的需求。
4. 利用腳手架生成 Operator 代碼
下面用 Operator-SDK 命令行工具生成腳手架代碼,先把 Operator-SDK、Go 和 git 都裝到環境裏。安裝步驟略。
# create the directory of your project
$ mkdir cicd-operator
$ cd cicd-operator
# This command will create the structure for your project
$ operator-sdk init --domain=cicd.com --repo=github.com/victorpaulo/cicd-operator
# This command will create the CRDs and Controller for your operator
$ operator-sdk create api --group=cicd --version=v1alpha1 --kind=CIBuild --resource=true --controller=true
執行了上面的步驟後 operator 的腳手架代碼就創建完了,目前創建的代碼可以監聽自定義的資源定義,並創建一個被 operator 管理的 pod。下面要刪掉自動生成的代碼,寫我們自己的邏輯。
步驟如下:
- 自己寫 operator 邏輯相關的數據的結構體。
這個例子裏有幾個值要放進代碼裏,包括 github 倉庫 endpoint 鏈接、SSH key 等,如下圖:
- 完成自定義數據結構(spec)、改好 controller 內邏輯,就可以把 operator 編譯一下推到遠端 docker 倉庫:
# 下面的 make 命令會編譯 docker 鏡像、給這個鏡像打包,鏡像裏包含 operator,最後把鏡像推到遠程倉庫。
# Eg.: make docker-build docker-push IMG=<some-registry>/<project-name>:<tag>
$ make docker-build docker-push IMG=kind-registry:5000/cicd-operator:v0.0.1
- 編譯完 operator 後,在 k8s 裏部署它:
# 通過創建 deployment 和 RBAC 資源,創建 k8s 中的 operator
$ make install
# E.g: make deploy IMG=<some-registry>/<project-name>:<tag>
$ make deploy IMG=kind-registry:5000/cicd-operator:v0.0.1
- operator 部署好已經正常運行了,創建 GitHub/Bitbucket 的密鑰和 CICD 需要的資源。
# operator 會基於 SSH 克隆 git 倉庫,因此需要創建能鏈接到這些 Scm 倉庫、包含 ssh key 的密鑰。
$ ssh-keygen -t rsa -N "" -f mykey
# 用 web 控制檯導入 mykey.pub 到 GitHub Bitbucket
#Github
$ ssh-keyscan github.com > /tmp/known_hosts
#Bitbucket
$ ssh-keyscan bitbucket.org > /tmp/known_hosts
# 創建密鑰
$ kubectl create secret generic git-creds --from-file=ssh=mykey --from-file=known_hosts=/tmp/known_hosts
github
bitbucket
- 到此爲止 operator 和 controller 都已經啓動好並且在 k8s 運行着,git 密鑰也已經放到了該放的地方。現在來創建自定義資源(CR),CR 作爲 k8s 的一個擴展的 API 創建好後,CICD 的進程就會開始。
CI 控制器會調用 reconcile 方法,創建一個 job 做編譯、推 docker 鏡像。job 跑完後 CD 控制器會基於從這個倉庫里拉到的鏡像創建應用 deployment 資源
# Creating the CRs
# this CR points to a IBM App Connect (ACE) code
$ kubectl apply -f config/samples/cicd_v1alpha1_cibuild_ace.yaml
# this CR points to a Go application
$ kubectl apply -f config/samples/cicd_v1alpha1_cibuild_go.yaml
# this CR points to a Nodejs application
$ kubectl apply -f config/samples/cicd_v1alpha1_cibuild_nodejs.yaml
# this CR points to a Java application
$ kubectl apply -f config/samples/cicd_v1alpha1_cibuild_java.yaml
ace
java
上圖中展示的代碼是基於 github 的,可以自行修改配置讓它基於 bitbucket 等平臺。
配置准入控制 webhook
准入控制器是在對象持久化之前用於對 Kubernetes API Server 的請求進行攔截的 http 回調,可以定義兩種類型的准入 webhook,驗證性質的准入 Webhook 和 修改性質的准入 Webhook。
驗證性質的准入 Webhook 可以執行 OpenAPI 格式檢查做不到的檢查。例如要保證一個值在創建後不可變,或是做基於用戶做 API server 的訪問控制。
驗證性質的准入 webhook 可以用來拒絕請求,增強自定義認證的控制。
修改性質的准入 Webhook 經常用在給創建的資源的值加默認值的場景裏。
istio 就是通過_修改性質的准入 Webhook_ 把 sidecar 代理插入用戶 pod 的。
webhook 運行的環境是需要在 kubernetes 上裝 Cert Manager,operator 的 dockerfile 中設置ENABLE_WEBHOOKS=true
打開控制器的 webhook 邏輯。
cert-manager 是 k8s 的一個插件,這個插件會自動管理、頒發 TLS 證書、定期檢查證書有效性並且在合適的時機嘗試更新證書。
- 安裝 cert manager
$ kubectl apply — validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.16.0/cert-manager.yaml
上面的命令創建了 cert-manager 這個 namespace 下的 3 個 deployment,分別是 cert-manager, ca-injector 和 webhook。
總結
希望這篇文章可以幫到 operator 的新入門選手。你會發現 k8s 上幾乎所有東西都依賴 controller,理解了 controller 也能幫你更好理解很多 k8s 生態的項目。
operator 代碼在 github 上鍊接是:
https://github.com/victorpaulo/cicd-operator
Go 開發大全
參與維護一個非常全面的 Go 開源技術資源庫。日常分享 Go, 雲原生、k8s、Docker 和微服務方面的技術文章和行業動態。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/V3-fW4iqZBpmIoTvsD9mzg