Kubernetes中自定义Controller(上)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: Kubernetes中自定义Controller

大家好,我是乔克。


在Kubernetes中,Pod是最小的调度单元,它由各种各样的Controller管理,比如ReplicaSet Controller,Deployment Controller等。


Kubernetes内置了许多Controller,这些Controller能满足80%的业务需求,但是企业里也难免需要自定义Controller来适配自己的业务需求。


网上自定义Controller的文章很多,基本都差不多。俗话说:光说不练假把式,本篇文章主要是自己的一个实践归档总结,如果对你有帮助,可以一键三连

本文主要从以下几个方面进行介绍,其中包括理论部分和具体实践部分。


640.png


Controller的实现逻辑


当我们向kube-apiserver提出创建一个Deployment需求的时候,首先是会把这个需求存储到Etcd中,如果这时候没有Controller的话,这条数据仅仅是存在Etcd中,并没有产生实际的作用。


所以就有了Deployment Controller,它实时监听kube-apiserver中的Deployment对象,如果对象有增加、删除、修改等变化,它就会做出相应的相应处理,如下:


// pkg/controller/deployment/deployment_controller.go 121行
.....
    dInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  AddFunc:    dc.addDeployment,
  UpdateFunc: dc.updateDeployment,
  // This will enter the sync loop and no-op, because the deployment has been deleted from the store.
  DeleteFunc: dc.deleteDeployment,
 })
......


其实现的逻辑图如下(图片来自网络):


640.png


可以看到图的上半部分都由client-go实现了,下半部分才是我们具体需要去处理的。


client-go主要包含ReflectorInformerIndexer三个组件。


  • ReflectorList&Watch kube-apiserver中的特定资源,然后会把变化的资源放入Delta FIFO队列中。
  • Informer会从Delta FIFO队列中拿取对象交给相应的HandleDeltas
  • Indexer会将对象存储到缓存中。


上面部分不需要我们去开发,我们主要关注下半部分。


当把数据交给Informer的回调函数HandleDeltas后,Distribute会将资源对象分发到具体的处理函数,这些处理函数通过一系列判断过后,把满足需求的对象放入Workqueue中,然后再进行后续的处理。


code-generator介绍


上一节说到我们只需要去实现具体的业务需求,这是为什么呢?主要是因为kubernetes为我们提供了code-generator【1】这样的代码生成器工具,可以通过它自动生成客户端访问的一些代码,比如InformerClientSet等。


code-generator提供了以下工具为Kubernetes中的资源生成代码:


  • deepcopy-gen:生成深度拷贝方法,为每个 T 类型生成 func (t* T) DeepCopy() *T 方法,API 类型都需要实现深拷贝
  • client-gen:为资源生成标准的 clientset
  • informer-gen:生成 informer,提供事件机制来响应资源的事件
  • lister-gen:生成 Lister**,**为 get 和 list 请求提供只读缓存层(通过 indexer 获取)

如果需要自动生成,就需要在代码中加入对应格式的配置,如下:


640.png


其中:


  • // +genclient表示需要创建client
  • // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object表示在需要实现k8s.io/apimachinery/pkg/runtime.Object这个接口


除此还有更多的用法,可以参考Kubernetes Deep Dive: Code Generation for CustomResources【2】进行学习。


CRD介绍


CRD全称CustomResourceDefinition,中文简称自定义资源,上面说的Controller主要就是用来管理自定义的资源


我们可以通过下面命令来查看当前集群中使用了哪些CRD,如下:


# kubectl get crd
NAME                                                 CREATED AT
ackalertrules.alert.alibabacloud.com                 2021-06-15T02:19:59Z
alertmanagers.monitoring.coreos.com                  2019-12-12T12:50:00Z
aliyunlogconfigs.log.alibabacloud.com                2019-12-02T10:15:02Z
apmservers.apm.k8s.elastic.co                        2020-09-14T01:52:53Z
batchreleases.alicloud.com                           2019-12-02T10:15:53Z
beats.beat.k8s.elastic.co                            2020-09-14T01:52:53Z
chaosblades.chaosblade.io                            2021-06-15T02:30:54Z
elasticsearches.elasticsearch.k8s.elastic.co         2020-09-14T01:52:53Z
enterprisesearches.enterprisesearch.k8s.elastic.co   2020-09-14T01:52:53Z
globaljobs.jobs.aliyun.com                           2020-04-26T14:40:53Z
kibanas.kibana.k8s.elastic.co                        2020-09-14T01:52:54Z
prometheuses.monitoring.coreos.com                   2019-12-12T12:50:01Z
prometheusrules.monitoring.coreos.com                2019-12-12T12:50:02Z
servicemonitors.monitoring.coreos.com                2019-12-12T12:50:03Z


