Heroku + MongoHQ が素晴らしい

16th Nov, 2010 | heroku mongodb rails ruby

前から気になっていた 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で動かしてある程度軌道に乗ったところ次を考える」というのが可能だ。これはかなり魅力的(今、世の中はロックインが流行ってるようだし!)。



githubにRails開発者の求人広告載せてみた

13th Aug, 2010 | ruby rails github

githubには個人でも会社でもお世話になってるので新しくできたJobボードに載せてみた。もちろん求人自体は本物なので興味ある人は応募してください。勤務地は東京です。

https://jobs.github.com/positions/a9bc8e26-a6d5-11df-8cf1-63a2a0cb612b

載せるのはとても簡単。Webフォームに求人内容を書いて、クレジットカード情報を入力して完了。プレビュー含めインターフェースがよくできてるのはさすが。載せると修正用のURLが送られてくるのでtypoとかにビビリ過ぎなくても大丈夫(自分は知らなかったのでかなりビビった)。

どのくらい応募があるのか楽しみ。

この求人についてちょっと補足すると、海外に興味はあるけど、いきなりは海外は自信ないとかの人の練習にちょうどいいと思います。技術があれば英語はこれからでも大丈夫です。日本オフィスにも英語な人がたくさんいます。でも、社内公用語は英語です! とかは言ってないので安心です!

CV送れ、と書いてありますけど、日本語で履歴書とか業務経歴書でも大丈夫です。

追記(2010-10-04): 求人広告の成果ですが、一人いい人を採用できました。どこまで言っていいかわからないのですが、応募者数はそんなに多くなかったです。応募者の質は高かったと思います。

githubの広告はexpiredしてしまいましたが、まだ若干名募集しているので、興味のある人は、 こちらまで



Rails 3 + mongoDB + haml + RSpec + jQuery のインストール - 2

11th Jul, 2010 | ruby rails mongodb haml rspec

(2010-08-30: Rails 3.0.0がリリースされたのでそれにあわせて更新。generator関連が少し変わってる)

前回 の続き。

主に FactoryGirlMongoMapper の話。基本的に何も考えなくてもそのまま使えるのだけど。

まず、設定。

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 で、できたかな。



Rails 3 + mongoDB + haml + RSpec + jQuery のインストール - 1

2nd Jul, 2010 | ruby rails mongodb haml rspec

(2010-08-30: Rails 3.0.0がリリースされたのでそれにあわせて更新。generator関連が少し変わってる)

会社用の、小物Webアプリを作ろうかと思い、せっかくなのでRuby on Rails 3でmongoDB使ってみようかな、と思い、とりあえず環境を作るところまでのメモ。

Rails 3 のインストール

とりあえず Rails 3 のインストール。Bundlerで入れる。Bundler自体のバージョンが1.0以上でないとダメみたいなんで、もしそれ未満しか入っていない場合にはBundlerのインストールからする。

プロジェクトのトップディレクトリとなるところを作成し、そこにGemfileを作る。

$ mkdir ~/workspace/hoge_prj
$ cd ~/workspace/hoge_prj

Gemfile

source 'http://rubygems.org'
gem "rails", ">=3.0.0"

rails 3のgemのインストール

$ bundle install

railsコマンドでrailsプロジェクトを作成する。

$ bundle exec rails new . --skip-activerecord

このコマンド自体がGemfileをもう一度作る。上書きしてしまえばいい。MongoMapperを使う予定なので --skip-activerecord を指定した。

次に、自分が必要なライブラリをGemfileに追加して、もう一回 bundle install する。

今回は、

gem "mongo_mapper"
gem "bson_ext"
gem "rails3-generators"
gem "haml"
gem "haml-rails"
gem "jquery-rails"
gem "rspec-rails", ">= 2.0.0.beta.13", :require => nil
gem "factory_girl_rails", :require => nil 

を追加した。 rspec / haml / factory_girl は普段も使ってるのであまり考えずにそのまま使ってみる。Rails 3では、rspec 2が必要らしい。

$ bundle install

次に、config/application.rb を開いて、

config.generators do |g|
  g.orm :mongo_mapper
  g.template_engine :haml
  g.test_framework  :rspec, :fixture => true
  g.fixture_replacement :factory_girl, :dir => "spec/factories"
end

をそれっぽいところに書く。

rspecとjqueryの関連ファイルを作成する。

$ ./script/rails g rspec:install
$ ./script/rails g jquery:install

今回は Mac上にインストールしたmongoDB を使っている。

次にmongodbの設定をする。

$ ./script/rails g mongo_mapper:install test_project —host=localhost

test_project の部分はmongoDBで使うデータベース名を設定する。これは単純にconfig/database.mongo.ymlに書かれるだけなので変えたければ後で簡単に変えられる。

これで、ほぼインストール完了。 rails3-generators のおかげで mongo_mapper を使った scaffold を作ることもできる。

