如何使Chef在运行其他配方之前运行apt-get update

46

目前我在我的Vagrantfile中有以下内容:

config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "cookbooks"
    chef.add_recipe "apt"
    chef.add_recipe "build-essential"
    chef.add_recipe "chef-redis::source"
    chef.add_recipe "openssl"
    chef.add_recipe "git"
    chef.add_recipe "postgresql::server"
    chef.add_recipe "postgresql::client"
end
为了安装添加到我的 recipe_list 的软件,我需要让 VM 在安装其他软件之前发出 apt-get 更新命令。
我曾经认为这是 'apt' recipe 的一个特性 - 它会第一时间运行更新程序。
当我执行 vagrant provision 命令时的输出结果为:
[Sat, 11 Feb 2012 22:20:03 -0800] INFO: *** Chef 0.10.2 ***
[Sat, 11 Feb 2012 22:20:03 -0800] INFO: Setting the run_list to ["recipe[apt]", "recipe[build-essential]", "recipe[chef-redis::source]", "recipe[openssl]", "recipe[git]", "recipe[postgresql::server]", "recipe[postgresql::client]", "recipe[vagrant-main]"] from JSON
[Sat, 11 Feb 2012 22:20:03 -0800] INFO: Run List is [recipe[apt], recipe[build-essential], recipe[chef-redis::source], recipe[openssl], recipe[git], recipe[postgresql::server], recipe[postgresql::client], recipe[vagrant-main]]
[Sat, 11 Feb 2012 22:20:03 -0800] INFO: Run List expands to [apt, build-essential, chef-redis::source, openssl, git, postgresql::server, postgresql::client, vagrant-main]
[Sat, 11 Feb 2012 22:20:03 -0800] INFO: Starting Chef Run for lucid32
[Sat, 11 Feb 2012 22:20:03 -0800] INFO: Processing package[postgresql-client] action install (postgresql::client line 37)
[Sat, 11 Feb 2012 22:20:04 -0800] ERROR: package[postgresql-client] (postgresql::client line 37) has had an error
[Sat, 11 Feb 2012 22:20:04 -0800] ERROR: Running exception handlers
[Sat, 11 Feb 2012 22:20:04 -0800] ERROR: Exception handlers complete
[Sat, 11 Feb 2012 22:20:04 -0800] FATAL: Stacktrace dumped to /tmp/vagrant-chef-1/chef-stacktrace.out
[Sat, 11 Feb 2012 22:20:04 -0800] FATAL: Chef::Exceptions::Exec: package[postgresql-client] (postgresql::client line 37) had an error: apt-get -q -y install postgresql-client=8.4.8-0ubuntu0.10.04 returned 100, expected 0

1
Chef 12.7 引入了本地的 apt_update 资源,这使得大多数答案都变得过时了。https://docs.chef.io/resource_apt_update.html - spuder
12个回答

32

您可以在一开始包含apt食谱:

include_recipe 'apt'

这将运行更新命令。


1
对我来说,我必须要:include_recipe 'apt' - badmadrad

21

apt-get update 应该先运行,这是您的方式。但是,这个命令只会每 24 小时更新一次:

execute "apt-get-update-periodic" do
  command "apt-get update"
  ignore_failure true
  only_if do
    File.exists?('/var/lib/apt/periodic/update-success-stamp') &&
    File.mtime('/var/lib/apt/periodic/update-success-stamp') < Time.now - 86400
  end
end

1
问题在于apt-get update最初不会运行。此外,似乎chef.add_recipe "postgresql::server"和chef.add_recipe "postgresql::client"会在apt配方之前先运行。 - ashchristopher
这真的很奇怪,但应该可以工作。你能试着删除 /var/lib/apt/periodic/update-success-stamp 吗? - StephenKing
@ashchristopher - 你想让它最初运行 - 参见下面的答案。我也想要那个。 - jmontross
6
如果时间戳文件不存在,这将不会运行apt-get update,初始情况下时间戳文件并不存在。在我看来,正确的逻辑应该是!File.exists?(...) || File.mtime ... - Lars Haugseth

10
以下是针对Ubuntu系统的三种资源,我在我的12.04版本的64位系统上使用过,效果很好:
  1. 当其他配方需要时,随时运行apt-get-update

  2. 安装update-notifier-common包,这会给我们更新的时间戳

  3. 定期检查时间戳并进行更新。在此示例中为86400秒后。

以下是这三个资源的详细说明。
execute "apt-get-update" do
  command "apt-get update"
  ignore_failure true
  action :nothing
end

package "update-notifier-common" do
  notifies :run, resources(:execute => "apt-get-update"), :immediately
end

