2024-03-19
技术笔记
00
请注意,本文编写于 60 天前,最后修改于 60 天前,其中某些信息可能已经过时。

目录

启动
初始化
Run函数
web框架
kubesphere自有api
buildHandlerChain

本文基于kubesphere v3.3.2版本解读ks-apiserver的启动及路由注册相关代码

启动

初始化

命令行框架使用cobra,在cmd/ks-apiserver/app/server.go 使用NewAPIServerCommand创建一个cobra command然后execute.

NewAPIServerCommand先从文件读取配置:

go
conf, err := apiserverconfig.TryLoadFromDisk()

配置使用的是viper包,数据结构在pkg/apiserver/config定义。

然后构建一个cobra Command,将Run函数注册进去。

注册一个helper函数,用于打印命令行帮助信息。

go
usageFmt := "Usage:\n %s\n" cols, _, _ := term.TerminalSize(cmd.OutOrStdout()) cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { fmt.Fprintf(cmd.OutOrStdout(), "%s\n\n"+usageFmt, cmd.Long, cmd.UseLine()) cliflag.PrintSections(cmd.OutOrStdout(), namedFlagSets, cols) })

添加一个version子命令,用于打印版本信息。

Run函数

首先调用NewAPIServer构建一个APIServer对象(cmd/ks-apiserver/app/options/options),在这个函数中会把所有要用到的client对象、informer、http server以及其它的helper给创建出来,放到APIServer中。

然后调用apiserver.PrepareRun,这个函数主要是用来注册路由,下面详解。

最后但用apiserver.Run, 调用waitForResourceSync等待informer同步,然后启动http server.

注意Run函数的声明:

go
func Run(s *options.ServerRunOptions, ctx context.Context) error {

参数里的context,是在调用时传入,这里用到了k8s社区的signals包注册信号处理:

go
import ( "sigs.k8s.io/controller-runtime/pkg/manager/signals ) // ...... return Run(s, signals.SetupSignalHandler())

这个context会传给apiserver.Run, 当context close时关闭http server.

web框架

ks-apiserver使用"github.com/emicklei/go-restful"框架,在这个框架中一个路由对象称为Container. 在apiserver的PrepareRun函数中(pkg/apiserver/apiserver.go)注册handler以及midware. 包含以下几种路由:

  • installKubesphereAPIs 注册kubesphere自有的api
  • buildHandlerChain -> WithKubeAPIServer 将/api开头的请求转发到k8s apiserver
  • buildHandlerChain -> WithMultipleClusterDispatcher 将url中指定集群的请求转发过去

kubesphere自有api

kubesphere的api在pkg/kapis/中定义,每一类api都会定义一个AddToContainer函数,然后在installKubesphereAPIs中调用,以notification为例:

go
urlruntime.Must(notificationv1.AddToContainer(s.container, s.Config.NotificationOptions.Endpoint))

notification有三个版本,v1实际上没有内容,只是把请求转发到其它的版本:

go
// there are no versions specified cause we want to proxy all versions of requests to backend service var GroupVersion = schema.GroupVersion{Group: "notification.kubesphere.io", Version: ""} func AddToContainer(container *restful.Container, endpoint string) error { proxy, err := generic.NewGenericProxy(endpoint, GroupVersion.Group, GroupVersion.Version) if err != nil { return err } return proxy.AddToContainer(container) }

notification v2beta1的AddToContainer是这样的:

go
func AddToContainer(container *restful.Container, option *nm.Options) error { //创建一个接口handler h := newHandler(option) //创建一个webservice,相当于一个路由group ws := runtime.NewWebService(GroupVersion) //注册路由 ws.Route(ws.POST("/configs/notification/verification"). Reads(""). To(h.Verify). Returns(http.StatusOK, api.StatusOK, http.Response{}.Body)). Doc("Provide validation for notification-manager information") ws.Route(ws.POST("/configs/notification/users/{user}/verification"). To(h.Verify). Param(ws.PathParameter("user", "user name")). Returns(http.StatusOK, api.StatusOK, http.Response{}.Body)). Doc("Provide validation for notification-manager information") container.Add(ws) return nil }

buildHandlerChain

buildHandlerChain函数用于给apiserver的http server添加调用链,相当于配置http中间件。添加的顺序是:

go
//转发k8s原生api请求 handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{}) //审计 handler = filters.WithAuditing(handler,audit.NewAuditing(s.InformerFactory, s.Config.AuditingOptions, stopCh)) //鉴权 handler = filters.WithAuthorization(handler, authorizers) //身份认证 handler = filters.WithAuthentication(handler, authn) //保存请求信息到context handler = filters.WithRequestInfo(handler, requestInfoResolver)

执行时,最后添加的handler会最先执行,也就是说,首先执行的是将解析请求为RequestInfo然后保存到context中。

本文作者:renbear

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC 2.0 许可协议。转载请注明出处!