Railsアプリケーションを動かすサーバ作ろうとすると、

  • Rubyのセットアップ
  • Rackサーバ(PassengerとかUnicornとか)のセットアップ
  • Webサーバ(ApacheとかNingxとか)のセットアップ
  • DBのセットアップ
  • アプリケーションのデプロイ用設定ファイル作成(Capistranoとか)

といった、アプリケーションの開発自体とは関係ない諸々の作業が必要な訳で、そんな作業しなくてもデプロイ以降の作業をお任せできるHerokuはとても便利。

そんなHerokuのようなPaaSをEC2上に立ててみようとdokkuを使ってオレオレHeroku環境作ってみる。(そんなの構築しなくてもAWSにはBeanstalkがあるじゃないか?というツッコミもあるけど)

dokkuは内部でdockerを使用しており、docker自体は、LXC(Linux Container)というLinuxのコンテナ技術を使用して(最近LXC非依存になったけど)サーバ上に独立したユーザー空間を確保しその中で仮想OSのプロセスやネットワークを動作させる仮想環境。

lxc

コンテナベースの仮想化を行うことで、1つのカーネル上で複数のコンテナをユーザープロセスを分けて運用できる点で、Virtual Boxみたいなゲスト用のカーネルが別に立ち上がることが無いのでオーバーヘッドが少なくすごく軽量に動作する。(確かHerokuのdynoも同様のLXCベースのアーキテクチャを採用してるんじゃなかろうか。)

AWSのEC2上にdokkuをセットアップ

dokkuのREADMEにUbuntu 13.10推奨とあったのでAmazon Linuxではなく64bit版のUbuntu Server 13.10を選択してインスタンスを起動する。続いて以下のコマンドでdokkuをインストールする。

$ wget -qO- https://raw.github.com/progrium/dokku/v0.2.1/bootstrap.sh | sudo DOKKU_TAG=v0.2.1 bash	 	 
...

↑のスクリプトで、dockerのインストールも一緒に行われ、dokkuユーザが作成される。

ドメインの設定

/home/dokku/VHOST にドメインを設定する(デフォでホスト名が設定されてる)。もしファイルが存在しない場合は手動で作成する。今回はdokku.haw.bizと設定。

