DynamoDBを他環境に作るのに手動では耐えられなくなった件

# はじめに

こんにちは、Gakken LEAP のエンジニアの shogawa です。
今回は、開発環境向けに構築しておいたAWSのDynamoDBを検証環境・本番環境向けに、中身を除いて項目や設定だけをコピーする方法について調べてみたので記事にしてみます。

# 前提

- AWSの環境が整っていること
- AWS CLIが実行できること(実施バージョン：aws-cli/2.13.32)
- jq がインストールされていること(実施バージョン：version 1.6)
- DynamoDB に元にするテーブルが作成されていること

また、プロジェクトのAWS環境ではMFA認証しているため、shell でもAWS認証する必要がありますが、その部分については省略します。

# ステップの確認

1. テーブル定義の取得
1. 設定をjsonに出力（省略できるかも）
1. コピー先のテーブルを作成
1. オートスケーリングとターゲットトラッキングポリシーの設定
1. １つのshellとして作成

# テーブル定義の取得

まず、コピー元のテーブル定義を aws コマンドにて取得します。

```sh
aws dynamodb describe-table --table-name "origin_table_name"
```

# 設定をjsonに出力

この後のコピー先のテーブルを作成するのに、データの削除や設定のコピーのためのjsonファイルに出力をしておきます。
このあたりはバージョン等によって要否がわかれることがあるので、公式ドキュメントを参考に必要な設定を出力してください。

```sh
    jq '.Table' |
    jq 'del(.ProvisionedThroughput.NumberOfDecreasesToday)' |
    jq 'del(.ProvisionedThroughput.LastIncreaseDateTime)' |
    jq 'del(.ProvisionedThroughput.LastDecreaseDateTime)' |
    jq 'del(.TableSizeBytes)' |
    jq 'del(.TableStatus)' |
    jq 'del(.ItemCount)' |
    jq 'del(.CreationDateTime)' |
    jq 'del(.TableArn)' |
    jq 'del(.TableId)' |
    jq 'del(.LatestStreamLabel)' |
    jq 'del(.LatestStreamArn)' |
    jq 'del(.StreamSpecification.StreamEnabled)' |
    jq 'del(.StreamSpecification.StreamViewType)' |
    jq 'del(.RestoreSummary.RestoreDateTime)' |
    jq 'del(.RestoreSummary.RestoreInProgress)' |
    jq 'del(.RestoreSummary.SourceBackupArn)' |
    jq 'del(.RestoreSummary.SourceTableArn)' |
    jq 'del(.SSEDescription.Status)' |
    jq 'del(.SSEDescription.SSEType)' |
    jq 'del(.SSEDescription.KMSMasterKeyArn)' |
    jq 'del(.SSEDescription.InaccessibleEncryptionDateTime)' |
    jq 'del(.SSEDescription.Error)' |
    jq 'del(.TableClassSummary)' |
    # テーブル名を変換して出力
    jq --arg table "copy_table_name" '.TableName = "origin_table_name"'
```

また、グローバルセカンダリインデックスやローカルセカンダリインデックスがある場合も、設定をjsonに出力しておきます。

```sh
# グローバルセカンダリインデックス
    jq 'del(.GlobalSecondaryIndexes[].IndexArn)' |
    jq 'del(.GlobalSecondaryIndexes[].ItemCount)' |
    jq 'del(.GlobalSecondaryIndexes[].IndexStatus)' |
    jq 'del(.GlobalSecondaryIndexes[].IndexSizeBytes)' |
    jq 'del(.GlobalSecondaryIndexes[].ProvisionedThroughput.NumberOfDecreasesToday)' |
    jq 'del(.GlobalSecondaryIndexes[].ProvisionedThroughput.LastIncreaseDateTime)' |
    jq 'del(.GlobalSecondaryIndexes[].ProvisionedThroughput.LastDecreaseDateTime)'

# ローカルセカンダリインデックス
    jq 'del(.LocalSecondaryIndexes[].IndexArn)' |
    jq 'del(.LocalSecondaryIndexes[].ItemCount)' |
    jq 'del(.LocalSecondaryIndexes[].IndexStatus)' |
    jq 'del(.LocalSecondaryIndexes[].IndexSizeBytes)' |
    jq 'del(.LocalSecondaryIndexes[].ProvisionedThroughput.NumberOfDecreasesToday)' |
    jq 'del(.LocalSecondaryIndexes[].ProvisionedThroughput.LastIncreaseDateTime)' |
    jq 'del(.LocalSecondaryIndexes[].ProvisionedThroughput.LastDecreaseDateTime)'
```

