👻

DynamoDB localをDockerで構築する

2020/10/10に公開

背景

現在担当しているプロダクトではAppSyncを使っており、バックエンドにLambdaを使うことが多いです。その際にLambdaのロジックに対する単体テストを書くのですがDynamoDBとのやり取りについては保留していました。
開発が進むにつれていろんな処理が入ってきたこともあり、「実際にアクセスしてみないと分からない」という状況では色々不都合が出てきたのでそろそろテストを入れようと思って調べたことをまとめています。

この記事に掲載しているコードの全体はyu-croco/DynamoDB-local-Sampleにアップロードしていますので、ご自由にお使いください。

追記

投稿後に気づいたので追記です。
Circle CI上に建てたDockerコンテナに対して、Circle CIのローカル上からアクセスする(例:http://localhost:8000 )のは無理っぽいので、Circle CI上でこの記事のようにDocker化したDynamoDB localを使う場合には、GolangのリソースもDocker化してください。

DynamoDB local

DynamoDB localとはAWSが提供しているローカル上で起動できるDynamoDBのことです。
AWS公式のSetting Up DynamoDB Localによると、

ダウンロード可能なバージョンの Amazon DynamoDB では、DynamoDB ウェブサービスにアクセスせずに、アプリケーションを開発してテストすることができます。代わりに、データベースはコンピュータ上で自己完結型となります。アプリケーションを本番稼働環境にデプロイする準備ができたら、コード内のローカルエンドポイントを削除します。その後、これは DynamoDB ウェブサービスを指します。

このローカルバージョンを使用することで、スループットやデータストレージ、データ転送料金を節約しやすくなります。また、アプリケーションを開発している間インターネットに接続しておく必要はありません。

とのことです。
「いちいちDynamoDB叩くと面倒だよね」というので、よりハンディなものを提供してくれている感じです。

今回はこれを使います。

Docker化してみる

CI上で利用したいこともあり、毎回DynamoDB localを直接インストールしたくはありません。幸い、DynamoDB localはAWSが公式でDocker Hubにイメージを公開しているので今回はこれを使います。
amazon/dynamodb-local - Docker Hub

Dockerfileはこんな感じです。オプションなどは公式を参照頂くとよいかと思います。
DynamoDB Local Usage Notes

FROM amazon/dynamodb-local

CMD ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-dbPath", ".", "-optimizeDbBeforeStartup"]
$ docker build -t dynamodb_sample .
$ docker run -p 8000:8000 -v dynamodb:/home/dynamodblocal dynamodb_sample

みたいにすれば、ざっと動きます。

DynamoDBにデータを入れる

起動したDynamoDB localにテーブル作成やデータ投入をします。
このためにわざわざコードを書くのは面倒なので、今回はAWS CLI経由でデータを入れます。
AWS CLI用のコンテナを起動し、以下のようなスクリプトを実行することでDynamoDB localのコンテナに対してテーブル操作を行います。

テーブル作成用

#!/usr/bin/env bash

aws dynamodb \
  --region ap-northeast-1 \
  --endpoint-url http://dynamodb:8000 \
    create-table \
  --table-name SampleTable \
  --attribute-definitions \
    AttributeName=userId,AttributeType=N \
    AttributeName=userName,AttributeType=S \
  --key-schema \
    AttributeName=userId,KeyType=HASH AttributeName=userName,KeyType=RANGE \
  --billing-mode PAY_PER_REQUEST

データ投入用

#!/usr/bin/env bash

aws dynamodb \
  --region ap-northeast-1 \
  --endpoint-url http://dynamodb:8000 \
    put-item \
  --table-name SampleTable \
  --item '
    {
      "userId": {
        "N": "1"
       },
      "userName": {
        "S": "山田太郎"
      },
      "age": {
        "N": "29"
      },
      "contactNumber": {
        "S": "080-1234-5678"
      }
    }
  '

およその全体像

最終的にはざっくり以下の感じになります。

Dockerfile(AWS CLI用)

FROM amazon/aws-cli:2.0.56

ENV AWS_ACCESS_KEY_ID=fake_access_key\
    AWS_SECRET_ACCESS_KEY=fake_secret_access_key\
    DYNAMODB_REGION=ap-northeast-1
# AWS CLIから実行したい処理をbin配下にshellscriptで作っておく
COPY bin bin

docker-compose.yml

version: '3'
services:
  dynamodb:
    image: amazon/dynamodb-local
    command: -jar DynamoDBLocal.jar -sharedDb -dbPath . -optimizeDbBeforeStartup
    volumes:
      - dynamodb:/home/dynamodblocal
    ports:
      - 8000:8000
  awscli:
    build: .
    entrypoint: [""]
    tty: true
    command:
      - /bin/sh
volumes:
  dynamodb:
    driver: local

これでdocker-compose upしてDynamoDB localとAWS CLIのそれぞれのコンテナを起動します。その後binファイルに入っているshellscriptを叩いてテーブル作成などを行います。

コンテナ起動後に自動でテーブル作成などできればいいですが、それはまた別の機会にでも。

セッションの設定

Golangの場合ではDynamoDBへのセッションを定義する処理でEndpointを http://0.0.0.0:8000 とすれば既存のDynamoDBの処理が動くようになるかと思います。

const region = "ap-northeast-1"
var svc = dynamodb.New(session.New(&aws.Config{
		Region:   aws.String(region),
		Endpoint: aws.String("http://0.0.0.0:8000"),
	})
)

まとめ

DynamoDBへのアクセス処理に関するテストを行うためにDynamoDB localを使いました。DynamoDB localへのテストデータの投入は面倒なので、AWS CLI用のDockerを起動してそこからDynamoDB localへデータを投入するのがおすすめです(CI上とかでも使えるので)。

それでは幸せなDynamoDB生活を!

Discussion