Ruby で書いた AWS Lambda の関数を Apex を使ってデプロイできたので、その時の覚書です。 誤りや認識間違いが混ざっているかもしれません。ご指摘いただけたら幸いです。
仕事ではずっと Serverless + nodejs を使っていたのでこちらの方が慣れているのですが、別件で Apex を使うことになったのでその学習も兼ねて。
インストールとか
詳細は公式サイトを参照してください。
$ curl https://raw.githubusercontent.com/apex/apex/master/install.sh | sh
プロジェクトを作成する
適当なディレクトリを作成して移動し、apex init コマンドを実行します。
プロジェクト名とその説明の入力を求められるので、適当な内容を入力します。
$ mkdir apex-ruby
$ cd apex-ruby/
$ apex init
_ ____ _______ __
/ \ | _ \| ____\ \/ /
/ _ \ | |_) | _| \ /
/ ___ \| __/| |___ / \
/_/ \_\_| |_____/_/\_\
Enter the name of your project. It should be machine-friendly, as this
is used to prefix your functions in Lambda.
Project name: apex-ruby
Enter an optional description of your project.
Project description: Apex + AWS Lambda + Ruby
[+] creating IAM apex-ruby_lambda_function role
[+] creating IAM apex-ruby_lambda_logs policy
[+] attaching policy to lambda_function role.
[+] creating ./project.json
[+] creating ./functions
Setup complete, deploy those functions!
$ apex deploy
作成されるディレクトリとファイルはこんな感じ。
$ tree . ├── functions │ └── hello │ └── index.js └── project.json
project.json の内容はこんな感じ。
{ "name": "apex-ruby", "description": "Apex + AWS Lambda + Ruby", "memory": 128, "timeout": 5, "role": "arn:aws:iam::548673361492:role/apex-ruby_lambda_function", "environment": {} }
function/hello/index.js は不要なので削除します。
$ rm functions/hello/index.js
関数を作成する
function/hello/index.rb を作成してエントリポイントを記述します。
エントリポイントは event と context をキーワード引数で受け取るトップレベルのメソッドかクラスメソッドとして定義します。
def handler(event:, context:) 'Hello, Apex + AWS Lambda + Ruby!' end
念のため動作を確認。
$ ruby -r './functions/hello/index.rb' -e 'puts handler(event: nil, context: nil)' Hello, Apex + AWS Lambda + Ruby!
設定を記述する
functions/hello/ の下に function.json というファイルを作成し設定を記述します。
{ "runtime": "ruby2.5", "handler": "index.handler" }
runtime は AWS Lambda で利用するランタイムです。今回は Ruby を使うので ruby2.5 を指定します。
handler は実行時に呼び出されるメソッドを指定します。トップレベルのメソッドの場合は ファイル名.メソッド名 という形式で、クラスメソッドの場合は ファイル名.クラス名.メソッド名 という形式で指定します。
デプロイする
apex deploy コマンドでデプロイします。
$ apex deploy • creating function env= function=hello • created alias current env= function=hello version=1 • function created env= function=hello name=apex-ruby_hello version=1
デプロイ状況を確認します。ここでは AWS CLI で関数名の一覧を取得して確認しています。
$ aws lambda list-functions --query Functions[].FunctionName
[
"apex-ruby_hello"
]
関数名は プロジェクト名_(functionsの下の)ディレクトリ名 という形式になっています。
実行する
apex invoke コマンドで実行します。指定する関数名は AWS Lambda の関数名ではなく、Apex 内の名前になります。
$ apex invoke hello "Hello, Apex + AWS Lambda + Ruby!"
削除する
apex delete コマンドで削除します。
$ apex delete Are you sure? (yes/no) yes • deleting env= function=hello • function deleted env= function=hello
AWS SDK for Ruby を使う
AWS SDK for Ruby はランタイムに含まれているので require するだけで利用することができます。
require 'aws-sdk-lambda' def handler(event:, context:) Aws::Lambda::GEM_VERSION end
再デプロイして実行します。
$ apex deploy $ apex invoke hello "1.15.0"
なお関数をデプロイしただけでは色々と許可されていないので、このままでは AWS のリソースにアクセスしようとするとエラーになってしまいます。
require 'aws-sdk-lambda' def handler(event:, context:) # AWS Lambda の関数を一覧する client = Aws::Lambda::Client.new resp = client.list_functions resp.functions.map(&:function_name).join(',') end
$ apex deploy $ apex invoke hello ⨯ Error: function response: User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/apex-ruby_lambda_function/apex-ruby_hello is not authorized to perform: lambda:ListFunctions on resource: *
Terraform で色々設定する
Apex 自身にはこれらを設定する機能はありませんが、Terraform を利用して解決します。
プロジェクト内にディレクトリ infrastructure を作成し、そこに Terraform の設定を記述します。
ポリシーの設定を infrastructure/main.tf に記述します。ここで role は project.json にある role の内容を記述します。
resource "aws_iam_role_policy" "lambda_policy" {
name = "lambda_policy"
role = "apex-ruby_lambda_function"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:*",
"Resource": "*"
}
]
}
EOF
}
適用は infrastructure ディレクトリに移動して terraform コマンドを実行する代わりに、apex infra init 、 apex infra apply を実行することで実行できます。
$ apex infra init $ apex infra apply
これでリソースにアクセスできるようになります。
$ apex invoke hello "apex-ruby_hello"
これらは関数のデプロイとは独立しているので、適用や更新や削除はそれぞれ実行する必要があります。
gem を利用する
標準添付ライブラリと AWS SDK for Ruby 以外の gem を利用する場合はインストールする必要があります。
Faker を使う例で試してみます。
require 'faker' def handler(event:, context:) Faker::Name.name end
このままでデプロイ、実行すると、予想通り gem を読み込めずにエラーになります。
$ apex deploy $ apex invoke hello ⨯ Error: function response: cannot load such file -- faker
Ruby のバージョンを AWS Lambda に合わせる
gem をインストールする前に、Ruby のバージョンを AWS Lambda のランタイムに合わせておきます。 rbenv などを利用してバージョンを切り替えます。
$ rbenv local 2.5.3
Gemfile を作成する
関数のディレクトリに移動し bundle init を実行するなどして Gemfile を作成します。
$ cd functions/hello/ $ bundle init
Gemfile に faker を追加します。
# frozen_string_literal: true source 'https://rubygems.org' git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gem 'faker'
gem をインストールする
Apex は関数を定義しているディレクトリの内容を AWS Lambda にアップロードします。
--path オプションで vendor/bundle/ 以下にインストールするか、Gemfile.lock 生成後に --deployment オプションを指定してインストールを実行します。
この辺りの詳細については Bundler のドキュメントを確認してください。
$ bundle --path vendor/bundle Fetching gem metadata from https://rubygems.org/.. Using bundler 2.0.1 Fetching concurrent-ruby 1.1.4 Installing concurrent-ruby 1.1.4 Fetching i18n 1.5.3 Installing i18n 1.5.3 Fetching faker 1.9.3 Installing faker 1.9.3 Bundle complete! 1 Gemfile dependency, 4 gems now installed. Bundled gems are installed into `./vendor/bundle`
インストールできたらプロジェクトのディレクトリに戻ります。
あらためてデプロイする
gem をインストールした状態でデプロイすると、gem も合わせてアップロードされます。
$ apex deply
実行すると gem がロードできないというエラーは出なくなりますが、タイムアウトが発生すると思います。
$ apex invoke hello ⨯ Error: function response: 2019-02-23T02:31:00.996Z fc0f76c0-2238-434b-8a21-722917e1042f Task timed out after 5.00 seconds
Apex はタイムアウトの初期値として 5 秒を設定していますが、初回の起動時に 10 秒あまりかかるためタイムアウトしてしまいます。
project.json の timeout の値を 15 程度に変更してから再度デプロイし実行します。
$ apex invoke hello "Dwayne Murphy DVM"
無事起動しました。
gem のインストールを自動化する
Apex のフック機能を利用して gem のインストールを自動化します。
function.json に hooks を追加します。build で --deployment オプションを付けてのインストールを指定します。デプロイ後に「凍結」を解除したいので frozen の削除を clean で指定しています。
{ "runtime": "ruby2.5", "handler": "index.handler", "hooks": { "build": "bundle install --deployment", "clean": "bundle config --delete frozen" } }
native extension を利用する gem を利用する場合
native extension を利用する gem の場合は実行環境と同等の環境でインストールする必要がありますが、Docker で同等の環境を入手することができるのでこれを利用すれば可能になります。
この中でタグ build-ruby2.5 を指定すると Ruby もインストールされた環境を入手できます。
環境を切り替える
開発版やリリース版などの環境を切り替える場合、設定ファイルに環境名を追加します。また Terraform は環境ごとのディレクトリを作成します。
ここまでのファイルの内容:
apex-ruby/ ├── functions/ │ └── hello/ │ ├── Gemfile │ ├── Gemfile.lock │ ├── function.json │ └── index.rb ├── infrastructure/ │ └── main.tf └── project.json
これを development と production に分離します。
apex-ruby/ ├── functions/ │ └── hello/ │ ├── Gemfile │ ├── Gemfile.lock │ ├── function.development.json │ ├── function.production.json │ └── index.rb ├── infrastructure/ │ ├── development/ │ │ └── main.tf │ └── production/ │ └── main.tf ├── project.development.json └── project.production.json
project.環境名.json で指定する name の値など、環境によって内容が異なるものはそれぞれの環境用の値を設定します。
適用する環境は、コマンドの実行時に --env オプションで指定します。
$ apply --env development deploy
apply infra コマンドで --env を指定すると、infrastructure ディレクトリの下の環境名のディレクトリの内容が適用されます。
いつか読むはずっと読まない:2019-02-22、タッチダウン
pixivFANBOXを更新[公開限定]「はやぶさ2」タッチダウン覚え書き(3) https://t.co/ewjlutgLjB
— しきしま (@shikishima) 2019年2月22日
想像していたよりもガチな内容で楽しめました。曰く「相模原でカプセルのフタを開けるまでが遠足です。まだまだ長いです」。今からでも副読本にどうぞ。

- 作者: しきしまふげん,松浦晋也,へかとん
- 出版社/メーカー: 三才ブックス
- 発売日: 2009/06/10
- メディア: 単行本(ソフトカバー)
- 購入: 26人 クリック: 523回
- この商品を含むブログ (86件) を見る

- 作者: しきしまふげん,松浦晋也,へかとん
- 出版社/メーカー: 三才ブックス
- 発売日: 2014/11/19
- メディア: 単行本
- この商品を含むブログ (4件) を見る