🚇

Rakeタスクを雑にAWS Lambdaへ持っていく

2024/03/01に公開

DBレコードを参照・更新するような既存のRakeタスクを、シュッとLambdaに持っていった時のメモを残しておく。

前提

AWS Lambdaでは/tmp以外のディレクトリにファイルを出力することができず、容量も512MBしかない。Lambda起動時に都度bundle installする選択肢は、所要時間の点でも取るべきではない。

bundle install --path vendor/bundleした後、vendorディレクトリごとzipで固めると動作させることはできるが、細かい点で特有の手順が必要だったので、メモに残しておく。

方針

  • Gemごと固めてzipをアップロードする
    • M1 macなのでDocker内に環境を作る
  • ログを標準出力のみに変える
    • 前述の通りファイル出力が不可なため
  • Lambyは使用しない
    • 定時実行のみが必要で、エンドポイントを公開したい訳ではないので、今回は導入しなかった

手順

Gemfileの用意

なるべくzipファイルのサイズを減らしたいので、タスク実行に不要なGemを取り除いておく。最終的に二つだけになった。

Gemfile
source "https://rubygems.org"

ruby "3.2.2"

gem "rails", "~> 7.1.3"
gem "mysql2", "~> 0.5"

ログ出力先の修正

Railsのログをファイル出力していると、Rakeタスクの実行時、出力先のパーミッションがチェックされ、エラーになるので修正する必要がある。

config/environments/production.rb
config.logger = ActiveSupport::Logger.new(STDOUT)

Lambaで実行するハンドラー関数の作成

Railsアプリのディレクトリ直下で、lambda_function.rbを作成する。Rakeタスクが実行できればなんでもよいが、今回はOpen3を使用した。

lambda_function.rb
require "open3"

def lambda_handler(event:, context:)
  o, e = Open3.capture3('bundle exec rails test:foo')
  puts o unless o.empty?

  unless e.empty?
    puts e
    raise e
  end

  'done'
end

Dockerfileの用意

公式で提供されているLambda用のイメージを使用する。

Lambdaでは、システムライブラリのパスLD_LIBRARY_PATHという環境変数で定義されている。
yum installしたMySQLのファイルは/lib64/mysqlに配置されるが、LD_LIBRARY_PATHには含まれないので、エラーになってしまう。

パスが通っている場所にlibmysqlclient.so.18のシンボリックリンクを作成する必要がある。

Dockerfile
FROM --platform=linux/x86_64 public.ecr.aws/lambda/ruby:3.2 AS zip-stage

WORKDIR /var/task

COPY . /var/task
RUN yum -y install gcc make mysql-devel libyaml-devel zip
RUN ln -s /lib64/mysql/libmysqlclient.so.18 .

RUN gem update bundler

RUN bundle config set --local path 'vendor/bundle'
RUN bundle install

RUN zip -r lambda_package.zip .

FROM scratch

COPY --from=zip-stage /var/task/lambda_package.zip /

CMD ["/bin/bash"]

Dockerビルドの実行

ビルド時に生成されるzipファイルを、ゲストからホストへファイルコピーしている。詳しくはこちら

DOCKER_BUILDKIT=1 docker build --output . .

Lambdaの設定

zipファイルをアップロードして完成。最低限、下記の環境変数が必要になる。

  • DB_HOST
  • DB_NAME
  • DB_PASSWORD
  • DB_USER
  • RAILS_ENV
  • SECRET_KEY_BASE

Discussion