DOCKER源码分析3 docker daemon路由初始化

上一篇介绍了docker daemon到serverapi的初始化过程,这一篇介绍从serverapi到docker run的调用;

上文分析到initRouter(api, d, c),它初始化了client发来的各种命令的路由,在其中可以追踪到对于create和start命令;

源码阅读基于docker 19.03

 

1. initRouter(api, d, c)路由初始化

1.1 源码

initRouter的实现位于moby/cmd/dockerd/daemon.go,代码的主要内容是:

func initRouter(opts routerOptions) {decoder:=runconfig.ContainerDecoder{}//获取解码器routers:=[]router.Router{...//与container的路由,例如/containers/create,/containers/{name:.*}/startcontainer.NewRouter(opts.daemon, decoder),...}//如果允许网络控制,则添加network相关的路由if opts.daemon.NetworkControllerEnabled() {routers = append(routers, network.NewRouter(opts.daemon, opts.cluster))
}//如果是experimental模式将所有路由数据项中的experimental模式下的api路由功能激活if opts.daemon.HasExperimental() {
        for _, r := range routers {
            for _, route := range r.Routes() {
                if experimental, ok := route.(router.ExperimentalRoute); ok {
                    experimental.Enable()
                }
            }
        }
//根据设置好的路由表routers来初始化apiServer的路由器opts.api.InitRouter(routers...)}

1.2 流程图

从图中可以看到,docker run发出的"/containers/create""/containers/{name:.*}/start"最后会分别路由到postContainersCreatepostContainersStart函数,在这两个函数中分别做容器创建和启动的工作,这两个函数将在后面的文章中分析,本文将进一步分析initRouter这个函数,了解清楚路由是如何分发的。 路由初始化过程分为以下三个步骤:

  • 向路由表routers中添加路由数据项router
  • 检验experimental模式,若为experimental模式则激活该模式下的api路由功能
  • 根据路由表初始化apiServer路由器

下面挑选步骤1和步骤3详细讲解。

 

2. 路由初始化步骤分析

2.1 路由表添加路由数据

对于docker run命令的路由表添加为container.NewRouter(d, decoder),代码为:

//从上文container.NewRouter(d, decoder)可以看出,传入的b=d,为daemon.Daemon实例//这里初始化了一个container相关的路由器对象funcNewRouter(bBackend,decoderhttputils.ContainerDecoder)router.Router{r:=&containerRouter{backend:b,decoder:decoder,}r.initRoutes()returnr}//初始化路由数据func(r*containerRouter)initRoutes(){r.routes=[]router.Route{...// POSTrouter.NewPostRoute("/containers/create",r.postContainersCreate),router.NewPostRoute("/containers/{name:.*}/start",r.postContainersStart),...}}

可以从上面的r.initRoutes()中看到,container相关的添加了create和start相关的路由,下面先看看router.NewPostRoute()函数:

//初始化一个以HTTP POST方式的路由,返回类型为RoutefuncNewPostRoute(pathstring,handlerhttputils.APIFunc)Route{returnNewRoute("POST",path,handler)}//初始化一个local routefuncNewRoute(method,pathstring,handlerhttputils.APIFunc)Route{returnlocalRoute{method,path,handler}}typelocalRoutestruct{methodstring//该路由中方法名pathstring//该路由中方法所在的路径handlerhttputils.APIFunc//该方法的handler}

以create为例,那么它的localRoute的method=”POST”,path=”/containers/create”,handler=r.postContainersCreate,所以当daemon收到client发来的"/containers/create"时,会调用r.postContainersCreate

 

2.2 apiServer路由器的初始化

该部分由s.InitRouter(debug.IsEnabled(), routers...)完成,代码如下:

func(s*Server)InitRouter(enableProfilerbool,routers...router.Router){s.routers=append(s.routers,routers...)//将创建好的路由表信息追加到apiServer对象中的routers。m:=s.createMux()//追加后再次初始化apiServer路由器进行更新,下文详细解释//这里设置好了mux.Route之后,将该route设置到apiServer的路由交换器中去,至此所有deamon.start()的相关工作处理完毕s.routerSwapper=&routerSwapper{router:m,}}

下面是s.createMux()的具体实现分析:

func(s*Server)createMux()*mux.Router{/*
	mux位于api\server\server.go ,该函数新建一个mux.go中的Route(路由数据项)对象并追加到mux.Router结构体中的成员routes中去,
     然后返回该路由器mux.Route m
	*/m:=mux.NewRouter()//遍历所有apiserver中的api路由器如:containerfor_,apiRouter:=ranges.routers{//遍历每个apiRouter的子命令路由r如"/containers/create"  for_,r:=rangeapiRouter.Routes(){//给每个r的路由handler包裹了一层中间件(这里还不是很清楚)f:=s.makeHTTPHandler(r.Handler())/*
			在mux.Route路由结构中根据这个r.Path()路径设置一个适配器来匹配方法method和handler,当满足versionMatcher+r.Path()路径的正则表达式要求就可以适配到相应的方法名及该handler
			*/m.Path(versionMatcher+r.Path()).Methods(r.Method()).Handler(f)//同上m.Path(r.Path()).Methods(r.Method()).Handler(f)}}...returnm}

结语

本文分析了从apiserver路由到具体的命令执行函数,相对于docker run就是到达r.postContainersCreater.postContainersStart,后面的文章会分别对这两个函数详细分析。

参考

posted @ 2020-12-25 10:08  luoyuna  阅读(324)  评论(0编辑  收藏  举报