Chefのレシピで、ミドルウェアのインストール後にserviceリソースでaction :start
するのをよくやると思うのですが(自分もよくやってた)、これはやめたが方がいいんじゃないかと思いました。
なんでダメなのか
よくあるChefレシピの例で考えてみましょう。
Nginx をインストールするこんなレシピ。
package "nginx" do
action :install
end
service "nginx" do
action [:enable, :start] # ← コレ
supports :start => true, :status => true, :restart => true, :reload => true
end
template "/etc/nginx/nginx.conf" do
notifies :reload, 'service[nginx]'
end
一見なんの変哲もないレシピですが、実は問題があります。
問題その1:サーバの設定ファイルに文法エラーがある状態だと起動に失敗する
例えばサーバの設定ファイルを手動でいじってしまい、文法エラーが入ってNginxが起動しなくなったとします。
(本番ではやらないとしても、vagrantで実験してるときはよくあります)
このとき、レシピの設定ファイルは正しい状態なので、「Chefを実行すれば正しい状態に戻るだろう」と思うのですが、実際やってみるとそうはならない。
なぜかというと、設定ファイルのプロビジョニングよりも action :start
が先に効いてしまうので、文法エラーのまま起動しようとして起動エラーになり、Chefクライアントが処理を中断してしまうからです。
問題その2:パッケージデフォルトの状態で起動してしまう
問題2点目はレシピの実行順序にあります。
例のようにaction :start
を書いた場合、Chefの挙動は
- Nginxパッケージをインストールする
- そのままNginxを起動する (!!)
- 自作の設定ファイルを設置する
- (notifies機能によりChef実行フェーズの最終段階で) Nginxをreloadする
というフローになります。
自作設定ファイルを設置する前に、パッケージデフォルトのまま起動してしまうのですが、ここで問題がおきます。
例えばApacheとNginxを別々のポートで動くようなレシピを書いたとして、action :start
をつけてしまうと、両方とも80番ポートで起動しようとしてポートの取り合いが起きます。(後から起動した方がこける)
解決策
- serviceリソースでは
:start
しない。 - templateリソースから、
notifies :start
してnotifies :reload
する
これで、初回プロビジョン時も2回目以降のプロビジョン時もうまく動きます。
package "nginx" do
action :install
end
service "nginx" do
action [:enable] # ← :start しない
supports :start => true, :status => true, :restart => true, :reload => true
end
template "/etc/nginx/nginx.conf" do
notifies :start, 'service[nginx]' # ← ここで:start指令を送る
notifies :reload, 'service[nginx]'
end
Happy Cooking !
(なんかおかしなところがあればコメントで教えてください)