# コピー先のテーブルを作成

続いて、出力したjsonファイルを元にテーブルを作成します。

```sh
aws dynamodb create-table --cli-input-json "file://copy_table_name.json"
```

# オートスケーリングとターゲットトラッキングポリシーの設定

これまでの設定ではコピーされない、オートスケーリングとターゲットラッキングポリシーの設定をしていきます。

```sh
# オートスケーリングの設定
aws application-autoscaling register-scalable-target \
    --service-namespace dynamodb \
    --resource-id table/copy_table_name \
    --scalable-dimension dynamodb:table:ReadCapacityUnits \
    --min-capacity 1 \
    --max-capacity 10

# オートスケーリングの設定
aws application-autoscaling register-scalable-target \
    --service-namespace dynamodb \
    --resource-id table/copy_table_name \
    --scalable-dimension dynamodb:table:WriteCapacityUnits \
    --min-capacity 1 \
    --max-capacity 10
```

```sh
# ターゲットトラッキングポリシー(ReadCapacity)の設定
aws application-autoscaling put-scaling-policy \
    --service-namespace dynamodb \
    --resource-id table/copy_table_name \
    --scalable-dimension dynamodb:table:ReadCapacityUnits \
    --policy-name ReadCapacityScalingPolicy \
    --policy-type TargetTrackingScaling \
    --target-tracking-scaling-policy-configuration file://read-target-tracking-policy.json

# ターゲットトラッキングポリシー(WriteCapacity)の設定
aws application-autoscaling put-scaling-policy \
    --service-namespace dynamodb \
    --resource-id table/copy_table_name \
    --scalable-dimension dynamodb:table:WriteCapacityUnits \
    --policy-name WriteCapacityScalingPolicy \
    --policy-type TargetTrackingScaling \
    --target-tracking-scaling-policy-configuration file://write-target-tracking-policy.json

```

## １つの shell として作成

各コマンドを実行すればDynamoDBのテーブルコピーができていると思います。
複数環境にたいしてコピーがやりやすいよう、１つのshellとしてまとめていきます。

コピー元、コピー先のファイル名はshellのパラメータにして各コマンドを整えていきます。

```sh
# 実行コマンド
./copyDynamoDB.sh origin-table-name copy-table-name
```

以下、shellの全容です。

