2015年12月11日金曜日

ChefのLWRPについて

いい加減まとめないとなぁと思いつつはや幾年・・・
ChefのLWRPについてのメモ

ここより詳しい解説がこの辺りにありますけど・・


LWRPとは、簡単に言うとレシピ内で使用するリソースを自分で定義したものです。
これの何がうれしいかというと、レシピの中でゴリゴリ書いているとどうしても可読性が落ちたり、
汎用性に乏しかったりするのですが、それを隠蔽し、共通処理をくくりだすことにより再利用しやすくなります。
たとえば、ApacheのインストールはApacheのクックブックで出来ますが、その後ほぼ必ずConfの設定が必要だったり、証明書の設置などが必要になります。
それらはほぼ同じ処理なのにドメイン名が違うなど微妙に異なります。
また、DBをインストールするときも同様にConfの設定やユーザの作成、DBの作成やバックアップなどのレシピが必要になります。

それらを隠蔽し、リソースのように定義できれば可読性も上がり、コピペミスによる動作不良などを防ぐことが出来ます。


LWRP自体はエディタでゴリゴリ書いてもいいのですが、ここではChefdkをインストールし、スケルトンを作成します。

まずは以下のコマンドを実行し、クックブックとLWRPのスケルトン(雛形)を作成します。
chef generate cookbook test
cd test
chef generate lwrp testlwrp

このようなディレクトリ、ファイルが作成されます。

C:.
└─test
    ├─providers
    ├─recipes
    ├─resources
    ├─spec
    │  └─unit
    │      └─recipes
    └─test
        └─integration
            ├─default
            │  └─serverspec
            └─helpers
                └─serverspec
LWRPで使用するのは、このうちprovidersとresourcesです。
この2つのディレクトリには同じファイル名「testlwrp.rb」が生成されています。
この2つのファイルはペアで使用するため、必ず同じファイル名にする必要がありかつ、
LWRPで作成するリソース名にする必要があります。

ここでは、簡単な例として、/tmp/hogehogeというファイルに指定した内容を書き込むものを作成します。

まず、resourceディレクトリ内にあるtestlwrp.rbに以下の内容を記載してください。

# デフォルトのアクションを指定
default_action :install
#このLWRPで定義するアクション
actions :install, :uninstall
#アトリビュートを定義する。
attribute :input_data, :kind_of => String, :required => true

次に、providersディレクトリ内にあるtestlwrp.rbに以下の内容を記載してください。

def whyrun_supported?
  true
end
action :install do
  converge_by("install test #{new_resource.input_data}") do
    file "/tmp/hogehoge" do
      mode "0644"
      content "install test #{new_resource.input_data}"
      action :create
    end
  end
end
action :uninstall do
  converge_by("uninstall test #{new_resource.input_data}") do
    file "/tmp/hogehoge" do
      mode "0644"
      content "uninstall test #{new_resource.input_data}"
      action :create
    end
  end
end


では順に説明していきます。
resourceでは以下の内容を記述しました。
# デフォルトのアクションを指定
default_action :install
chefのリソースを使用する際、アクションと呼ぶ処理の塊を呼び出します。
無指定の場合呼び出すアクションを規定します。
#このLWRPで定義するアクション
actions :install, :uninstall
コメントのままなんですが、このLWRPに定義されているアクションを列挙します。
列挙されているものが外部のレシピから使用することが出来ます。

#アトリビュートを定義する。
attribute :input_data, :kind_of => String, :required => true
このLWRPのに渡す引数です。
input_dataが引数を格納する変数名、kind_ofで引数の型を、requiredで必須入力がそうでないかの指定をします。

次に、実際の処理が記述されているproviderを見ていきます。

頭に以下のような記述があります

def whyrun_supported?
  true
end

これはwhy-runをサポートしているよ。という宣言になります。
why-runって何?なのですが、実際には実行しないテストモードになります。

次にinstallアクションを見ていきます。

action :install do
  converge_by("install test #{new_resource.input_data}") do
    file "/tmp/hogehoge" do
      mode "0644"
      content "install test #{new_resource.input_data}"
      action :create
    end
  end
end
action :install ブロックでinstallアクションを定義しています。
converge_byブロックで囲まれた中身が実際の処理になります。
fileリソースを使用して、/tmp/hogehogeファイルを作成しています。
このように、レシピを書く感じでほかのリソースを呼び出したりして作成することが出来ます。

ここで、new_resource.input_dataという変数を参照しています。
前述のアトリビュートで宣言した変数名の前に、new_resourceをつけることで、その中身を参照することが出来ます。

同様にuninstallアクションも定義してあります。

