[AWS]DynamoDB Localを使ってみた

# はじめに
最近DynamoDB Localを使ってローカル環境でアプリケーションの開発＆テストをする機会があったので、そのやり方について簡単にまとめてみました。
- 動作環境
    - amazon/dynamodb-local:latest(1.21.0)
    - aaronshaf/dynamodb-admin:latest(4.5.1)
    - aws-cli/2.9.21
    - Python 3.11.0
    - boto3==1.26.79

# DynamoDB Localとは
AWSが提供しているダウンロード可能なDynamoDBで、ローカル環境でDynamoDBを構築することができます。Apache Maven dependencyまたはDockerイメージとして使用できます。
今回はAWSが提供している「dynamodb-local」というDockerイメージを使ってローカルでDynamoDBを構築し、テーブル作成やアイテム作成などをやってみたいと思います。

# DynamoDB Localの構築
## コンテナを起動する
docker runコマンドでdynamodb-localのコンテナを起動する場合は以下のコマンドが使えます。`-p`オプションでコンテナの8000ポートをホストの`8000ポート`にマッピングします。該当イメージがない場合は自動で最新のイメージをpullしてからコンテナを作成します。
```bash
$ docker run -d --name dynamodb -p 8000:8000 amazon/dynamodb-local
```
コンテナ作成後、DynamoDB Localのエンドポイントとして`http://localhost:8000`を使うことができます。

## データの永続化
上記のコマンドではコンテナを削除後データが消えます。データを永続化したい場合は以下のコマンドが使えます。`-v`オプションで/dataディレクトリをローカルの`./data`ディレクトリにマウントして、データを保存します。（`-sharedDb`オプションが指定される場合は単一のデータベースファイルが使われます）
```bash
$ docker run -p 8000:8000 -v $(pwd)/data:/data amazon/dynamodb-local -jar DynamoDBLocal.jar -sharedDb -dbPath /data

```

## dynamodb-admin
「dynamodb-admin」という、ローカルで構築したDynamoDBをGUIで操作できるツールがあるので、合わせて紹介します。
- https://github.com/aaronshaf/dynamodb-admin

dynamodb-adminを使ってブラウザ上でテーブル＆アイテム作成などを行うことができます。

## docker composeでdynamodb-localとdynamodb-adminを構築する
docker-composeでdynamodb-localとdynamodb-adminを一緒に構築してみます。下記のdocker composeファイルに、dynamodb-localサービスとdynamodb-adminサービスを定義します。
dynamodb-localサービスでは、コンテナの8000ポートをホストの`8000ポート`にマッピングし、/dataディレクトリをローカルの`./data`ディレクトリにマウントして、データを保存します。
dynamodb-adminサービスでは、コンテナの8001ポートをホストの`8001ポート`にマッピングし、DYNAMO_ENDPOINTを`dynamodb-local:8000`に設定します。
```yaml: docker-compose.yaml
version: '3'
services:
  dynamodb-local:
    image: amazon/dynamodb-local
    ports:
      - "8000:8000"
    volumes:
      - "./data:/data"
    command: ["-jar", "DynamoDBLocal.jar", "-sharedDb", "-dbPath", "/data"]
  dynamodb-admin:
    image: aaronshaf/dynamodb-admin
    ports:
      - "8001:8001"
    environment:
      - DYNAMO_ENDPOINT=http://dynamodb-local:8000
```

以下のコマンドでコンテナを起動します。
```bash
$ docker-compose up -d
```
ブラウザで`http://localhost:8001`にアクセスすると、dynamodb-adminの管理画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954734/31e32d08-132a-d1ed-3895-f5630795648d.png)


DynamoDB Localのエンドポイントとして`http://localhost:8000`を使うことができます。


# テーブル＆アイテム操作
上記docker composeで構築したdynamodb-localのコンテナを使って、CLIとPythonでテーブル＆アイテム作成などの基本的なDynamoDB操作をやってみます。dynamodb-adminを使ったGUIでの操作方法の説明は割愛します。
## CLI
aws cliのオプションでDynamoDBのendpointを`http://localhost:8000`に指定することで、基本的に通常のCLIと同じやり方でローカル環境のDynamoDBを操作することができます。

まずテーブルを作成します。
```bash
$ aws dynamodb \
  --endpoint-url http://localhost:8000 \
    create-table \
  --table-name test-table \
  --attribute-definitions \
    AttributeName=Id,AttributeType=N \
  --key-schema \
    AttributeName=Id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST
```

テーブル作成後、以下のコマンドでテーブル一覧を確認します。
```bash
$ aws dynamodb list-tables --endpoint-url http://localhost:8000
{
    "TableNames": [
        "test-table"
    ]
}
```

アイテムを作成します。
```bash
$ aws dynamodb put-item --endpoint-url http://localhost:8000 --table-name test-table --item '{"Id": {"N": "1"}, "Name": {"S": "kawamura-god"}}'
```

キーを指定して上記作成したアイテムを取得します。
```bash
$ aws dynamodb get-item --endpoint-url http://localhost:8000 --table-name test-table --key '{"Id": {"N": "1"}}'
{
    "Item": {
        "Id": {
            "N": "1"
        },
        "Name": {
            "S": "kawamura-god"
        }
    }
}
```


## Python
boto3.client()でdynamodbを指定する際に、`endpoint_url`引数も一緒に渡しておきます。
まずテーブルを作成します。
```python
import boto3

dynamodb = boto3.client("dynamodb", endpoint_url="http://localhost:8000")

dynamodb.create_table(
    AttributeDefinitions=[
        {
            "AttributeName": "Id",
            "AttributeType": "N"
        },
    ],
    TableName="test-table",
    KeySchema=[
        {
            "AttributeName": "Id",
            "KeyType": "HASH"
        },
    ],
    BillingMode="PAY_PER_REQUEST"
)
```

テーブル一覧を確認します。
```python
print(dynamodb.list_tables()["TableNames"])
```
```
['test-table']
```

アイテムを作成します。
```python
dynamodb.put_item(TableName="test-table", Item={
    "Id": {"N": "1"},
    "Name": {"S": "kawamura-god"}
})
```

キーを指定して上記作成したアイテムを取得します。
```python
item = dynamodb.get_item(
    TableName="test-table", Key={"Id": {"N": "1"}}
)
print(item["Item"])
```
```
{'Id': {'N': '1'}, 'Name': {'S': 'kawamura-god'}}
```

# 参考
- [DynamoDB ローカル (ダウンロード可能バージョン) のセットアップ](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.html)
- [コンピュータ上で DynamoDB をローカルでデプロイする](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html#docker)
- [amazon/dynamodb-local](https://hub.docker.com/r/amazon/dynamodb-local)
- [aaronshaf/dynamodb-admin ](https://hub.docker.com/r/aaronshaf/dynamodb-admin/)