Ruby on Jets Quick Start on Cloud9

 最近になって Ruby on Jets というサーバレスフレームワークを知る機会があり、面白そうだったので触ってみました。Ruby on Jets は Rails ライクな開発ができるフレームワークで、それを AWS Lambda の Ruby Runtime の Function としてデプロイできるサーバレスなフレームワークです。

rubyonjets.com

 環境としてはローカルのPCで開発する場合は Lambda にデプロイする際に AWS アカウントの認証が必要になりますが、 Cloud9 環境であればシームレスに AWS サービスと連携できるので、今回は Cloud9 環境で試してみます。Ruby on Jets にはチュートリアル的に Quick Start が用意されているので、これを一通り試してみます。

rubyonjets.com

環境構築

 Cloud9 自体の環境構築については今回は割愛しますので、よろしければこちらをご参照ください。

blog.akanumahiroaki.com

 まず Ruby ですが、 Ruby on Jets は 2020/3/27 現在では Ruby 2.5系までの対応となっていますので、2.5系の最新版である 2.5.7 を使用します。

$ ruby -v
ruby 2.5.7p206 (2019-10-01 revision 67816) [x86_64-linux]

 ちなみに Ruby 2.6 以降を使っているとローカルで開発している段階では問題なくても、 Lambda への deploy 時に下記のように怒られてしまいます。

You are using Ruby version 2.7.0 which is not supported by Jets.
Jets uses Ruby 2.5.3.  You should use a variant of Ruby 2.5.x

 Jets では Rails 同様に bundler を使用しますので、インストールしておきます。

$ rbenv exec gem install bundler
Fetching: bundler-2.1.4.gem (100%)
Successfully installed bundler-2.1.4
1 gem installed
$ 
$ rbenv exec gem which bundler
/home/ec2-user/.rbenv/versions/2.5.7/lib/ruby/gems/2.5.0/gems/bundler-2.1.4/lib/bundler.rb

 そして Jets をインストールします。

$ rbenv exec gem install jets

 下記のようにインストールされたことが確認できます。

$ rbenv exec gem which jets
/home/ec2-user/.rbenv/versions/2.5.7/lib/ruby/gems/2.5.0/gems/jets-2.3.14/lib/jets.rb
$
$ rbenv exec jets -v
2.3.14

 また、 Jets では yarn を使用していますのでインストールします。

$ npm install -g yarn
/home/ec2-user/.nvm/versions/node/v10.19.0/bin/yarn -> /home/ec2-user/.nvm/versions/node/v10.19.0/lib/node_modules/yarn/bin/yarn.js
/home/ec2-user/.nvm/versions/node/v10.19.0/bin/yarnpkg -> /home/ec2-user/.nvm/versions/node/v10.19.0/lib/node_modules/yarn/bin/yarn.js
+ yarn@1.22.4
added 1 package in 0.445s

 今回 DB に MySQL互換の Aurora を使用しますので、下記ライブラリをインストールしておきます。

$ sudo yum install mysql-devel

プロジェクト作成

 Jets では Rails 同様に new コマンドでプロジェクトを作成できます。 Database Adopter はデフォルトで MySQL が選択されます。

$ rbenv exec jets new demo

 これで素のプロジェクトが作成されましたので、ひとまずサーバを起動して動作することを確認してみます。デフォルトでは 8888 ポートが使用されますが、 Cloud9 のプレビューで動作確認する場合、対応しているポートが 8080、8081、8082 なので、ポート番号を指定して起動します。

$ cd demo/
$ rbenv exec jets server --port 8080
〜〜〜中略〜〜〜
warning package.json: No license field                                                                                                                      
warning No license field                                                                                                                                    
=> bundle exec rackup --port 8080 --host 127.0.0.1
Jets booting up in development mode!
[DEPRECATED] `Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env` (called at /home/ec2-user/.rbenv/versions/2.5.7/lib/ruby/gems/2.5.0/gems/jets-2.3.14/lib/jets/commands/main.rb:46)
warning package.json: No license field                                                                                                                      
warning No license field                                                                                                                                    
Puma starting in single mode...
* Version 4.3.3 (ruby 2.5.7-p206), codename: Mysterious Traveller
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://127.0.0.1:8080
Use Ctrl-C to stop

 Cloud9 の IDE 画面から Preview を実行します。

f:id:akanuma-hiroaki:20200326014651p:plain

 下記のように Jets の画面が表示されれば Jets が起動できています。

f:id:akanuma-hiroaki:20200326014728p:plain

Scaffold での確認

 今回はとりあえず動作確認ができれば良いので、 Scaffold を試してみます。今回 DB は Aurora を使用しますので、 まずは RDS のコンソールから下記のような内容で DB を作成します。

f:id:akanuma-hiroaki:20200328213126p:plain

 このままだと DB と同じ VPC の中からしか接続できないので、VPC セキュリティグループのインバウンドルールを変更します。最初は下記画像の上段のようになっていますので、とりあえず今回は試すだけということでどこからでも接続できるように下段のような内容に変更します。

f:id:akanuma-hiroaki:20200329154230p:plain

 設定を変更したら Cloud9 のターミナルから接続できることを確認します。

$ mysql -h ruby-on-jets-demo.cluster-cnxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -u admin -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 37
Server version: 5.6.10 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

 ひとまずこれで DB の準備ができたので、下記コマンドで Scaffold を作成します。

$ rbenv exec jets generate scaffold post title:string

 DB の接続先情報については環境設定ファイル .env.development に下記のように設定します。