では、作成したLWRPを動かしてみます。
今回は作成した一式をVagrant上のCentOSで動作させます。
Chefサーバを用意してとかは大変なので、chef-soloでお手軽に実行したいと思います。
まずは、vagrantのディレクトリに作成したディレクトリを一式置きますと、Vagrantないでは/vagrant配下で参照できます。
recipeディレクトリのdefault.rbを以下のように記述します。

test_testlwrp "lwrp_test" do
  input_data  "test"
end
LWRPで作成したリソースを呼び出すには、<クックブック名>_<LWRP名>で呼び出すというルールがあります。
今回作成したのはtestクックブックのtestlwrpという名前なので、test_testlwrpという名前で呼び出します。
このブロックに囲まれたinput_dataというのがアトリビュートですね。
では、chef-soloを動かす準備をします。
/vagrant/testという感じで作成したクックブック一式が見えている前提です。

/vagrant/solo.rbというファイルを作成し、中身に以下の記述をします。

cookbook_path [/vagrant]
では、chef-soloを実行してみましょう。まずはwhy-runモードで変更を加えずに実行してみます。
 chef-solo -c ./solo.rb -o test -w
こんな感じに出ればOKです。

Starting Chef Client, version 11.8.0
[2015-12-11T13:32:13+00:00] WARN: Run List override has been provided.
[2015-12-11T13:32:13+00:00] WARN: Original Run List: []
[2015-12-11T13:32:13+00:00] WARN: Overridden Run List: [recipe[test]]
Compiling Cookbooks...
Converging 1 resources
Recipe: test::default
  * test_testlwrp[lwrp_test] action install
    - Would install test test
Chef Client finished, 1 resources would have been updated
作成したテスト用のレシピ(default.rb)にはアクションを明示的に指定していませんでしたが、デフォルトのアクションであるinstallが呼ばれています。
また、ファイルの中身として、install test testになることが示されています。
また、実際に/tmp/hogehogeファイルは作成されていません。シミュレーションしただけですね。

では、アクションを変えてみます。default.rbを次のように変更して再度chef-soloを実行してみます。

test_testlwrp "lwrp_test" do
  input_data  "test"
  action :uninstall
end
Starting Chef Client, version 11.8.0
[2015-12-11T13:37:11+00:00] WARN: Run List override has been provided.
[2015-12-11T13:37:11+00:00] WARN: Original Run List: []
[2015-12-11T13:37:11+00:00] WARN: Overridden Run List: [recipe[test]]
Compiling Cookbooks...
Converging 1 resources
Recipe: test::default
  * test_testlwrp[lwrp_test] action uninstall
    - Would uninstall test test
Chef Client finished, 1 resources would have been updated

installの箇所がuninstallに変わりましたね。
では、実際に適用してみましょう。

chef-solo -c ./solo.rb -o test
Starting Chef Client, version 11.8.0
[2015-12-11T13:45:18+00:00] WARN: Run List override has been provided.
[2015-12-11T13:45:18+00:00] WARN: Original Run List: []
[2015-12-11T13:45:18+00:00] WARN: Overridden Run List: [recipe[test]]
Compiling Cookbooks...
Converging 1 resources
Recipe: test::default
  * test_testlwrp[lwrp_test] action uninstall
    - uninstall test test
Recipe: <Dynamically Defined Resource>
  * file[/tmp/hogehoge] action create
    - create new file /tmp/hogehoge
    - update content in file /tmp/hogehoge from none to 3c37bb
        --- /tmp/hogehoge       2015-12-11 13:45:18.458044278 +0000
        +++ /tmp/.hogehoge20151211-27701-vdbtpv 2015-12-11 13:45:18.460044278 +0000
        @@ -1 +1,2 @@
        +uninstall test test
    - change mode from '' to '0644'
Chef Client finished, 2 resources updated
uninstallアクションが実行され、/tmp/hogehogeファイルが作成、パーミッションが0644、中身がuninstall test testなファイルが作成されました。
一応中身を確認してみましょう。

[root@localhost vagrant]# more /tmp/hogehoge
uninstall test test
[root@localhost vagrant]# ls -lah /tmp/hogehoge
-rw-r--r-- 1 root root 19 Dec 11 13:45 /tmp/hogehoge
意図したとおりになっていますね。

以上簡単ですけれど、基本的な動作は抑えていると思いますので、これを応用すればオレオレリソースを作成するのも可能だと思います。
さらに複雑、高度なリソースを定義しようとすると、Rubyのモジュールやクラスを作成して呼び出す・・・なんてことも出来ます。
ただ、あまりRubyをゴリゴリ書くと、作った人しか分からない負の遺産が出来るので(経験談)ほどほどが良いです。


0 件のコメント:

コメントを投稿