Google App Engine上でRubyアプリを動かす手順
前置き
Google App Engine(以下GAE)では、公式には現在のところPythonとJavaしかサポートされていませんが、JRubyという素晴らしいプロダクトのおかげでJava VM上でRubyスクリプトを実行できるため、考えようによってはRubyも既にサポート対象になっていると言えなくもありません。
実際にググってみても既に結構な量の情報が存在するのですが、どうもJRuby on Railsを対象とした情報が多く、素のRubyアプリケーションを動かすための情報があまり無いように感じました。
Google App Engine for Java環境の構築
GAE上でJRubyを動かすには、当然ながらGAE上でJavaアプリが動作する環境(Google App Engine for Java、以下GAEJ)が必要ですので、まずは以下のページを参考に、Google App Engine SDK for Java(後述)に付いているJavaのデモアプリを動かせる環境を作ります。
GAE上で動くJavaアプリを開発するために最低限必要なものは以下の二つです。
- Java SE Development Kit (JDK)
- Google App Engine SDK for Java
JDKのインストール
以下のページから、最新版のLinux用JDK(jdk-6u14-linux-i586-rpm.bin)をダウンロードし、インストールします。
ダウンロードしたファイルはシェルスクリプトになっていますので、
$ sudo sh jdk-6u14-linux-i586-rpm.bin
というように実行します。利用許諾にyesと答えてしばらく待っているとインストールが完了します。
インストールが終わったら、以下の環境変数を設定しておきます。Bashを使っているのであれば.bashrcあたりに追加しておくと良いです。
export JAVA_HOME=/usr/java/default export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
軽くバージョンの確認をしておきます。
$ java -version java version "1.6.0_14" Java(TM) SE Runtime Environment (build 1.6.0_14-b08) Java HotSpot(TM) Client VM (build 14.0-b16, mixed mode, sharing)
$ javac -version javac 1.6.0_14
問題無さそうです。
Google App Engine SDK for Javaのダウンロード
以下のページからGoogle App Engine SDK for Javaをダウンロードし、適当な場所に展開しておきます。*1
$ wget http://googleappengine.googlecode.com/files/appengine-java-sdk-1.2.1.zip $ unzip appengine-java-sdk-1.2.1.zip
この時点で、既にローカルでGAEJアプリケーションを動作させる環境は整いました。試しに今展開したSDK同梱のデモアプリを動かしてみます。
$ appengine-java-sdk-1.2.1/bin/dev_appserver.sh --address=0.0.0.0 appengine-java-sdk-1.2.1/demos/guestbook/war The server is running at http://localhost:8080/
8080ポートでアクセスして、guestbookアプリの画面が表示されればOKです。*2
JRuby on GAEJ環境の準備
無事GAEJ環境が出来ましたので、次はこの上でJRubyを動作させるために必要なものを準備していきます。
GAEJ上でJRubyを動かすのに必要なものは以下の二つです。
jruby-complete.jarの作成
jruby-complete.jarはJRubyの動作のために必要な機能が全て詰め込まれたライブラリです。普通にパッケージからJRubyをインストールした場合は付いてきませんので、ソースから自分でコンパイルする必要があります。
JRubyをソースからコンパイルするためにgitとantが必要になりますので、まずはそれらを用意します。
gitのインストール
以下のページを参考に、yumからパッケージインストールします。
リポジトリを二つ追加します。
$ sudo vim /etc/yum.repos.d/git.repo
[git] name=Base git repository baseurl=http://www.kernel.org/pub/software/scm/git/RPMS/$basearch enabled=1 gpgcheck=0
$ sudo vim /etc/yum.repos.d/rpmforge.repo
[rpmforge] name = Red Hat Enterprise $releasever - RPMforge.net - dag mirrorlist = http://apt.sw.be/redhat/el5/en/mirrors-rpmforge enabled = 0 gpgcheck = 0
以下のコマンドでインストールできます。最新版だと依存性のエラーが出ますので、バージョンを指定しています。
$ sudo yum install git-1.5.6.1-1 --enablerepo=rpmforge
JRuby-Rackのダウンロード
次にJRuby-Rackをダウンロードしますが、その前に、JRuby-Rackがどんな物かを説明しておきます。
GAEJではJava サーブレット標準という仕組みを利用してWebアプリケーションを作成します。GAEJ上でJRubyを動かそうと思った場合、サーブレットとJRubyの橋渡しをする部分のJavaプログラムを自作しなくてはいけません。
しかし、ありがたいことにJRuby-Rackがその面倒な部分を全て請け負ってくれます。
JRuby-Rackというのは、Ruby用に元々存在しているRackというソフトウェアの拡張版です。Rackに関する説明は以下のページが参考になります。
一言で説明すると、WebサーバとWebアプリケーションフレームワークの間に挟まって、各々の非互換性を吸収してくれるミドルウェアです。
JRuby-Rackは、RackがサポートしているWebサーバに加えてJavaサーブレットもサポートしているため、それを利用することで、サーブレットを意識することなく楽にWebアプリケーションが書けるようになる…というわけです。
今回はWebアプリケーションフレームワークを使いませんので、アプリケーションから直接Rackを触ることになります。少々癖がありますが、覚えること自体は少ないですので、先ほどのページや以下のページにざっと目を通して感覚を掴んでおくと良いかもしれません。
- ウノウラボ Unoh Labs: RackでWebアプリのWebサーバー依存を無くす
- 満足せる豚。眠たげなポチ。:CGI から Mongrel まで、Rack で Web アプリを Web サーバから抽象化する
- Greenbear Laboratory - Rack日本語リファレンス
ではJRuby-Rackをダウンロードします。特にインストール作業などをする必要はなく、適当な場所に置いておけばOKです。
$ wget http://kenai.com/projects/jruby-rack/downloads/download/jruby-rack-0.9.4.jar
サンプルアプリケーションの作成
さて、ここまででJRuby on GAEJアプリに必要な材料は全て揃いました。とりあえず、簡単なHello worldアプリを作成してみます。
まずはアプリケーションのファイルを格納するディレクトリを作ります。GAEJでは、Javaサーブレット規格で定められた通りの配置でファイルが存在している必要があります。
$ mkdir sample-jruby-on-gaej $ cd sample-jruby-on-gaej $ mkdir -p WEB-INF/lib
アプリケーションのトップディレクトリにWEB-INFという名前のディレクトリが存在し、その中にアプリケーションに必要なファイルが全て収まっていなければいけません。
今作ったlibディレクトリには、これまで集めてきたJRuby on GAEJの動作に必要な3つのファイルを格納します。
$ cp ../appengine-java-sdk-1.2.1/lib/user/appengine-api-1.0-sdk-1.2.1.jar WEB-INF/lib $ cp ../jruby/lib/jruby-complete.jar WEB-INF/lib $ cp ../jruby-rack-0.9.4.jar WEB-INF/lib
アプリケーション本体は、以下のページを参考に、"Hello world!"というプレーンテキストを出力するだけのシンプルなものを書きます。配置場所はWEB-INFディレクトリ直下です。
$ vim WEB-INF/hello.rb
require 'rubygems' require 'rack' class HelloWorld def call(env) [ 200, { 'Content-Type' => 'text/plain' }, [ 'Hello world!' ] ] end end
設定ファイルの作成
更に、GAEJの動作には二つの設定ファイルが必須となります。以下のページを参考に、最低限必要な設定を書いていきます。
- プロジェクトの作成 - Google App Engine - Google Code
- JRuby-Rack: Wiki: Home ― Project Kenai
- GettingStarted - appengine-jruby - Getting Started with JRuby on Google App Engine - Google Code
$ vim WEB-INF/appengine-web.xml
<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <application>sample-jruby-on-gaej</application> <version>1</version> <system-properties> <property name="jruby.management.enabled" value="false" /> <property name="os.arch" value="" /> </system-properties> </appengine-web-app>
$ vim WEB-INF/web.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"> <context-param> <param-name>public.root</param-name> <param-value>/</param-value> </context-param> <context-param> <param-name>rackup</param-name> <param-value> require 'hello.rb' run HelloWorld.new </param-value> </context-param> <filter> <filter-name>RackFilter</filter-name> <filter-class>org.jruby.rack.RackFilter</filter-class> </filter> <filter-mapping> <filter-name>RackFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.jruby.rack.RackServletContextListener</listener-class> </listener> </web-app>
サンプルアプリケーションの動作確認
ここまででアプリケーションの動作に必要なファイルは一通り揃いました。
試しにローカルで起動して、きちんと動くことを確かめてみます。
$ ../appengine-java-sdk-1.2.1/bin/dev_appserver.sh --address=0.0.0.0 . The server is running at http://localhost:8080/
dev_appserver.shの最後の引数にはアプリケーションのトップディレクトリ(WEB-INFの親ディレクトリ)を指定します。
8080番ポートでアクセスして"Hello world!"が表示されたら成功です。
ついでですので、先ほどのhello.rbを色々書き換えてみて、確かにRubyスクリプトが動いているのだということを確認してみます。*4
require 'rubygems' require 'rack' class HelloWorld include Rack::Utils # Rack::Utils.escape_htmlを簡単に使うため def call(env) req = Rack::Request.new(env) res = Rack::Response.new res.write '<html><head><title>Sample of JRuby on GAEJ</title></head><body>' # いくつか基本的な情報を表示 res.write '<h2>Ruby version</h2>' res.write "<p>#{RUBY_VERSION}</p>" res.write '<h2>Platform</h2>' res.write "<p>#{RUBY_PLATFORM}</p>" res.write '<h2>Load paths</h2>' res.write '<ul>' res.write $:.map { |path| "<li>#{escape_html(path)}</li>" }.join res.write '</ul>' # POSTパラメータの一覧を表示 res.write '<h2>Posted parameters</h2>' res.write '<dl>' req.params.each_pair do |key, value| res.write "<dt>#{escape_html(key)}</dt><dd>#{escape_html(value)}</dd>" end res.write '</dl>' # 環境変数の一覧を表示 res.write '<h2>Environment variables</h2>' res.write '<dl>' req.env.each_pair do |key, value| res.write "<dt>#{escape_html(key)}</dt><dd>#{escape_html(value)}</dd>" end res.write '</dl>' res.write '</body></html>' res.finish end end
今のところRubyスクリプトを書き換えたらサーバを再起動する必要がある?ようですので、先ほど立ち上げたdev_appserver.shをCtrl+Cで一旦終了し、再度実行します。
同じようにローカルの8080番にアクセスしてみると、色々と情報が列挙されるはずです。
アプリケーションのアップロード
あとはこのままこのアプリケーションをGAE上にアップロードすれば、世界中からアクセス可能なWebアプリの完成です。
当然GAEのアカウントを取得して、かつGAEJが使用可能な状態になっていなければなりません。*5
アップロードは以下のコマンドを実行するだけです。*6途中でGoogleアカウントとパスワードを聞かれてきますので入力します。
$ ../appengine-java-sdk-1.2.1/bin/appcfg.sh --enable_jar_splitting update .
最後の引数には先ほどと同様にアプリケーションのトップディレクトリを指定します。
GAEのダッシュボードで正しくアップロードされていることを確認し、表示されているURLにアクセスしてみます。先ほどのローカルでのテストと比較して、$LOAD_PATHなどの値が異なっていることが見てとれると思います。
JRuby on GAEJアプリのテンプレート
ここまででJRuby on GAEJアプリの作り方は一通り完了です。思ったよりも作業量が多くて疲れてしまいますね…。
しかし、よくよく手順を見返してみると、大変だったのは主に必要なライブラリの準備でしたし、またアプリケーション自体も本体のファイル(今回の場合は"hello.rb")以外はほぼ定型であり、そのまま使いまわすことができそうです。
ということで、JRuby on GAEJアプリの雛形として使えるパッケージを作成しました。
といっても、上のHello worldアプリをtarボールにしただけです。JRubyやJRuby-Rackは同梱されていますので改めて準備する必要はありませんが、JDKとGoogle App Engine SDK for Javaは別途インストールしておいてください。
また、将来的にGAEやSDKのアップデートによって使えなくなる可能性もありますので、その点もご了承下さい。
使い方としては、
- 展開して
- WEB-INF/hello.rbを好きなように書き換えて
- WEB-INF/appengine-web.xmlのapplicationタグとversionタグの値を適切に設定して
- GAEへアップロード(またはローカルでテスト起動)
するだけです。
ごく簡単なWebアプリなら、これでとりあえずは書けるかも?
もう少し本格的なアプリケーションの作成
とは言え、現状のままではできることも貧弱です。
JRuby on GAEJアプリは、GAEのサンドボックス内で動作する関係上、様々な機能的制限をかけられています。
例えばファイルへのアクセスも禁止されていますし、net/httpも使えませんのでHTTP経由で他のリソースを取得することもままなりません。
しかし、それらの制限された機能を補うために、GAE独自のインターフェースが用意されていますので、これを使うことでパワフルなWebアプリケーションを作成することも可能です。
GAEJで提供されているのは当然Javaへのインターフェースとなりますが、JRubyからそれらを使えるようにしてくれるラッパーライブラリもいくつか作成されています。
以下のページによると、DBへのアクセス、アカウント情報の取得、画像処理、メールサービスの利用、URLフェッチなど、めぼしい機能は一通りJRubyからも利用できるようです。
次回*7は、これらのライブラリを利用して、JRuby on GAEJ上で動作する簡単なTwitter BOT作りに挑戦してみようと思います。*8
*1:英語版のほうがバージョンが新しい場合がありますので、一度英語版のページをチェックしたほうが手間がかからなくて済みます。
*2:端末と開発環境が同一筐体の場合は"--address=0.0.0.0"オプションは不要です。
*3:少し古い資料では、このあと更にライブラリを分割する手順が説明されていますが、現在のGAEではその作業は必要ありません。
*4:Rack::Response#finishは本来ブロックを使った書き方もできるのですが、何故か手元の環境ではブロックを渡して使うと正しく本文データを返してくれないので、仕方なく少々格好悪い書き方をしています。
*5:その辺はばっさり省略します。
*6:この--enable_jar_splittingオプションのおかげで、jruby-complete.jarファイルの分割が不要になっているようです。
*7:もしくは近い未来…あるいは遠い未来
*8:Webアプリじゃない?こまけぇこたぁ(略)