```sh
# テーブル名は引数にて指定
ORG_TABLE_NAME=$1
COPY_TABLE_NAME=$2

# もとテーブルの定義を取得
DESCRIPTION=$(aws dynamodb describe-table --table-name $ORG_TABLE_NAME)

# 不要な項目の削除
CONTENTS=`echo $DESCRIPTION |
        jq '.Table' |
        jq 'del(.ProvisionedThroughput.NumberOfDecreasesToday)' |
        jq 'del(.ProvisionedThroughput.LastIncreaseDateTime)' |
        jq 'del(.ProvisionedThroughput.LastDecreaseDateTime)' |
        jq 'del(.TableSizeBytes)' |
        jq 'del(.TableStatus)' |
        jq 'del(.ItemCount)' |
        jq 'del(.CreationDateTime)' |
        jq 'del(.TableArn)' |
        jq 'del(.TableId)' |
        jq 'del(.LatestStreamLabel)' |
        jq 'del(.LatestStreamArn)' |
        jq 'del(.StreamSpecification.StreamEnabled)' |
        jq 'del(.StreamSpecification.StreamViewType)' |
        jq 'del(.RestoreSummary.RestoreDateTime)' |
        jq 'del(.RestoreSummary.RestoreInProgress)' |
        jq 'del(.RestoreSummary.SourceBackupArn)' |
        jq 'del(.RestoreSummary.SourceTableArn)' |
        jq 'del(.SSEDescription.Status)' |
        jq 'del(.SSEDescription.SSEType)' |
        jq 'del(.SSEDescription.KMSMasterKeyArn)' |
        jq 'del(.SSEDescription.InaccessibleEncryptionDateTime)' |
        jq 'del(.SSEDescription.Error)' |
        jq 'del(.TableClassSummary)' |
        # テーブル名を変換して出力
        jq --arg table "$COPY_TABLE_NAME" '.TableName = $table'
    `

# グローバルセカンダリインデックスがある場合
if test `echo $CONTENTS | jq 'has("GlobalSecondaryIndexes")'` = true; then
  echo has GlobalSecondaryIndexes
  CONTENTS=`echo $CONTENTS |
        jq 'del(.GlobalSecondaryIndexes[].IndexArn)' |
        jq 'del(.GlobalSecondaryIndexes[].ItemCount)' |
        jq 'del(.GlobalSecondaryIndexes[].IndexStatus)' |
        jq 'del(.GlobalSecondaryIndexes[].IndexSizeBytes)' |
        jq 'del(.GlobalSecondaryIndexes[].ProvisionedThroughput.NumberOfDecreasesToday)' |
        jq 'del(.GlobalSecondaryIndexes[].ProvisionedThroughput.LastIncreaseDateTime)' |
        jq 'del(.GlobalSecondaryIndexes[].ProvisionedThroughput.LastDecreaseDateTime)'
    `

# ローカルセカンダリインデックスがある場合
elif test `echo $CONTENTS | jq 'has("LocalSecondaryIndexes")'` = true; then
  echo has LocalSecondaryIndexes
  CONTENTS=`echo $CONTENTS |
        jq 'del(.LocalSecondaryIndexes[].IndexArn)' |
        jq 'del(.LocalSecondaryIndexes[].ItemCount)' |
        jq 'del(.LocalSecondaryIndexes[].IndexStatus)' |
        jq 'del(.LocalSecondaryIndexes[].IndexSizeBytes)' |
        jq 'del(.LocalSecondaryIndexes[].ProvisionedThroughput.NumberOfDecreasesToday)' |
        jq 'del(.LocalSecondaryIndexes[].ProvisionedThroughput.LastIncreaseDateTime)' |
        jq 'del(.LocalSecondaryIndexes[].ProvisionedThroughput.LastDecreaseDateTime)'
    `
fi

# jsonファイルに出力
echo $CONTENTS > $COPY_TABLE_NAME.json

# コピーテーブルを作成
aws dynamodb create-table --cli-input-json "file://"$COPY_TABLE_NAME".json"

# オートスケーリングの設定
aws application-autoscaling register-scalable-target \
    --service-namespace dynamodb \
    --resource-id table/$COPY_TABLE_NAME \
    --scalable-dimension dynamodb:table:ReadCapacityUnits \
    --min-capacity 1 \
    --max-capacity 10

# オートスケーリングの設定
aws application-autoscaling register-scalable-target \
    --service-namespace dynamodb \
    --resource-id table/$COPY_TABLE_NAME \
    --scalable-dimension dynamodb:table:WriteCapacityUnits \
    --min-capacity 1 \
    --max-capacity 10

# ターゲットトラッキングポリシー(ReadCapacity)の設定
aws application-autoscaling put-scaling-policy \
    --service-namespace dynamodb \
    --resource-id table/$COPY_TABLE_NAME \
    --scalable-dimension dynamodb:table:ReadCapacityUnits \
    --policy-name ReadCapacityScalingPolicy \
    --policy-type TargetTrackingScaling \
    --target-tracking-scaling-policy-configuration file://read-target-tracking-policy.json

# ターゲットトラッキングポリシーの(WriteCapacity)の設定
aws application-autoscaling put-scaling-policy \
    --service-namespace dynamodb \
    --resource-id table/$COPY_TABLE_NAME \
    --scalable-dimension dynamodb:table:WriteCapacityUnits \
    --policy-name WriteCapacityScalingPolicy \
    --policy-type TargetTrackingScaling \
    --target-tracking-scaling-policy-configuration file://write-target-tracking-policy.json

```

# まとめ

今回は、大きめのプロジェクトでDynamoDBを使うことになり、開発環境で作ったものの検証環境、本番環境に向けて構築をどうしようかと思い、shell一発でテーブルのコピーができるようにしてみました。
参考になれば幸いです。

Gakken LEAP では教育をアップデートしていきたいエンジニアを絶賛大募集しています！！

https://gakken-leap.co.jp/recruit/