はじめに
serverless deploy後にLambdaとDynamoDBの接続で色々詰まった点をまとめてみます。ファイルに関しては数が多いため記載は一部にしています
前提
-
Docker in Dcokerでserverless deploy
開発環境のコンテナの中でデプロイコマンド(sls deploy)を実行しています -
React+Rails+DynamoDB+APIGateway+Lambda
-
フロント(React)からバックエンド(Rails)にパラメータを渡せている
ディレクトリ構成(一部)
この記事の環境に関しては前回の続きになります
sample-project
┣ backend
┃ ┗ config
┃ ┗ aws.yml
┣ Dockerfile
┣ serverless.yml
┣ docker-compose.yml
詰まった点
フロントからパラメータを送ると、DynamoDB側で500エラーが発生
CloudWatchでログを確認すると以下のエラーが出ました
task time out after 25seconds...
このときバックエンド側でパラメータは受け取れていたので、
LambdaとDynamoDBが接続できているか、以下の点をserverless.ymlで確認しましたが問題は見つかりませんでした
- DynamoDBのアクセス権限をroleに設定できているか
- DynamoDBのテーブルに対して参照権限を設定できているか
エラー文で検索をかけてみると以下の記事を見つけました
こちらの記事を参考にし、Lambdaの実行制限時間を25秒から適当に180秒まで増やして再度動作を確認したところ、エラー文が以下に変わりました
service: sample-project-api
provider:
name: aws
runtime: ruby2.7
timeout: 180
略
Failed to open TCP connection to dynamodb-hoge-local:8000 (getaddrinfo: Name or service not known)
接続先が本番環境のDynamoDBではなく、開発環境のDynamoDBになっていました
開発環境でフロントからパラメータを送った際はDynamoDBにレコードが作成されているため、本番環境では認証もできていなさそうでした
そこでDynamoDBとの接続や認証まわりについて確認することにしました
本番環境のDynamoDBで認証ができていない
まず開発環境のコンテナ内で.envから認証情報を取得できているか確認するとnilでした
# 開発環境用のコンテナに入ってrails c
> Rails.env
これを解決するためにはAWS Parameter StoreやSecrets Managerを利用するようです
今回はAWS Parameter Storeを使いました
serverless.ymlに以下のような書き方をすると、
パラメータストアに入れた情報を取得できるようです
environment:
QIITA_DYNAMODB_ACCESS_KEY_ID: ${ssm:/sample-project/AWS_ACCESS_KEY_ID}
QIITA_DYNAMODB_SECRET_ACCESS_KEY: ${ssm:/sample-project/AWS_SECRET_ACCESS_KEY}
RAILS_ENV: production
略
この時、Lambdaで予約されている環境変数名と被らないようにする必要があったので「QIITA_DYNAMODB_」を頭につけています
パラメータストアに情報を入れる際は以下のようなコマンドを実行します
# 開発環境のコンテナに入る
aws ssm put-parameter --name "/sample-project/AWS_ACCESS_KEY_ID" \
--type "Secure String" --value "hogehogehogehoge"
aws ssm put-parameter --name "/sample-project/AWS_SECRET_ACCESS_KEY" \
--type "Secure String" --value "fugafugafugafuga"
接続先が本番環境のDynamoDBではなく、開発環境のDynamoDBになっている
次に開発環境のDynamoDBに接続しようとしていた点ですが、
aws.ymlの設定に不足がありました
以下の記事を参考に、本番環境用の設定を追加しました
default: &default
access_key_id: <%= ENV['QIITA_DYNAMODB_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['QIITA_DYNAMODB_SECRET_ACCESS_KEY'] %>
region: ap-northeast-1
development:
<<: *default
endpoint: http://dynamodb-hoge-local:8000
production:
<<: *default
- これで開発環境のコンテナからserverless deployした際にACCESS_KEY_ID/KEYを環境変数としてrailsに渡すことができます
- developmentにのみendpointを指定しているのは、GUIとして使用しているdynamodb-adminの参照先にdynamodb-localを指定する必要があるためです
次に本番用のDockefileでenvの切り替えができるようにコマンドを追加しました
# 本番用
ARG RUBY_TAG
FROM public.ecr.aws/lambda/ruby:${RUBY_TAG} as builder
ENV LANG C.UTF-8
RUN yum update -y && yum groupinstall -y "Development Tools"
WORKDIR /var/task
# 追加分
ENV RAILS_ENV=production
ENV RACK_ENV=production
ARG BUNDLE_VER
RUN gem install bundler -v ${BUNDLE_VER}
COPY lambda.rb /var/task
COPY ./Gemfile /var/task
COPY ./Gemfile.lock /var/task
ENV GEM_HOME=/var/task
RUN bundle install
ADD . /var/task/
CMD ["lambda.App::Handler.process"]
これでsls removeした後に改めてsls deployを行い、フロントからパラメータを送るとDynamoDBにレコードが作成されることを確認できました!!
おまけ
最終的なserverless.yml
service: sample-project-api
provider:
name: aws
runtime: ruby2.7
timeout: 180
stage: ${opt:stage, 'dev'}
region: ap-northeast-1
deploymentBucket:
name: sample-project-api-dev-slsdeploymentbucket
ecr:
scanOnPush: true
images:
lambda_docker:
path: ./backend
buildArgs:
BUNDLE_VER: ${env:BUNDLE_VER, '2.3.5'}
RUBY_TAG: ${env:RUBY_TAG, '2.7'}
environment:
RAILS_ENV: production
SECRET_KEY_BASE: hogehogehogehoge
QIITA_DYNAMODB_ACCESS_KEY_ID: ${ssm:/sample-project/AWS_ACCESS_KEY_ID}
QIITA_DYNAMODB_SECRET_ACCESS_KEY: ${ssm:/sample-project/AWS_SECRET_ACCESS_KEY}
iamRoleStatements:
- Effect: 'Allow'
Action:
- dynamodb:*
Resource:
- arn:aws:dynamodb:ap-northeast-1:○○:table/*
custom:
defaultStage: dev
deploymentBucket:
blockPublicAccess: true
functions:
api:
timeout: 25
image:
name: lambda_docker
events:
- http:
path: /
method: ANY
cors: &cors
origin: http://localhost:43000
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
- Access-Control-Allow-Headers
- Access-Control-Allow-Origin
- Time-Zone
- Cache-Control
allowCredentials: false
- http:
path: /{proxy+}
method: ANY
cors: *cors
resources:
Resources:
HogeTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: dynamoid__${self:provider.environment.RAILS_ENV}_hoge
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: "id"
AttributeType: "S"
KeySchema:
- AttributeName: "id"
KeyType: "HASH"
plugins:
- serverless-deployment-bucket
おわりに
LambdaとDynamoDB間の繋ぎこみや細かい箇所でたくさん詰まりましたがなんとかできました。引き続いて理解を深めていきます。