【天赢金创】Puppet Hacking Guide —— Puppet 的启动:子命令

在阿里巴巴实习期间,由于各种机缘巧合,我开始专注于研读配置自动化管理软件 Puppet 。这项工作持续了两个月,期间我在内网发布过多篇技术文章,详细地剖析 Puppet 的运行原理。业已实习完毕,所有的技术文档、演示幻灯以及部分实例源码,均已通过阿里巴巴对外数据披露备案,被允许向开源社区分享这些技术文档。社区曾给了我很多帮助,我想,现在是时候我向社区尽一些绵薄之力了。

虽然没有把 Puppet 多达 10 万行的源码彻底地分析清楚,但大致的脉络已经理清。整个系列文集将会以《Puppet Hacking Guide》为总标题(致敬《Ruby Hacking Guide》),解释 Puppet 3.X 版本的内部运行原理,为用户定制 Puppet 提供指导。整个系列以我在阿里巴巴内部发表的文章为草稿,重新组织整理,以期能以一种清晰的思路引导读者理解 Puppet 源码。

尽管本文几经修改,但笔者才疏学浅,难免有所纰漏,欢迎各位指正!另外,出于职业道德,我不能向各位透露以下信息,也请各位不要打听,见谅:

  1. Puppet 在阿里巴巴内部的使用场景;
  2. 阿里巴巴所使用的 Puppet 具体版本号;
  3. 阿里巴巴所采用的 Puppet 体系架构;

另外,如果读者所在公司有定制 Puppet 的需求,可以联系我,我可以在研究需求后,给出一些可行的方案。

Acknowledgment

本系列文集是在我受雇于阿里巴巴期间撰写的一系列技术文档重新整理而成,其版权属于阿里巴巴公司以及我本人。经雇主同意,现特许以技术交流为目的,在开源技术社区分享此文集。

因此您可以:在保留原作者 DeathKing 以及阿里巴巴-技术保障部署名的情况下,以学习交流为目的,以非盈的形式将本文以电子版或印刷版的形式分发给您的朋友,或者转载到任何一个开源社区;

以下行为是禁止的:

  1. 以盈利为目的,将文章转载到微信公众号等媒体平台;
  2. 去掉原作者 DeathKing 以及阿里巴巴-技术保障部的署名,以自己的名义发布本文集;

请在转载时,保留以下署名:

  1. 本系列文章作者 DeathKing
  2. 阿里巴巴技术保障部

【天赢金创】Puppet Hacking Guide —— Puppet 的启动:子命令

Puppet 的启动:子命令

Puppet 的启动方式

Puppet Agent 通常通过命令行触发,而 Puppet Master 即可以通过命令行方式触发,以 WEBrick 服务器模式运行,也可以通过设置 config.ru 文件,以 Rack 中间件的形式运行。

下面的命令可以让 Puppet Master 以 WEBrick 服务器的模式启动,选项 --no-daemonize 可以阻止 Master 的后台化,在测试的时候,我们通常添加一个 --debug 选项用以设置日志等级,让 Puppet 显示更多有用的信息方便系统管理员进行调试:

$ puppet master --no-daemonize --debug

下面的命令可以启动 Agent 使之与 Master 进行通信并完成一次完整的工作流。与默认的 Agent 与 Master 每30分钟同步一次不同,选项 --onetime 使得 Agent 与 Master 只进行一次同步,完成后立即退出。

$ puppet agent --no-daemonize --debug --onetime

要了解 Master 和 Agent 的启动过程,就需要先知道 Puppet 中子命令的概念,使用 puppet help 可以查看 Puppet 的使用帮助:

$ puppet help Usage: puppet <subcommand> [options] <action> [options]

Available subcommands:

  agent             The puppet agent daemon
  apply             Apply Puppet manifests locally
  ca                Local Puppet Certificate Authority management. # 有意省略了整个列表。

