ブログ

読んで思い出す。忘れるために書く

Amazon API Gateway + AWS Lambda で「診断メーカー」の結果を返す機能を実装する

AWS Lambda が Ruby をサポート した (2018年11月) ので、既存のコード (gouf/shindan) と組み合わせて、動作するものを作る

作ったものはコレ: gouf/serverless_ruby_demo

前提

  • AWS のアカウントがある
  • AWS Lambda にアクセス可能な IAM を用意してある
  • Node.js が手元の環境で動く
  • Ruby 2.5 が手元の環境で動く
  • Docker が手元の環境で動く
  • RSpec でテストコードが書ける (任意)

プロジェクトの初期化

Serverless の公式ページ にあるように、まずは初期セットアップを済ませる

# Step 1. Install serverless globally
$ npm install serverless -g

# Step 2. Create a serverless function
$ serverless create --template hello-world

# Step 3. deploy to cloud provider
$ serverless deploy

デプロイまで うまくいっていれば、 sls invoke -f hello で実行結果が返ってくるはず

このテンプレート初期化状態をもとに、実装を進めていく

テストコードを書く

診断メーカー に存在する任意のID を渡すとその結果を返す」という動作を実現するコードを書きたい

簡単だけれど、次のように成功パターン・失敗パターンのテストコードを記述する

# spec/handler_spec.rb
describe 'AWS Lambda Ruby Shindan' do
  context 'Invoke shindan function' do
    it 'returns error response' do
      response = shindan(event: {}, context: {})

      expect(response[:statusCode]).to eq 400
    end

    it 'returns succeed response' do
      # NOTE: 任意の有効な shindan_id を指定する
      response = shindan(event: {'shindan_id' => 865_730}, context: {})

      expect(response[:statusCode]).to eq 200
    end
  end
end

(handler.rbspec/spec_helper.rb, .rspec など適当なところで require しておく)

実装コードを書く

# handler.rb
require 'json'
require 'bundler'
Bundler.setup(:deployment)
require 'shindan'

def shindan(event:, context:)
  shindan_id = event['shindan_id']
  name = [*('A'..'z'), *('0'..'9')].sample(10) # "診断" に使う適当な名前の生成
  scraper = ::Shindan::Scraper.new(shindan_id, name)

  { statusCode: 200, body: JSON.generate(scraper.shindan.lines.first.chomp) }
rescue => e
  # なにか問題があれば 問答無用で 400 エラーを返す
  { statusCode: 400, body: JSON.generate(e.message) }
end

API Gateway を設定する

Web ブラウザから AWS のコンソールにアクセスしなくても、こちら側 (serverless.yml) で必要な設定を記述できる

template の設定を書くことで、 API Gateway のエンドポイント + ?shindan_id=99999 と QueryString を組み合わせたリクエストで Lambda function に任意の値を渡せるようになる

# serverless.yml
functions:
  shindan:
    handler: handler.shindan

#    The following are a few example events you can configure
#    NOTE: Please make sure to change your handler code to work with those events
#    Check the event documentation for details
    events:
      - http:
          path: shindan/create
          method: get
          integration: lambda
          request:
            template:
              application/json: '{ "shindan_id" : "$input.params(''shindan_id'')" }'

plugin を追加・設定する

このままだと、 AWS Lambda 環境内では あとから bundle といったやり方ができないので、gem の実態ファイルもろとも向こうの環境にアップロードするプラグインとその設定を追加する

npm install --save serverless-hooks-plugin
# serverless.yml
plugins:
  - serverless-hooks-plugin
custom:
  hooks:
    package:initialize:
      - docker run -v `pwd`:`pwd` -w `pwd` lambci/lambda:build-ruby2.5 /bin/bash -c "bundle install --deployment --without development && bundle package"
    package:finalize:
      - rm -rf vendor
      - rm -rf .bundle

デプロイと実行

sls deploy でデプロイ実行

デプロイが成功すると、API Gateway のエンドポイント URL を教えてくれるので、そこにリクエストを送信してみる

f:id:innocent-zero:20190226032354p:plain
curlAPI Gateway エンドポイントにリクエストを送信した様子

成功すれば、上の画像のような結果が得られる

Links

Serverless:

AWS: