LoginSignup
5
2

More than 5 years have passed since last update.

Chef実行順序の影響と回避方法まとめ

Last updated at Posted at 2018-07-25

ChefでRubyコンパイル時とChefリソース収束時による実行順序の問題が生じるケースと対応をまとめておく

問題ケース1 : システムの状態を取得し使用するロジックが、レシピではその前方にあるChefリソースの収束結果を使用できない

File.exist?('/tmp/test.txt')
s = `cat #{path}`

などの、Rubyによるシステム状態の取得は、Rubyコンパイル時に実行される為、Chef Resourceによる収束の結果を使用できない。

  • 例外:Chefリソースにおけるnot_ifなどのbarrierの評価は、収束時に行われる。

対応1a : 収束時にRubyによるシステム状態の取得を行う(後実行)

1a-1 : ruby_blockを用いる

この場合、このruby_block内からChefのリソースを呼び出すことは、以下の対応により可能。

chef の ruby_block 内でリソースを実行する

scope0 = self
ruby_block 'foo' do
  block do
    scope0.package 'bar'
  end
end

notifiesも使用可能。
使用する場合、宛先リソースの指定にはxxx.は不要。

ネストも可能。

デメリット (あまり思いつかないが)

  • ruby_blockにnot_ifなどのbarrierを付けなければ収束時に実行リソース数を0にできない。(気分的な問題)
  • 単に大きなruby_blockにして全体を包んでしまうとbarrierの作成が困難に
    • 情報を入手し利用する単位でブロック化

1a-2 : リソースの属性においてはlazy {}による遅延評価が使用可能

個人的にはfileリソースのcontent属性でよく使用している。

file '/tmp/foo' do
  content lazy {
    %x(cat #{path} | sed 's/A/B/g')
  }
end

fileリソースの中で p (lazy { ... }).class などとすると、Chef::DelayedEvaluator クラスであることが判る。

以下の書式も可能

  • content lazy { ... }
  • content (lazy { ... })
  • content (lazy do ... end)
  • content(lazy { ... })
  • content(lazy do ... end)

以下はエラーとなった

  • content lazy do ... end

対応1b : コンパイル時にChefリソースの実行を行う(前実行)

Chefリソースに.run_action(:some_action)を用いる

Chef公式:Run in Compile Phase

resource_name 'foo' do
  action :nothing
end.run_action(:some_action)

この場合、デフォルトアクションの選択は無いので、:some_actionとしてアクションの明示指定が必要。
リソース本体にaction :nothingが無ければ、収束時にも実行されるので、冪等性が不十分だと問題が生じ得る。

デメリット

問題ケース2 : 収束時に遅延実行されるRubyロジックにおける変数

以下などが該当

  • lazy指定によるChefリソースの属性 (対応1aケースも該当)
  • ruby_blockにおけるblock (対応1aケースも該当)
  • Chefリソースにおけるnot_ifなどのbarrier

Chefリソースのlazy指定による属性におけるRuby変数の値

val = 'true'

file '/tmp/test.txt' do
  content lazy {

    # 何らかの文字列を返す処理
    `cat #{path} | sed -e 's/^AAA=.*$/AAA=#{val}/'`  # この#{val}の値は'false'

  }
end

...

val = 'false' # 別の処理でvalに'false'を設定して使用

他のChefリソースの反映結果を取り込む為に遅延評価のlazyを使用する。
すると、lazyブロック内の変数に関して、コンパイル時ではなく、収束時に評価される為、上の例では AAA=false となってしまう。

この問題は、ruby_blockやnot_ifなどのバリヤーでも該当する。

ruby_blockにおけるRuby変数の値

ruby_block内のsは遅延評価される為、最後に設定された値が使用される

s = "abc"

ruby_block 'test' do
  block do
    %x(echo #{s} > /tmp/test.txt) # このsは"def"
  end
end

s = "def"

Chefリソースでのnot_ifなどのbarrierにおけるRuby変数の値

barrierで使用されるbは遅延評価される為、最後に設定された値が使用される

b = true

file '/tmp/test.txt' do
  content %x(date)
  not_if { b } # このbはfalse
end

b = false

結果としてこの例では変更される

対応2: 対象の変数をイテレーターブロックへの変数としてしまう

以下に記載した対応が可能。
Chefで、Ruby実行時とリソース実行時の変数の値の差を抑えるには、イテレータで変数として渡された値を使うと容易

nodeオブジェクトから.eachにより渡せるとスマートに対応可能。

とりあえず強引かつシンプルに対応するには、例えば以下の様にする事で可能。

["abc"].each do |s|
  ruby_block 'test' do
    block do
      %x(echo #{s} > /tmp/test.txt) # このsは"abc"
    end
  end
end

s = "def" # この影響は受けない

きっともっとスマートな方法があるとは思うのですが。。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2