前から気になっていた Heroku + MongoHQ を試してみた。HerokuはRubyアプリケーションを走らせるホスティングサービスで、MongoHQはMongoDBのホスティングサービスだ。この二つを組み合わせることで、MongoDBを使ったRubyアプリケーションを一瞬で運用開始することができる。
あまりにも簡単に使えてあまり書くこともないんだけどメモ。
まず、両方とも最低限の環境は無料で使用できる(ただしHerokuからMongoHQを使うためにはクレジットカードの登録は必要っぽい)。
今回は Ruby on Rails 3 + Mongoid で作ったアプリを置いてみた。
手順
1. まず、普通に RoR + Mongoid のアプリケーションを作る
2. Herokuにアカウントを作りアプリケーションを登録する (http://docs.heroku.com/quickstart )
3. HerokuでMongoHQを有効にする (http://docs.heroku.com/mongohq )
$ heroku addons:add mongohq:free
4. Mongoidの接続情報であるconfig/mongoid.ymlのproduction環境のところを以下のように書き換える。
production:
uri: <%= ENV['MONGOHQ_URL'] %>
追記: もしくは、 config/initializers/mongohq.rb のようなファイルを作り、 そこで指定する。
if ENV['MONGOHQ_URL']
# For Heroku (See: http://docs.heroku.com/mongohq)
Mongoid::Config.instance.from_hash({"uri"=> ENV['MONGOHQ_URL']})
end
5. 普通にHerokuへdeployする (具体的には、gitでHerokuにpushする)
これだけで、MongoHQのMongoDBを使うようになる。超簡単。
(ちなみに、ここ最近、RubyとMongoDB間のORMはMongoidしか使っていない。一時期、 MongoMapper を使っていたのだけど、Mongoid の使いやすさに負けた。機能的には大きく変わらないのだけど、ちょっとした細かいところとか、センスの良さが全然違う。)
気付いたところ
1. Heroku経由でMongoHQを使うとMongoHQにログインできない?
MongoHQにも管理画面的なのがあるっぽいのでこれはちょっと嫌だ。(何か方法ありそうだが)
2. ここ にあるように、MongoDBの接続情報を取得し、自分の環境からHerokuを通さずに直接接続も可能。
バックアップもmongodumpコマンドで普通に取れるし、 Mongoシェル でアクセスするのも問題ない。これは便利。
ただ、便利なのだけど、つまりMongoHQのMongoDBは全世界から接続可能なので、接続情報や、ID/パスワードの管理は慎重にする必要があるのがちょっと厳しい。例えば、Rubyアプリ側では、 ENV[“MONGOHQ_URL”] にID/パスワード含む接続情報がすべて入っているので、デバッグ目的などでENVを間違えて表示しちゃったりすると大惨事になりそう(これはHerokuのMySQLとかPostgreSQLとかも同じっぽいが)。
3. HerokuからMongoHQまでのlatencyは一桁ミリ秒だった。
なんとなく不安だったのだけど問題なさそう。
まとめ
RoRとMongoDB(Mongoid)のスキーマレスでの素早い開発と、簡単にデプロイできるHeroku+MongoHQの相性がとてもよい。ちょっとしたアプリケーションを作ったり、プロトタイプを作って色々な人に見せたい場合にとても向いていると感じた。もちろん、アクセス数やデータベース容量にあわせて、Heroku/MongoHQともに有料コースに切り替えて本番環境として使い続けるのも問題ないだろう。
また、「Heroku+MongoHQで動かす」と言っても、アプリケーション自体はもちろん普通のRuby(Rails)とMongoDBなので、Heroku/MongoHQが嫌になったら、他へ移ることも、自分で運用するのにも何の問題もない。「とりあえず作ってHeroku+MongoHQで動かしてある程度軌道に乗ったところ次を考える」というのが可能だ。これはかなり魅力的(今、世の中はロックインが流行ってるようだし!)。
前回書いた さようならPuppet、こんにちはChef が、それなりに反響あったので調子に乗ってもうちょっと書いてみる。
前回、ChefはPuppetに比べて簡単!とか書いたが、実際には慣れるまでそれなりに戸惑うところがあった。
ドキュメント を読み、実際に触っただけでは一発で理解できなかった部分を、自分のメモを元に晒しておく。これだけ読んでもいまいちだと思うので、関連するドキュメントへのリンクも張っておくので合わせて読んでみると高速でChefを理解できるかも!
client vs node
ドキュメントを読んだりChefを触っていると client
と node
という二つのワードが出てくる。この二つは似ているけど別物。
client
は文字通り Chef server
の相手になるもの。 Chef server
にアクセスするものはすべて client
になる。例えば、管理ツールである knife
(後述)や、管理用のWeb UIなども client
になる。認証や通信はすべて client
が Chef server
と行う。
一方、 node
というのは、Chefで管理するマシン/サーバ自体を指す。各 node
は Chef server
にアクセスし自分の設定を持ってくるのが、これは実際には node
にある client
経由で行う。
そのため通常は node
は client
でもある。そして、 client数 >= node数
になる。
書いてしまうと簡単なのだけど、最初は両者がごちゃごちゃになってしまっていた。ここをしっかり理解しておくとトラブルシューティングもしやすくなる。
トラブルシューティングの例:
ある
node
(のclient
)で、サーバにアクセスするのに必要な秘密鍵を間違って消してしまいChef server
へのアクセスができなくなってしまった。この場合どうすればいいか?対応策: 鍵情報は
client
に結びついているので、client
の鍵ペアを作り直せばいい(または、client
をサーバから削除して作り直す)。 どちらの場合も、node
の情報をいじる必要ない。
knife
Chefサーバ上にあるデータを触るためのコマンドラインツール。主に手作業でサーバの情報にアクセスしたいときに使う。管理者が使用するもの。上に書いたように knife
自体も client
になる。管理者毎に別の client
として登録する。
特に、 node
の情報は、他の設定と違いサーバ上にしかないので、このコマンドをよく使う。
例1: node
の情報(JSON形式)を $EDITOR で開き編集する
$ knife node edit hogehoge.example.com
例2: 指定した client
を削除
$ knife client delete fugafuga.example.com
node と cookbook (recipe) と role
まず、 node
は、上にも書いたように管理対象のサーバのこと。
cookbook
は、実際にChefで行いたい設定の手順を記述したもので、基本的に環境に依存するのものは書かない。依存するものは role
や node
の attributes
に書く(後述)。
recipe
とは、設定を記述したrubyスクリプトのことを指す(ドキュメント等で cookbook
のことを recipe
と呼んでいるケースもあるので注意)。
一つの cookbook
は複数の recipe
を持つことができる。例えば, LDAP cookbookの中に、 LDAPクライアント用の recipe
と、LDAPサーバ用の recipe
を持つことができる。
role
は、サーバの役割を記述したもの。 どの recipe
を使うかということを主に書く。また、 node
は、基本的に1つ以上の role
を持つ。ただし、ちょっとややこしいが、 node
は recipe
を role
を介さないで直接持つこともできる。
たとえば、 “hoge-system-app” role
というのを考えてみる。これは “hoge-system” という Webシステムのアプリケーションサーバを想定する。
ここで、
- hoge-system-app1.example.com
- hoge-system-app2.example.com
- hoge-system-app3.example.com
という3つの node
があったとする。3つとも、"hoge-system-app" role
を持つ。ただし “hoge-syste-app1.example.com” だけは、特別に、"git" クライアントも入れたい。こういう場合には、gitの recipe
を直接指定することもできる。
- hoge-system-app1.example.com
- hoge-system-app
role
- git
recipe
- hoge-system-app
- hoge-system-app2.example.com
- hoge-system-app
role
- hoge-system-app
- hoge-system-app3.example.com
- hoge-system-app
role
- hoge-system-app
もちろん、gitの recipe
を持つような、 developer role
みたいのを作り、それにgitを持たせ、 node
にセットすることもできる。
基本的には、 role
経由で管理したほうがいいが、本当に例外の場合はわざわざ role
を作らなくてもいいかもしれない。これは設計次第。
attributes
attributes
は、実際に設定したい値(パラメータ)のこと。上述したように、 cookbook
/ recipe
には、サイト固有の情報を持たせないのが原則なので、そういうものはすべて attributes
にして外出しにする。
attributes
は recipe
自体や、 recipe
経由で template
(erbで記述) から使われる。基本的にrubyのHashそのもの。
attributes
は recipe
にも設定できるし、 role
や node
にも書ける。
recipe
に書いた attributes
がその recipe
のデフォルト値で、それを role
や node
の attributes
で必要に応じて上書きする、と考えると理解しやすい。
例:まず、
tokyo-office
というrole
があったとしよう。とある東京オフィスにあるサーバ群はこのrole
を使うというルールになっている。今、このrole
に対して、LDAPクライントを設定したいとする。最初に、LDAP
cookbook
(recipe
) を作成する。このとき、LDAPサーバのホスト名(IP address)みたいなのがサイト固有の情報になるので、attributes
に切り出す。次に作成した、
recipe
をtokyo-office role
に設定する。このときattributes
として切り出した LDAPサーバの IP addresses をrole
のattributes
ととして設定する。ここで、もし、その中のある、特定の
node
だけに対して例外的に特別な値(例えば、テスト用のLDAPサーバを見させたいとか)を書きたい場合には、 そのnode
のattributes
に書いて、role
のattributes
を上書きする。
自分が理解しているのはこんな感じ。enjoy cooking!
ここ最近、サーバの設定ファイルの管理で Chef を使い始めている。まだ全然詳しくないけど、今感じている「Chefの楽しさ」を誰かに伝えておきたかったので、ファーストインプレッションを簡単に。
Puppetを今までそこそこ使っていたので、どうしてもそことの比較な感じになっちゃいます。Puppetも良いのだけど、Chefは後発ということでさらに良くなっている感じ。
基本的な仕組
これは、Puppetとほぼ同じ。クライアント-サーバ型のシステム。設定を書き、それをサーバに置いておく。クライアントはサーバと接続し、自分自身の設定を書き換えたり、必要なソフトウェアをインストールしたりする。
rubyな設定ファイル
Puppetは基本的に独自DSLで設定ファイルを記述すので「覚えるのがめんどくさい」「細かいこと、ちょっと無茶なことをしようとすると大変」。Chefの設定ファイルはrubyそのものなので、rubyで表現できることは何でもできる。とは言えDSL風にもなってるのでrubyを知らなくてもなんとかなるレベルでもある。その辺はさすがにruby。実際にどんな感じで設定を書くか、というのは、 この辺参照 。
ローカルでのテストが楽
chef-soloというコマンドがあり、これを使うとChefサーバに接続せずにローカルだけでテストができる。大袈裟なテスト環境を作らなくても自分の環境でテストできるのがとても楽。
設定ファイルが直感的
Puppetは、manifestとかmoduleとかclassとか、どうも最後まで慣れなかったが、Chefは初めの数時間で、すーっと頭に入ってきた。この差はでかい。
いくつかの理由があるけど、まず、設定ファイルのディレクトリ構成がわかりやすいというのが大きい。
設定ファイル内の主要なディレクトリは、cookbooksとrolesの二つだけ。cookbooksの中には、cookbookと呼ばれるソフトウェア毎の設定を置くサブディレクトリがある。一つのcookbookに関連する設定はそのサブディレクトリ内にすべて置く。
例:
cookbooks
+ sshd
+ recipes
+ templates
+ attributes
+ sudo
+ recipes
+ templates
+ attributes
+ apache2
+ recipes
+ templates
+ attributes
+ .....
roles
+ hoge_app.rb
+ hoge_rproxy.rb
この「cookbook内で完結し独立している」というのがとても扱いやすい。完結しているので、自分で管理するのももちろん楽だけど、他の人や会社がが作っているcookbookの流用もしやすい。 Chef公式のcookbook はもちろん、 37 signalsのcookbooks もよく参考にしている。
roleは、どのcookbookをどういう設定で使うか、というのを書く。そして作成したroleを実際のサーバに割り当てる。一つのサーバが複数のroleを持つこともできる。このroleとcookbookという関係もとてもわかりやすい。
各roleの中にはどのcookbookを使うか、というのを羅列する。例えば,
run_list "recipe[apache2]", "recipe[apache2::mod_ssl]", "role[monitor]"
みたいな感じ。そして、cookbookに対する設定もrole内に書く。
default_attributes "apache2" => { "listen_ports" => [ "80", "443" ] }
(このサンプル から引用)
こんな感じ。とってもわかりやすいでしょ?
Git等との距離感
設定ファイルはもちろんGitとかで管理するのだけど、それと実際のChefの動作部分は特に関係ない。設定ファイルを書き換えた後commit/pushで設定の反映ではなく、rake upload_cookbooks 等のコマンドでサーバへ反映。これがなかなか気持ちいい。ソフトウェアをdeployする感覚と似ている。
よくわかってないところ
サーバ側の設定は 弊社のすばらしいシステム管理者の人 がしてくれたので自分はよくわかってない。結構ややこしいみたい。
嫌いなところ
SEO的にどうよ、な部分。Chef自体もそうだし、出てくる単語もcookbookとかrecipeとかknifeとか、ぐぐるの大変だよ!
そんなわけで
とりあえず使い始めてみましたよ。という感じです。
追記: もうちょっと書いた。
githubには個人でも会社でもお世話になってるので新しくできたJobボードに載せてみた。もちろん求人自体は本物なので興味ある人は応募してください。勤務地は東京です。
https://jobs.github.com/positions/a9bc8e26-a6d5-11df-8cf1-63a2a0cb612b
載せるのはとても簡単。Webフォームに求人内容を書いて、クレジットカード情報を入力して完了。プレビュー含めインターフェースがよくできてるのはさすが。載せると修正用のURLが送られてくるのでtypoとかにビビリ過ぎなくても大丈夫(自分は知らなかったのでかなりビビった)。
どのくらい応募があるのか楽しみ。
この求人についてちょっと補足すると、海外に興味はあるけど、いきなりは海外は自信ないとかの人の練習にちょうどいいと思います。技術があれば英語はこれからでも大丈夫です。日本オフィスにも英語な人がたくさんいます。でも、社内公用語は英語です! とかは言ってないので安心です!
CV送れ、と書いてありますけど、日本語で履歴書とか業務経歴書でも大丈夫です。
追記(2010-10-04): 求人広告の成果ですが、一人いい人を採用できました。どこまで言っていいかわからないのですが、応募者数はそんなに多くなかったです。応募者の質は高かったと思います。
githubの広告はexpiredしてしまいましたが、まだ若干名募集しているので、興味のある人は、 こちらまで。
(2010-08-30: Rails 3.0.0がリリースされたのでそれにあわせて更新。generator関連が少し変わってる)
前回 の続き。
主に FactoryGirl と MongoMapper の話。基本的に何も考えなくてもそのまま使えるのだけど。
まず、設定。
spec_helper.rb で、
config.use_transactional_fixtures = true
を
config.use_transactional_fixtures = false
にする。そうしないと、ActiveRecord::TestFixtures が呼ばれてしまうのだが、ActiveRecordを入れてないので落ちる。
そもそもMongoDBにはトランザクションとかないのでfalseでいい。
後は、ActiveRecordで使う場合と同じように、FactoryGirlの定義の読み込みを spec_helper.rb の中で行う。
Factory.find_definitions
mongoDBにはトランザクションがないので、テストの前にデータを自分で消しておいたほうがいいだろう。
config.before(:each) do
MongoMapper.database.collections.each {|collection| collection.remove}
end
全体的にはこんな感じ。generatorが作ったものに、上記変更を入れただけ。
# This file is copied to spec/ when you run 'rails generate rspec:install' ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails'
# Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
require 'factory_girl' Factory.find_definitions
RSpec.configure do |config| # == Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr config.mock_with :rspec
# config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, comment the following line or assign false # instead of true. config.use_transactional_fixtures = false
config.before(:each) do MongoMapper.database.collections.each {|collection| collection.remove} end end
試しに一つFacotryとテストを書いてみる。
File: spec/factories/entry.rb
Factory.define :entry do |f|
f.title "MyString"
f.body "MyString"
end
簡単なお試し用のテストを書いてみる。(テスト自体に意味はない)
require 'spec_helper'
describe Entry do it "should create 10 entries" do 10.times { Factory(:entry) } Entry.count.should == 10 end end
書いたテストの実行
$ bundle exec rspec spec/models/entry_spec.rb .
Finished in 0.03308 seconds 1 example, 0 failures
すばらしい!
FactoryGirlで、MongoMapper::Document同士のassociationも問題なく書ける。(ただし、MongoMapper::EmbeddedDocument から 親Documentへのassociationはうまく作ってくれなかった(正格には作ってくれるんだけど、Saveされない))
こんな感じで、Rails 2 + ActiveRecord時代とほとんど変わらない環境が Rails 3 + MongoMapper で、できたかな。