我是Puppet的新手,但很清楚这些概念。 Puppet Manifests调用Puppet Modules,模块执行实际任务。
我试图了解Puppet Module层发生了什么。命令如何实际执行?以下面的例子为例,哪些命令实际传递给操作系统?此外,定义在哪里?
package { 'ntp': ensure => installed, }
答案 0 :(得分:10)
摘要: Puppet根据系统的facts
和Puppet本身的配置确定需要运行的命令。
因此,当Puppet编译目录以在其系统上运行时,它看起来如下:
"我需要安装一个名为ntp的Pacakge资源。我是RedHat家族的CentOS系统。默认情况下,我在RedHat上使用yum命令。所以我需要运行yum install ntp
"
更长的解释
Puppet知道运行命令以及如何运行它们的方式称为资源抽象层。
当它完全失效时,Puppet没有做任何神奇的事情:在系统上运行的命令与人类操作员运行的命令相同。
也许Puppet已经想出了一个聪明的方法来做到这一点,并考虑到你所处理的平台上的模糊错误和陷阱,或者是因为你正在尝试做的事包含一个错误拼写错误或类似。
但最终,必须使用系统实际应用程序和工具来执行操作。
RAL实际进入的地方。它是Puppet中最大的抽象层:将与基本系统的所有交互转变为一致的界面。
在您给出的示例中,包非常简单。至少在过去二十年中,安装软件包的概念(大多数)与几乎所有操作系统相同:
packagesystemtool keywordforinstall packagename
通常,install关键字是install,但也有一些例外。 BSD' pkg
例如使用pkg add
。
但是:可以在该包中管理的实际属性可能有很大差异:
大量其他可选参数,例如代理信息,错误记录级别。
RAL允许用户以一致的方式定义资源的特征,而不管其实现如何:
type { 'title':
attribute => 'value',
}
每个资源都遵循相同的语法:
所以我们的包声明如下:
package {'tree':
ensure => 'present',
}
RAL可以在已定义的每个平台上处理该行为,并在可用的情况下支持不同的程序包功能,所有这些功能都以明确定义的方式,默认情况下对用户隐藏。
我听到 RAL 的最佳比喻是天鹅在湖上滑行:
当你在水体上看天鹅时,它看起来很优雅 优雅,滑翔。它看起来几乎没有工作。
隐藏在水面下的活动是在水面下进行的活动。那只天鹅正在踢它的脚蹼,不那么优雅,它看起来很顶:Puppet正在运行的实际命令就是在水下踢腿。
好的,有足够的背景,你可能会问......
它是如何运作的? RAL将系统上的所有资源分成两个元素:
这使您可以以适用于任何系统的方式描述资源。每种资源,无论它是什么,都有一个或多个提供者。提供者是底层操作系统和资源类型之间的接口。
通常,类型会有默认提供程序,但如果需要,您可以指定特定的提供程序。
对于软件包,默认提供程序将是系统的默认软件包提供程序:RHEL为yum
,Debian为apt
,BSD为pkg
等。这由a确定,它从系统中获取事实。
例如,yum
提供商具有以下内容:
defaultfor :osfamily => :redhat
但是你可能想安装一个pip包或一个gem。为此,您将指定提供程序,因此它将使用不同的命令安装它:
package {'tree':
ensure => 'present',
provider => 'pip',
}
这意味着我们要对RAL说:"嘿,我知道yum是默认安装包,但这是我需要的python包,所以我告诉你改为使用pip"
属性类型中最重要的资源在操作系统中通常在概念上是相同的,无论实际实现方式如何不同。
就像我们说的那样,大多数软件包都会安装package installer install packagename
因此,资源的描述可以从其实现中抽象出来:
Puppet使用 RAL 来读取和修改系统上的资源状态。由于它是一个声明系统,Puppet首先要了解资源应该具有的状态。
要同步资源,它使用RAL查询当前状态,将其与所需状态进行比较,然后再次使用RAL进行任何必要的更改。它使用工具来获取系统的当前状态,然后确定将状态更改为资源定义的状态需要做什么。
当Puppet应用包含资源的目录时,它将读取目标系统上资源的实际状态,将实际状态与所需状态进行比较,并在必要时更改系统以强制执行所需状态。 / p>
让我们来看看RAL将如何管理这个:
ntp
yum install
所以之前我们讨论过Puppet如何使用RAL来读取和修改系统上的资源状态。
" getter" RAL是提供程序中的self.instances方法。
根据资源类型,通常采用以下两种方式之一:
rpm实例步骤与后者相同。它使用一些给定的标志运行rpm -qa
以检查系统上的软件包:
def self.instances
packages = []
# list out all of the packages
begin
execpipe("#{command(:rpm)} -qa #{nosignature} #{nodigest} --qf '#{self::NEVRA_FORMAT}'") { |process|
# now turn each returned line into a package object
process.each_line { |line|
hash = nevra_to_hash(line)
packages << new(hash) unless hash.empty?
}
}
rescue Puppet::ExecutionFailure
raise Puppet::Error, "Failed to list packages", $!.backtrace
end
packages
end
所以它正在运行/usr/bin/rpm -qa --nosignature --nodigest --qf '%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n'
,然后从该命令中获取stdout,循环遍历每行输出,并使用nevra_to_hash
方法将STDOUT行转换为哈希。
self::NEVRA_REGEX = %r{^(\S+) (\S+) (\S+) (\S+) (\S+)$}
self::NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch]
private
# @param line [String] one line of rpm package query information
# @return [Hash] of NEVRA_FIELDS strings parsed from package info
# or an empty hash if we failed to parse
# @api private
def self.nevra_to_hash(line)
line.strip!
hash = {}
if match = self::NEVRA_REGEX.match(line)
self::NEVRA_FIELDS.zip(match.captures) { |f, v| hash[f] = v }
hash[:provider] = self.name
hash[:ensure] = "#{hash[:version]}-#{hash[:release]}"
hash[:ensure].prepend("#{hash[:epoch]}:") if hash[:epoch] != '0'
else
Puppet.debug("Failed to match rpm line #{line}")
end
return hash
end
所以基本上它是输出的正则表达式,然后将这些位从正则表达式转换为给定的字段。
这些哈希值成为资源的当前状态。
我们可以运行--debug来实现这一点:
Debug: Prefetching yum resources for package
Debug: Executing: '/usr/bin/rpm --version'
Debug: Executing '/usr/bin/rpm -qa --nosignature --nodigest --qf '%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n''
Debug: Executing: '/usr/bin/rpm -q ntp --nosignature --nodigest --qf %{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n'
Debug: Executing: '/usr/bin/rpm -q ntp --nosignature --nodigest --qf %{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n --whatprovides'
因此它使用RAL来获取当前状态。 Puppet正在做以下事情:
ntp
不在这里...... ntp
这里有很多逻辑:
def install
wanted = @resource[:name]
error_level = self.class.error_level
update_command = self.class.update_command
# If not allowing virtual packages, do a query to ensure a real package exists
unless @resource.allow_virtual?
execute([command(:cmd), '-d', '0', '-e', error_level, '-y', install_options, :list, wanted].compact)
end
should = @resource.should(:ensure)
self.debug "Ensuring => #{should}"
operation = :install
case should
when :latest
current_package = self.query
if current_package && !current_package[:ensure].to_s.empty?
operation = update_command
self.debug "Ensuring latest, so using #{operation}"
else
self.debug "Ensuring latest, but package is absent, so using #{:install}"
operation = :install
end
should = nil
when true, false, Symbol
# pass
should = nil
else
# Add the package version
wanted += "-#{should}"
if wanted.scan(ARCH_REGEX)
self.debug "Detected Arch argument in package! - Moving arch to end of version string"
wanted.gsub!(/(.+)(#{ARCH_REGEX})(.+)/,'\1\3\2')
end
current_package = self.query
if current_package
if rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(current_package[:ensure])) < 0
self.debug "Downgrading package #{@resource[:name]} from version #{current_package[:ensure]} to #{should}"
operation = :downgrade
elsif rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(current_package[:ensure])) > 0
self.debug "Upgrading package #{@resource[:name]} from version #{current_package[:ensure]} to #{should}"
operation = update_command
end
end
end
# Yum on el-4 and el-5 returns exit status 0 when trying to install a package it doesn't recognize;
# ensure we capture output to check for errors.
no_debug = if Facter.value(:operatingsystemmajrelease).to_i > 5 then ["-d", "0"] else [] end
command = [command(:cmd)] + no_debug + ["-e", error_level, "-y", install_options, operation, wanted].compact
output = execute(command)
if output =~ /^No package #{wanted} available\.$/
raise Puppet::Error, "Could not find package #{wanted}"
end
# If a version was specified, query again to see if it is a matching version
if should
is = self.query
raise Puppet::Error, "Could not find package #{self.name}" unless is
# FIXME: Should we raise an exception even if should == :latest
# and yum updated us to a version other than @param_hash[:ensure] ?
vercmp_result = rpm_compareEVR(rpm_parse_evr(should), rpm_parse_evr(is[:ensure]))
raise Puppet::Error, "Failed to update to version #{should}, got version #{is[:ensure]} instead" if vercmp_result != 0
end
end
这是一些严重的天鹅腿踢。这里有很多逻辑,对于Yum上包的更复杂的用例,但要确保它适用于各种版本的Yum avaliable,包括RHEL 4和5.
逻辑被分解:我们还没有指定版本,所以我们不需要检查要安装的版本。只需使用指定的默认选项
运行yum install treeDebug: Package[tree](provider=yum): Ensuring => present
Debug: Executing: '/usr/bin/yum -d 0 -e 0 -y install tree'
Notice: /Stage[main]/Main/Package[tree]/ensure: created
Ta-dah,已安装。
答案 1 :(得分:2)
这取决于你的linux风格。
首先检查是否安装了包ntp
。
如果没有,它将被安装。
CentOS示例:
yum list installed ntp
如果没有安装
yum install ntp
Debian示例:
dpkg -s ntp
如果没有安装
apt-get install ntp
这一切都由您选择的Linux上的软件包提供程序处理。