$ vi .env.development
$ cat .env.development 
# Example .env.development, meant to be updated.
ENV_DEVELOPMENT_KEY=example1
DATABASE_URL=mysql2://admin:xxxxxxxx@ruby-on-jets-demo-instance-1.cnxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com/demo_development?pool=5

 migrate を実行して Database とテーブルを作成します。

$ rbenv exec jets db:create db:migrate

 Aurora の方で確認すると下記のように posts テーブルが作成されています。

mysql> show tables;
+----------------------------+
| Tables_in_demo_development |
+----------------------------+
| ar_internal_metadata       |
| posts                      |
| schema_migrations          |
+----------------------------+
3 rows in set (0.01 sec)

 サーバを起動して動作を確認してみます。

$ rbenv exec jets server --port=8080

 先ほどと同様に Cloud9 の Preview でアクセスしてみます。 /posts にアクセスすると下記のように posts の CRUD が行えるようになっています。

f:id:akanuma-hiroaki:20200328214742p:plain

 画面から posts の作成や変更が行えていればOKです。

f:id:akanuma-hiroaki:20200328214820p:plain

 Aurora でも確認してみます。下記のように posts のレコードが作成されています。

mysql> select * from posts;
+----+-------+----------------------------+----------------------------+
| id | title | created_at                 | updated_at                 |
+----+-------+----------------------------+----------------------------+
|  1 | Test1 | 2020-03-26 00:13:46.882807 | 2020-03-26 00:13:46.882807 |
|  2 | Test2 | 2020-03-26 00:13:54.803820 | 2020-03-26 00:13:54.803820 |
+----+-------+----------------------------+----------------------------+
2 rows in set (0.00 sec)

Lambda にデプロイする

 ローカルでの確認がひとまず終わりましたので、 Lambda 環境へデプロイしてみます。Quick Start に記載されている手順通りにここまでやってくると、私が試した時点では nokogiri の 1.10.9 が使われるのですが、このまま Lambda 環境へデプロイしてアクセスすると下記のようなエラーが出てしまいました。

  "errorMessage": "Could not find nokogiri-1.10.9 in any of the sources",
  "errorType": "Init<Bundler::GemNotFound>",

 調べた結果これは nokogiri のバージョンによるもののようで、 1.10.8 を使えば回避できそうでしたので、 Gemfile に下記の設定を追加します。

gem 'nokogiri', '1.10.8'

 bundle update で gem を更新します。

$ rbenv exec bundle update nokogiri

 ローカルでの動作時は環境設定に .env.development というファイルを使用していましたが、 deploy 時は .env.development.remote が使用されますので、ファイルを作成します。DB のホストはローカルと同じ Aurora を使用し、 DB名だけ変更してみます。

$ cp .env.development .env.development.remote
$ vi .env.development.remote
$ diff .env.development .env.development.remote 
3c3
< DATABASE_URL=mysql2://admin:xxxxxxxx@ruby-on-jets-demo-instance-1.cnxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com/demo_development?pool=5
---
> DATABASE_URL=mysql2://admin:xxxxxxxx@ruby-on-jets-demo-instance-1.cnxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com/demo_development_remote?pool=5

 DB の migrate を実行します。環境変数として JETS_ENV=development JETS_ENV_REMOTE=1 を設定することによって接続先情報として .env.development.remote が参照されるようになります。

$ JETS_ENV=development JETS_ENV_REMOTE=1 bundle exec jets db:create db:migrate

 Aurora で Database とテーブルが作成されたことを確認します。

mysql> show tables;
+-----------------------------------+
| Tables_in_demo_development_remote |
+-----------------------------------+
| ar_internal_metadata              |
| posts                             |
| schema_migrations                 |
+-----------------------------------+
3 rows in set (0.00 sec)

 これで deploy する準備ができましたので、 deploy コマンドを実行します。

$ bundle exec jets deploy

 実行途中で下記のように gem を Lambdagems に送って良いか聞かれますので、Y と答えて処理を続行します。 nokogiri などのネイティブ拡張を使ってコンパイルされるような gem は、ローカルでコンパイルしたものをそのまま Lambda に配置しても使用できませんので、 Jets は Lambdagems からプリコンパイルされた gem をダウンロードして使用するようです。

Is it okay to send your gem data to Lambdagems? (Y/n)?
Y

 deploy に成功すると下記のようにアクセス先の URL が表示されます。

Stack success status: UPDATE_COMPLETE
Time took for stack deployment: 1m 32s.
Prewarming application.
API Gateway Endpoint: https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/

 ブラウザから上記のURLにアクセスしてローカルでプレビューした時と同様に jets の画面が表示され、 /posts で posts の CRUD が行えれば正しく deploy されています。

 Lambda のコンソールから確認すると、下記のように Lambda Function が作成されています。

f:id:akanuma-hiroaki:20200328220339p:plain

 また、 API Gateway の下記のように自動的に作成されています。

f:id:akanuma-hiroaki:20200328220504p:plain

まとめ

 今回はひとまず Quick Start の内容を一通り試してみただけですが、ローカルでは Rails ライクに開発することができ、 Lambda のことを意識しなくても deploy すれば自動的に Lambda Function を構成してくれますので、手軽に Serverless なサーバサイド開発が行えました。 Cloud9 であれば AWS サービス利用時の認証等の手間もありませんので、とても相性よく使えると思います。 Ruby on Jets はまだプロダクション環境での実績はあまりないようですが、 Serverless な構成ができると実運用でのメンテナンスやスケーリングのコストもかなり削減できますので、いろいろ試して将来的には実際のサービスに使えると良いなと思っています。