export-table-to-point-in-timeでDynamoDBのバックアップを行う(&ついでにリストアの手順も解説)
はじめに
複数のDynamoDBテーブルの全Itemを コスパよく 取得したかったので、色々な方法がある([1])中からexport-table-to-point-in-time[2]を利用して実施することにしました。
初めてなので実際にやってみました。
結論
- export-table-to-point-in-timeでDynamoDBテーブルの全データをJSON形式で出力できる
- S3バケットへ出力できる
- ただし、テーブルのポイントインタイムリカバリが有効になっている必要あり
- list-exportsでバックアップの状況を確認できる
- export-table-to-point-in-timeの完了までは時間がかかるので、list-exportsと組み合わせて「終わるまでポーリング」する使い方が便利
- バックアップデータをDynamoDBのAWS SDKで
putItem
できる- ただし、バックアップデータは分割して保存されていることがあるので、事前にマージが必要
前提
- CLIはV2系を利用します
- jqを利用します
$ aws --version
aws-cli/2.1.1 Python/3.7.4 Darwin/19.6.0 exe/x86_64
$ jq --version
jq-1.6
やってみたこと
データ準備
テーブルを2つ作成しました。
マネジメントコンソール上から以下のようにテーブル名とキー設定だけ実施し、他はデフォルトのまま作成しています。
- テーブル名:table1
- パーティションキー:id
- ソートキー:なし
- テーブル名:table2
- パーティションキー:id
- ソートキー:なし
作成したテーブルにItemを入れておきます。
適当にidが1〜6のItemを作成しました。
その上で、後述のバックアップ機能を利用するため、ポイントインタイムリカバリの設定を有効化しています。
マネジメントコンソールからの設定手順は次のとおりです。
バックアップ先(S3バケット)作成
バックアップデータの格納先バケットを作成します。
ここでは hirobel-ddb-bkp
というバケットを作成しました。
バックアップ取得
export-table-to-point-in-time[2:1]を利用してバックアップを取得をリクエストします。
レスポンスの ExportDescription.ExportArn
がデータの格納先です。
リストア時に必要な情報になるので、ファイルへ出力しておくことにしました。
ちなみにレスポンス全体はこんな感じです。
{
"ExportDescription": {
"ExportArn": "arn:aws:dynamodb:REGION:ACCOUNT:table/tablename/export/01234567890abc-1234abc",
"ExportStatus": "IN_PROGRESS",
"StartTime": ...,
"TableArn": ...,
"TableId": ...,
"ExportTime": ...,
"ClientToken": ...,
"S3Bucket": ...,
"S3Prefix": ...,
"S3SseAlgorithm": ...,
"ExportFormat": ...,
}
}
list-exports[3]を利用してバックアップの状況を確認できます。
export-table-to-point-in-timeによるバックアップでは、実際にデータが出力完了するまでにしばらく時間がかかるため、これを利用して状況をポーリングするような形にしました。
作ってみたコードは以下になります。
バックアップコード
#!/bin/bash
tables=("table1" "table2")
if [ -z "$EXPORT_S3_BUCKET" ]; then
echo "EXPORT_S3_BUCKETが指定されていません。終了します。"
exit 0
fi
for i in "${tables[@]}"
do
aws dynamodb export-table-to-point-in-time \
--table-arn arn:aws:dynamodb:ap-northeast-1:$(aws sts get-caller-identity | jq -r .Account):table/$i \
--s3-bucket $EXPORT_S3_BUCKET \
--export-format DYNAMODB_JSON | jq -r .ExportDescription.ExportArn | cut -d "/" -f 4 > $i-export-path
done
for i in "${tables[@]}"
do
while :
do
path=`cat $i-export-path`
targetExportArn=arn:aws:dynamodb:ap-northeast-1:$(aws sts get-caller-identity | jq -r .Account):table/$i/export/$path
status=`aws dynamodb list-exports | jq --arg targetExportArn $targetExportArn '.ExportSummaries[] | select(.ExportArn == $targetExportArn)' | jq -r .ExportStatus`
if [ "$status" = "COMPLETED" ]; then
echo "${i}のバックアップデータ出力は完了しました"
break
fi
echo "${i}のバックアップデータ出力は未完了です。リトライします..."
sleep 60
done
done
これを実行してみるとこんな感じになります。
$ EXPORT_S3_BUCKET=katoaki-ddb-backup-test bash backup.bash
table1のバックアップデータ出力は未完了です。リトライします...
table1のバックアップデータ出力は未完了です。リトライします...
table1のバックアップデータ出力は未完了です。リトライします...
table1のバックアップデータ出力は未完了です。リトライします...
table1のバックアップデータ出力は未完了です。リトライします...
table1のバックアップデータ出力は完了しました
table2のバックアップデータ出力は完了しました
データを変更
後続のリストアの効果を検証するために、table1からid=1のデータを削除します。
リストア
バックアップしたデータはDDB-JSON形式でS3バケット上に保存されています。
読み込むには、オブジェクトをダウンロードしてテーブル毎に一つのファイルにマージし、マージしたファイルを読み込んでputItem[4]すればデータを元に戻すことができます。
pubItemを行う処理はこんな感じです。
const AWS = require("aws-sdk");
AWS.config.update({ region: "ap-northeast-1" });
const dynamodb = new AWS.DynamoDB(); // バックアップのjsonがDDB-JSON形式なのでdocumentClientを使わない
fs = require("fs");
const tableName = process.env.TABLE_NAME; //e.g. table1
const readline = require("readline");
const filePath = process.argv[2];
const stream = fs.createReadStream(filePath, {
encoding: "utf8",
});
const rl = readline.createInterface({
input: stream,
});
let Items = [];
const main = async () => {
for await (const line of rl) {
Items.push(JSON.parse(line).Item);
}
await Promise.all(
Items.map(async (item) => {
const params = {
TableName: tableName,
Item: item,
};
try {
await dynamodb.putItem(params).promise();
} catch (e) {
console.log(e);
console.log("テーブルの更新処理でエラーが発生しました。終了します。");
}
})
);
};
main();
pubItemを行う処理のインプットとして、読み込むファイルと読み込んだデータを格納するテーブルが必要になります。
そのため、こんな感じで実行してみました。
BACKUP_OBJECT_PATH=$(cat <テーブル名>-export-path)
mkdir -p backup
aws s3 cp s3://${EXPORT_S3_BUCKET}/AWSDynamoDB/${BACKUP_OBJECT_PATH}/data/ backup --recursive
gzip -d backup/*
cat backup/* > backup/merged.json
node restore.js <テーブル名> "backup/merged.json"
リストアが行われるのでtable1は実行後にこんな感じになります
参考
-
Amazon S3 に DynamoDB テーブルデータをエクスポートする - Amazon DynamoDB https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DataExport.html ↩︎
-
export-table-to-point-in-time — AWS CLI 2.2.20 Command Reference https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/export-table-to-point-in-time.html ↩︎ ↩︎
-
list-exports — AWS CLI 2.2.25 Command Reference https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudformation/list-exports.html ↩︎
-
Class: AWS.DynamoDB — AWS SDK for JavaScript https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#putItem-property ↩︎
Discussion