Docker 源码走读 - 在运行 Docker run 时发生了什么?
来源:互联网 发布:手机照一寸照的软件 编辑:程序博客网 时间:2024/04/30 10:41
Docker 源码走读 - 在运行 Docker run 时发生了什么?
标签(空格分隔): Docker
原文作者是 Frank Scholten,原文地址是Docker code walkthrough – What happens during a Docker run?
在这篇博文中我将回答一下问题:运行一个 Docker run 期间 Docker 内部发生了什么?
开始
首先从 Docker Github repo clone 并检查代码。
$ git clone https://github.com/docker/docker
范读
用你喜欢的编辑器打开工程并且范读下 main tree。Docker 是使用 golang 编写并由很多包组成。比如,从顶部到底部开始浏览,你会看到 api, builder,builtins, contrib 等等。一些目录包含更多的子目录。
Docker executable 内部
第一件事,就是寻找 func main,当我们运行 Docker executable 时被执行的 Golang 函数。实际上,在代码树中有超过 30 个 main 函数。这些都是工具类的函数,我不会立即就深入其中。让我们继续我们主要寻找的:存在于docker/docker.go
中的一个 main 函数。让我们更仔细的看。
解析 flags
看以下的代码片段,显示了 main func 的前十几行。当 Docker 启动,它通过 reexec 运行任何的初始化(initializers),如果有,这时它为 Docker executable 解析通过mflag 包传递的参数。这个包在pkg/mgflag
下面,别名为 flag
。如果需要的话,在这一点它可以打印出版本信息或是开启 debug 模式并记录 debug 日志。
func main() { if reexec.Init() { return } flag.Parse() // FIXME: validate daemon flags here if *flVersion { showVersion() return } if *flDebug { os.Setenv("DEBUG", "1") } initLogging(*flDebug) ... SNIP}
Dispatch Docker command & error handling
在选择解析 Docker 捕获的主机设置后,如果有必要,对服务器执行 TLS verification 校验。这个发生在 40 和 107 行之间。看以下的代码片段。flags 解析早于传递给来自于 api/client/cli.go
的DockerCli
类型的 Cmd
方法。
如果一个错误发生,它会被记录,然后程序退出。
func main() { ... SNIP if err := cli.Cmd(flag.Args()...); err != nil { if sterr, ok := err.(*utils.StatusError); ok { if sterr.Status != "" { log.Println("%s", sterr.Status) } os.Exit(sterr.StatusCode) } log.Fatal(err) }}
cli 包
让我们深入 cli
包看看 Docker 命令被怎样处理的。为了能够运行子命令,我们关注 3 件事:
- DockerCli struct
- Cmd 方法
- GetMethod 方法
DockerCli
DockerCli struct 包含每个 Docker 命令必需的数据结构,比如使用的协议, in-, output- 和 error writers 以及 TLS 指定的数据结构。
type DockerCli struct { proto string addr string configFile *registry.ConfigFile in io.ReadCloser out io.Writer err io.Writer key libtrust.PrivateKey tlsConfig *tls.Config scheme string // inFd holds file descriptor of the client's STDIN, if it's a valid file inFd uintptr // outFd holds file descriptor of the client's STDOUT, if it's a valid file outFd uintptr // isTerminalIn describes if client's STDIN is a TTY isTerminalIn bool // isTerminalOut describes if client's STDOUT is a TTY isTerminalOut bool transport *http.Transport}
Cmd 方法
Cmd 函数的职责是通过使用 getMethod
函数 把命令参数转换成一个函数。它将来已经支持多个命令,也许是 docker groups create,尽管目前为止我知道还没有这样的命令实现。
func (cli *DockerCli) Cmd(args ...string) error { if len(args) > 1 { method, exists := cli.getMethod(args[:2]...) if exists { return method(args[2:]...) } } if len(args) > 0 { method, exists := cli.getMethod(args[0]) if !exists { fmt.Println("Error: Command not found:", args[0]) return cli.CmdHelp(args[1:]...) } return method(args[1:]...) } return cli.CmdHelp(args...)}
getMethod
注意 getMethod 是小写字母。这意味着它无法导出包外,因此它仅仅是在 cli 内可用的。因此这个方法怎样找出正确的函数?看下面的代码片段。它首先建立一个以 Cmd 开始的由大写字母组合的 string。万一 Docker 运行methodName 变量将被 CmdRun
。使用来自于 Golang reflect 包的 MethodByName
函数,它检索到一个函数指针并返回它。
func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) { camelArgs := make([]string, len(args)) for i, s := range args { if len(s) == 0 { return nil, false } camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) } methodName := "Cmd" + strings.Join(camelArgs, "") fmt.Println(methodName) method := reflect.ValueOf(cli).MethodByName(methodName) if !method.IsValid() { return nil, false } return method.Interface().(func(...string) error), true}
CmdRun
最后我们到达一个负责容器运行的函数:在 api/client/commands.go
的 CmdRun。这个文件包含了所有的 Docker 命令。它自己运行的参数被解析,比如 image,command 和其他参数。因为我们已经通读,我不会展示那些代码,我会以展示更有趣的事代替:运行命令启动一个新的容器。
创建容器
以下代码片段展示了容器是怎样被创建的。容器的配置被合并到 run config 和 host config。
实际创建一个容器是调用一个 HTTP POST 给 Docker 服务器。
func (cli *DockerCli) CmdRun(args ...string) error { SNIP ... runResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName) if err != nil { return err } SNIP ...}func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) { containerValues := url.Values{} if name != "" { containerValues.Set("name", name) } mergedConfig := runconfig.MergeConfigs(config, hostConfig) var containerIDFile *cidFile if cidfile != "" { var err error if containerIDFile, err = newCIDFile(cidfile); err != nil { return nil, err } defer containerIDFile.Close() } //create the container stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false) //if image not found try to pull it if statusCode == 404 { fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image) // we don't want to write to stdout anything apart from container.ID if err = cli.pullImageCustomOut(config.Image, cli.err); err != nil { return nil, err } // Retry if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil { return nil, err } } else if err != nil { return nil, err } var result engine.Env if err := result.Decode(stream); err != nil { return nil, err } for _, warning := range result.GetList("Warnings") { fmt.Fprintf(cli.err, "WARNING: %s\n", warning) } if containerIDFile != nil { if err = containerIDFile.Write(result.Get("Id")); err != nil { return nil, err } } return result, nil}
这些覆盖了在 Docker 客户端里面发生了什么。当然在 Docker 服务器和 libcontainer 还有更多的代码等待探索,但这将会留给以后的博文。
- Docker 源码走读 - 在运行 Docker run 时发生了什么?
- Docker在LXC基础上做了什么 && Docker与虚拟机之间有什么不同
- 函数运行时栈发生了什么
- supervisord + docker run = web页面管理运行的docker
- docker registry v2 install run安装运行
- docker run 运行容器自动结束
- docker源码学习-docker run 的具体实现(2 )
- docker实验一:在docker运行nginx
- 点击 Run 之后发生了什么?
- 【docker 17 源码分析】 docker run container 源码分析二 docker start
- 当一个.NET托管程序运行时都发生了什么,关闭时发生了什么
- 【docker】docker run命令详解
- 【docker】docker run命令详解
- 【docker】docker run命令详解
- 通过docker run 命令来在容器内运行一个应用程序 输出Hello world
- [docker]docker命令docker run的小结
- 在Docker中运行DPDK
- 在Docker中运行ONOS
- django 1.7 自定义用户user模型的三种方法
- 黑马程序员——Java基础---IO(四)(IO包中的其他类)--第21天--第265-273集
- django1.7使用allauth社交用户系统的安装配置
- PowerBuilder数据窗口对象
- 串的模式匹配——KMP算法攻克
- Docker 源码走读 - 在运行 Docker run 时发生了什么?
- docker专题(2):docker常用管理命令(上)
- 数据结构Java实现——①栈-->栈的应用三、算术表达式求值
- android 代码混淆(jar包混淆)
- 2014年最新810多套源码2.46GB免费一次性打包下载
- PHP调用VC编写的COM组件实例
- linux的makefile教程
- JAVA1.6 String类摘要
- centos5.5下python2.7.9安装mysqldb