前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何成为一名优秀的Docker代码贡献者

如何成为一名优秀的Docker代码贡献者

作者头像
IT阅读排行榜
发布2018-08-13 11:13:16
3150
发布2018-08-13 11:13:16
举报
文章被收录于专栏:华章科技

来源:炼数成金

成为一个流行开源项目(如Docker)的贡献者有如下好处:

>> 你可以参与改进很多人都在使用的项目,以此来获得认同感;

>> 你可以与开源社区中的那些聪明绝顶的人通力合作;

>> 你可以通过参与理解和改进这个项目来使自己成为一名更加出色的程序员。

但是,从一个新的基准代码(codebase)入手绝对是一件恐怖的事情。目前,Docker已经有相当多的代码了,哪怕是修复一个小问题,都需要阅读大量的代码,并理解这些部分是如何组合在一起的。

不过,它们也并不如你想象的那么困难。你可以根据Docker的贡献者指南来完成环境的配置。然后按照如下5个简单的步骤,配合相关的代码片段来深入代码基。你所历练的这些技能,都将会在你的编程生涯的每个新项目中派上用场。那么还等什么,我们这就开始。

步骤1:从『func main()』开始

正如一句古话所述,从你知道的开始。如果你和大部分Docker用户一样,你可能主要使用Docker CLI。因此,让我们从程序的入口开始:‘main’函数。

* 此处为本文的提示,我们将会使用一个名为Sourcegraph的站点,Docker团队就使用它完成在线检索和代码浏览,和你使用智能IDE所做的差不多。建议在阅读本文时,打开Sourcegraph放在一边,以更好地跟上文章的进度。

在Sourcegraph站点,让我们搜索Docker仓库中的‘func main()’。

我们正在寻找对应docker命令的main函数,它是docker/docker/docker.go中的一个文件。点击搜索结果,我们会跳到其定义(如下所示)。花一点时间浏览一下这个函数(为了便于微信阅读,省略了部分代码):

代码语言:javascript
复制
func main() {
if reexec.Init() {
return
}
.......
}

在‘main’函数的顶部,我们看了许多与日志配置,命令标志读取以及默认初始化相关的代码。在底部,我们发现了对『client.NewDockerCli』的调用,它似乎是用来负责创建结构体的,而这个结构体的函数则会完成所有的实际工作。让我们来搜索『NewDockerCli』。

步骤2:找到核心部分

在很多的应用和程序库中,都有1到2个关键接口,它表述了核心功能或者本质。让我们尝试到达这个关键部分。

点击‘NewDockerCli’的搜索结果,我们会到达函数的定义。由于我们感兴趣的只是这个函数所返回的结构体——「DockerCli」,因此让我们点击返回类型来跳转到其定义(为了便于微信阅读,省略了部分代码)。

代码语言:javascript
复制
func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli {
......
// The transport is created here for reuse during the client session
.......
// Why 32? See issue 8035
.......
return &DockerCli{
proto:         proto,
addr:          addr,
.......
scheme:        scheme,
transport:     tr,
}
}

点击『DockerCli』将我们带到了它的定义。向下滚动这个文件,我们可以看到它的方法,getMethod、Cmd、Subcmd和LoadConfigFile。其中,Cmd值得留意。它是唯一一个包含docstring的方法,而docstring则表明它是执行每条Docker命令的核心方法。

步骤3:更进一步

既然我们已经找到了DockerCli,这个Docker客户端的核心『控制器』,接下来让我们继续深入,了解一条具体的Docker命令是如何工作的。让我们放大『docker build』部分的代码。

代码语言:javascript
复制
type DockerCli struct {
........
// 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
}

阅读『DockerCli.Cmd』的实现可以发现,它调用了『DockerCli.getMethod』方法来执行每条Docker命令所对应的函数。

代码语言:javascript
复制
func (cli *DockerCli) Cmd(args ...string) error {
if len(args) > 1 {
.....
}
if len(args) > 0 {
......
}
return cli.CmdHelp()
}

在『DockerCli.getMethod』中,我们可以看到它是通过对一个函数的动态调用实现的,其中这个函数名的形式为在Docker命令前预置“Cmd”字符串。那么在『docker build』这个情况下,我们寻找的是‘DockerCli.CmdBuild’。但在这个文件中并没有对应的方法,因此让我们需要搜索CmdBuild。

代码语言:javascript
复制
func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) {
camelArgs := make([]string, len(args))
for i, s := range args {
......
}
methodName := "Cmd" + strings.Join(camelArgs, "")
method := reflect.ValueOf(cli).MethodByName(methodName)
.......
return method.Interface().(func(...string) error), true
}

搜索结果显示『DockerCli』中确实有一个『CmdBuild』方法,因此跳到它的定义部分。由于『DockerCli.CmdBuild』的方法体过长,因此就不在本文中嵌入了。

这里有很多内容。在方法的顶部,我们可以看到代码会为Dockerfile和配置处理各种输入方法。通常,在阅读一个很长的方法时,倒过来读是一种很不错的策略。从底部开始,观察函数在最后做了什么。很多情况中,它们都是函数的本质,而之前的内容无非只是用来补全核心行为的。

在CmdBuild的底部,我们可以看到通过cli.stream构造的‘POST’请求。通过一些额外定义的跳转,我们到达了DockerCli.clientRequest,它构造一个HTTP请求,这个请求包含你通过docker build传递给Docker的信息。因此在这里,docker build所做的就是发出一个设想的POST请求给Docker守护进程。如果你愿意,你也可以使用『curl』来完成这个行为。

至此,我们已经彻底了解了一个单独的Docker客户端命令,或许你仍希望更进一步,找到守护进程接受请求的部分,并一路跟踪到它和LXC以及内核交互的部分。这当然是一条合理的路径,但是我们将其作为练习留给各位读者。接下来,让我们对客户端的关键组件有一个更加全面的认识。

步骤4:查看使用示例

更好地理解一段代码的方式是查看展示代码如何被应用的使用示例。让我们回到DockerCli.clientRequest方法。在右手边的Sourcegraph面板中,我们可以浏览这个方法的使用例子。结果显示,这个方法在多处被使用,因为大部分Docker客户端命令都会产生传到守护进程的HTTP请求。

为了完全理解一个代码片段,你需要同时知晓它是如何工作的以及是如何来使用的。通过阅读代码的定义部分让我们理解前者,而查看使用示例则是涵盖了后者。

请在更多的函数和方法上尝试它,并理解它们的内部联系。如果这有帮助,那么请就应用的不同模块如何交互画一张图。

步骤5:选择一个问题并开始coding

既然你已经对Docker的代码基有了一个大概的认识,那么可以查阅一下issue跟踪系统,看看哪些问题亟待解决,并在遇到你自己无法回答的问题时,向Docker社区的成员申援。由于你已经花了时间来摸索并理解代码,那么你应该已经具备条件来提出“聪明”的问题,并知道问题大概出在哪里。

如果你觉得有必要,可以一路做好笔记,记录你的经历,并像本文一样作为博客发布。Docker团队非常乐意看到你研究他们代码的经历。

有效地贡献

对一个巨大且陌生的基准代码的恐惧,俨然已经成为了一个阻止人们参与到项目中的误解。我们经常假设,对于程序员而言,工作的难点在于写代码,然而阅读并理解他人的代码却往往是最关键的一步。认识到这一切,并坚定地迎接任务,辅以优秀的工具,会帮助你克服心理防线,以更好地投入到代码中。

相关图书

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2015-07-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大数据DT 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器镜像服务
容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档