インフラ部の荒井(@ryot_a_rai)です。この記事ではクックパッドで利用しているプロビジョニングツール "Itamae"の紹介と細々した Tips を紹介します。
式年遷宮とプロビジョニングツール
現在、弊社ではインフラの式年遷宮*1を進めています。式年遷宮以前、弊社では Puppet を利用してサーバをセットアップしていましたが、式年遷宮に際して既存のプロビジョニングに関するコードは捨てることになるため、プロビジョニングツールの再検討を行うことになりました。
Puppet, Chef, Ansible, SaltStack を検討した結果、
- 言語特性の観点では、Ruby DSL な Chef が良い
- アーキテクチャ・エコシステムの観点では、シンプルな Ansible が良い
といった点から、どれも決め手に欠ける状況で、Ruby DSL で記述できるシンプルなプロビジョニングツールが必要とされていました。 そこで、以前から筆者が細々と開発していたItamae(当時は Lightchef と呼んでいました)が採用され、開発が進められました。
Itamae とは
Itamaeは一言で言うとかなりシンプルな Chef で、以下のような特徴があります。
- Chefにおける cookbook, role, environment などの概念はなく、レシピのみを管理します
- 低い学習コストで使うことができます
- プロビジョニング対象のサーバに Itamae が入っている必要がない
- SSH 経由で他サーバをプロビジョンすることが可能です
- Gem でプラグインを作ることができる
- Bundlerのみで依存関係を記述することができる
- Chef における Berkshelf の代替
入門 Itamae
Itamae の使い方について軽く触れてみます。
まず、Ruby と Bundlerをインストールしておいてください。作業用ディレクトリを作ります。
$ mkdir itamae-getting-started $ cd itamae-getting-started
Itamae をインストールするために Gemfile を置きます。直接gem install
でインストールすることも可能ですが、レシピが意図せず動かなくならないように Bundler で Itamae のバージョンを固定することをおすすめします。プラグインを使う場合には、この Gemfile にプラグインを追加するだけで利用できます。
# Gemfile source 'https://rubygems.org' gem 'itamae'
Itamae をインストールします。
$ bundle install
レシピを書いてサーバの状態を記述することができます。ここでは sl コマンドをインストールしてみます。
# sl.rb package "sl"do action :install# デフォルト値なので省略可能end
$ bundle exec itamae local sl.rb INFO : Starting Itamae... INFO : Recipe: /home/ryotarai/itamae-getting-started/sl.rb INFO : package[sl] installed will change from 'false' to 'true'
なお、SSH 越しに実行する場合は以下のように実行します
$ bundle exec itamae ssh --host host001.example.jp sl.rb
上記のレシピに書かれているpackage
はリソースと呼ばれ、サーバ上の何かしらのリソース(パッケージやファイルなど)の状態を記述します。Itamae には他にも様々なリソースが用意されていますが、代表的なものをいくつか紹介します。
# package リソース package "nginx"do action :install version "..."end# remote_file リソース# ファイルを特定のパスに置くことができます remote_file "/etc/nginx/nginx.conf"do source "nginx.conf"end# template リソース# remote_file リソースと同様ですが、eRuby (ERB)として評価した結果を書き出します template "/etc/nginx/conf.d/itamae"do source "itamae.erb"end# execute リソース# 任意のコマンドを実行することができます execute "echo Hello >> /etc/something"do not_if "grep Hello /etc/something"# このコマンドが失敗した場合のみ実行されます# only_if "grep -v Hello /etc/something" # このコマンドが成功した場合のみ実行されますend
ほかにもリソースが用意されているので一度 https://github.com/itamae-kitchen/itamae/wiki/Resourcesに目を通してみることをおすすめします。
もう一つよく使われる機能として、レシピから他のレシピを読み込む機能があります。
include_recipe "sl.rb"
上記のように、include_recipe
に読み込みたいレシピのパスを渡すと他のレシピを読み込むことができます。パスはinclude_recipe
を書いたレシピがあるディレクトリからの相対パスになります。
ちなみに、同じレシピを複数回include_recipe
しても1度しか読み込まれないようになっているので、ご注意ください。
基本的な使い方は以上です。これだけ覚えれば使い始められるので、ぜひ導入してみてください。
さらに詳しい使い方などはドキュメントを参照してください。
Itamae Tips
クックパッドでの Itamae の使い方で特徴的な点をいくつか紹介します。
role、cookbook
前述の通り、Itamae には role や cookbook を管理する仕組みはありませんが、include_recipe
で他のレシピを読み込むことで同様の機能を実現しています。
例えば、
├── bootstrap.rb ├── cookbooks │ ├── nginx │ │ ├── default.rb │ │ └── templates │ │ └── etc │ │ └── nginx │ │ └── nginx.conf.erb │ └── ruby │ └── default.rb └── roles └── web └── default.rb
このように、cookbook と role のディレクトリを用意し特定の命名規則にしたがってファイルを置いています。
# bootstrap.rb# 2015/05/12 10:03 修正moduleRecipeHelperdefinclude_cookbook(name) include_recipe File.join(__dir__, "cookbooks", name, "default.rb") endendItamae::Recipe::EvalContext.include(RecipeHelper) include_recipe File.join("roles", node[:role], "default.rb")
# roles/web/default.rb include_cookbook "nginx" include_cookbook "ruby"
role はサーバによって異なるので、node[:role]
を参照して実行するようにしています。クックパッドでは EC2 インスタンスのタグで role を指定しているので specinfra-ec2_metadata-tagsを利用して、実行するレシピを決定しています。
このように準備しておくと、以下のように実行することができます。
$ echo '{"role": "web"}' > node.json $ bundle exec itamae local --node-json=node.json bootstrap.rb
remote_file, templateのsource :auto
remote_file, template リソースにはsource
アトリビュートがあり、これでファイルやテンプレートを指定します。
$ ls recipe.rb nginx.conf.erb
# recipe.rb template "/etc/nginx/nginx.conf"do source "nginx.conf.erb"end
通常、source
にはファイルパスを指定しますが、特別な値として:auto
が用意されています。:auto
が指定されると、Itamaeは配置先のファイルパスから自動的にファイルを探します(詳細)。ちなみに、source
のデフォルト値は:auto
なので、これは省略することができます。
├── recipe.rb └── templates └── etc └── nginx └── nginx.conf.erb
# recipe.rb template "/etc/nginx/nginx.conf"doend
この方法を利用すると、ディレクトリ構成がわかりやすくなりファイルが増えた場合にも管理しやすくなると感じています。
Node#reverse_merge!
ノードアトリビュートにデフォルト値を設定する場合は、Node#reverse_merge!
が便利です。Node#reverse_merge!
はすでに値がセットされている場合は上書きせず、ディープマージを行います。
例えば、以下のように使います。
# recipe.rb node.reverse_merge!({ nginx: { worker_processes: 4 } }) template "/etc/nginx/nginx.conf"
$ bundle exec itamae local recipe.rb # この場合、worker_processesは4になります $ echo '{"nginx": {"worker_processes": 8}}' > node.json $ bundle exec itamae local --node-json=node.json recipe.rb # この場合、worker_processesは8になります
SSHを使わない
Itamae はコマンドを実行して、その結果を受け取ってから、次のコマンドを実行するため、レイテンシが高いサーバに対して SSH 実行を行うと、遅く感じることがあります。そのような場合は、対象サーバに Itamae をインストールすることで解消します。クックパッドでも最初は SSH 実行を使っていましたが、国外のサーバが増えるにつれ、レイテンシが気になるようになりサーバ上でのローカル実行に切り替えました。
システムに入っている Ruby を使って Itamae をインストールすることも可能ですが、Ruby のバージョンアップなどによって Itamae が動かなくなってしまうことを防ぐため、Ruby などの依存関係ごとインストールするパッケージを用意しています。現在、Ubuntu 14.04 用の Debian Package のみビルドしていますが、chef/omnibusを使っているので、他ディストリビューション用のパッケージもビルドできると思います。
オペレーションフロー
実際に Itamae を実行する際には複数台にまとめて実行するため、Capistrano でレシピを転送したあと Itamae を実行しています。
ただ、この方法だと
- 台数が増えた時に遅い
- 台数が増えてくるとオペレーションのコストがかかる
- 手動で実行していると、レポジトリ上のコードとサーバの状態が食い違う
- コミットされたからといって Itamae が実行されるわけではない
といった問題があり、現在自動実行の仕組みを開発中です。
GitHubへのプルリクエストの作成やプッシュを契機として、dry-run や実際の実行を行います。上図の通りクックパッドでは Consul を利用しようとしていますが、Itamae の SSH 実行など他のバックエンドも用意しようと考えています。
今後の方向性
今後も低い学習コストで使い始められ、軽量・シンプルに使えるという特徴を維持していきます。 それと同時に、大きな規模になっても使える機能を備えていきたいと考えています。
欲しい機能がある場合やバグを見つけた場合は、遠慮なく Issue や Pull Request を作成していただければと思います。
Itamae Meetup is coming soon
ついに実用段階に入った、と言っても過言ではない Itamae ですが、利用事例も少しずつ聞くようになってきたのでミートアップを開催する予定です。日時などはまだ未定ですが、ぜひお越しください&発表してください。