5分でAWS Lambdaにデプロイできる「Ruby on Jets」

今週の [Ruby Weekly](https://rubyweekly.com/issues/451) でこんな記事が紹介されていました：

『**[Serverless Ruby on AWS Lambda with the Jets framework](https://www.twilio.com/blog/serverless-ruby-on-aws-lambda-with-the-jets-framework)**』（Jetsフレームワークを使った AWS Lambda でのサーバーレス Ruby）

AWS Lambda で Ruby がサポートされたのはわりと最近の話ですが（*2018年11月: [Announcing Ruby Support for AWS Lambda](https://aws.amazon.com/jp/blogs/compute/announcing-ruby-support-for-aws)*）、「**[Jets](http://rubyonjets.com/)**」というサーバーレス用のRubyフレームワークを使えば、短時間で AWS Lambda にデプロイできるみたいです。

面白そうだと思ったので少し触ってみました。

# Ruby on Jets とは？

[Jets](http://rubyonjets.com/)は、サーバーレスサービスを簡単に構築・デプロイするためのRubyフレームワークです。**AWSサービスとのシームレスな連携**が売りのようですね。

公式ページでは、次のように説明されています。

> Ruby on Jets allows you to create and deploy serverless services with ease, and to seamlessly glue AWS services together with the most beautiful dynamic language: Ruby.

ざっと見た感じ、**AWS Lambdaへのデプロイに特化した簡易的なRails**といった印象です。

## Ruby on Jets の設計

Jets で AWS Lambda にデプロイした場合、設計は次のようになります。

<img width="1150" alt="スクリーンショット 2019-05-25 9.23.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138789/094157eb-8a8a-2f59-be9c-75f0df8b263a.png">


# Ruby on Jets をデプロイしてみる

Ruby on Jets の AWS Lambda へのデプロイがどれぐらいシームレスなのか、実際に検証してみます。

参考にした記事は次のとおりです：

- *[Quick Start - Jets](http://rubyonjets.com/quick-start/)*
- *[Serverless Ruby on AWS Lambda with the Jets framework](https://www.twilio.com/blog/serverless-ruby-on-aws-lambda-with-the-jets-framework)*

## Ruby on Jets アプリの作成

GETリクエストで「Hello from Ruby on Jets」を返すだけのシンプルなAPIアプリを用意します。

### インストール
Ruby のバージョンは 2.5.0 です。

```
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin18]
```

まずは Jets をグローバルにインストールします。

```
$ gem install jets
```

インストールできました。

```
$ jets -v
1.9.1
```

### プロジェクトの作成

`rails` コマンドのように `jets new YOUR_APP_NAME` でプロジェクトを作成できます。

今回は API で、かつ、DB不要の簡易アプリなので、オプションに `--mode api --no-database` を渡します。

```
$ jets new hello_ruby_on_jets --mode api --no-database
```

作れるファイルは次のような感じ。ほぼ Rails ですね。

```
your_app_name
├── app
│   ├── controllers
│   ├── helpers
│   ├── jobs
│   └── models
├── config
├── db
├── public
├── spec
├── config.ru
├── Gemfile
├── Gemfile.lock
├── Procfile
├── Rakefile
└── README.md
```

サーバを起動させてみます。

```
$ jets server
```

`http://127.0.0.1:8888/` でサーバが立ち上がります。

<img width="1440" alt="スクリーンショット 2019-05-24 21.37.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138789/f53b54c8-9d5e-00db-d82c-811e5bf90647.png">

### アプリの実装

Greetings コントローラーを作成します。

```
$ jets g controller Greetings index
      create  app/controllers/greetings_controller.rb
       route  get 'greetings/index'
```

index アクションにプレーンテキストを返す処理を書きます。

```ruby:app/controllers/greetings_controller.rb
class GreetingsController < ApplicationController
  def index
    render plain: 'Hello from Ruby on Jets!'
  end
end
```

ルーティングはこんな感じ。

```ruby:config/routes.rb
Jets.application.routes.draw do
  get 'greetings', to: 'greetings#index'
  root "jets/public#show"

  # The jets/public#show controller can serve static utf8 content out of the public folder.
  # Note, as part of the deploy process Jets uploads files in the public folder to s3
  # and serves them out of s3 directly. S3 is well suited to serve static assets.
  # More info here: http://rubyonjets.com/docs/assets-serving/
  any "*catchall", to: "jets/public#show"
end
```

試しにサーバを起動させて `curl` を叩いてみます。

```
$ jets serve
```
```
$ curl http://localhost:8888/greetings
Hello from Ruby on Jets!
```

問題なさそうですね。

## AWS Lambda でのデプロイ

作ったアプリを AWS Lambda にデプロイしてみます。

必要なステップは「1. 認証キーの発行」と「2. デプロイ」です。

### 1. 認証キーの発行

AWSにデプロイするには認証キーが必要です。

#### ポリシーの作成

AWSのコンソールから「IAM」のページに飛び、新しくポリシーを作成します。

<img width="1440" alt="スクリーンショット 2019-05-24 22.02.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138789/5a9d3b0d-128e-a4e1-8d9c-2e1416f43cdc.png">

JSONタブを選択し、公式ドキュメント「[Minimal Deploy IAM Policy](http://rubyonjets.com/docs/extras/minimal-deploy-iam/)」に提示されている次の権限設定をコピペします。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "apigateway:*",
                "cloudformation:*",
                "dynamodb:*",
                "events:*",
                "iam:*",
                "lambda:*",
                "logs:*",
                "route53:*",
                "s3:*"
             ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```

#### ユーザーの作成

ポリシーの作成後、新規ユーザーを作成します。

「プログラムによるアクセス」を選択します。

<img width="1439" alt="スクリーンショット 2019-05-24 22.09.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138789/c069ad97-e624-75f5-38d4-1858acc7a920.png">

そして、「既存のポリシーを直接アタッチ」を選び、先ほど作成したポリシーを適用します。

<img width="1440" alt="スクリーンショット 2019-05-24 22.11.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138789/310f802f-5487-a076-5365-3d3f3cdf2550.png">

ユーザーの作成が終わると、「**アクセスキー ID**」と「**シークレットアクセスキー**」が発行されるので、それらを保管します。

### 2. デプロイ

ここからが Jets の最大の強み。

**なんと、わずか１行でAWS Lambdaにデプロイできます。**

認証キーを持たせて `jets deploy` するだけです。

```
$ AWS_ACCESS_KEY_ID=YOUR_ID AWS_SECRET_ACCESS_KEY=YOUR_SECRET_KEY jets deploy
```

- *`AWS_ACCESS_KEY_ID`: アクセスキー ID*
- *`AWS_SECRET_ACCESS_KEY`: シークレットアクセスキー*



コマンドを実行するとデプロイが始まります。

```
Deploying to Lambda hello-ruby-on-jets-dev environment...
Building CloudFormation templates.
Deploying CloudFormation stack with jets app!
10:17:57PM CREATE_IN_PROGRESS AWS::CloudFormation::Stack hello-ruby-on-jets-dev User Initiated
10:18:01PM CREATE_IN_PROGRESS AWS::S3::Bucket S3Bucket 
10:18:02PM CREATE_IN_PROGRESS AWS::S3::Bucket S3Bucket Resource creation Initiated
10:18:22PM CREATE_COMPLETE AWS::S3::Bucket S3Bucket 
10:18:25PM CREATE_COMPLETE AWS::CloudFormation::Stack hello-ruby-on-jets-dev 
Stack success status: CREATE_COMPLETE

（省略）

10:23:37PM UPDATE_COMPLETE_CLEANUP_IN_PROGRESS AWS::CloudFormation::Stack hello-ruby-on-jets-dev 
10:23:38PM UPDATE_COMPLETE AWS::CloudFormation::Stack hello-ruby-on-jets-dev 
Stack success status: UPDATE_COMPLETE
Time took for stack deployment: 2m 29s.
Prewarming application.
API Gateway Endpoint: https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/
```

最終行に `API Gateway Endpoint` が表示されれば、デプロイ完了です。

`10:17:57PM` に開始して `10:23:38PM` に完了したので、**デプロイにかかった時間は５分ちょっと**。これはかなりお手軽じゃないでしょうか？

試しに `curl` でエンドポイントを叩いてみます。

```
$ curl https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/greetings
Hello from Ruby on Jets!
```

ちゃんと動いていますね。

## AWS Lambda をコンソールで確認

デプロイしたアプリを AWS Lambda のコンソールから確認します。

エンドポイントに `us-east-1` とあるので、リージョンは「バージニア北部」を選択します。

<img width="1440" alt="スクリーンショット 2019-05-25 9.08.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138789/3a65ef4a-258c-e855-733a-bdb141374225.png">

デプロイしたアプリを確認できました。

# 感想

AWS Lambda は詳しくないですが Jets を使うと、わずか1行でサクッとデプロイできました。

Lambda の設定は Jets に依拠して、Ruby のコードだけに集中できるのが良いですね。

Ruby on Rails を知っていれば、学習コストはほとんどないので、ちょっとしたサーバーレスアプリの開発に使えるんじゃないかな、と思いました。