開発メモ#4 : EC2スナップショットとの差分は chef-solo で解決

開発メモその4です。

開発メモ#2 : AWS でのホスト / クラウドネイティブなデプロイ - naoyaのはてなダイアリー で、システム構成の変更時に EC2のスナップショットからインスタンスを複製して Elastic IP で切り替えているという話をしました。

ただ、この方法はそのままでは一点問題があります。スナップショットを取ったタイミングと現時点でシステム構成に差分があった場合にどうするか、です。例えば nginx の設定をほんの少しだけ書き換えたい、とかその都度スナップショットを取っていては流石に面倒。

その手のスナップショット時点からの差分を複製されたインスタンスに簡単に適用するために、基本的なサーバー設定周りは chef-solo で管理してます。chef はサーバー構築自動化ツールで、chef-solo は chef のクライアント・サーバーを必要としないライト版、とでもいえばいいのか。

chef-solo のレシピ (管理したいサーバーの設定等々のこと) は git に放り込んでバージョン管理しておく。

全ての環境を chef-solo で構築することもできるでしょうが、それはそれで大変だしそれだけだと毎回の復元に時間がかかってしまう。基本、インスタンスの立ち上げは少し前のスナップショットから行ってその後 chef-solo とアプリケーションのデプロイを行うことで全てが最新になります。何がどうなっているか忘れてもコマンド一発で現状復帰できる、かなり幸せ。

以下、chef-solo の使い方について要点を紹介。

ワークフロー

chef-solo にはクライアントもサーバもないので、基本的には sshリモートホストにインストールされている chef-solo を実行することになります。

  • ローカルでレシピ (chef) をがりがり書く
  • chef レシピの lint foodcritic でチェック
  • git commit して github に push
  • chef-solo を Cinnamon を使って、リモートで実行
  • リモートでは git fetch で最新のレシピを取ってきて、chef-solo が実行される

という手順で進めてます。

rbenv な環境で chef-solo + Cinnamon

Amazon Linux にデフォルトで入っている ruby は 1.8系、chef の推奨バージョンではないので ruby は別途 rbenv で入れました。

で、chef の実行は先日紹介した Cinnamon にタスクを作って実行している。

task chef => {
    run => sub {
        my ($host, @args) = @_;
        my $deploy_to = get('deploy_to');
        remote {
            sudo "chef-solo -c $deploy_to/chef/solo.rb -j $deploy_to/chef/nodes/appserver.json";
        } $host;
    },
}

こんな感じですね。

% cinnamon development chef:run

で、リモート側で chef-solo が実行される。リモートユーザーに sudo で chef-solo の NOPASSWD を付けておくとよい。

chef のレシピは、アプリケーションのレポジトリの中に一緒に入れてしまっている。分けるかどうか迷いましたが、github との複数公開鍵連携の問題など諸々あるのでとりあえず。

ちなみに knife-solo という、chef のフロントエンドツールみたいなものがありまして、これを使うと特にデプロイツールと連携などさせなくてもローカルからリモートにレシピの同期と chef-solo の実行を発行できるようですが自分の環境ではうまく設定できなかった、かつワークフロー的に github を間に挟みたかったので前述のようにしております。

例: chef-solo で supervisord を配備してみる

ひとつ、何か設定を変更したいというときの例を備忘録代わりにでも書いておきましょう。supervisord を chef-solo で管理するのを想定してみます。

% cd chef/site-cookbooks
% knife cookbook create supervisord -o .

まずはレポジトリ内で chef-solo のレシピを保存しているディレクトリに移動。chef、という名前にしてます。それから、クックブックのひな形を生成する。

site-cookbooks/supervisord/recipes/default.rb を編集して、レシピを書く。

# supervisor をパッケージでインストール
package "supervisor" do
  action :install
end

# 設定ファイルをテンプレートから生成して配置
template "supervisord.conf" do
  path "/etc/supervisord.conf"
  source "supervisord.conf.erb"
  owner "root"
  group "root"
  mode 0644
  notifies :reload, 'service[supervisord]'
end

# supervisord を立ち上げ
service "supervisord" do
  supports :status => true, :restart => true, :reload => true
  action [ :enable, :start ]
end

supervisord の設定のキモになる supervisord.conf は、同クックブックレシピ内の templates ディレクトリにあるものが使われる。テンプレートは erb で書けますが、変数は一切使っておらずリモートにあった supervisord.conf をそのままコピーしてきて site-cookbooks/supervisord/templates/supervisord.conf.erb として置いてるだけです。

あとは foodcritic を実行して、シンタックスに問題ないかなどを確認する。ちなみにテンプレートを作り忘れたりすると

☁  chef [master] ⚡ foodcritic site-cookbooks
FC033: Missing template: site-cookbooks/gemrc/recipes/default.rb:9

こんな感じでエラーが出る。

できたら ndoes/appserver.json という、自分のアプリケーションサーバー向けのノードファイル・・・これこれこのレシピをこういう条件で実行しろとか書く設定ファイル、に supervisord レシピを追記する。

{
    "recipes":[
        "mosh",
        "git",
        "zsh",
        "nginx",
        "oh-my-zsh",
        "gitconfig",
        "supervisord",
        ...
    ]
}

あとは git commit, git push して

% cinnamon development deploy:update && cinnamon development chef:run

と、リモートレポジトリをアップデートして chef-solo を走らせて完了。

後日 supervisord.conf に変更を加えたいなと思ったときは、テンプレートの中身を弄って chef-solo を走らせれば良い。変更履歴は git & github で管理されることになる、万歳。

ちなみに

これらのResourceは「何度繰り返しても同じ結果になります」大事です。英語で言うと、idempotentです。冪等性(べきとうせい)とも言います。

shell scriptでmkdir hogeと二回打つと、二回目は「File exists」というエラーになりますよね?(-p使えという話もありますが)しかし、chefを使った場合ではエラーになりません。これはchefが自動的に判断して実行しないからです。

また、templateであれば、もし手動でなにかを書き換えていた場合にはその変更は書き潰されますし、ownerを変更した場合はchefのレシピ通りに戻されます。

ということで、先のレシピだと supervisord のインストールなんかが書かれているけど、毎回インストーラが走るなんてことななく、その辺は chef が良い感じに面倒みてくれるようになっています。気になる人もいるかもなので、一応触れておきます。

落ち穂拾い

supervisord の他には、nginx や memcached、redis などのツールも chef-solo で管理していますし、zsh や oh-my-zsh や、zshrc や gemrc、gitconfig などの設定ファイルなんかもそうしています。

chef のレシピは自分で書かなくても、有志が作って公開されているものが沢山あります。例えば nginx のクックブックが欲しい・・・と思ったら chef nginx cookbook とかでググれば出てきます。自分はやりたいことはシンプルなので今のところその辺は利用していません。

chef-solo の動作は本番適用の前に手元で検証したい、という場合は vagrant を使うと良いそうです。knife-solo には vagrant とうまい具合に連携する機能なんかがついてて、簡単に検証用の VM を立ち上げることができる模様。

自分の場合、検証が必要になるような大きな変更は例によってスナップショットで本番とは別環境を立ち上げてから適用するようにしていて手元での検証はあまりしていません。vagrant 使い始めました。便利すぎました、すみません。

レシピのテストがないのが不安・・・!! という方は Cucumber, ChefSpecとchefでテスト駆動のサーバ構築管理 - Qiita この辺を見ると良いでしょう。テスト駆動サーバー構築、なんて芸当を実行している御大が。

と、いうわけでシステム構成を chef-solo で管理するおはなしでした。