AWS Lambda Ruby 2.7 でPostgreSQLのコマンドを実行する方法


My Redmine

三度の飯よりAWSが好き!(テキトー
寝ても覚めてもAWSに夢中!!(うなされる系
今月のAWSの請求額を見て「あれ?おかしいな。。暑いのに震えが止まらない。。。」
こんにちは吉岡です。

以前、AWS Lambda Ruby 2.5 からPostgreSQLを操作してゴニョゴニョしたことがあるのですが、なんと!この度AWS LambdaでRuby 2.5のサポート終了となりました!!!強制的にRuby 2.7にアップデートしないといけなかったので、今回はアップデート作業の一部(AWS lambda Ruby 2.7 からpsqlコマンドを実行する方法)を紹介したいと思います。

大まかな流れは以下の通りです。

  1. AWSが提供しているLambdaのDockerイメージを利用して、PostgreSQLをインストール
  2. psqlコマンドに必要なバイナリーファイルをローカルにダウンロード
  3. AWS SAMの初期化
  4. AWS SAMの設定(template.yml, lambda, layer)
  5. ローカルでpsqlコマンド呼び出しのテスト
  6. AWSへデプロイ

注)AWS Lambdaのベースイメージ(OS)は Amazon Linux (Ruby 2.5) からAmazon Linux2(Ruby 2.7)に変更になりました。
参考:ランタイムサポートポリシー - AWS Lambda

1. AWSが提供しているLambdaのDockerイメージを利用して、PostgreSQLをインストールする

Dockerファイルのビルド

まずは以下のDockerfileを作成します。

FROM lambci/lambda:build-ruby2.7

RUN yum install -y postgresql postgresql-devel

CMD "/bin/bash"

Dockerfile作成後、作成した場所で以下のコマンドを実行し、Dockerイメージをビルドします。

$ docker build -t lambda-ruby2.7-postgresql:latest .

2. psqlコマンドに必要なバイナリーファイルをローカルにダウンロード

ビルドが正常に終わりましたら、以下のコマンドでコンテナを起動してログインします。

$ docker run --rm -it -v $PWD:/var/task -w /var/task lambda-ruby2.7-postgresql:latest

コンテナにログイン後、以下のコマンドでpsqlコマンド実行に必要なファイルをダウンロードします。

必要ライブラリーのコピー

mkdir lib
cp -a /usr/lib64/libpq.so.5.5 /var/task/lib/libpq.so.5
cp -a /usr/lib64/libldapr-2.4.so.2.10.7 /var/task/lib/libldapr-2.4.so.2
cp -a /usr/lib64/liblber-2.4.so.2.10.7 /var/task/lib/liblber-2.4.so.2
cp -a /usr/lib64/libsasl2.so.3.0.0 /var/task/lib/libsasl2.so.3
cp -a /usr/lib64/libssl3.so /var/task/lib/
cp -a /usr/lib64/libsmime3.so /var/task/lib/
cp -a /usr/lib64/libnss3.so /var/task/lib/
cp -a /usr/lib64/libnssutil3.so /var/task/lib/

必要コマンドのコピー

mkdir bin
cp -a /usr/bin/createdb /var/task/bin/
cp -a /usr/bin/createuser /var/task/bin/
cp -a /usr/bin/dropdb /var/task/bin/
cp -a /usr/bin/dropuser /var/task/bin/
cp -a /usr/bin/pgdump /var/task/bin/
cp -a /usr/bin/pgdumpall /var/task/bin/
cp -a /usr/bin/pg_restore /var/task/bin/
cp -a /usr/bin/psql /var/task/bin/
cp -a /usr/bin/reindexdb /var/task/bin/
cp -a /usr/bin/vacuumdb /var/task/bin/

これで準備OKです。

3. AWS SAMの初期化

AWS SAMの初期化(プロジェクト作成)

次はAWS SAMを利用してlambdaの設定をしていきます。
以下を参考に初期化をして、入力していきます。

プロジェクト名は今回はlambda-ruby27-postgresqlとしています。

$ sam init
Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1
What package type would you like to use?
        1 - Zip (artifact is a zip uploaded to S3)
        2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1

Which runtime would you like to use?
        1 - nodejs14.x
        2 - python3.8
        3 - ruby2.7
        4 - go1.x
        5 - java11
        6 - dotnetcore3.1
        7 - nodejs12.x
        8 - nodejs10.x
        9 - python3.7
        10 - python3.6
        11 - python2.7
        12 - ruby2.5
        13 - java8.al2
        14 - java8
        15 - dotnetcore2.1
Runtime: 3

Project name [sam-app]: lambda-ruby27-postgresql

Cloning app templates from https://github.com/aws/aws-sam-cli-app-templates

AWS quick start application templates:
        1 - Hello World Example
        2 - Step Functions Sample App (Stock Trader)
Template selection: 1

    -----------------------
    Generating application:
    -----------------------
    Name: lambda-ruby27-postgresql
    Runtime: ruby2.7
    Dependency Manager: bundler
    Application Template: hello-world
    Output Directory: .

    Next steps can be found in the README file at ./lambda-ruby27-postgresql/README.md


SAM CLI update available (1.26.0); (1.23.0 installed)
To download: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html

4. AWS SAMの設定(template.yml, lambda, layer)

無事にsam initが終わりましたら以下のようにファイルを変更していきます。

lambda layer

今回の肝となるバイナリーファイルをlambda layer に設置します。

# レイヤー用のディレクトリ作成
mkdir ./lambda-ruby27-postgresql/layers-postgres

# 先ほど作成したバイナリーファイルをlambda layerに配置
cp -r ./bin ./lambda-ruby27-postgresql/layers-postgres
cp -r ./lib ./lambda-ruby27-postgresql/layers-postgres

lambdaファイル

lambda-ruby27-postgresql/ に移動後、以下のようにhello_world/app.rbファイルを書き換えます。

# require 'httparty'
require 'json'
require 'open3'

# psqlのバージョンを取得
def lambda_handler(event:, context:)
  command = %*psql --version*
  o, e, s = Open3.capture3(command)
  raise "psql error. - #{e}" if !(e.empty?)

  {
    statusCode: 200,
    body: {
      message: o,
    }.to_json
  }
end

templateファイル

以下のようにtemplate.ymlファイルを書き換えます。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  lambda-ruby27-postgresql

  Sample SAM Template for lambda-ruby27-postgresql

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3
    Layers:
      - !Ref RubyLibLayer # layerを全体に適用します。

Resources:

  # レイヤーの作成
  RubyLibLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: 'postgresql-sample'
      Description: some-command is included
      ContentUri: layers-postgres/
      CompatibleRuntimes:
        - ruby2.7

  # lambda関数の作成
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: ruby2.7

Outputs:
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

5. ローカルでpsqlコマンド呼び出しのテスト

ローカルでテスト

sam local invoke を実行し出力を確認します。

$ sam local invoke

Invoking app.lambda_handler (ruby2.7)
RubyLibLayer is a local Layer in the template
Building image........................
Skip pulling image and use local one: samcli/lambda:ruby2.7-520794dcbcd13821d5d3c8df8.

Mounting /home/ec2-user/environment/data/docker/lambda-ruby27/lambda-ruby27-postgresql/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
END RequestId: 65a1fd1a-a175-461a-8463-bda83cce87b2
REPORT RequestId: 65a1fd1a-a175-461a-8463-bda83cce87b2  Init Duration: 0.08 ms  Duration: 147.03 ms     Billed Duration: 200 ms Memory Size: 128 MB     Max Memory Used: 128 MB
{"statusCode":200,"body":"{\"message\":\"psql (PostgreSQL) 9.2.24\\n\"}"}

{"statusCode":200,"body":"{\"message\":\"psql (PostgreSQL) 9.2.24\\n\"}"} と出力されればOKです。

これで無事に通信完了です。

6. AWSへデプロイ

あとはこれを実際にAWSにデプロイして動作確認してみます。

以下はデプロイコマンドの例です。

$ sam package --template-file template.yaml --output-template-file packaged.yaml --s3-bucket [your bucket name]
$ sam deploy --template-file packaged.yaml --stack-name lambda-ruby27-test --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM

デプロイが完了しましたら、AWS マネージメントコンソールからlambdaのテストを実行してみてください。

AWSにログイン後lambdaの関数に移動し、先ほど作成した「lambda-ruby27-test-HelloWorldFunction-xxxxxxxx」に移動します。
ここでlayerが適用されているか、コードが反映されているかなどもチェックできます。

テストボタンをクリックして新規のテストデータを作成します。

作成が終わったらテストを実行し、出力結果を確認します。上記のように表示されれば問題なく実行が確認されます。

以上、lambda ruby 2.7を利用してRDS(postgresql)と通信をできるようにする方法の紹介でした。この記事が参考になれば幸いです。
参考サイト:AWS Lambda Ruby 2.7 Pg Gem LibLDAP Error | Northsail

こちらの記事もオススメです!
AWS Amplify のサンドボックスを試してみた
AWS Amplifyのサンドボックスを利用するとAWSアカウント無しでAmplifyの一部機能を試せます。
Redmine公式に投稿しているチケットやパッチの傾向を調べてみました
Redmine公式サイトへの投稿履歴から取り組みやすいカテゴリやチケットが分かりました。
Q. Rocky LinuxはCentOSの後継?(A. ちょっと違います)
CentOSのプロジェクト方針変更を受けスタートしたRocky LinuxやRHEL互換OSについて整理してみました。
Redmine/RedMicaの新機能解説セミナーを初開催
Redmine/RedMicaの最新バージョンの機能をピックアップして紹介。定員いっぱいのご参加をいただきました。
Redmine 15周年 これまで追加された主な機能をふりかえる
2021年6月25日はRedmineのリリース15周年。15年間で追加された機能を時系列で紹介します。
ファーエンドテクノロジーからのお知らせ(2024/04/24更新)
入門Redmine 第6版 出版記念企画セミナー「Redmineのアクセス制御」【2024/5/30開催】
入門Redmine 第6版(2024年3月23日発売)の書籍から「Redmineのアクセス制御」について解説します。
My Redmine 初回ご契約で「入門Redmine 第6版」プレゼントのお知らせ
Redmineのクラウドサービス「My Redmine」を初めてご契約いただいたお客様にRedmine解説書「入門Redmine 第6版」を進呈いたします。
2024年度ブランドパートナーに島根県在住のモデル ユイさんを継続起用
ユイさん(モデルスタジオミューズ所属)をファーエンドテクノロジーの2024年度ブランドパートナーとして継続して起用します。
My Redmine スタンダードプランおよびAdminサポートデスクプランの料金改定のお知らせ【2024年4月ご利用分より】
2024年4月ご利用分より、My Redmine スタンダードプラン(民間企業・個人向け及び官公庁向け)とAdminサポートデスクプランの料金を改定いたします。
Redmineの最新情報をメールでお知らせする「Redmine News」配信中
新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け