この記事はGoodpatch Advent Calendar 2018の17日目の記事です。
諸般の事情(家庭内パンデミック!)の影響で記事執筆が遅れたことをお詫びします
私は普段Ruby on RailsのREST APIのインフラ・バックエンド周りを担当しています。
先日のAWSのイベント AWS re:Invent 2018 で発表された新サービスと機能 | AWS の発表がどれも素晴らしいものばかりでした。
今回はお気軽に試せそうなAWS Lambdaの Ruby対応を試してみましたが、
今までRuby on Railsのみで乗り越えてきた方や、プログラミング初心者の方でも、REST APIやバックエンドで起こっていることについて、理解しやすい感じがしたので、初心者目線で紹介します。
(途中から若干その目線を忘れているかも)
- この記事はWebブラウザだけで完結することを縛りにしました。
- AWSのアカウントは作成済み、Tokyoリージョンにあるものという前提。
- CloudWatchについては今回触れません。
- Rubyの
Lambda関数
≠AWS Lambda
のRubyなので、以降Lambda
という場合はAWS Lambda
であると思ってください。
作ってみるもの
クリスマスっぽく、
messageを送るとサンタさんからの簡単なreplyが返ってくる
( POST /messages
でreplyを返却)
という単純なものにします。
AWS Lambda でRubyを動かす
Lambdaの関数を作成
AWSコンソールにログイン後、Lambda > 関数 > 関数の作成で、今回は1から作成してみます。
以下のように適当に入力します。
ロール名にはあまり意味がありませんが、santaにしておきますか。
できました!早速「テスト」をしてみます
テストイベントを作成
PostMessageなどのテストイベント名にして以下のようなお願いをしてみます。
{
"message": "Please give me something like special, Santa!"
}
成功!
でも Hello from Lambda!
とか言われても子供たちには意味がわかりません。
サンタは何を考えているんでしょうか
しかしこれは関数のデフォルトコードを全く変更してないからですねw
Lambdaの関数をCloud9で編集
関数のコードの編集はCloud9というツールで行います。
ブラウザ上から変更可能で意外と便利だなと思いました。
まだRubyのFormatterが動作しないようなので、そこは今後に期待ですね。
初期のファイルには以下のように書かれています。
require 'json'
def lambda_handler(event:, context:)
# TODO implement
{ statusCode: 200, body: JSON.generate('Hello from Lambda!') }
end
まず、この関数の引数 event:, context:
がどんな情報かを確かめるために一旦以下のようにを全て出力してみましょう。
def lambda_handler(event:, context:)
res = {
event: {
class: event.class,
inspect: event.inspect,
},
context: {
class: context.class,
inspect: context.inspect,
},
}
{ statusCode: 200, body: res.to_json }
end
ここで間違えやすいのが、ファイルの保存後、右上の「保存」もしないと保存されません。
再度「テスト」を走らせると、以下のようなレスポンスが返ってきていることがわかります。
{
"statusCode": 200,
"body": "{\"event\":{\"class\":\"Hash\",\"inspect\":\"{\\\"message\\\"=>\\\"Please give me something like special, Santa!\\\"}\"},\"context\":{\"class\":\"LambdaContext\",\"inspect\":\"#<LambdaContext:0x0000555970e298d8 @clock_diff=xxxxxxxxxxx, @deadline_ms=xxxxxxxxxxxx, @aws_request_id=\\\"xxxxxxxxxxxxxxxx\\\", @invoked_function_arn=\\\"arn:aws:lambda:ap-northeast-1:XXXXXXXX:function:post_message\\\", @log_group_name=\\\"/aws/lambda/post_message\\\", @log_stream_name=\\\"yyyy/mm/dd/[$LATEST]XXXXXXXXXXXX\\\", @function_name=\\\"post_message\\\", @memory_limit_in_mb=\\\"128\\\", @function_version=\\\"$LATEST\\\">\"}}"
}
event
はリクエストボディを Hash
で
context
はLambdaの実行環境の LambdaContext
が返ってきていることがわかりました。
messageの取得
eventはHashなので、event['message']
でメッセージが取得できそうです。
contextはこの APIでは利用することはないので扱いません。
replyの作成
一旦雑にgeneratorを定義して固定の文言を返します。
これもFile > NewでCloud9で新しいrbファイルを追加して lambda_function.rb
と同じフォルダにおいておきます。
class ReplyGenerator
def self.generate(message)
'Ho! Ho! Ho!'
end
end
そしてそれを lambda_function.rb
から呼び出すようにしておきます。
require 'reply_generator'
def lambda_handler(event:, context:)
message = event['message']
return { statusCode: 400, body: {reply: 'error'}.to_json } if message.nil?
reply = ReplyGenerator.generate(message)
res = {
reply: reply
}
{ statusCode: 200, body: res.to_json }
end
入力チェックはひとまず簡単にnil判定だけにしちゃいます。(空文字でも通る)
これで「テスト」すると・・・!
{
"statusCode": 200,
"body": "{\"reply\":\"Ho! Ho! Ho!\"}"
}
Ho!Ho!Ho!
これでサンタのメッセージが返ってきました。
もうここらで良いかなと思いましたがせっかくなのでインターネットから呼び出したいですよね。
API GatewayでAWS Lambdaを動かす
API Gatewayを設定することでLambdaがインターネットから呼び出せるようになります。
- AWS LambdaのDesignerからAPIを追加できるのですが、そうするとちょっとややこしかったので、API Gatewayを直接追加していくことにします。
APIの作成
APIゲートウェイへ移動し、APIの作成を行います。
名前は適当に Santa-Message-API
とかにしました。
リソースの作成
エンドポイントは messages
になるように設定します。
メソッドの作成
テスト
テストをクリックした後、 リクエスト本文
には以下のような大人のお願いを入れておきます。
{
"message": "Oh, Santa! Give me more money!"
}
APIのデプロイとステージの作成
ここで「APIのデプロイ」すると、世界中のインターネットからアクセス可能になります。
セキュリティや利用制限など気になる方はAPIキーの設定、またはスロットリングの設定を適切に行なってください。
API>Santa-Message-API>ステージから「作成」します。
ステージの名前は普通はbeta
とかproduction
とかですがノリで christmas
にしておきますか。
- 念の為スロットリング設定を10 request/秒で設定しました。それくらいで十分でしょ。
作成したchristmas
のステージからメソッドをたどって該当のAPIのURIまで辿り付けます。
こんなURIになります。
https://XXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/christmas/messages
インターネットから試してみる
普段はcurlなどから叩きたいですが、ブラウザだけで完結したいので API Tester という外部サービスを使ってみます。
メッセージには雑に心無いメッセージを入れておきます。
できました!
しかしここらでいかなるお願いにもHo! Ho! Ho!
で返すサンタに苛立ちを隠しきれません。
ちゃんとメッセージを返す
さて、ここらでさっき適当に作ってしまった、 ReplyGenerator
をもう少しだけマシにしましょう。
メッセージをランダムに返す
簡単ですね。
class ReplyGenerator
REPLIES = [
'Ho! Ho! Ho!',
'Thanks for your message!',
'Hope your enjoy with my present!',
'Merry Chrinstmas!',
'I wish Happy New Year!'
]
def self.generate(message)
REPLIES.sample
end
end
良くないメッセージは適当に流す
適当に流す文章を作るのに一番時間がかかりました。
フィルタリングは雑にw
class ReplyGenerator
module Replies
TO_GOODIES = [
'Ho! Ho! Ho!',
'Thanks for your message!',
'Hope your enjoy with my present!',
'Merry Chrinstmas!',
'I wish Happy New Year!'
]
TO_BADDERS = [
'Hey! Did you mean it? Merry Chrinstmas.',
'Work hard and keep calm, and hope do your best!',
'Take a better rest. and I wish your Happy New Year.'
]
end
CHECK_REGEX = /money|(go away)/
class << self
def generate(message)
detect_reply(message).sample
end
private
def detect_reply(message)
CHECK_REGEX.match?(message&.downcase) ? Replies::TO_BADDERS : Replies::TO_GOODIES
end
end
end
これで振り分けもできましたw
インターネットのエンドポイントからもテスト
以下のようにリクエストすると
{"message": "Hey, Santa! Go away!"}
ちゃんと返ってきましたね。
{"statusCode":200,"body":"{\"reply\":\"Work hard and keep calm, and hope do your best.\"}"}
これでサンタさんは心無い大人にも純粋な子供にもメッセージを返すことができるようになりました
めでたしめでたし
まとめ
- AWS LambdaのRubyは簡単に実行だった
- Cloud9使えば全てブラウザから編集できる
- API Gatewayの設定も意外と簡単なので、ブラウザだけで APIが作れる
と、いう初心者向けの内容でした。
- Rubyはわかるけどインフラよくわからない
- AWS怖い・・・
- LambdaってNodeかPythonのものだったよね・・・
という方は是非AWS Lambdaで遊んでみてくださいね。
では Merry Christmas!
HO!HO!HO!