📦

データ入りの DynamoDB Local Docker イメージを作る

2021/12/25に公開

データソースとして DynamoDB を使う場合、開発中は DynamoDB Local を使うことが多いのではないでしょうか。初期データの入った DynamoDB Local の Docker イメージをチームで共有したかったので、Docker の muilti-stage build でイメージを作ったという話です。

DynamoDB Local について

DynamoDB Local は、AWS が開発やテストの用途向けに提供している Amazon DynamoDB のローカル版です。Java で作られており、jar ファイルまたは Docker イメージの形で配布されています。
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.html

以下のような感じで起動して使用できます。

$ docker run --rm -p 8000:8000 amazon/dynamodb-local:latest                                                                    ✘ 125 sc-48497-add-operation-id-to-material-stock-closing
Initializing DynamoDB Local with the following configuration:
Port:   8000
InMemory:       true
DbPath: null
SharedDb:       false
shouldDelayTransientStatuses:   false
CorsParams:     *

起動の際にいくつかのオプションが指定できますが、データ入りのイメージを作るにあたっては以下のオプションが関係します。

-dbPath (-inMemory)

DynamoDB Local の Docker イメージは、デフォルトでは -inMemory オプションがついた状態で DynamoDB Local が起動します。 この場合、テーブルやアイテムが作成されてもファイルに永続化されず、Dynamo DB Local を終了すると全てのデータが消失します。

-dbPath オプションでデータベースファイルの場所を指定すると、データベースファイルに永続化され、DynamoDB Local を終了してもデータが保持されるようになります。今回はこのオプションを使って初期データを DynamoDB Local にロードさせます。

-sharedDb

クライアントが DynamoDB に接続する際、AWS アクセスキーと AWS リージョンを指定して接続します。DynamoDB Local は、デフォルトではクライアントの AWS アクセスキー ID と AWS リージョンごとに異なるデータを提供する動きになります。つまり、ある AWS アクセスキー ID で接続したクライアントの作ったデータが、他の AWS アクセスキー ID からは見えません。-sharedDb オプションを指定して起動すると、クライアントの AWS アクセスキー ID および AWS リージョンによらず、同一のデータを提供するようになります。

-sharedDb オプションを指定しない場合、初期データを投入する際の AWS アクセスキー ID と利用する際の AWS アクセスキー ID が同一でなければならないのは不便です。そこで -sharedDb オプションを指定します。

データ入りの DynamoDB Local イメージを作る

それではデータ入りの DynamoDB Local イメージを作ります。大まかな流れは以下のとおりです。

  1. -sharedDb-dbPath を指定した DynamoDB Local を起動する
  2. 初期データを投入するプログラムを実行して DynamoDB Local にデータを入れると、-dbPath で指定した先にデータベースファイルができる
  3. 上で作ったデータベースファイルを含む新しい DynamoDB Local Docker イメージを作る

これを実行する方法はいくつか考えられますが、Docker の multi-stage build を使用すると、docker build を一度実行するだけで完了します。

初期データを投入するプログラムは何でも構いませんが、今回は説明を簡単にするため、以下のように仮定します (実際、私の状況は以下の通りでした)。

  • 投入プログラムは Java で作られている (import-data.jar とする)
  • プログラムは localhost:8000 を DynamoDB のエンドポイントとして動作する
  • プログラムは単独で実行可能な fat jar にパッケージングしている

ビルドに使う Dockerfile は以下のようなものです。

FROM amazoncorretto:11 as builder

USER root

RUN mkdir /app
COPY ./import-data.jar /app
COPY ./dynamodb-local-init.sh /

RUN yum install -y tar gzip \
  && curl -O https://s3-ap-northeast-1.amazonaws.com/dynamodb-local-tokyo/dynamodb_local_latest.tar.gz \
  && mkdir /dynamodb \
  && tar zxf dynamodb_local_latest.tar.gz -C /dynamodb \
  && mkdir /data

# このスクリプトが DynamoDB Local を起動して import-data.jar を実行します
RUN /dynamodb-local-init.sh

# ここまでがビルド用のイメージ (builder) で、ここからが配布用のイメージです
FROM amazon/dynamodb-local

# ビルド用のイメージから配布用のイメージにデータベースファイルをコピーしています
COPY --from=builder data/ /data/

USER root
RUN chown -R dynamodblocal /data/
USER dynamodblocal

EXPOSE 8000
ENTRYPOINT ["java"]
CMD ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-dbPath", "/data"]
dynamodb-local-init.sh
#!/bin/bash

# DynamoDB Local を起動して PID を取得
java -jar -Djava.library.path=/dynamodb/DynamoDBLocal_lib /dynamodb/DynamoDBLocal.jar -sharedDb -dbPath /data &
echo $! >/tmp/dynamodb.pid

# 起動が終わるまで念のため待機
sleep 1

# データ投入プログラムを実行
java -jar /app/import-data.jar

# DynamoDB Local を停止
kill "$(cat /tmp/dynamodb.pid)"

ビルド用のイメージのベースとして amazoncorretto:11 を使っていますが、DynamoDB Local が動く環境であれば何でも良いです。最初はビルド用のイメージも amazon/dynamodb-local をベースにしようとしたのですが、Java のバージョンが古かったので amazoncorretto:11 にしました。

イメージをビルドするには、上記の Dockerfile、dynamodb-local-init.sh、import-data.jar があるディレクトリで以下を実行するだけです。

$ docker build -t my-project/dynamodb-local:latest .

以上でデータ入りのイメージ my-project/dynamodb-local:latest が完成します。

Discussion