今後デプロイするアプリケーションのURLにここで設定したドメインが使われる。
(http://アプリケーション名.dokku.haw.biz/みたいに)

尚、立ち上げたインスタンスのIPに対してここで設定したドメインでアクセスできるように別途Route53とかで名前解決できるようにしておく。

鍵の設定

続いてデプロイする際に鍵認証が発生するので、公開鍵をdokkuのサーバに登録する。デプロイマシンで以下のコマンドを実行して鍵を登録する。

$ cat ~/.ssh/id_rsa.pub | ssh dokku.haw.biz -l ubuntu -i dokkuのインスタンスにアクセスする際の鍵 "sudo sshcommand acl-add dokku oreore"

成功すると/home/dokku/.ssh/authrized_keysにデータが追加される。

以上で環境の準備は終わり。意外とあっさりできる。続いていよいよデプロイ。

node.jsアプリのデプロイ

Hedokuで公開されてるnode.jsのアプリをデプロイしてみる。手元のマシンで

$ git clone https://github.com/heroku/node-js-sample.git
Cloning into 'node-js-sample'...
...

続いて、push先にdokkuのサーバを指定

$ cd node-js-sample
$ git remote add oreore dokku@dokku.haw.biz:node-js-sample

そしていよいよpush!

$ git push oreore master
Counting objects: 319, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (272/272), done.
Writing objects: 100% (319/319), 201.33 KiB | 0 bytes/s, done.
Total 319 (delta 19), reused 319 (delta 19)
-----> Building node-js-sample ...
       Node.js app detected
-----> Resolving engine versions
       Using Node.js version: 0.10.21
       Using npm version: 1.3.11
-----> Fetching Node.js binaries
-----> Vendoring node into slug
-----> Installing dependencies with npm
...
npnで依存ライブラリのインストール
...
-----> Building runtime environment
-----> Discovering process types
       Procfile declares types -> web
-----> Releasing node-js-sample ...
-----> Deploying node-js-sample ...
-----> Cleaning up ...
=====> Application deployed:
       http://node-js-sample.dokku.haw.biz

To dokku@dokku.haw.biz:node-js-sample
 * [new branch]      master -> master
$

とアプリが起動する。表示されたURLにアクセスすると「Hello World!」画面が表示される。
(デプロイするとdokkuのサーバの/home/dokku/以下にデプロイしたアプリのgitリポジトリができてる。)

dokku側はこのdeployの際にnode.jsやnpnといったツールをインストールする。今回はnode.jsだったけど、実際にビルドできる言語は

progrium/buildstep

でサポートされてる言語になる。RubyやJava、PHPといったメジャーどころにはしっかり対応してる。

Railsアプリケーションのデプロイ

node.jsのアプリケーションだけだと味気ないのでRailsのアプリケーションもデプロイしてみる。アプリは同じくHerokuのサンプルを利用。

PostgreSQL Pluginのインストール

RailsアプリではDBのインストールが必要になる。dokkuでこういったDBや他のミドルウェアを使いたい場合は、別途公開されているPluginをインストールすることになる。

Plugins · progrium/dokku Wiki

今回はPostgreSQL Pluginを利用する。Pluginのインストール自体はdokkuのインスタンス上で行う。インストール自体は簡単で以下のステップで実行するだけ。

cd /var/lib/dokku/plugins
$ git clone https://github.com/Kloadut/dokku-pg-plugin postgresql
$ dokku plugins-install
...
Successfully built 95a893eae17b
$ 

これ以降は、手元のマシンで実行できる。まず、DBの作成

$ ssh dokku@dokku.haw.biz postgresql:create rails-sample-pg
-----> PostgreSQL container created: postgresql/rails-sample-pg

       Host: 172.17.42.1
       Port: 49168
       User: 'root'
       Password: '3bGybchOvHjDmC6I'
       Database: 'db'

       Url: 'postgres://root:3bGybchOvHjDmC6I@172.17.42.1:49168/db'

$ 

実行するとDBのコンテナが作成されアクセス情報が返される。

ちなみにpostgresql:createで作成したDBはそれぞれ別のユーザープロセスとして動作するようになる。↑のrails-sample-pg以外のDB例えばrails-sample-pg2というDBを作成すると2つのPostgreSQLプロセスが起動している状態になる。

Railsアプリのデプロイ

サンプルアプリを取ってきてpushする。

$ git clone https://github.com/heroku/ruby-rails-sample.git
...
$ cd ruby-rails-sample
$ git remote add oreore dokku@dokku.haw.biz:ruby-rails-sample
$ git push oreore master
Counting objects: 7, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 372 bytes | 0 bytes/s, done.
Total 4 (delta 3), reused 0 (delta 0)
-----> Building ruby-rails-sample ...
       Ruby app detected
-----> Compiling Ruby/Rails
-----> Using Ruby version: ruby-2.1.1
-----> Installing dependencies using Bundler version 1.3.2
       Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin --deployment
... 
gemのインストール処理
...
       Your bundle is complete! It was installed into ./vendor/bundle
       Bundle completed (0.64s)
       Cleaning up the bundler cache.
-----> Writing config/database.yml to read from DATABASE_URL
-----> Preparing app for Rails asset pipeline
       Running: rake assets:precompile
       Asset precompilation completed (2.45s)
       Cleaning assets
-----> Discovering process types
       Procfile declares types -> web
       Default process types for Ruby -> rake, console, web, worker
-----> Releasing ruby-rails-sample ...
-----> Deploying ruby-rails-sample ...
-----> Cleaning up ...
=====> Application deployed:
       http://ruby-rails-sample.dokku.haw.biz

To dokku@dokku.haw.biz:ruby-rails-sample
   7dfd643..37601b0  master -> master
$ 

このままではさっき作ったDBとのリンクはできてないので、続いて、以下のコマンドでRailsアプリと接続先のDBのコンテナをリンクする。(なのでdatabase.ymlを編集する必要は無い)

$ ssh dokku@dokku.haw.biz postgresql:link ruby-rails-sample rails-sample-pg
-----> Creating /home/dokku/ruby-rails-sample/ENV
-----> Setting config vars and restarting ruby-rails-sample
DATABASE_URL: postgres://root:3bGybchOvHjDmC6I@172.17.42.1:49168/db
-----> Releasing ruby-rails-sample ...
-----> Release complete!
-----> Deploying ruby-rails-sample ...
-----> Deploy complete!

-----> ruby-rails-sample linked to postgresql/rails-sample-pg database

これで完成! pushした際に表示されるURLアクセスするとHello World画面が表示される。

dokkuのインスタンス上ではさっきのnode.jsのサンプルとRailsのサンプルとそのDBの計3つのコンテナがそれぞれ別のユーザー空間で動作している。

少しスペックの良いEC2のインスタンスを1台立てて、その上で複数のサイトをVPSみたいに運用するような、誰でもVPSビジネスができる環境がわりと簡単にできつつある。(まぁサービスとして提供するにはクリアすべき課題は多いと思うけど)