但是仅仅是创建一个CRD对象是不够的,因为它是静态的,创建过后仅仅是保存在Etcd中,如果需要其有意义,就需要Controller配合。


创建CRD的例子如下:


apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # name 必须匹配下面的spec字段:<plural>.<group>
  name: students.coolops.io
spec:
  # group 名用于 REST API 中的定义:/apis/<group>/<version>
  group: coolops.io
   # 列出自定义资源的所有 API 版本
  versions:
  - name: v1    # 版本名称,比如 v1、v1beta1
    served: true    # 是否开启通过 REST APIs 访问 `/apis/<group>/<version>/...`
    storage: true   # 必须将一个且只有一个版本标记为存储版本
    schema:         # 定义自定义对象的声明规范
      openAPIV3Schema:
        type: object
        properties:
          spec:
            type: object
            properties:
              name:
                type: string
              school:
                type: string
  scope: Namespaced    # 定义作用范围:Namespaced(命名空间级别)或者 Cluster(整个集群)
  names:
    plural: students   # plural 名字用于 REST API 中的定义:/apis/<group>/<version>/<plural>
    shortNames:        # shortNames 相当于缩写形式
    - stu
    kind: Student      # kind 是 sigular 的一个驼峰形式定义,在资源清单中会使用 
    singular: student  # singular 名称用于 CLI 操作或显示的一个别名


具体演示


本来准备根据官方的demo【3】进行讲解,但是感觉有点敷衍,而且这类教程网上一大堆,所以就准备自己实现一个数据库管理的一个Controller。


因为是演示怎么开发Controller,所以功能不会复杂,主要的功能是:


  • 创建数据库实例
  • 删除数据库实例
  • 更新数据库实例


开发环境说明


本次实验环境如下:


软件 版本
kubernetes v1.22.3
go 1.17.3
操作系统 CentOS 7.6


创建CRD


CRD是基础,Controller主要是为CRD服务的,所以我们要先定义好CRD资源,便于开发。


apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databasemanagers.coolops.cn
spec:
  group: coolops.cn
  versions:
    - name: v1alpha1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                deploymentName:
                  type: strin
                replicas:
                  type: integer
                  minimum: 1
                  maximum: 10
                dbtype:
                  type: string
            status:
              type: object
              properties:
                availableReplicas:
                  type: integer
  names:
    kind: DatabaseManager
    plural: databasemanagers
    singular: databasemanager
    shortNames:
      - dm
  scope: Namespaced


创建CRD,检验是否能创建成功。


# kubectl apply -f crd.yaml 
customresourcedefinition.apiextensions.k8s.io/databasemanagers.coolops.cn created
# kubectl get crd | grep databasemanagers
databasemanagers.coolops.cn                           2021-11-22T02:31:29Z


自定义一个测试用例,如下:


apiVersion: coolops.cn/v1alpha1
kind: DatabaseManager
metadata:
  name: example-mysql
spec:
  dbtype: "mysql"
  deploymentName: "example-mysql"
  replicas: 1


创建后进行查看:


# kubectl apply -f example-mysql.yaml 
databasemanager.coolops.cn/example-mysql created
# kubectl get dm
NAME            AGE
example-mysql   9s


不过现在仅仅是创建了一个静态数据,并没有任何实际的应用,下面来编写Controller来管理这个CRD。


开发Controller


项目地址:https://gitee.com/coolops/database-manager-controller


自动生成代码


1、创建项目目录database-manager-controller,并进行go mod 初始化


# mkdir database-manager-controller
# cd database-manager-controller
# go mod init


2、创建源码包目录pkg/apis/databasemanager


# mkdir pkg/apis/databasemanager -p
# cd pkg/apis/databasemanager


3、在pkg/apis/databasemanager目录下创建register.go文件,并写入一下内容


package databasemanager
// GroupName is the group for database manager
const (
 GroupName = "coolops.cn"
)


4、在pkg/apis/databasemanager目录下创建v1alpha1目录,进行版本管理


# mkdir v1alpha1
# cd v1alpha1


5、在v1alpha1目录下创建doc.go文件,并写入以下内容


// +k8s:deepcopy-gen=package
// +groupName=coolops.cn
// Package v1alpha1 is the v1alpha1 version of the API
package v1alpha1


其中// +k8s:deepcopy-gen=package// +groupName=coolops.cn都是为了自动生成代码而写的配置。


