sesrverless frameworkを使ってrubyでAPIを作る

The Serverless Application Framework | Serverless.com
以前から気にはなっていたのですが、簡易的なAPIを作るために利用してみました。
 
同様のServerless Frameworkとしては、Ruby on Jets というのがあります。
こちらも触ってみたいところ。

ざっくり利用方法

Installation

sesrverless入れるだけです。

mkdir your_project
cd your_project
yarn init
yarn add serverless -D

Create your project

yarn run serverless create --template aws-ruby

Deploy

コマンド1発で、CloudFormation作ってくれて、s3、IAM、Lambda、ApiGateway、CloudWatchまで一気にやってくれます。
これはめちゃ便利で、驚きました。

yarn run serverless deploy -v

各種設定

Serverless Framework - AWS Lambda Guide - Serverless.yml Reference
ここに全部乗ってるので、詳細は公式Documentみるのが良いでしょう。

以下、今回利用した設定内容。baseはこれだけ。

provider

こんな感じ。

provider:
  name: aws
  runtime: ruby2.7
  stage: dev
  region: ap-northeast-1
  apiGateway:
    shouldStartNameWithService: true

shouldStartNameWithService は、正直どっちでも良いですが、次のmajor versionからAPI Gatewayのnaming-ruleが変更されるため、新しい方式に合わせるために入れてます。
Serverless Framework Deprecations

functions

こんな感じ。ポイントはcors: trueぐらいでしょうか。

functions:
  exec:
    handler: handler.exec
    events:
      - http:
          path: html2haml
          method: post
          cors: true

注意事項

あとはhandler.rbにロジック書くだけで出来ちゃいますが、2つハマったところがあるので記載していきます。

Native Extension Gem

今回html2hamlを利用としたのですが、当然の如くnokogiri使ってまして、macでbuildしたgemをdeployしても動かないわけです。
そこで利用するのがlambci/lambda - Docker Hub
buildの例にrubyがなかったり、ruby2.5の例のままですが、現在のlambda同様2.7が利用できます。

# build
docker run -v `pwd`:/var/task -it lambci/lambda:build-ruby2.7 bundle install --path vendor/bundle --without development test
# local実行例
# handler、eventが指定できる
docker run --rm -v $PWD:/var/task:ro,delegated lambci/lambda:ruby2.7 handler.exec  '{"body": "{\"html\":\"<span foo=\\\"foo\\\">test</span>\"}"}'

 
しかし、bundle installしたgemなどをzipに含めると転送量が多くなるという問題があり、これを解決するためにはlayerとして別管理するか、serverless-hooks-pluginを使ってdockerでinstallするという手法があるようです。(後者試してないのであとでやってみます。) 個人的にはどうも、この辺が面倒だと感じるので、serverlessバンザイ!という気持ちには今一つなれません。

API GatewayのResponse形式

ここに書いてるんですが、Responseの形式が決まっています。
API ゲートウェイでの「不正な Lambda プロキシ応答」または 502 エラーの解決

見落としてハマっていたのが、bodyの形式です。JSON.stringifyした文字列が必要でした。 rubyなので、hashをto_jsonすることで解消。

"body": JSON.stringify(responseBody),

ご参考

See Also

htmlをhamlに変換するサイトhtml2hamlを勢いで作った - rochefort's blog

vuecliでの環境変数の利用方法

ここに全部書いてるんだけど、知らずにハマったのでメモ。
Modes and Environment Variables | Vue CLI

Mode

環境ごとの処理を分けれるように、modeという概念がある。
defaultは、development,、test、production。

ちなみに、vue-cli-service build 時は、productionが自動でセットされる。
 

Environment

.env                # loaded in all cases
.env.local          # loaded in all cases, ignored by git
.env.[mode]         # only loaded in specified mode
.env.[mode].local   # only loaded in specified mode, ignored by git

という感じに環境変数を書いていく。
アプリで利用するために VUE_APP_ というprefixをつける必要がある。これを理解していなくてハマってしまった。

あとは、secret keyとか書くなよということが一応書いている。

WARNING

Do not store any secrets (such as private API keys) in your app!

Environment variables are embedded into the build, meaning anyone can view them by inspecting your app's files.

一応、このprefixを利用している理由としては、ついうっかり他の環境変数もjsにbundleされないようにするためのよう。

htmlをhamlに変換するサイトhtml2hamlを勢いで作った

こちら html2hamlf:id:rochefort:20201122143927p:plain
 
当初全く別のものを作ってたのですが、css frameworkのサイトに記載されてるhtmlの例をいちいちhaml(hamlit)に変換するのが面倒だと思い勢いで作りました。
知らないことばっかり試せたので、いろいろ面白かったです。

実装方式

フロント

Vue.js。reactでも良かったのですが、なんとなく。
css frameworkは、Vuetify
あまりこだわりはなかったのですが、vue.jsと親和性の高いものが良かったので選定。

久しぶりにvue触ったら、Vue CLI が出来ていて、さくっとテンプレ作ってくれて助かりました。

# project作成(prettier使いたかったので、manualを選択)
vue create my_project

# plugin追加
vue add vuetify
# version
vue: 2.6.12
vue/cli: 4.5.8

APIのRequestはaxios使ってささっと実装。
ちょっと調べたところだと、google analyticsどうするのかぐらいで、割とフロント周りは悩まずに出来ました。
環境変数の扱いが特殊で、そこだけははまりました。(あとで詳細書きます。)

サーバーサイド

どれ使うか結構悩んだのですが、最終的にはThe Serverless Application Frameworkを利用し、 AWS+API Gateway+Lambdaでの実装となりました。
 
serverless 自体は、コマンド打ったら勝手にデプロイしてくれてめちゃ便利ではあるのですが、API Gateway + Lambdaの辛みが結構しんどかったです。 正直、sinatraAPI作って、githubにpushして、herokuにデプロイが最短だった気がします。

特にLambdaでrubyのnative extensionsのgemを利用する方法が切ないです。
lambci/lambda - Docker Hub を使ってbuild・開発するのが定石のようなのですが、 それ自体がだるいのでlambda側でデプロイ時にGemfile.lock見てinstallしてくれよ。頼むよって感じです。 (serverless-hooks-pluginというのでhookできそう。ただ、buildに時間がかかるのでlayerを使って別管理するのが良いのかも)
この辺は忘れそうなので、詳細起こしておこうと思います。

変換処理自体は、haml/html2hamlを利用しているだけです。ありがとうございます。

# serverless version
Framework Core: 2.12.0 (local)
Plugin: 4.1.2
SDK: 2.3.2
Components: 3.4.0

ドメイン

Google Domain。設定変更もsimpleで使いやすい印象。
他の詐欺みたいな業者使ったらダメですよ!
Google Domainを利用してみた - rochefort's blog

デプロイ

環境は(glitchなどと)迷いましたが、Netlify を利用しました。
Github Actionsを使ってbuild結果を送信することで、自動デプロイの仕組みを実現しています。
Github Actionsそれなりに便利でいいんですけどね(jenkinsおじさんを思い出す)。