前言:本文是一篇 kubernetes(下文用 k8s 代替)的入門文章,將會涉及 k8s 的技術歷史背景、架構、集群搭建、一個 Redis 的例子,以及如何使用 operator-sdk 開發 operator 的教程。在文章過程中,會穿插引出 Pod、Deployment、StatefulSet 等 k8s 的概念,這些概念通過例子引出來,更容易理解和實踐。文章參考了很多博客以及資料,部分在文章中用鏈接指示,部分放在最后參考資料部分。
背景
- 2004-2007 年,Google 大規模的使用 Cgroups 技術用來做進程級別的隔離。
- 2008 年,Cgroups 合並進入 Linux 內核主線。
- 2013 年,雲計算市場,AWS、OpenStack 利用虛擬機技術獨大。
- 以 Cloud Foundry 為代表的開源 PASS 項目成為一股清流,他們主要特點是提供了應用托管能力,底層是還是
虛擬機
,開發結束后,執行cf push myapp
,就可以一鍵部署起整個應用,其存在一個主要問題:即開發人員需要為不同的平台維護多個腳本,經常是本地運行好好的應用,到了線上就啟動不了,還需要修改參數等,調通整個上線過程非常的麻煩。 - 2013 年,還有一個不起眼的小公司 dotCloud 開源了自己的產品 Docker,當時 Cloud Foundry 的首席產品經理分析之后並沒有把它放在眼里,因為沒有什么技術創新, 主要技術就是 Cgroups + Namespace,說白了這些能力都是內核提供的,但是它卻迅速火了起來,主要原因其實是它開創性的提出了 Docker 鏡像的概念,解決了部署環境不一致的問題。凡是使用 Docker 鏡像的應用,打包鏡像之后
docker build image
,可以直接一鍵在遠端平台上部署起來docker run image
。 - 除了 Docker 鏡像的部署便捷性,Docker 開源之后迅速火熱的另一個原因是把開發者放在了一個比較重要的位置,1分鍾搭建一個 nginx,3分鍾部署 wordpress,很容易讓開發者上手。但是 Docker 公司也清楚,真實的業務場景下不可能只有一台 Docker,因此它們需要 Docker 的集群部署平台, 於是推出了 Swarm 作為自己的容器管理平台,其間也收購了一些公司,其中 Fig 其實是率先提出
容器編排
概念的項目,眼看着 Docker 公司就要一統江湖了,但是一些仍舊存在的本質問題:調度問題,還是漸漸顯露了出來,這里文章后面說到 k8s Pod 概念的時候也會講。 - 2014 年 6 月,Google 宣布 k8s 的誕生,它不是憑空產生的,k8s 來源於 Google 的內部系統 Borg/Omega,這兩個系統在 Google 內部千錘百煉,特別是 Borg。
- 2015 年 7 月,Google 牽頭成立 CNCF,以 k8s 為基礎,開始引擎容器編排的標准,k8s 借鑒了很多 Borg/Omega 的經驗,推出之后設計思想也比較靠前,也吸引了很多社區開發者追捧。
- 2017 年 10 月,Docker 公司宣布企業版內置 K8s
- 2018 年至今,雲原生的概念和應用層出不窮,雲原生的版圖正在不斷擴大。
k8s架構
介紹完背景,接下來我們看下 k8s 集群的架構,這張圖是 k8s 官網的架構圖。從左到右,分為兩部分,第一部分是 Master 節點(也就是圖中的 Control Plane),第二部分是 Node 節點。
Master 節點一般包括四個組件,apiserver、scheduler、controller-manager,他們分別的作用是啥呢:
- Apiserver:上知天文下知地理,上連其余組件,下接ETCD,提供各類 api 處理、鑒權,和 Node 上的 kublet 通信等,只有 apiserver 會連接 ETCD。
- Controller-manager:控制各類 controller,通過控制器模式,致力於將當前狀態轉變為期望的狀態。
- Scheduler:調度,打分,分配資源。
- Etcd:整個集群的數據庫,也可以不屬於 Master 節點,單獨搭建。
Node 節點一般也包括三個組件,docker,kube-proxy,kubelet
- Docker:具體跑應用的載體。
- Kube-proxy:主要負責網絡的打通,早期利用 iptables,現在使用 ipvs技術。
- Kubelet:機器上的 agent,負責管理容器的生命周期。
總結一下就是 k8s 集群是一個由兩部分組件 Master 和 Node 節點組成的架構,其中 Master 節點是整個集群的大腦,Node 節點來運行 Master 節點調度的應用,我們后續會以一個具體的調度例子來解釋這些組件的交互過程。
搭建 k8s 集群
上面說完了 k8s 集群中有哪些組件,接下來我們先看下如何搭建一個 k8s 集群,有以下幾種方法:
- 當我們安裝了 Docker Desktop APP 之后,勾選 k8s 支持就能搭建起來。
- 使用 MiniKube 來搭建,社區提供的一鍵安裝腳本。
- 直接在雲平台購買,例如阿里雲 ack。
- 使用 kubeadmin,這是 k8s 社區推薦的可以部署生產級別 k8s 的工具。
- 使用二進制,下載各組件安裝,此教程需要注意,下載的各組件版本要和博客中保持一致,就可以成功。
本文后面的例子均采用本地 Docker Desktop APP 搭建的 k8s。
➜ ~ kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.4", GitCommit:"3cce4a82b44f032d0cd1a1790e6d2f5a55d20aae", GitTreeState:"clean", BuildDate:"2021-08-11T18:16:05Z", GoVersion:"go1.16.7", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.4", GitCommit:"3cce4a82b44f032d0cd1a1790e6d2f5a55d20aae", GitTreeState:"clean", BuildDate:"2021-08-11T18:10:22Z", GoVersion:"go1.16.7", Compiler:"gc", Platform:"linux/amd64"}
從需求出發
下面我們從一個實際的需求出發,來看看如何在 k8s 上部署 Redis 服務。
- 部署一個Redis服務
- 支持高可用
- 提供統一的 EndPoint 訪問地址
部署單機版
如果我們想在 k8s 上部署一個單機版本 Redis,我們執行下面的命令即可:
➜ ~ kubectl run redis --image=redis
pod/redis created
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 5s
可以用 kubectl exec 來進入到 Pod 內部連接 Redis 執行命令:
➜ ~ kubectl exec -it redis -- bash
root@redis:/data# redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
那么 Pod 和 Redis 是什么關系呢?這里的 Redis 其實是一個 Docker 進程啟動的服務,但是在 k8s 中,它叫 Pod。
Pod 與 Deployment
我們來講下第一個 k8s 的概念 Pod,Pod 是 k8s 中最小的調度單元,一個 Pod 中可以包含多個 Docker,這些 Docker 都會被調度到同一台 Node 上,這些 Docker 共享 NetWork Namespace,並且可以聲明共享同一個 Volume 來共享磁盤空間。
這樣的好處是什么呢?其實在真實的世界中,很多應用是有部署在同一台機器的需求的,比如 Redis 日志采集插件要采集日志,肯定需要和 Redis 部署在同一台機器上才能讀到 Redis 的日志,我們前面講述背景的時候說到了 Docker Swarm 存在一些問題,其中之一就是它只是基於 Docker 調度,雖然也可以設置親和度讓兩台 Docker 調度在同一個機器上,但是因為不能一起調度,所以會存在一個Docker 提前被調度到了一個資源少的機器上,從而導致第二個 Docker 調度失敗。
例如我們一共有 2 台容器,A和B,分別為 Redis 和 日志采集組件,各需要 2g 內存,現在有兩台 node,node1 3.5 內存,node2 4g內存,在 Docker swarm 的調度策略下,先調度 Redis,有可能被調度到了 node1 上,接下來再來調度日志采集組件,發現 node1 只有 1.5g 內存了,調度失敗。但是在 k8s 中,調度是按照 pod 來調度的,兩個組件在一個 pod 中,調度就不會考慮 node1。
雖然 Pod 已經可以運行 Redis 服務了,但是他不具備高可用性
,因為一旦一個 Pod 與一個節點(Node)綁定,除非這個綁定發生了變化(pod.spec.node 字段被修改),否則它永遠都不會離開這個節點,這也就意味着,如果這個宿主機宕機了,這個 Pod 也不會主動遷移到其他節點上去。為了讓服務可以一直在,需要使用 Deployment 這樣的控制器。
➜ ~ kubectl create deployment redis-deployment --image=redis
deployment.apps/redis-deployment created
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 32m
redis-deployment-866c4c6cf9-8z8k5 1/1 Running 0 8s
➜ ~
redis-deployment-866c4c6cf9-8z8k5
就是剛才通過 kubectl create 創建的新的 Deployment,為了驗證高可用
,我們把 redis
和 redis-deployment-866c4c6cf9-8z8k5
都刪掉看會發生什么。
➜ ~ kubectl delete pod redis redis-deployment-866c4c6cf9-8z8k5
pod "redis" deleted
pod "redis-deployment-866c4c6cf9-8z8k5" deleted
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 10s
➜ ~
redis
已經消失了,但是redis-deployment-866c4c6cf9-zskkb
換了個名字又出現了!
Deployment 可以定義多副本個 Pod,從而為應用提供遷移能力,如果單純使用 Pod,實際上當應用被調度到某台機器之后,機器宕機應用也無法自動遷移,但是使用 Deployment,則會調用 ReplicaSet(一種控制器) 來保證當前集群中的應用副本數和指定的一致。
k8s 使用 yaml 來描述命令
k8s 中,可以使用 kubectl 來創建簡單的服務,但是還有一種方式是對應創建復雜的服務的,就是提供 yaml 文件。例如上面的創建 Pod 的命令,我們可以用下面的 yaml 文件替換,執行 kubectl create 之后,可以看到 redis Pod 又被創建了出來。
➜ ~ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
➜ ~ kubectl create -f pod.yaml
pod/redis created
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 6s
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 6m32s
k8s 組件調用流程
下面我們看下kubectl create deployment redis-deployment --image=redis
下發之后,k8s 集群做了什么。
- 首先 controller-manager, scheduler, kubelet 都會和 apiserver 開始進行
List-Watch
模型,List 是拿到當前的狀態,Watch 是拿到期望狀態,然后 k8s 集群會致力於將當前狀態達到達期望狀態。 - kubectl 下發命令到 apiserver,鑒權處理之后將創建信息存入 etcd,Deployment 的實現是使用 ReplicaSet 控制器,當 controller-manager 提前拿到當前的狀態(pod=0),接着接收到期望狀態,需要創建 ReplicaSet(pod=1),就會開始創建 Pod。
- 然后 scheduler 會進行調度,確認 Pod 被創建在哪一台 Node 上。
- 之后 Node 上的 kubelet 真正拉起一個 docker。
這些步驟中,apiserver 的作用是不言而喻的,所以說上接其余組件,下連 ETCD,但是 apiserver 是可以橫向擴容的,然后通過負載均衡,倒是 ETCD 在 k8s 架構中成了瓶頸。
最開始看這架構的時候,會想着為啥 apiserver, scheduler, controller-manager 不合成一個組件,其實在 Google Borg 中,borgmaster 就是這樣的,功能也是這些功能,但是合在了一起,最后他們也發現集群大了之后 borgmaster 會有些性能上的問題,包括 kubelet 的心跳就是很大一塊,所以 k8s 從一開始開源,設計中有三個組件也是更好維護代碼吧。
部署主從版本
上面我們已經部署了 Redis 的單機版,並通過 Deployment 實現了服務持續運行,接下來來看下主從版本如何部署,其中一個比較困難的地方就是如何確定主從的同步關系。
StatefulSet
k8s 為有狀態應用設計了 StatefulSet 這種控制器,它主要通過下面兩個特性來服務有狀態應用:
- 拓撲狀態:實例的創建順序和編號是順序的,會按照 name-index 來編號,比如 redis-0,redis-1 等。
- 存儲狀態:可以通過聲明使用外部存儲,例如雲盤等,將數據保存,從而 Pod 重啟,重新調度等都能讀到雲盤中的數據。
下面我們看下 Redis 的 StatefulSet 的例子:
apiVersion: apps/v1
kind: StatefulSet # 類型為 statefulset
metadata:
name: redis-sfs # app 名稱
spec:
serviceName: redis-sfs # 這里的 service 下面解釋
replicas: 2 # 定義了兩個副本
selector:
matchLabels:
app: redis-sfs
template:
metadata:
labels:
app: redis-sfs
spec:
containers:
- name: redis-sfs
image: redis # 鏡像版本
command:
- bash
- "-c"
- |
set -ex
ordinal=`hostname | awk -F '-' '{print $NF}'` # 使用 hostname 獲取序列
if [[ $ordinal -eq 0 ]]; then # 如果是 0,作為主
echo > /tmp/redis.conf
else
echo "slaveof redis-sfs-0.redis-sfs 6379" > /tmp/redis.conf # 如果是 1,作為備
fi
redis-server /tmp/redis.conf
接着啟動這個 StatefulSet,發現出現了 redis-sfs-0 和 redis-sfs-1 兩個 pod,他們正式按照 name-index 的規則來編號的
➜ ~ kubectl create -f server.yaml
statefulset.apps/redis-sfs created
➜ ~ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 65m
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 71m
redis-sfs-0 1/1 Running 0 33s # 按照
redis-sfs-1 1/1 Running 0 28s
接着我們繼續看下主從關系生效了沒,查看 redis-sfs-1 的日志,卻發現:
➜ ~ kubectl logs -f redis-sfs-1
1:S 05 Nov 2021 08:02:44.243 * Connecting to MASTER redis-sfs-0.redis-sfs:6379
1:S 05 Nov 2021 08:02:50.287 # Unable to connect to MASTER: Resource temporarily unavailable
...
Headless Service
似乎 redis-sfs-1 不認識 redis-sfs-0,原因就在於我們還沒有讓它們互相認識,這個互相認識需要使用 k8s 一個服務叫 Headless Service
,Service 是 k8s 項目中用來將一組 Pod 暴露給外界訪問的一種機制。比如,一個 Deployment 有 3 個 Pod,那么我就可以定義一個 Service。然后,用戶只要能訪問到這個 Service,它就能訪問到某個具體的 Pod,一般有兩種方式:
- VIP:訪問 VIP 隨機返回一個后端的 Pod
- DNS:通過 DNS 解析到后端某個 Pod 上
Headless Service 就是通過 DNS 的方式,可以解析到某個 Pod 的地址,這個 DNS 地址的規則就是:
<pod-name>.<svc-name>.<namespace>.svc.cluster.local
下面我們創建集群對應的 Headless Service:
apiVersion: v1
kind: Service
metadata:
name: redis-sfs
labels:
app: redis-sfs
spec:
clusterIP: None # 這里的 None 就是 Headless 的意思,表示會主動由 k8s 分配
ports:
- port: 6379
name: redis-sfs
selector:
app: redis-sfs
再次查看,發現 redis-sfs-1 已經主備同步成功了,因為創建 Headless Service 之后,redis-sfs-0.redis-sfs.default.svc.cluster.local
在集群中就是唯一可訪問的了。
➜ ~ kubectl create -f service.yaml
service/redis-sfs created
➜ ~ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24d
redis-sfs ClusterIP None <none> 6379/TCP 33s
➜ ~ kubectl logs -f redis-sfs-1
...
1:S 05 Nov 2021 08:23:31.341 * Connecting to MASTER redis-sfs-0.redis-sfs:6379
1:S 05 Nov 2021 08:23:31.345 * MASTER <-> REPLICA sync started
1:S 05 Nov 2021 08:23:31.345 * Non blocking connect for SYNC fired the event.
1:S 05 Nov 2021 08:23:31.346 * Master replied to PING, replication can continue...
1:S 05 Nov 2021 08:23:31.346 * Partial resynchronization not possible (no cached master)
1:S 05 Nov 2021 08:23:31.348 * Full resync from master: 29d1c03da6ee2af173b8dffbb85b6ad504ccc28f:0
1:S 05 Nov 2021 08:23:31.425 * MASTER <-> REPLICA sync: receiving 175 bytes from master to disk
1:S 05 Nov 2021 08:23:31.426 * MASTER <-> REPLICA sync: Flushing old data
1:S 05 Nov 2021 08:23:31.426 * MASTER <-> REPLICA sync: Loading DB in memory
1:S 05 Nov 2021 08:23:31.431 * Loading RDB produced by version 6.2.6
1:S 05 Nov 2021 08:23:31.431 * RDB age 0 seconds
1:S 05 Nov 2021 08:23:31.431 * RDB memory usage when created 1.83 Mb
1:S 05 Nov 2021 08:23:31.431 # Done loading RDB, keys loaded: 0, keys expired: 0.
1:S 05 Nov 2021 08:23:31.431 * MASTER <-> REPLICA sync: Finished with success
^C
➜ ~ kubectl exec -it redis-sfs-1 -- bash
root@redis-sfs-1:/data# redis-cli -h redis-sfs-0.redis-sfs.default.svc.cluster.local
redis-sfs-0.redis-sfs.default.svc.cluster.local:6379> ping
PONG
redis-sfs-0.redis-sfs.default.svc.cluster.local:6379>
此時無論我們刪除哪個 Pod,它都會按照原來的名稱被拉起來,從而可以保證准備關系,這個例子只是一個 StatefulSet 的示例,分析下來可以發現,雖然它可以維護主備關系,但是當主掛了的時候,此時備無法切換上來,因為沒有組件可以幫我們做這個切換操作,一個辦法是用 Redis Sentinel,可以參考這個項目的配置:k8s-redis-ha-master,如果你的 k8s 較新,需要 merge 此 PR.
Operator
雖然有了 StatefulSet,但是這只能對基礎版有用,如果想自己定制更加復雜的操作,k8s 的解法是 operator,簡而言之,operator 就是定制自己 k8s 對象及對象所對應操作的解法。
那什么是對象呢?一個 Redis 集群,一個 etcd 集群,zk 集群,都可以是一個對象,現實中我們想描述什么,就來定義什么,實際上我們定一個是k8s yaml 中的 kind,之前的例子中,我們使用過 Pod,Deployment,StatefulSet,它們是 k8s 默認實現,現在如果要定義自己的對象,有兩個流程:
- 定義對象,比如你的集群默認有幾個節點,都有啥組件
- 定義對象觸發的操作,當創建對象時候要做什么流程,HA 時候要做什么流程等。
operator 的方式是基於編程實現的,可以用多種語言,用的最多的就是 go 語言,通常大家會借助 operator-sdk 來完成,因為有很多代碼會自動生成。相當於 operator 會生成框架,然后我們實現對應的業務邏輯。
准備工作
- 安裝好 go 環境
- 安裝 operator-sdk
初始化項目
然后我們按照官網的 sdk 例子,來一步一步實現一個 memcached 的 operator,這里也可以換成 Redis,但是為了保證和官網一致,我們就按照官網來創建 memcached operator。
➜ ~ cd $GOPATH/src
➜ src mkdir memcached-operator
➜ src cd memcached-operator
➜ memcached-operator operator-sdk init --domain yangbodong22011 --repo github.com/yangbodong22011/memcached-operator --skip-go-version-check // 這里需要注意 domain 最好是和你在 https://hub.docker.com 的注冊名稱相同,因為后續會發布 docker 鏡像
Writing kustomize manifests for you to edit...
Writing scaffold for you to edit...
Get controller runtime:
$ go get sigs.k8s.io/controller-runtime@v0.9.2
Update dependencies:
$ go mod tidy
Next: define a resource with:
$ operator-sdk create api
創建 API 和 Controller
➜ memcached-operator operator-sdk create api --group cache --version v1alpha1 --kind Memcached --resource --controller
Writing kustomize manifests for you to edit...
Writing scaffold for you to edit...
api/v1alpha1/memcached_types.go
controllers/memcached_controller.go
Update dependencies:
$ go mod tidy
Running make:
$ make generate
go: creating new go.mod: module tmp
Downloading sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1
go get: installing executables with 'go get' in module mode is deprecated.
To adjust and download dependencies of the current module, use 'go get -d'.
To install using requirements of the current module, use 'go install'.
To install ignoring the current module, use 'go install' with a version,
like 'go install example.com/cmd@latest'.
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.
...
go get: added sigs.k8s.io/yaml v1.2.0
/Users/yangbodong/go/src/memcached-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
➜ memcached-operator
上面的步驟實際上生成了一個 operator 的框架,接下來我們首先來定義 memcached 集群都包括啥,將默認實現修改為 Size,表示一個 Memcached 集群中 Memcached 的數量,最后調用 make generate 和 make manifests 來自動生成 deepcopy 和 CRD 資源。
➜ memcached-operator vim api/v1alpha1/memcached_types.go // 修改下面 Memcached 集群的定義
// 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"`
}
➜ memcached-operator make generate
/Users/yangbodong/go/src/memcached-operator/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
➜ memcached-operator make manifests
/Users/yangbodong/go/src/memcached-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
➜ memcached-operator
實現 Controller
接下來是第二步,定義當創建一個 Memcached 集群時候,具體要干啥。
➜ memcached-operator vim controllers/memcached_controller.go
https://raw.githubusercontent.com/operator-framework/operator-sdk/latest/testdata/go/v3/memcached-operator/controllers/memcached_controller.go //
將 example 換成 yangbodong22011,注意,// 注釋中的也要換,實際不是注釋,而是一種格式
➜ memcached-operator go mod tidy; make manifests
/Users/yangbodong/go/src/memcached-operator/bin/controller-gen "crd:trivialVersions=true,preserveUnknownFields=false" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
發布 operator 鏡像
➜ memcached-operator vim Makefile
將 -IMG ?= controller:latest 改為 +IMG ?= $(IMAGE_TAG_BASE):$(VERSION)
➜ memcached-operator docker login // 提前登錄下 docker
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: yangbodong22011
Password:
WARNING! Your password will be stored unencrypted in /Users/yangbodong/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
➜ memcached-operator sudo make docker-build docker-push
...
=> => writing image sha256:a7313209e321c84368c5cb7ec820fffcec2d6fcb510219d2b41e3b92a2d5545a 0.0s
=> => naming to docker.io/yangbodong22011/memcached-operator:0.0.1 0.0s
fac03a24e25a: Pushed
6d75f23be3dd: Pushed
0.0.1: digest: sha256:242380214f997d98186df8acb9c13db12f61e8d0f921ed507d7087ca4b67ce59 size: 739
修改鏡像和部署
➜ memcached-operator vim config/manager/manager.yaml
image: controller:latest 修改為 yangbodong22011/memcached-operator:0.0.1
➜ memcached-operator vim config/default/manager_auth_proxy_patch.yaml
因為國內訪問不了 gcr.io
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 修改為 kubesphere/kube-rbac-proxy:v0.8.0
➜ memcached-operator make deploy
...
configmap/memcached-operator-manager-config created
service/memcached-operator-controller-manager-metrics-service created
deployment.apps/memcached-operator-controller-manager created
➜ memcached-operator kubectl get deployment -n memcached-operator-system // ready 說明 operator 已經部署了
NAME READY UP-TO-DATE AVAILABLE AGE
memcached-operator-controller-manager 1/1 1 1 31s
➜ memcached-operator
創建 Memcached 集群
➜ memcached-operator cat config/samples/cache_v1alpha1_memcached.yaml
apiVersion: cache.yangbodong22011/v1alpha1
kind: Memcached
metadata:
name: memcached-sample
spec:
size: 1
➜ memcached-operator kubectl apply -f config/samples/cache_v1alpha1_memcached.yaml
memcached.cache.yangbodong22011/memcached-sample created
➜ memcached-operator kubectl get pods
NAME READY STATUS RESTARTS AGE
memcached-sample-6c765df685-xhhjc 1/1 Running 0 104s
redis 1/1 Running 0 177m
redis-deployment-866c4c6cf9-zskkb 1/1 Running 0 3h4m
redis-sfs-0 1/1 Running 0 112m
redis-sfs-1 1/1 Running 0 112m
➜ memcached-operator
可以通過 kubectl logs 來查看 operator 的日志:
➜ ~ kubectl logs -f deployment/memcached-operator-controller-manager -n memcached-operator-system
2021-11-05T09:50:46.042Z INFO controller-runtime.manager.controller.memcached Creating a new Deployment {"reconciler group": "cache.yangbodong22011", "reconciler kind": "Memcached", "name": "memcached-sample", "namespace": "default", "Deployment.Namespace": "default", "Deployment.Name": "memcached-sample"}
至此,我們的 operator-sdk 的任務暫時告一段落。
總結
本文介紹了 k8s 的架構,各組件的功能,以及通過一個循序漸進的 Redis 例子介紹了 k8s 中 Pod, Deployment, StatefulSet 的概念,並通過 operator-sdk 演示了一個完整的 operator制作的例子。
參考資料
[1] 《深入剖析Kubernetes》張磊,CNCF TOC 成員,@阿里雲
[2] 《Kubernetes 權威指南》第五版
[3] 《Large-scale cluster management at Google with Borg》