在 cmd/dockerd/daemon.go 文件 initRouter 函数中 初始化 router
routers := []router.Router{
checkpointrouter.NewRouter(d, decoder),
container.NewRouter(d, decoder),
image.NewRouter(d, decoder),
systemrouter.NewRouter(d, c),
volume.NewRouter(d),
build.NewRouter(dockerfile.NewBuildManager(d)),
swarmrouter.NewRouter(c),
pluginrouter.NewRouter(d.PluginManager()),
}
以pull命令为例, image的处理函数在 api/server/router/image 的image 包
image.go 新建路由 NewRouter 函数创建 imageRouter 实例并初始化路由
func (r *imageRouter) initRoutes() { r.routes = []router.Route{ // GET router.NewGetRoute("/images/json", r.getImagesJSON), router.NewGetRoute("/images/search", r.getImagesSearch), router.NewGetRoute("/images/get", r.getImagesGet), router.NewGetRoute("/images/{name:.*}/get", r.getImagesGet), router.NewGetRoute("/images/{name:.*}/history", r.getImagesHistory), router.NewGetRoute("/images/{name:.*}/json", r.getImagesByName), // POST router.NewPostRoute("/commit", r.postCommit), router.NewPostRoute("/images/load", r.postImagesLoad), router.Cancellable(router.NewPostRoute("/images/create", r.postImagesCreate)), router.Cancellable(router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush)), router.NewPostRoute("/images/{name:.*}/tag", r.postImagesTag), router.NewPostRoute("/images/prune", r.postImagesPrune), // DELETE router.NewDeleteRoute("/images/{name:.*}", r.deleteImages), } }
pull命令对应的restful api是"/images/create", 会调用 imageRouter 的postImagesCreate 函数,函数位于image包的image_routes.go
func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { ... if image != "" { //pull .... err = s.backend.PullImage(ctx, image, tag, metaHeaders, authConfig, output) } else { //import ... err = s.backend.ImportImage(src, repo, tag, message, r.Body, output, r.Form["changes"]) } ... }
函数解码之后对于pull命令调用imageRouter 的 backend.PullImage 。
image包中backend.go是一些接口,真正的backend是初始化时传入的daemon,因此调用的是 daemon/image_pull.go的 PullImage 函数
func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { ... return daemon.pullImageWithReference(ctx, ref, metaHeaders, authConfig, outStream) }
如果传入tag,则需要处理tag,然后调用pullImageWithReference 函数
func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { progressChan := make(chan progress.Progress, 100) writesDone := make(chan struct{}) ctx, cancelFunc := context.WithCancel(ctx) go func() { progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan) close(writesDone) }() imagePullConfig := &distribution.ImagePullConfig{ .... } err := distribution.Pull(ctx, ref, imagePullConfig) close(progressChan) <-writesDone return err }
progressChan 通道是用来显示pull操作的状态, writesDone 用来等待协程处理结束,创建config实例,调用 distribution/pull.go 内的Pull 函数
func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullConfig) error { repoInfo, err := imagePullConfig.RegistryService.ResolveRepository(ref) ... endpoints, err := imagePullConfig.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name)) ..... for _, endpoint := range endpoints { ... puller, err := newPuller(endpoint, repoInfo, imagePullConfig) ... if err := puller.Pull(ctx, ref); err != nil { ..... } ... } .... }
获取仓库端信息,遍历端点,根据api版本创建V1或V2版本的puller的Pull 函数,先分析到这里