$ ./script/rails g scaffold entry title:string body:string
      invoke  mongo_mapper
      create    app/models/entry.rb
       route  resources :entries
      invoke  scaffold_controller
      create    app/controllers/entries_controller.rb
      invoke    haml
      create      app/views/entries
      create      app/views/entries/index.html.haml
      create      app/views/entries/edit.html.haml
      create      app/views/entries/show.html.haml
      create      app/views/entries/new.html.haml
      create      app/views/entries/_form.html.haml
      invoke    rspec
      create      spec/controllers/entries_controller_spec.rb
      create      spec/views/entries/edit.html.haml_spec.rb
      create      spec/views/entries/index.html.haml_spec.rb
      create      spec/views/entries/new.html.haml_spec.rb
      create      spec/views/entries/show.html.haml_spec.rb
      invoke      helper
      create        spec/helpers/entries_helper_spec.rb
      create      spec/routing/entries_routing_spec.rb
      invoke      test_unit
      create        test/integration/entry_test.rb
      invoke    helper
      create      app/helpers/entries_helper.rb
      invoke      rspec
      invoke  stylesheets
      create    public/stylesheets/scaffold.css

とりあえず、コンソールからレコード(mongoDB風に言うとドキュメント)が作成できるか確認してみる。

% ./script/rails console
Loading development environment (Rails 3.0.0)
irb(main):001:0> Entry.create!(:title => "HOGEHOGE", :body => "Hello")
=> #<Entry body: "Hello", title: "HOGEHOGE", _id: BSON::ObjectId('4c7bc200a90e0844f8000001')>
irb(main):002:0> 

簡単!

本当に保存されたか、mongoシェルで確認してみると、

% ~/somewhere/mongodb-osx-x86_64-1.5.3/bin/mongo
MongoDB shell version: 1.5.3
connecting to: test
type "help" for help
> use hoge-development
switched to db blog-development
> db.entries.find()
{ "_id" : ObjectId("4c7bc200a90e0844f8000001"), "title" : "HOGEHOGE", "body" : "Hello" }
> 

入ってる!

サーバを起動して、画面でも見てみる。

$ ./script/rails server

ブラウザで http://localhost:3000/entries/ を開いて確認。



面白く無いいつもの scaffold がちゃんとmongoDBで動いているのが確認できる。

とりあえずここまでで、RSpec / factory_girl あたりを 次回

参考:
http://www.mongodb.org/display/DOCS/Rails+3+-+Getting+Started
http://paulbarry.com/articles/2010/01/13/customizing-generators-in-rails-3
http://groups.google.com/group/mongomapper/browse_thread/thread/cd89810b98eb7abf
http://github.com/rspec/rspec-rails
http://stackoverflow.com/questions/3004489/mongodb-initialization-error-in-rails



resque-scheduler (resqueでcronみたいなことをする)

27th Feb, 2010 | ruby rails resque scheduler

resqueシリーズ。 前回resque-scheduler について少し触れたのだけど。resque-schedulerのメインであるcron的に使う機能について書いてなかったのでメモ。deployについても書いた。

cronと比較して

*nix系のシステムだと、伝統的に、何かを定期的に実行したいcronを使うのだけど、アプリケーションからcronを使う場合、いくつかの使いづらい点がある。

  • アプリケーション本体から離れた場所でcronの動きを管理しないといけない。通常、/etc/crontab 等をメンテナンスしないといけないのだけど、そこはアプリケーションの外側なので、メンテナンスするときにアプリケーション側で使っている方法を使いづらい(SCMもそうだし、deployとかも別で考えないといけない)。
  • cronから起動する場合、起動する環境について考えないといけない。環境変数が代表例。アプリケーションと同じような環境になるように注意しないといけない。

resque-schedulerを使うと、cronでするようなことをアプリケーション内で完結することできる。crontabのようなものをアプリケーション内に置けるので、アプリケーション本体を管理するSCMを使うこともできるし、deployもアプリケーションと常に同じ方法でできるようになる。

ただ、基本的にはcronの動きを踏襲しているので、cronの以下の(場合によっては)負の特徴も引き継いでいる。

  • cronと同じように、指定した時間にresque-schedulerが立ち上がってない場合、その処理は行われない。
  • cronと同じように、同一のタスクが同時に走ってしまう場合がある。例えば、毎分実行するような設定を書いた場合、その処理に1分以上かかってしまった場合、前の処理が終わる前に次の処理が走ってしまう。それが致命的な場合、自分でロック処理を書く必要がある。
  • 複数のresque-schedulerを走らせることが考えられていない。複数のresque-schedulerを実行すると、同じ時刻に同じタスクが複数回走ってしまう。つまり、「冗長化のために、2台のアプリケーションサーバ上でresque-schedulerをそれぞれ動かす」ということができずに、冗長化した構成が取れない。

最後のは、嫌なので、複数のところで走らせても同一のタスクが複数回走らないようなパッチを書いて作者に投げてある。しかし、「冗長化構成が取れるように」ということには賛同を得たのだけど、そのパッチについてはいくつか課題がありそうなので保留になっている。気が向いたらもうちょっと頑張るつもり。

使い方

resque-scheduler を見るのが早いが、crontabで定義するようことをyamlで定義する。そのドキュメントの例を使わせてもらうと、

queue_documents_for_indexing:
  cron: "0 0 * * *"
  class: QueueDocuments
  args: 
  description: "This job queues all content for indexing in solr"

こんな感じになる。この場合、毎日0時0分に、QueueDocumentsというResqueのJobがQueueに入ることになる。ただし、ResqueのQueueに入るだけで、resque-schdulerは実際の実行はしない、ということに注意。Queueを処理するのはResque自体のWorkerの仕事になる。

deployについて

resqueと同じように、resque-scheduler自身には、起動スクリプトみたいなものがついていないので、godやmonitのようなもので管理するのがいいっぽい。

自分はmonitを使って、こんな感じのwrapperを書いて、monitではそのwrapper経由で、起動、停止をしている。