execute "apt-get-update-periodic" do
  command "apt-get update"
  ignore_failure true
  only_if do
   File.exists?('/var/lib/apt/periodic/update-success-stamp') &&
   File.mtime('/var/lib/apt/periodic/update-success-stamp') < Time.now - 86400
  end
end

谢谢 - 这对我有用,只需要做一个小修改 - 第一个执行调用应该是 'apt-get-update'。 - Andy Triggs

8

看起来最新版本的Opscode Apt Cookbook允许您在编译时运行它。

config.vm.provision :chef_solo do |chef|
  chef.cookbooks_path = "cookbooks"
  chef.add_recipe "apt"
  chef.json = { "apt" => {"compiletime" => true} }
end

只要在运行列表中先运行apt,然后再运行其他编译时的菜谱(比如postgres),这样就可以正常工作。

4
很多其他回答可能会导致资源克隆的警告。
根据Apt cookbook documentation,您应该能够通过设置node['apt']['compile_time_update'] = true来实现这一点,但是我自己使用这种方法并不成功
取而代之的是,我做了以下操作:
这将加载原始的apt-get update资源,并确保它在不向资源集合添加重复条目的情况下运行。这将导致apt-get update在编译阶段的每个Chef运行期间执行:
# First include the apt::default recipe (so that `apt-get update` is added to the collection)
include_recipe 'apt'

# Then load the `apt-get update` resource from the collection and run it
resources(execute: 'apt-get update').run_action(:run)

显然,您还需要在metadata.rb文件中包含apt cookbook:
# ./metadata.rb
depends 'apt'

3

4
这是一种非常奇怪的安装软件包的方式(即使用action: nothingrun_action(:install))。这似乎是在 chef-client 运行的编译阶段安装软件包,而不是执行阶段。有关详细信息,请参见[http://wiki.opscode.com/display/chef/Anatomy+of+a+Chef+Run]。将 recipe[apt] 添加到节点运行列表的开头通常应该作为第一个操作运行 apt-get install - Tim Potter
1
将配方[apt]添加到节点运行列表的前面通常应该作为第一个操作运行apt-get install。这对我有用。 - Dustin Rasener

1
最简单直接的解决该问题的方法是应用以下补丁(感谢@ashchristopher):

https://github.com/wil/cookbooks/commit/a470a4f68602ec3bf3374830f4990a7e19e9de81

问题在于postgresql::client配方在postgresql/recipes/client.rb:39和44处的compile-time上运行包资源的安装操作,而不是像正常情况下一样在运行时运行,这导致它们被Chef评估(因此安装)在任何其他内容运行之前。
pg_packages.each do |pg_pack|
  package pg_pack do
    action :nothing
  end.run_action(:install)
end

gem_package "pg" do
  action :nothing
end.run_action(:install)

我相信这是为了数据库食谱的postgres提供程序而完成的,该程序依赖于postgresql食谱,并依赖于在编译之前安装pg gem。应用上述补丁可能会破坏数据库食谱。

另一种替代解决方案是创建一个配方,在编译时运行apt-get update并将其放在postgresql食谱之前的run_list中。最简单的形式可能是:

execute "apt-get update" do
  action :nothing
end.run_action(:install)

1
没有补丁,这是一个通用的方法来解决问题,将在每次运行时更新:
bash "update-apt-repository" do
  user "root"
  code <<-EOH
  apt-get update
  EOH
end

值得考虑的是,每次运行此操作会占用系统资源约30秒钟; 您可能希望创建一个名为recipe::update_apt的特殊配方,并通过cron或其他事件运行它。

chef-client -o "recipe[yourrecipe::update_apt]"

1

https://web.archive.org/web/20120913065512/http://wiki.opscode.com/display/chef/Evaluate+and+Run+Resources+at+Compile+Time - Rudger
1
维基出了点问题,链接没有sessionid就无法访问。不过archive.org的链接还是可以用的。或许这个链接也可以:https://wiki.opscode.com/display/chef/Evaluate+and+Run+Resources+at+Compile+Time;jsessionid=BBE750D0DC249823649B3F4F70F24C82 - Rudger

0

友情提醒,将所有这些食谱添加到vagrant provision中很快就会变得难以管理。

更好的做法是创建一个chef角色 chef/my-fancy-role.rb

# Name of the role should match the name of the file
name "my-fancy-role"

# Run list function we mentioned earlier
run_list(
    "recipe[apt]",
    "recipe[build-essential]",
    "recipe[chef-redis::source]",
    "recipe[openssl]"
)

然后将此角色添加到Vagrantfile配置部分:

config.vm.provision :chef_solo do |chef|
  chef.roles_path = "chef/roles"
  chef.cookbooks_path = ["chef/site-cookbooks", "chef/cookbooks"]
  chef.add_role "my-fancy-role"
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接