6

マルチノード Web サービスの Chef レシピがあります。各ノードは、他のノードのホスト名と IP を取得して、それを独自のローカル構成に配置する必要があります。

コードを以下に示します。問題は、node.set[][] 割り当てが示されているように ruby​​_block で行われると、それらに依存するテンプレートが作成されるときに値が空になることです。そのテンプレートを作成したい場合は、ruby_block のコードをすべて外部に移動し、レシピで "loose" する必要があります。これにより、Chefspec などで単体テストを行うことが難しくなります。

シェフの第一人者は私を正すことができますか? ruby_block 内でこのように node.set[] を実行することは単に不可能ですか? もしそうなら、なぜドキュメントでそう言わないのですか?

$cm = { :name => "web", :hostname => "" , :ip_addr => "" }
$ca = { :name => "data", :hostname => "" , :ip_addr => "" }
$cg = { :name => "gateway", :hostname => "" , :ip_addr => "" }
$component_list = [$cm, $ca, $cg]

ruby_block "get host addresses" do
  block do
    for cmpnt in $component_list
       # do REST calls to external service to get cmpnt.hostname, ip_addr
       # .......
       node.set[cmpnt.name]['name'] = cmpnt.name
       node.set[cmpnt.name]['host'] = cmpnt.hostname
       node.set[cmpnt.name]['ip'] = cmpnt.ip_addr   
    end
  end
end

template "/etc/app/configuration/config.xml" do
  source "config.xml.erb"
  variables( :dataHost => node['data']['host'],
       :webHost =>  node['web']['host'],
       :gatewayHost =>  node['gateway']['host'] )
  action :create
end

私も追加しました

  subscribes  :create, "ruby_block[get host addresses]", :immediately

テンプレート定義に追加して、テンプレートが作成される前に ruby​​_block が実行されるようにします。これは違いはありませんでした。

4

4 に答える 4

5

これは古い投稿だと思いますが、将来の参考のために、コンパイルと収束フェーズでのノード変数の割り当ての良い例を示すこの要点に出くわしました。要点をあなたの例に合わせるには、次のようなコードを に追加する必要がありますruby_block

       template_r = run_context.resource_collection.find(:template => "/etc/app/configuration/config.xml")

       template_r.content node['data']['host']
       template_r.content node['web']['host']
       template_r.content node['gateway']['host']

Chef 11 については、Lazy Attribute Evaluationも参照してください。

于 2014-03-14T22:13:59.980 に答える
2

問題は、実際にリソースを呼び出す前に、テンプレート リソース定義内の属性値が評価されることです。つまり、ファイルは最初に単純な Ruby として実行され、リソースがコンパイルされ、リソース アクションのみが呼び出されます。その頃には、もう手遅れです。特定の属性操作をリソースにカプセル化しようとしたときに、同じ問題に遭遇しました。それは単に機能しません。誰かがこの問題の解決策を知っていれば、とても感謝しています。

編集:

b = ruby_block...
...
end
b.run_action(:create)

おそらくトリックを行うことができます。リソースをすぐに呼び出します。

于 2013-05-28T14:17:31.700 に答える
1

これに対する最も簡単な答えは、chef 属性を使用せず、ruby_block を使用して REST API と対話する作業を行わないことです。再利用しやすいように、コードをカスタム リソースに移動することもできます。

unified_mode true
provides :my_resource

action :run do
  cm = { :name => "web", :hostname => "" , :ip_addr => "" }
  ca = { :name => "data", :hostname => "" , :ip_addr => "" }
  cg = { :name => "gateway", :hostname => "" , :ip_addr => "" }
  component_list = [cm, ca, cg]

  hash = {}

  for cmpnt in component_list
     # do REST calls to external service to get cmpnt.hostname, ip_addr
     # .......
     hash[cmpnt.name] = {}
     hash[cmpnt.name]['name'] = cmpnt.name
     hash[cmpnt.name]['host'] = cmpnt.hostname
     hash[cmpnt.name]['ip'] = cmpnt.ip_addr   
  end

  template "/etc/app/configuration/config.xml" do
    source "config.xml.erb"
    variables( :dataHost => hash['data']['host'],
       :webHost =>  hash['web']['host'],
       :gatewayHost =>  hash['gateway']['host'] )
    action :create
  end
end

また、unified_mode を使用してカスタム リソースに移行することで、lazy {} や ruby​​_blocks を使用しなくてもノード属性を簡単に使用できるようになります。また、このコードを呼び出す前にシェフの構成 (REST 呼び出しを行う前に resolv.conf やその他のネットワーク要件を設定するなど) を行うこともできますが、レシピのコンテキストで 2 つのパスの問題をコンパイル/収束することについて考える必要はありません。

管理下のシステムを変更しない純粋な ruby​​ 処理を行うために ruby​​_block のようなリソースを使用する理由もありません。この場合、ruby_block は純粋にデータを収集するために REST サービスにアクセスしています。これは、Chef リソースに配置する必要はありません。質問者が「ベストプラクティス」だったためにそれが行われていたのか(この場合はそうではありません)、それとも他のことを可能にするために実行をコンパイル時に移動するために行われていたのかは、質問からは明らかではありません最初に起動する問題の一部ではないシェフのリソース (この場合、カスタム リソースを使用する方が ruby​​_block を使用するよりもはるかに優れたソリューションです)。

于 2020-08-20T23:13:24.487 に答える