AWS Lambda with Ruby3

2023年3月時点で AWS Lambda は Ruby3 をサポートしておりません。[Ruby2 は 2023年3月末で EOL](https://www.ruby-lang.org/ja/downloads/branches/) の予定です。
[AWS Lambda Base Container Images](https://github.com/aws/aws-lambda-base-images/issues/54) は 2023年3月末までにコンテナイメージを公開するようです。

2020年12月に AWS Lambda はコンテナイメージをサポートし、任意のバージョンを利用することができます。
そこで SAM CLI のコマンドを利用して Ruby3 による Lambda 関数の構築を紹介します。

## SAM Init

- SAM CLI のバージョンは `1.77.0` を利用します。
  - Ruby のランタイムは `2.7` のみサポートしています。 (Ruby3 をサポートしておりません。)

```sh
$ sam --version # SAM CLI, version 1.77.0
$ sam init --help | grep ruby # ruby2.7
```

- `sam init` コマンドは _Hello World!_ の コンテナイメージを利用します。
  - _X-Ray tracing_ と _CloudWatch monitoring_ は利用しません。

```sh
$ sam init --name sam-ruby-version-check --base-image amazon/ruby2.7-base --app-template hello-world-lambda-image --no-tracing --no-application-insights
$ cd sam-ruby-version-check
```

## SAM Build

### Ruby Version 2

- `hello_world/app.rb` の戻り値に `RUBY_VERSION` を追加します。

```ruby
require 'json'

def lambda_handler(event:, context:)
  {
    statusCode: 200,
    body: {
      message: "Hello World!",
      ruby_version: RUBY_VERSION
    }.to_json
  }
end
```

- `sam build` と `sam local invoke` コマンドを実行すると `ruby_version` に `2.7.7` が出力されます。

```bash
$ sam build
$ sam local invoke HelloWorldFunction --event events/event.json # {"statusCode":200,"body":"{\"message\":\"Hello World!\",\"ruby_version\":\"2.7.7\"}"}
```

### Ruby Version 3

- `hello_world/DockerFile` に [AWS Lambda Ruby Runtime Interface Client](https://github.com/aws/aws-lambda-ruby-runtime-interface-client) の Example を記述します。
  - Example の `CMD ["app.App::Handler.process"]` を `CMD ["app.lambda_handler"]` に変更します。
- 2023年3月中旬に [Amazon Linux 2023 がリリース](https://aws.amazon.com/jp/about-aws/whats-new/2023/03/amazon-linux-2023/)されて `FROM amazonlinux:latest` は **Ruby3.2** がサポートされています。
  - Example の `amazon-linux-extras install -y ruby2.6` を `dnf install -y ruby3.2` に変更します。

```DockerFile
FROM amazonlinux:latest

# Define custom function directory
ARG FUNCTION_DIR="/function"

# Install ruby
RUN dnf install -y ruby3.2

# Install the Runtime Interface Client
RUN gem install aws_lambda_ric

# Copy function code
RUN mkdir -p ${FUNCTION_DIR}
COPY app.rb ${FUNCTION_DIR}

WORKDIR ${FUNCTION_DIR}

ENTRYPOINT ["/usr/local/bin/aws_lambda_ric"]

CMD ["app.lambda_handler"]
```

- `hello_world/app.rb` の require に`io/console` と `stringio` を追加します。
  - `aws_lambda_ric` は Ruby3 に対応していないので _`uninitialized constant #<Class:AwsLambda::Marshaller>::StringIO`_ が発生します。

```ruby
require 'json'
require 'io/console'
require 'stringio'

def lambda_handler(event:, context:)
  {
    statusCode: 200,
    body: {
      message: "Hello World!",
      ruby_version: RUBY_VERSION
    }.to_json
  }
end
```

- `sam build` と `sam local invoke` コマンドを実行すると `ruby_version` に `3.2.1` が出力されます。

```bash
$ sam build
$ sam local invoke HelloWorldFunction --event events/event.json # {"statusCode":200,"body":"{\"message\":\"Hello World!\",\"ruby_version\":\"3.2.1\"}"}
```

## SAM Deploy

- `sam deploy` コマンドを実行で AWS Lambda にリリースします。

```
$ sam deploy
```

- API Gateway でアクセスすると `ruby_version` に `3.2.1` が出力されます。

```json
{ "message": "Hello World!", "ruby_version": "3.2.1" }
```

Ruby3 で AWS Lambda 関数の構築が確認できました。これで Ruby2 がサポート終了(EOL)されても安心です。

## Cleanup CloudFormation

- `aws cloudformation ` コマンドを実行で AWS にリリースしたリソースを削除します。

```
$ aws cloudformation delete-stack --stack-name sam-ruby-version-check
```

CloudFormation は自動でリソースを削除されるので便利です。（ECR は削除されないので手動で削除してください。）

## Tips

- SAM CLI で作成される Docker の コンテナにアクセスする方法を紹介します。
- `template.yaml` の `Resources` の `Metadata` で Docker の指定をしています。

```yaml
Resources:
  HelloWorldFunction:
    Metadata:
      DockerTag: ruby2.7-v1
      DockerContext: ./hello_world
      Dockerfile: Dockerfile
```

- `sam build` コマンドで `REPOSITORY` が `helloworldfunction` で `TAG` が `ruby2.7-v1` のイメージが作成されます。
- `docker run` コマンドを実行するとコンテナにアクセスできます。

```bash
$ sam build
$ docker run --rm -it --entrypoint bash helloworldfunction:ruby2.7-v1
bash-5.2# dnf search all ruby | grep ruby3.2.x86_64 # ruby3.2.x86_64 : An interpreter of object-oriented scripting language
```

この記事が公開されてしばらく経つと AWS Lambda で Ruby3 がサポート　されるようになります。