前言

kubernetes 的入门阶段是使用,进一步的深入就是基于 kubernetes api 进行开发应用。kubernetes 开发的入门就是了解 CRD 的创建,以及 CRD 资源状态的监听。
pod,deployment,node 等是 kubernetes 自带的资源,CRD 是我们自定义的资源。

kubernetes api 客户端主流的是 client-go, java 也有 kubernetes-client,这些客户端默认都只提供 k8s 原生资源的操作,如果是自定义的 CRD,就需要额外借助一些代码生成的工具来新增相关的客户端类,如 go-generate、kubebuild。

下面介绍 go 进行 k8s 开发的一个代码脚手架。java 相对应的脚手架后续也会安排。

技术栈说明

go-restful

go-restful 是一个 Golang 第三方库,是一个轻量的 RESTful API 框架,基于 Golang Build-in 的 http/net 库。
适用于构建灵活多变的 Web Application,k8s中许多框架都使用了go-restful, 包括 kubernetes 的api server。

kubebuilder

kubebuilder 是kubernetes提供的用于开发自定义CRD的工具集(SDK),通过kubebuilder可以在Go中快速构建和发布Kubernetes API。一般会借助他来生成 CRD 对应的 yaml 文件。

code-generator

跟 kubebuilder 类似,但 code-generate 生成的东西比较多,也比较复杂。 可以用它生成Informers,Clientset,listers,自定义workqueue消费k8s产生的事件,而通过kubebuilder无法生成 Informers信息。

client-go

client-go 是k8s官方提供的用于与api-server交互的客户端,client-go 提供的一系列的util工具,包括informer list-watch
机制,workqueue 机制等,如果自定义CRD 要与k8s交互,client-go是必不可少的一个包。

脚手架说明

目录结构

- hotwhell
  -- build      //放置 dockerfile
  -- cmd        //应用的入口函数
  -- docs       //说明文档
  -- examples   //实例代码,crd定义文件
  -- hack       //放置shell脚本等
  -- pkg        //项目的主要逻辑代码,如controlle等
  -- test       //放置e2e测试代码
  -- tools      //swagger生成的代码放置在此目录下
  -- vendor     //第三方数据包
  -- go.mod     //go 版本管理
  -- go.vendor  //go 版本管理
  -- Makefile   //make 命令集合

启动流程

go 的包依赖模式,vendor 与 mod 本来是 2 种对立的模式,vendor 的包会依赖 gopath,mod 模式下不会。该项目同时拥有 mod 和 vendor,因为 mod 中做了兼容处理,如果项目中有 vendor 会优先检索 vendor中的包。而引入 vendor 的原因是可以将 vendor 纳入 git 依赖,其他人员可以不用重复下载。
// 开启 mod 模式,设置打包成 linux amd64 的包,指定包下载代理
export GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOPROXY="https://goproxy.cn,direct"

go mod download

go mod vendor

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/hotwheel-agent cmd/hotwheel/main.go

Makefile 文件说明

生成 clientset,informers,listers

client:
 ./hack/generate_client.sh

生成自定义 CRD 的 yaml 文件

manifests:
 go run ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/... rbac:roleName=controller-perms "crd:trivialVersions=true" output:crd:artifacts:config=examples/crds

生成 swagger api

openapi:
 go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./vendor/k8s.io/apimachinery/pkg/runtime,./vendor/k8s.io/api/core/v1,./vendor/k8s.io/api/apps/v1,yummy.tech/hotwheel/pkg/apis/cluster/v1alpha1 -o ./ -p pkg/apis/cluster/v1alpha1 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list
 go run ./tools/cmd/doc-gen/main.go

生成docker镜像

release: build-go
 docker build -t hotwheel-agent:latest -f build/hotwheel/Dockerfile .

编译

build-go: fmt vet
 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/hotwheel cmd/hotwheel/main.go

代码说明

1.入口

go build -o bin/hotwheel-agent cmd/hotwheel/main.go
从启动命令我们知道入口方法在 cmd/hotwheel/main.go

package main

import (
 "log"

 "yummy.tech/hotwheel/cmd/hotwheel/app"
)

func main() {
 cmd := app.NewHotWheelCommand()

 if err := cmd.Execute(); err != nil {
  log.Fatalln(err)
 }
}

2. cobra 命令工具

main 里面调用了 app.NewHotWheelCommand()

func NewHotWheelCommand() *cobra.Command {
 c := config.NewConfig()

 cmd := &cobra.Command{
  Use:  "hotWheel agent",
  Long: `HotWheel is one of the magic weapons of Ne Zha, a well-known legend in Chinese mythology`,
  Run: func(cmd *cobra.Command, args []string) {
   if errs := c.Validate(); len(errs) != 0 {
    klog.Error(errors.NewAggregate(errs))
    os.Exit(1)
   }

   if err := run(c, signals.SetupSignalHandler()); err != nil {
    klog.Error(err)
    os.Exit(1)
   }
  },
  SilenceUsage: true,
 }

 fs := cmd.Flags()
 namedFlagSets := c.Flags()
 for _, f := range namedFlagSets.FlagSets {
  fs.AddFlagSet(f)
 }

 return cmd
}

入口的函数里面用到了 cobra的一个库,一般应用启动的时候支持用户输入一些指定的参数,而这个库的作用就是提供了丰富的命令参数。这个库在 kubernetes 源码中也有用到,就像如下 kubectl 支持 command,flag 等多种参数。