6、在v1alpha1目录下创建type.go文件,并写入以下内容


package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type DatabaseManager struct {
 metav1.TypeMeta   `json:",inline"`
 metav1.ObjectMeta `json:"metadata,omitempty"`
 Spec              DatabaseManagerSpec   `json:"spec"`
 Status            DatabaseManagerStatus `json:"status"`
}
// DatabaseManagerSpec 期望状态
type DatabaseManagerSpec struct {
 DeploymentName string `json:"deploymentName"`
 Replicas       *int32 `json:"replicas"`
 Dbtype         string `json:"dbtype"`
}
// DatabaseManagerStatus 当前状态
type DatabaseManagerStatus struct {
 AvailableReplicas int32 `json:"availableReplicas"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DatabaseManagerList is a list of DatabaseManagerList resources
type DatabaseManagerList struct {
 metav1.TypeMeta `json:",inline"`
 metav1.ListMeta `json:"metadata"`
 Items []DatabaseManager `json:"items"`
}


type.go主要定义我们的资源类型。


7、在v1alpha1目录下创建register.go文件,并写入以下内容


package v1alpha1
import (
 dbcontroller "database-manager-controller/pkg/apis/databasemanager"
 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 "k8s.io/apimachinery/pkg/runtime"
 "k8s.io/apimachinery/pkg/runtime/schema"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: dbcontroller.GroupName, Version: dbcontroller.Version}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
 return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
 return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
 // SchemeBuilder initializes a scheme builder
 SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
 // AddToScheme is a global function that registers this API group & version to a scheme
 AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
 scheme.AddKnownTypes(SchemeGroupVersion,
  &DatabaseManager{},
  &DatabaseManagerList{},
 )
 metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
 return nil
}


register.go的作用是通过addKnownTypes方法使得client可以知道DatabaseManager类型的API对象。


至此,自动生成代码的准备工作完成了,目前的代码目录结构如下:


# tree .
.
├── artifacts
│   └── database-manager
│       ├── crd.yaml
│       └── example-mysql.yaml
├── go.mod
├── go.sum
├── LICENSE
├── pkg
│   └── apis
│       └── databasemanager
│           ├── register.go
│           └── v1alpha1
│               ├── doc.go
│               ├── register.go
│               └── type.go


接下里就使用code-generator进行代码自动生成了。


相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
28天前
|
Kubernetes 负载均衡 应用服务中间件
k8s学习--ingress详细解释与应用(nginx ingress controller))
k8s学习--ingress详细解释与应用(nginx ingress controller))
|
3月前
|
弹性计算 运维 Kubernetes
Kubernetes(K8S) Controller - Deployment 介绍
Kubernetes(K8S) Controller - Deployment 介绍
32 1
|
3月前
|
Kubernetes 容器 Perl
在K8S中,Replica Set和Replication Controller之间有什么区别?
在K8S中,Replica Set和Replication Controller之间有什么区别?
|
3月前
|
存储 Kubernetes 关系型数据库
Kubernetes(K8S) Controller - StatefulSet、DaemonSet 介绍
Kubernetes(K8S) Controller - StatefulSet、DaemonSet 介绍
32 0
|
6月前
|
运维 Kubernetes 容灾
kubernetes核心技术之Controller控制器知识总结
kubernetes核心技术之Controller控制器知识总结
57 1
|
6月前
|
Kubernetes JavaScript API
云效常见问题之appstack连接自定义k8s连不上如何解决
云效(CloudEfficiency)是阿里云提供的一套软件研发效能平台,旨在通过工程效能、项目管理、质量保障等工具与服务,帮助企业提高软件研发的效率和质量。本合集是云效使用中可能遇到的一些常见问题及其答案的汇总。
328 1
|
6月前
|
消息中间件 Kubernetes 监控
kubernetes—Controller详解(二)
kubernetes—Controller详解(二)
77 0
|
6月前
|
Kubernetes 测试技术 Perl
kubernetes—Controller详解(一)
kubernetes—Controller详解(一)
84 0
|
6天前
|
JSON Kubernetes 容灾
ACK One应用分发上线:高效管理多集群应用
ACK One应用分发上线,主要介绍了新能力的使用场景
|
7天前
|
Kubernetes 持续交付 开发工具
ACK One GitOps:ApplicationSet UI简化多集群GitOps应用管理
ACK One GitOps新发布了多集群应用控制台,支持管理Argo CD ApplicationSet,提升大规模应用和集群的多集群GitOps应用分发管理体验。