1.CRD
CRD简介
kubernetes 有两种机制供用户扩展API
- 第一种:CRD,复用Kubernetes的API Server,无须编写额外的APIServer。用户只需要定义CRD,并且提供一个CRD控制器,就能通过Kubernetes的API管理自定义资源对象了,同时要求用户的CRD对象符合API Server的管理规范。 该方法更易用、不需要编码。但是缺乏灵活性。
- 第二种:使用API聚合机制 ,用户需要编写额外的API Server,可以对资源进行更细粒度的控制(例如,如何在各API版本之间切换),要求用户自行处理对多个API版本的支持。 该方法需要编码,允许通过聚合层的方式提供更加定制化的实现。
CRD
- CRD, 全称是 Custom Resource Definition,即用户自定义资源类型。顾名思义,它指的就是,允许用户在 Kubernetes 中添加一个跟 Pod、Node 类似的、新的 API 资源类型,即:自定义 API 资源。
- 在 Kubernetes 中一切都可视为资源,Kubernetes在1.7版本后增加了CRD,使得开发人员可以不修改Kubernetes的原有代码,而是通过扩展形式,来管理自定义资源对象,该功能大大提高了 Kubernetes 的扩展能力。
Custom resources
- 是对K8S API的扩展,代表了一个特定的kubetnetes的定制化安装。在一个运行中的集群中,自定义资源可以动态注册到集群中。注册完毕以后,用户可以通过kubelet创建和访问这个自定义的对象,类似于操作pod一样。
Custom controllers 自定义控制器
- Custom resources可以让用户简单的存储和获取结构化数据。只有结合控制器才能变成一个真正的declarative API(被声明过的API)。控制器可以把资源更新成用户想要的状态,并且通过一系列操作维护和变更状态。定制化控制器是用户可以在运行中的集群内部署和更新的一个控制器,它独立于集群本身的生命周期。
定制化控制器可以和任何一种资源一起工作,当和定制化资源结合使用时尤其有效。
Operator模式
- 是一个customer controllers和Custom resources结合的例子。它可以允许开发者将特殊应用编码至kubernetes的扩展API内。
通俗理解:CR即创建一个自定义资源,CRD是对资源的描述,CC是提供CRD对象的管理。
CRD本身只是一段声明,用于定义用户自定义的资源对象。但仅有CRD的定义并没有实际作用,用户还需要提供管理CRD对象的CRD控制器(CRD Controller),才能实现对CRD对象的管理。CRD控制器通常可以通过Go语言进行开发,并需要遵循Kubernetes的控制器开发规范,基于客户端库client-go进行开发,需要实现Informer、ResourceEventHandler、Workqueue等组件具体的功能处理逻辑。
CRD使用场景
crd在很多k8s周边开源项目中有使用,比如ingress-controller 、istio 、hpa和众多的operator。
Kubebuilder
Kubebuilder 是一个基于 CRD 来构建 Kubernetes API 的框架,可以使用 CRD 来构建 API、Controller 和 Admission Webhook。
2.项目搭建
2.1 背景
从零开始写一个 CRD 及其Controller ,它的开发方式经历过不同的阶段,从最早的参考 K8S 官方的控制器代码并手动复制,到用 client-gen 生成框架代码,到现在使用 kubebuilder ,已经非常方便我们完成 CRD/Controller,甚至 Operator 的开发(当然 Operator 的开发也有专用的 operator-sdk开源框架)。
2.2 环境准备
Centos中安装golang、kubebuilder、kustomize环境
kubernetes集群部署
golang 1.15.5
kubebuilder 2.3.1 (推荐使用二进制方式安装)
kustomize
kubernetes集群 1.20.1
- go中推荐打开
export GO111MODULE=on
以强制启用 Go module,它是目前最新的 Golang 包依赖管理工具,也是官方推荐,govender,godep 等工具已经不建议继续使用 - 执行
go env -w GOPROXY=goproxy.cn,direct
开启代理,配置默认从 goproxy.cn 拉去 Go Module 的依赖包,如果不存在走默认的方式,goproxy.cn 是七牛云维护的一个 golang 包代理库,测试下来性能是最好的,可以拉取很多被墙掉的包
go version #查看go的版本
kubebuilder version
kubectl get nodes # 查看k8s集群的版本
2.3 大体流程
go mod init my.domain
## 这两个复杂的命令可以通过kubebuilder --help查看
# 初始化
kubebuilder init --domain example.com --license apache2 --owner "The Kubernetes authors"
# 创建CRD api
kubebuilder create api --group webapp --version v1 --kind Frigate
# 安装CRD
make install
# 启动controller(本地)
make run
2.4 创建项目
我们首先将使用自动配置创建一个项目,该项目在创建 CR 时不会触发任何资源生成。
2.4.1 初始化
创建的项目路径位于 $GOPATH/kubebuilder-example。
go mod init my.domain
go mod init:初始化go mod, 生成go.mod文件,后可接参数指定 module 名
在项目路径下使用下面的命令初始化项目。
kubebuilder init --domain example.com --license apache2 --owner "The Kubernetes authors"
这时需要保证你的终端能访问 K8S 的测试集群,简单就是用 kubectl cluster-info
看看是否出错,如果不出错,就可以 go run main.go
了
开始执行,发现正常输出日志了
注意:此处如果出现端口被占用的情况,参考文末
2.4.2 创建CRD api
kubebuilder create api --group webapp --version v1 --kind Frigate
注意, group / version / kind 这三个属性组合起来来标识一个 K8S 的 CRD。
kind 要首字母大写而且不能有特殊符号。
在创建过程中,我们可以选择让 KubeBuilder 来是否生成 Resource / Controller 等。
执行上面的命令之后,KubeBuilder 就帮我们创建了两个文件 api/v1/frigate_types.go
和controllers/frigate_controller.go
, 前者是这个 CRD 需要定义哪些属性,而后者是对 CRD 的 Reconcile 的处理逻辑(也就是增删改 CRD 的逻辑), 我们后面再讲这两个文件。
我们看一下这两个文件的树状结构
2.4.3 安装CRD
先来 make install
一下, 将其安装到 K8S cluster 中
make install
kubectl get crd
此时已经将 CRD 安装到集群了
2.4.4 部署controller
有两种方式运行 controller:
- 本地运行,用于调试
- 部署到 Kubernetes 上运行,作为生产使用
方法1:本地运行 controller
要想在本地运行 controller,需要执行:
make run
方法2:将 controller 部署到 Kubernetes
构建docker镜像并发布到仓库中
按照其他相关博客 提示:
请修改DockerFile中:
a. 将FROM golang:1.12.5 as builder 替换成 FROM golang:1.13 as builder
b. 在COPY go.sum 下加上ENV GOPROXY=https://goproxy.cn,direct
c. 将FROM gcr.io/distroless/static:nonroot 替换成 FROM golang:1.13
d. 删除USER nonroot:nonroot
修改了相关部分:如下
再次运行:
make docker-build docker-push IMG=registry.cn-beijing.aliyuncs.com/anan_java/go_project_01:v1.0
注意:这个仓库是自己在阿里云建的仓库,可以使用自己的仓库名
成功构建镜像并上传到服务器
注意:此处如果出现问题,参考文末
部署镜像到集群
对于中国大陆用户,可能无法访问 Google 镜像仓库 gcr.io,因此需要修改 config/default/manager_auth_proxy_patch.yaml 文件中的镜像地址:
vim config/default/manager_auth_proxy_patch.yaml
替换成: image: registry.cn-hangzhou.aliyuncs.com/kubenode/kube-rbac-proxy:v0.4.0
make deploy IMG=registry.cn-beijing.aliyuncs.com/anan_java/go_project_01:v1.0
在初始化项目时,kubebuilder 会自动根据项目名称创建一个 Namespace,如本文中的 kubebuilder-example-system,查看 Deployment 对象和 Pod 资源。
看到running好开心,这个地方调了好多次!
注意:此处如果出现问题,参考文末
2.4.5 创建 CR
Kubebuilder 在初始化项目的时候已生成了示例 CR。
可以在源码目录下看到config文件下有个sample文件夹,里面有例子:
执行下面的命令部署 CR
kubectl apply -f config/samples/webapp_v1_frigate.yaml
执行下面的命令查看新创建的 CR
kubectl get frigate.webapp.example.com frigate-sample -o yaml
那就完成了一个简单的CRD demo了
2.5 增加业务逻辑
下面我们将修改 CRD 的数据结构并在 controller 中增加一些日志输出。
2.5.1 修改 CRD
我们将修改上文中使用 kubebuilder 命令生成的默认 CRD 配置,在 CRD 中增加 FirstName、LastName 和 Status 字段。
修改 api/v1/frigate_types.go
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
Status string `json:"Status"`
2.5.2 修改 Reconcile 函数
修改 controllers/frigate_controller.go
# 添加log包
# 添加如下代码
_ = r.Log.WithValues("apiexamplea", req.NamespacedName)
// 获取当前的 CR,并打印
obj := &webappv1.Frigate{}
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
log.Println(err, "Unable to fetch object")
} else {
log.Println("Geeting from Kubebuilder to", obj.Spec.FirstName, obj.Spec.LastName)
}
// 初始化 CR 的 Status 为 Running
obj.Status.Status = "Running"
if err := r.Status().Update(ctx, obj); err != nil {
log.Println(err, "unable to update status")
}
这段代码的业务逻辑是当发现有 CR 变更时,在控制台中输出日志。
2.5.3 部署测试
修改好 业务逻辑后,再测试一下新的逻辑是否可以正常运行
部署CRD和controller
make install # 部署CRD
make run # 本地运行controller 也可以将其部署到 Kubernetes 上运行
保持该窗口在前台运行。
部署 CR
修改 config/samples/webapp_v1_frigate.yaml 文件中的配置。
将其应用到 Kubernetes。
kubectl apply -f config/samples/webapp_v1_frigate.yaml
此时转到上文中运行 controller 的窗口,将在命令行前台中看到如下输出。
Geeting from Kubebuilder to Ning An 这正是在 Reconcile 函数中的输出。
获取当前的 CR
kubectl get frigate.webapp.example.com frigate-sample -o yaml
这正是我们在 CRD 里定义的字段。
删除 CR
使用下面的命令删除 CR。
kubectl delete frigate.webapp.example.com frigate-sample
此时在 controller 的前台输出中可以看到以下内容。
因为该 CR 被删除,因此日志中会提示资源找不到。
删除CRD
make uninstall
此时在 controller 的前台输出中可以看到以下内容:
部署结束!
2.6 注意
注意:
- 创建项目时,部署controller的两种方法都可以正常完成相关步骤
- 增加业务逻辑时,如果通过将controller部署到集群中实现,没有前台的界面,所以看不到前台的输出,而且,最后没有Status:running,不知道这样正不正常。
错误
错误提示:tcp :8080: bind: address already in use
unable to start manager {“error”: “error listening on :8080: listen tcp :8080: bind: address already in use”}
提示8080端口被占用,这是因为自己昨天运行了一下go run main.go,今天又运行了一下
解决方法: 找到占用8080端口的进程,kill掉即可
netstat -tnlp | grep 80
kill -9 进程号
错误 no new variable on left side
注意:如果make run时出现如下错误,说明改行变量声明有问题,将 ctx :=...
删掉即可
构建镜像错误
错误1:curl: (35) Network file descriptor is not connected
curl: (35) Network file descriptor is not connected
make: *** [test] 错误 35
有篇博客提到一些二进制文件的问题
要把 etcd / apiserver 等二进制可执行文件。这个在解压的 kubebuilder 的文件中可以找到。
如果构建有问题,需要放到 /usr/local/kubebuilder/bin/ 中
可是自己通过源码安装,没有二进制文件
所以,选择选择以二进制的方式重新安装kubebuilder
(至于怎么卸载呢?就把之前的那个文件夹删掉并且删除相关的环境变量)
错误2:The command ‘/bin/sh -c go mod download’ returned a non-zero code: 1
go: github.com/go-logr/logr@v0.1.0: Get https://proxy.golang.org/github.com/go-logr/logr/@v/v0.1.0.mod: dial tcp 172.217.160.113:443: connect: connection refused
The command ‘/bin/sh -c go mod download’ returned a non-zero code: 1
make: *** [docker-build] 错误 1
按照其他相关博客 提示:
请修改DockerFile中:
a. 将FROM golang:1.12.5 as builder 替换成 FROM golang:1.13 as builder
b. 在COPY go.sum 下加上ENV GOPROXY=https://goproxy.cn,direct
c. 将FROM gcr.io/distroless/static:nonroot 替换成 FROM golang:1.13
d. 删除USER nonroot:nonroot
修改了相关部分:如下
再次运行:
make docker-build docker-push IMG=registry.cn-beijing.aliyuncs.com/anan_java/go_project_01:v1.0
成功构建镜像并上传到服务器
部署镜像到集群错误
错误:/bin/sh: kustomize: 未找到命令
# 重新安装
go install sigs.k8s.io/kustomize/kustomize/v3
再次运行,即可成功部署
可以看到创建出了新的命名空间
错误:ImagePullBackOffer
vim config/default/manager_auth_proxy_patch.yaml
替换成: image: registry.cn-hangzhou.aliyuncs.com/kubenode/kube-rbac-proxy:v0.4.0
然后删除系统创建的资源,重新构建镜像并发布,从make docker build那部分开始
注意:第一次从这个部分开始出现了错误,删除CRD重头开始了
第二次从这个部分开始就正确了
# 自己删除了 镜像,命名空间,还有仓库里的镜像
docker rmi 镜像号
kubectl delete ns 命名空间
手动删除仓库里的镜像
错误:have() want(context.Context)
构建镜像时出现:
实在看不下去了,直接删除CRDmake uninstall
重新开始了
参考资料
CRD介绍:
Kubernetes中CRD的介绍和使用
kubernetes1.9管中窥豹-CRD概念、使用场景及实例
Kuberneters(K8s)CRD资源详解
kubernetes CRD
CRD及Controller创建:
K8S中编写自己的CRD及Controller简明指南 主参考 Kind: Frigate
使用 kubebuilder 创建 operator 示例 主参考 --kind Guestbook
kubernetes CRD开发指南 --kind VirtulMachine
云计算虚拟化:k8s进阶-CRD项目部署
Kubebuilder: