從入門到理解 K8s Operator

【導讀】作者用一個構建流程作爲例子,在這個場景下用 operator 實現了需求,本文比較概括地介紹了這個流程,詳細實現在文末 github 鏈接內。

本文中介紹了創建一個簡單 K8s operator 應用的方法,讀完本文你將會理解有關 Operator 的主要概念和其作用。

首先我會講解基本概念,之後將從最基礎開始講解如何着手編碼。主要包括:

  1. Operator 模式介紹

  2. Kubernetes controller 簡介

  3. 使用 Operator-SDK 腳手架工具創建 Operator

  4. Demo:實現一個 CICD operator

  5. 配置准入控制 webhook

  6. 總結

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 的開源項目有:

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。

2. 我應該創建什麼 Operator?

Operator SDK 有三種類型:

  1. Helm

  2. Ansible

  3. 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 是什麼意思?

比如你通過 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 集羣這個任務非常明確。我創建了一個 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。下面要刪掉自動生成的代碼,寫我們自己的邏輯。

步驟如下:

  1. 自己寫 operator 邏輯相關的數據的結構體。

這個例子裏有幾個值要放進代碼裏,包括 github 倉庫 endpoint 鏈接、SSH key 等,如下圖:

  1. 完成自定義數據結構(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
  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
  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

  1. 到此爲止 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 證書、定期檢查證書有效性並且在合適的時機嘗試更新證書。

$ 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