从使用帮助中,我们不难看出,跟在 puppet 命令后的参数被称作子命令(subcommand)。子命令通常对应了一个 Ruby 脚本文件或外部可执行文件。事实上,不单 agent 和 master 分别是一个 Puppet 子命令,连 help 也是 Puppet 的一个子命令,对应的是 lib/puppet/application/help.rb 文件。Puppet 将不同的功能模块抽象为子命令,并将这些子命令实现为不同的文件,这样实现和管理起来更为方便。

子命令分类

Puppet 子命令可以分为四类:

  • 空子命令:调用 Puppet 时,如果没有指定任何子命令,Puppet 则认为调用的是空子命令;
  • 应用子命令:存放在 puppet/application 文件夹中的脚本所对应的命令。需要注意的是,这是一个相对路径,Puppet 会在多个路径中搜索该相对路径下的文件。这类子命令的典型的代表是 agent、master 和 config 等;
  • 外部子命令:存放在环境变量 PATH 所指示的文件夹中、以 puppet- 开头的文件所对应的命令。例如,命令行调用 puppet foo 会使 Puppet 在环境变量 PATH 所指示的文件夹中搜寻名为 puppet-foo 的可执行文件;
  • 未知子命令:不是上述命令的其他子命令,Puppet 都将其视作未知子命令;

子命令的查找与加载

以命令行调用 Puppet 时,实际执行的是 bin\puppet 。作为整个系统的入口,这个文件却只有简单的几句代码:

  • 文件:bin\puppet
#!/usr/bin/env ruby # For security reasons, ensure that '.' is not on the load path # This is primarily for 1.8.7 since 1.9.2+ doesn't put '.' on the load path $LOAD_PATH.delete '.' require 'puppet/util/command_line' Puppet::Util::CommandLine.new.execute

bin\puppet 主要实例化了一个 Puppet::Util::CommandLine 对象,这个对象主要用于理清 Puppet 的调用信息:调用的是哪个命令、有哪些命令行选项等。在后面的章节中我们会发现,如果以 Rack 中间件的方式启动 Puppet Master ,会用到一些 trick ,这也是CommandLine 存在的原因。

CommandLine 对象实例化完毕后,execute 方法被执行。在这个方法中,最重要的是 find_subcommand.run 语句。

  • 文件:lib/puppet/util/command_line.rb
def execute Puppet::Util.exit_on_fail("initialize global default settings") do Puppet.initialize_settings(args) end setpriority(Puppet[:priority]) find_subcommand.run end

CommandLine 类的私有方法 find_command 是理解整个启动机制的关键点,Puppet 在这个方法中,通过一些规则确定子命令的分类,然后再调用子命令对应的类,加载对应的文件。

  • 文件:lib/puppet/util/command_line.rb
private def find_subcommand if subcommand_name.nil? NilSubcommand.new(self) elsif Puppet::Application.available_application_names.include?(subcommand_name) ApplicationSubcommand.new(subcommand_name, self) elsif path_to_subcommand = external_subcommand ExternalSubcommand.new(path_to_subcommand, self) else UnknownSubcommand.new(subcommand_name, self) end end

需要强调的是,Puppet 并不是根据命令的分类去查找文件,而是根据文件查找的结果,确定命令的分类,理解这一点很重要。在深入每句代码内部之前,我们应该对 find_subcommand 方法的模式有个大概认知:查找方法,然后实例化一个对应类的对象。比较令人疑惑的是,用于实例化的参数中有一个 self 。这里的 self 就是 CommandLine 对象,我们之前已经说过,CommandLine 对象已经理清了命令行调用的信息,因此我们可以在每个 Subcommand 对象中,通过 CommandLine 对象获得调用的命令行参数等信息。

Puppet 的命令分类我们已经在前面描述了,接下来,我们将详细地讨论查找的规则。由于空子命令和未知子命令的代码比较简单,我们先行介绍,接着我们将介绍外部子命令,我们最后再来分析最为复杂的内部子命令。

你可能感兴趣的:(【天赢金创】Puppet Hacking Guide —— Puppet 的启动:子命令)