• 【kubebuilder2.0】安装、源码分析


    什么是Kubebuilder

    Kubebuilder是一个用于在Go中快速构建和发布Kubernetes API的SDK。它建立在用于构建核心Kubernetes API的规范技术之上,以提供简化的抽象来减少开发工作。

    与Web开发框架(如Ruby on Rails和SpringBoot)类似,Kubebuilder提高了速度并降低了开发人员管理的复杂性。

    包含在Kubebuilder中:

    1、使用包括基本结构的项目初始化

    • 在规范版本中获取包依赖性。
    • 主程序入口点
    • 用于格式化,生成,测试和构建的Makefile
    • 用于构建容器映像的Dockerfile

    2、脚手架API

    • 资源(模型)定义
    • 控制器实现
    • 资源和控制器的集成测试
    • CRD定义

    3、用于实现API的简单抽象

    • Controllers
    • Resource Schema Validation
    • Validating Webhooks

    4、用于发布API以安装到集群中的工件

    • Namespace
    • CRDs
    • RBAC Roles and RoleBindings
    • Controller StatefulSet + Service

    API参考文档和示例
    Kubebuilder是在控制器运行时和控制器工具库之上开发的。

    环境准备

    Requirements

    除了上面的工具和环境以外,需要有一套可连接的kubernetes环境,要求配置好kubectl config,以便能直连进行调试。

    由于Feature gates的 --CustomResourceWebhookConversion参数是在v1.15及以上的版本k8s才默认为true,为了避免版本导致的额外问题,如果是新部署,建议安装v1.15.4 以上的版本,原有的集群版本较低的话请升级。

    参考这里:

    Feature gates

    安装

    go

    参考这里:Install Go

    docker

    mac安装包:

    wget https://download.docker.com/mac/stable/Docker.dmg

    linux根据不同的发行版来安装,网络资料很多,不再赘述

    kubebuilder

    二进制(推荐):

    os=$(go env GOOS)
    arch=$(go env GOARCH)
    curl
    -L https://go.kubebuilder.io/dl/2.3.1/${os}/${arch} | tar -xz -C /tmp/

    sudo mv /tmp/kubebuilder_2.3.1_${os}_${arch} /usr/local/kubebuilder export PATH=$PATH:/usr/local/kubebuilder/bin

    通过源码安装:

    git clone https://github.com/kubernetes-sigs/kubebuilder
    cd kubebuilder
    make build
    cp bin/kubebuilder $GOPATH/bin

    kustomize

    curl -s "https://raw.githubusercontent.com/
    kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"  | bash
    $ cat install_kustomize.sh

    #!/bin/bash # Downloads the most recently released kustomize binary # to your current working directory. # # Fails if the file already exists. where=$PWD if [ -f $where/kustomize ]; then echo "A file named kustomize already exists (remove it first)." exit 1 fi tmpDir=`mktemp -d` if [[ ! "$tmpDir" || ! -d "$tmpDir" ]]; then echo "Could not create temp dir." exit 1 fi function cleanup { rm -rf "$tmpDir" } trap cleanup EXIT pushd $tmpDir >& /dev/null opsys=windows if [[ "$OSTYPE" == linux* ]]; then opsys=linux elif [[ "$OSTYPE" == darwin* ]]; then opsys=darwin fi curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases | grep browser_download | grep $opsys | cut -d '"' -f 4 | grep /kustomize/v | sort | tail -n 1 | xargs curl -s -O -L tar xzf ./kustomize_v*_${opsys}_amd64.tar.gz cp ./kustomize $where popd >& /dev/null ./kustomize version echo kustomize installed to current directory.

    kubernetes

    安装方式众多,文档丰富,不再赘述。

    可参考:https://www.cnblogs.com/lizhewei/p/13366172.html

    创建项目

    查看现有的所有resource:

    kubectl api-resources -o wide

    查看现有的api groupVersion:

    kubectl api-versions

    Step 1: 初始化

    新建一个 gitlab 项目,运行

    mkdir $GOPATH/src/crd-demo
    cd $GOPATH/src/crd-demo
    export GO111MODULE=on
    
    # 如果路径位于GOPATH/src下,go mod这一步可省略
    go mod init ${CRD}

    在$GOPATH/src/crd-demo项目目录下执行
    kubebuilder init --domain=kruise.io

    参数解读:domain 指定了后续注册 CRD 对象的 Group 域名

    Step 2: 创建 API

    实际上不仅会创建 API,也就是 CRD,还会生成 Controller 的框架

    kubebuilder create api --group apps --version v1alpha1 --kind SidecarSet 

    参数解读:

    • group 加上之前的 domian 即此 CRD 的 Group: apps.kruise.io;

    • version 一般分三种,按社区标准;

      • v1alpha1: 此 api 不稳定,CRD 可能废弃、字段可能随时调整,不要依赖;

      • v1beta1: api 已稳定,会保证向后兼容,特性可能会调整;

      • v1: api 和特性都已稳定;

    • kind: 此 CRD 的类型,类似于社区原生的 Service 的概念;

    • namespaced: 此 CRD 是全局唯一还是 namespace 唯一,类似 node 和 Pod。

     目录结构

    Step 3:定义 CRD

    在图 2 中对应的文件定义 Spec 和 Status。

    Step 4:编写 Controller 逻辑

    在图 3 中对应的文件实现 Reconcile 逻辑。

    Step 5: 测试发布

    本地测试完之后使用 Kubebuilder 的 Makefile 构建镜像,部署我们的 CRDs 和 Controller 即可。

    源码阅读

    从 main.go 开始

    var (
        scheme   = runtime.NewScheme()
        setupLog = ctrl.Log.WithName("setup")
    )
    
    func init() {
        _ = clientgoscheme.AddToScheme(scheme)
    
        _ = extensionsv1alpha1.AddToScheme(scheme)
        // +kubebuilder:scaffold:scheme
    }
    
    func main() {
        var metricsAddr string
        var enableLeaderElection bool
        flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
        flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
            "Enable leader election for controller manager. "+
                "Enabling this will ensure there is only one active controller manager.")
        flag.Parse()
    
        ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
        
        // 1.init Manager
        mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
            Scheme:             scheme,
            MetricsBindAddress: metricsAddr,
            Port:               9443,
            LeaderElection:     enableLeaderElection,
            LeaderElectionID:   "f5e19998.sncloud.com",
        })
        if err != nil {
            setupLog.Error(err, "unable to start manager")
            os.Exit(1)
        }
        
        // 2.init Reconciler
        if err = (&controllers.SidecarSetReconciler{
            Client: mgr.GetClient(),
            Log:    ctrl.Log.WithName("controllers").WithName("SidecarSet"),
            Scheme: mgr.GetScheme(),
        }).SetupWithManager(mgr); err != nil {
            setupLog.Error(err, "unable to create controller", "controller", "SidecarSet")
            os.Exit(1)
        }
        // +kubebuilder:scaffold:builder
        
        setupLog.Info("starting manager")
        // 3.start Manager
        if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
            setupLog.Error(err, "problem running manager")
            os.Exit(1)
        }
    }

    可以看到在 init 方法里面我们将 appsv1alpha1 注册到 Scheme 里面去了,这样一来 Cache 就知道 watch 谁了,main 方法里面的逻辑基本都是 Manager 的:

    1. 初始化了一个 Manager;
    2. 将 Manager 的 Client 传给 Controller,并且调用 SetupWithManager 方法传入 Manager 进行 Controller 的初始化;
    3. 启动 Manager。

    我们的核心就是看这 3 个流程。

    Manager 初始化

    Manager 初始化代码如下

    // New returns a new Manager for creating Controllers.
    func New(config *rest.Config, options Options) (Manager, error) {
        
    ...

         // 创建Cache对象,用做client的读请求,以及生成informer
    cache, err := options.NewCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod, Namespace: options.Namespace}) if err != nil { return nil, err }

      // 创建读请求的client,即apiReader,读请求走的Cache
       apiReader, err := client.New(config, client.Options{Scheme: options.Scheme, Mapper: mapper})
        if err != nil {
            return nil, err
        }
      
      // 创建写请求的client,写请求直连APIServer writeObj, err :
    = options.NewClient(cache, config, client.Options{Scheme: options.Scheme, Mapper: mapper}) if err != nil { return nil, err }
    ...
    return &controllerManager{ config: config, scheme: options.Scheme, cache: cache, fieldIndexes: cache, client: writeObj, apiReader: apiReader, recorderProvider: recorderProvider, resourceLock: resourceLock, mapper: mapper, metricsListener: metricsListener, internalStop: stop, internalStopper: stop, port: options.Port, host: options.Host, certDir: options.CertDir, leaseDuration: *options.LeaseDuration, renewDeadline: *options.RenewDeadline, retryPeriod: *options.RetryPeriod, healthProbeListener: healthProbeListener, readinessEndpointName: options.ReadinessEndpointName, livenessEndpointName: options.LivenessEndpointName, }, nil }

    可以看到主要是创建 Cache 与 Clients。

    创建Clients

    ==> D:codegopkgmodsigs.k8s.iocontroller-runtime@v0.5.0pkgmanagermanager.go

    // defaultNewClient creates the default caching client
    func defaultNewClient(cache cache.Cache, config *rest.Config, options client.Options) (client.Client, error) {
        // Create the Client for Write operations.
        c, err := client.New(config, options)
        if err != nil {
            return nil, err
        }
    
        return &client.DelegatingClient{
            Reader: &client.DelegatingReader{
                CacheReader:  cache,
                ClientReader: c,
            },
            Writer:       c,
            StatusClient: c,
        }, nil
    }
  • 相关阅读:
    JZOJ Contest2633 总结
    6813. 【2020.10.05提高组模拟】送信
    HDU 1506 最大子矩形
    2020.10.07【NOIP提高A组】模拟 总结
    6815. 【2020.10.06提高组模拟】树的重心
    2020.10.06【NOIP提高A组】模拟 总结
    2020.10.05【NOIP提高A组】模拟 总结
    gmoj 3976. 【NOI2015模拟1.17】⑨
    2020.09.26【省选组】模拟 总结
    2020.09.19【省选组】模拟 总结
  • 原文地址:https://www.cnblogs.com/lizhewei/p/13214785.html
Copyright © 2020-2023  润新知