docker命令之push
来源:互联网 发布:js判断两个字符串相等 编辑:程序博客网 时间:2024/06/07 22:24
1 背景
NAME
docker-push - Push an image or a repository to the registry
SYNOPSIS
docker push NAME[:TAG]
DESCRIPTION
Push an image or a repository to a registry. The default registry is the Docker Index located at index.docker.io
(https://index.docker.io/v1/). However the image can be pushed to another, perhaps private, registry as demonstrated
in the example below.
本文以私有registry为例
registry地址:127.0.0.1
port:5000
namespace:name
repository:repo
tag:空
docker version:1.0
docker registry:0.9
2 代码分析
docker push 127.0.0.1:5000/namespace/repo
2.1 docker client发起push请求
代码位置:api/client/command.go:CmdPush
此方法是从docker命令行接收参数,并分析命令行所给的参数,给docker http server发送请求。主要解析的是127.0.0.1:5000/namespace/repo, 从这个参数中解析出registry的地址 127.0.0.1:5000,namespace:namespace,repository:repo,tag:空,另外从HOME命令下的.dockercfg中得到auth和email,其中auth是username和password的加密。两者用于在registry端进行认证,如果找不到.dockercfg文件,docker客户端会使用空间的user和password发送给registry端。
获取命令行参数,其中name=127.0.0.1:5000/namespace/repo
name := cmd.Arg(0)if name == "" {cmd.Usage()return nil}解析HOME目录下的.dockercfg文件。首先根据命令行参数的name,解析出remote和tag,此例中,remote=127.0.0.1:5000/namespce/repo,tag=空
再从remote中解析出hostname=127.0.0.1:5000,然后从.dockercfg中找到hostname对应的项,.dockercfg这是以key:value方式存储的,key就是registry的hostname,value就是auth和email。根据“/”解析namespace和repository是否合法。
<span style="white-space:pre"></span>cli.LoadConfigFile()remote, tag := parsers.ParseRepositoryTag(name)// Resolve the Repository name from fqn to hostname + namehostname, _, err := registry.ResolveRepositoryName(remote)if err != nil {return err}// Resolve the Auth config relevant for this serverauthConfig := cli.configFile.ResolveAuthConfig(hostname)// If we're not using a custom registry, we know the restrictions// applied to repository names and can warn the user in advance.// Custom repositories can have different rules, and we must also// allow pushing by image ID.if len(strings.SplitN(name, "/", 2)) == 1 {username := cli.configFile.Configs[registry.IndexServerAddress()].Usernameif username == "" {username = "<user>"}return fmt.Errorf("You cannot push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)", username, name)}在获取的PUSH http请求的要素后,构建push动作的执行体。将认证信息放在“X-Registry-Auth”头中。
push := func(authConfig registry.AuthConfig) error {buf, err := json.Marshal(authConfig)if err != nil {return err}registryAuthHeader := []string{base64.URLEncoding.EncodeToString(buf),}return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{"X-Registry-Auth": ,})}
如果push请求失败,根据状态码是否是401来决定是否发起login请求。在login请求中同样携带auth认证信息。
if err := push(authConfig); err != nil {if strings.Contains(err.Error(), "Status 401") {fmt.Fprintln(cli.out, "\nPlease login prior to push:")if err := cli.CmdLogin(hostname); err != nil {return err}authConfig := cli.configFile.ResolveAuthConfig(hostname)return push(authConfig)}return err}
2.2 docker httpserver处理push请求
push命令在httpserver的路由项在api/server/server.go中有createRouter方法,它定义了各种请求对应的handler。其中"/images/{name:.*}/push":postImagesPush,在这个方法中,主要解析docker client过来的请求,并设置job环境变量,为job启动做准备。postImagesPush的主要任务是解析从客户端过来的请求,准备job的环境变量,启动一个job。解析X-Rigestry-Auth头信息,如何没有此头,使用空的auth信息。
authConfig := ®istry.AuthConfig{}authEncoded := r.Header.Get("X-Registry-Auth")if authEncoded != "" {// the new format is to handle the authConfig as a headerauthJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {// to increase compatibility to existing api it is defaulting to be emptyauthConfig = ®istry.AuthConfig{}}} else {// the old format is supported for compatibility if there was no authConfig headerif err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {return err}}构建job运行环境,并启动,这个job也会有engine router找到对应的handler,有handler做实际的执行动作。
job := eng.Job("push", vars["name"])job.SetenvJson("metaHeaders", metaHeaders)job.SetenvJson("authConfig", authConfig)job.Setenv("tag", r.Form.Get("tag"))if version.GreaterThan("1.0") {job.SetenvBool("json", true)streamJSON(job, w, true)} else {job.Stdout.Add(utils.NewWriteFlusher(w))}if err := job.Run(); err != nil {if !job.Stdout.Used() {return err}sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))w.Write(sf.FormatError(err))}
</pre><h2><span style="font-family:Courier New">2.3 push job</span></h2>
push job的执行体放在graph/push.go文件中。在docker启动阶段,会注册push job的handler到engine中,关于graph的job处理handler都放在graph中的service.go的install方法中。
job handler的注册,push使用CmdPush handler。
func (s *TagStore) Install(eng *engine.Engine) error {for name, handler := range map[string]engine.Handler{"image_set": s.CmdSet,"image_tag": s.CmdTag,"tag": s.CmdTagLegacy, // FIXME merge with "image_tag""image_get": s.CmdGet,"image_inspect": s.CmdLookup,"image_tarlayer": s.CmdTarLayer,"image_export": s.CmdImageExport,"history": s.CmdHistory,"images": s.CmdImages,"viz": s.CmdViz,"load": s.CmdLoad,"import": s.CmdImport,"pull": s.CmdPull,"push": s.CmdPush,} {if err := eng.Register(name, handler); err != nil {return fmt.Errorf("Could not register %q: %v", name, err)}}return nil}
CmdPush handler放在了graph/push.go文件中。这个handler主要完成想registry发起请求的任务。为构建项registry请求解析job环境变量中的参数。
var (localName = job.Args[0]sf = utils.NewStreamFormatter(job.GetenvBool("json"))authConfig = ®istry.AuthConfig{}metaHeaders map[string][]string)tag := job.Getenv("tag")job.GetenvJson("authConfig", authConfig)job.GetenvJson("metaHeaders", &metaHeaders)
解析registry的hostname和namespace、repository,hostname为registry的地址"127.0.0.1:5000", remoteName为"/namespace/repo"
hostname, remoteName, err := registry.ResolveRepositoryName(localName)
检查registry是否可用,将hostname组装成http或https的url,做ping操作,检查是否可用
endpoint, err := registry.ExpandAndVerifyRegistryUrl(hostname)
获取需要push的镜像的graph,graph会单独用一篇文章介绍。
img, err := s.graph.Get(localName)
构建发向registry的请求
r, err2 := registry.NewSession(authConfig, registry.HTTPRequestFactory(metaHeaders), endpoint, false)if err2 != nil {return job.Error(err2)}
如果没有命令行中没有tag,会push所有repository下的镜像,如果有tag,只push tag对应的repository的镜像
if err != nil {reposLen := 1if tag == "" {reposLen = len(s.Repositories[localName])}job.Stdout.Write(sf.FormatStatus("", "The push refers to a repository [%s] (len: %d)", localName, reposLen))// If it fails, try to get the repositoryif localRepo, exists := s.Repositories[localName]; exists {if err := s.pushRepository(r, job.Stdout, localName, remoteName, localRepo, tag, sf); err != nil {return job.Error(err)}return engine.StatusOK}return job.Error(err)}
var token []stringjob.Stdout.Write(sf.FormatStatus("", "The push refers to an image: [%s]", localName))if _, err := s.pushImage(r, job.Stdout, remoteName, img.ID, endpoint, token, sf); err != nil {return job.Error(err)}
3 协议流程
3.1偷偷的ping
3.2 client上传镜像的lay id和tag信息
3.3 client端registry请求lay id为5111的lay json数据,如果registry有此id的json数据,说明registry有此lay,registry给client返回200 OK
如果没有,registry返回给client 404,client会将此lay的json,lay和checksum上传给registry
对image的所有lay做上述操作。
3.4上传image的tag信息,将tag所在的lay id给registry
3.5client最后做put请求images,但里面什么也没有带,registry发现是空的,就返回给client 204 NO CONTENT,就此push结束(这个地方有点LOW)
- docker命令之push
- docker命令之 docker commit
- docker命令之commit
- docker命令之login
- docker命令之pull
- docker命令之images
- docker命令之build
- docker命令之rmi
- docker命令之volume
- docker命令之详解
- Docker命令之:load命令
- Docker命令之:save命令
- Docker整理之docker命令(六)
- docker之docker基础命令集合
- Git原理杂谈之pull/push命令
- git命令之add,commit,push
- docker 基础命令之镜像篇
- docker入门之基本命令
- Syline6.5学习心得-web-命令使用
- MPEG2 PTS如何转为DirectShow的Reference Time
- 如何成为php高手?
- ORACLE数据字典的常用查询
- AudioRecord.getMinBufferSize参数解析
- docker命令之push
- 应对面试:《TCP/IP、HTTP、SOCKET的区别》、《HTTP协议详解》、《HTTP头部详解》
- ims 注册鉴权认证过程
- width:100% width:auto; 区别
- 安卓 SD卡写入文件后不能立即显示被写入的文件解决方法
- PHP echo用法集
- css代码书写习惯
- Eclipse 导入其他项目library时出现红叉解决方案
- hibernate3命名策略