$ kubectl
kubectl controls the Kubernetes cluster manager.

Find more information at https://github.com/kubernetes/kubernetes.

Usage:
  kubectl [flags]
  kubectl [command]

Available Commands:
  get            Display one or many resources
  describe       Show details of a specific resource or group of resources

3.初始化业务

// 初始化对象,执行apiServer和controllerManager
func run(c *config.Config, stopCh <-chan struct{}) (err error) {
 k8sClient, err := k8s.NewKubernetesClient(c.K8sOptions)
 if err != nil {
  klog.Errorf("failed new kubernetes client with error: %v", err)
  return err
 }

 informerFactory := informers.NewInformerFactories(k8sClient)
 apiServer, err := apiserver.NewAPIServer(c, k8sClient, informerFactory, stopCh)
 ...

cobra 里面触发了 run 函数,run 函数里面初始化了 k8s 客户端,informerfactory,apiserver 一个 http 服务。

4. http 服务

借助 go-restful 启动的 http 服务。

func (a *APIServer) PrepareRun() error {
 // 创建一个空的Container
 container := restful.NewContainer()
 // 设置 过滤器 打印请求过程的 log 日志
 container.Filter(logRequestAndResponse)
 // 设定路由为CurlyRouter(快速路由)
 container.Router(restful.CurlyRouter{})
 // 配置panic产生之后的恢复处理函数
 container.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) {
  logStackOnRecover(panicReason, httpWriter)
 })

 // 路由注册
 a.container = container
 a.registerContainers()

 //设置集群是否正常
 handler := filter.WithHealthCheck(a.container, a.clusterClient)

 //多集群转发策略
 clusterDispatcher := dispatch.NewClusterDispatch(a.clusterClient)
 handler = filter.WithClusterDispatcher(handler, clusterDispatcher)

 //authentication 认证
 //jwtAuthenticator := jwttoken.NewJwtAuthenticator()
 //handler = filter.WithAuthorization(handler, jwtAuthenticator)

 //获取到请求信息
 requestInfoFactory := &request.RequestInfoFactory{
  APIPrefixes:          sets.NewString("api", "apis", "kapis", "kapi"),
  GrouplessAPIPrefixes: sets.NewString("api", "kapi"),
 }
 handler = filter.WithRequestInfo(handler, requestInfoFactory)
 a.server.Handler = handler
 return nil
}

定义了路由与 handler 处理类

// 添加路由, 类似于MVC中的路由功能,在container里面有register跟handler
func (a *APIServer) registerContainers() {
 urlruntime.Must(terminalv1alpha1.AddToContainer(a.container, a.clusterClient))
 urlruntime.Must(clusterv1alpha1.AddToContainer(a.container, a.k8sClient.Hotwheel().ClusterV1alpha1()))
 urlruntime.Must(resourcev1alpha1.AddToContainer(a.container, a.clusterClient))
}

到此,启动的主流程都已介绍完毕。下面是一些分支流程

CRD 创建

CRD 官方链接说明
CRD 官方示例

  1. 用 kubebuilder 生成 CRD 的定义

    kubebuilder create api --group cluster.yummy.tech --version v1alpha1 --kind cluster

    会自动生成

    • pkg/apis/cluster/v1alpha1 CRD api 相关定义代码
    • apk/controller/cluster CRD 对应的 Operator 实现的代码
  2. 生成 CRD 的 mainfests,也就是 yaml 文件

    步骤 1 生成好了相关代码,对应的文件里面有注释提示语,指明要修改的地方,需要根据自己的需求定义 CRD 相关代码。
    使用make manifests可以快速的在examples/crds 目录下生成资源定义

    # Generate manifests e.g. CRD, RBAC etc.  生成 crd yaml
    manifests:
     go run ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go 
    object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/... rbac:roleName=controller-perms 
    "crd:trivialVersions=true" output:crd:artifacts:config=examples/crds
    

    controller-tools 是 kubernetes 的一个子项目

  3. code-generator 生成相关客户端代码
    步骤1中的 kubebuilder 只能生成 controller 对象,可以借助 code-generator 生成其他资源。通过make client 可以在pkg/client目录下生成clientset等信息

后期都是通过Informer list-watch 机制与k8s交互

Makefile

client:
 ./hack/generate_client.sh

generate_client.sh

set -o errexit
set -o nounset
set -o pipefail
set -x

SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/..
CODEGEN_PKG=${SCRIPT_ROOT}/vendor/k8s.io/code-generator/generate-groups.sh
chmod +x $CODEGEN_PKG

rm -rf ${SCRIPT_ROOT}/pkg/client

# the group version
GV="cluster:v1alpha1"

# generate the code with:
# --output-base    because this script should also be able to run inside the vendor dir of
#                  k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
#                  instead of the $GOPATH directly. For normal projects this can be dropped
$CODEGEN_PKG "deepcopy,client,informer,lister" yummy.tech/hotwheel/pkg/client yummy.tech/hotwheel/pkg/apis "$GV" --output-base=./ --go-header-file=${SCRIPT_ROOT}/hack/boilerplate.go.txt

mv yummy.tech/hotwheel/pkg/client ${SCRIPT_ROOT}/pkg
mv yummy.tech/hotwheel/pkg/apis/cluster/v1alpha1/zz_generated.deepcopy.go ${SCRIPT_ROOT}/pkg/apis/cluster/v1alpha1

rm -rf yummy.tech

点赞(5) 打赏

Comment list 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部