先日公開した記事『【個人開発】ごみのお知らせをしてくれるLINEBot「ごみのお知らせくん」を作りました。』を多くの方に見ていただいており、思った以上の反響に少し驚いています。Qiitaの通知が2桁だったことがなかったので、「何か悪いことをしてしまったのか...!?」とビビりました。LGTMやストックしていただき、ありがとうございます!
さて、本記事ではDynamoDBのデータ更新をトリガーにしてデータのバックアップを作成するというフローを、DynamoDBストリーム、Lambda、Pythonを使って実装する話について書いています。LINEBot『ごみのお知らせくん』で地区情報を更新した際にデータのバックアップを取っておきたいな、と思ったのがきっかけです。それでは早速いきましょう!
■構成
今回のシステム構成です。試行錯誤しながらだったので結構時間がかかったのですが、図にするとかなりシンプルですね。■作業手順
下記が今回の実行手順です。
やりたい内容に沿ってカスタマイズしてください。
1.DynamoDBの「DynamoDB ストリーム」を有効にする
2.IAMロールとIAMポリシーを作成する
3.Lambda関数を作成する
4.バックアップ作成用のプログラムをアップロードする
5.IAMポリシーを少し修正
6.データを更新してバックアップが作成されることを確認
1.DynamoDBの「DynamoDB ストリーム」を有効にする
まず、**DynamoDBのデータ変更をトリガーにするためDynamoDBストリームを有効にします。**これを有効にすることでデータ変更を取得できるようになります。
「概要」タブにある項目「DynamoDBストリームの詳細」の「DynamoDBストリームを管理」をクリックします。クリックすると”どの情報を保存するか”という設定ポップアップが開くので、用途に合わせて選択し、有効化をクリックします。僕の場合は変更後の状態を保存しておきたかったので「新しいイメージ」を選択しました。
有効にすると上画像のようにストリーム有効の値が「はい」の表示になります。この時表示されるARNについては後ほど、IAMポリシーの設定で使用します。この値は覚えておかなくても大丈夫です。
2.IAMロールとIAMポリシーを作成する
Lambdaに設定するIAMロールとIAMポリシーを作成します。特に重要なDynamoDBに付与するポリシーについては太字で書いています。作成するIAMポリシーは下記のようになります。これらのポリシーをIAMロールに付与し、IAMロールをLambdaに付与します。
■DynamoDB
アクション:書き込み『CreateBackup』
*リソース:DynamoDBのリソースを選択
アクション:読み込み
『GetRecords』『GetShardIterator』『ListStreams』『DescribeStream』
*リソース:DynamoDBストリームのリソースを選択
■Lambda
アクション:書き込み『InvokeFunction』
*リソース:Lambdaの関数を選択
■CloudWatch Logs
アクション:書き込み『すべて選択』
リソース:すべてのリソース
ここで、「*」をつけたDynamoDBとLambdaのリソースについては後でまとめて設定するので、一旦は「すべてのリソース」で設定します。
3.Lambda関数を作成する
バックアップを作成するLambda関数を作成します。ここでは「createDynamoDBBackup」という名前で作成しています。
関数作成の際、**先ほど作成したIAMロールを実行ロールに設定します。**また、今回はpythonで実装するのでランタイムにはpythonを選択しています。
4.バックアップ作成用のプログラムをアップロードする
Lambdaのコードソースからソースをアップします。僕の場合はdockerで構築した開発環境でソースコードを用意しました。下記が今回使用した、「pyファイル」「docker-compose.yml」「Dockerfile」です。Lambdaにアップロードするときにはoptディレクトリ配下をzipで圧縮します。dockerによる開発環境の構築については『補足』に記載しています。
■ ディレクトリ構成
docker-compose.yml
Dockerfile
opt
L lambda_function.py
import json
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
import boto3
DATABASE = os.environ['DATABASE']
TABLE_NAME = os.environ['TABLE_NAME']
def lambda_handler(event, context):
logger.info("backup取得開始")
dynamodb_client = boto3.client(DATABASE)
res = dynamodb_client.create_backup(
TableName = TABLE_NAME, # テーブル名
BackupName = TABLE_NAME + '_bk' # バックアップファイル名
)
logger.info(res)
return {
'statusCode': 200,
'body': json.dumps('backup取得完了')
}
version: "3"
services:
python3:
build: .
container_name: "python3_lambda"
working_dir: "/root/opt"
tty: true
volumes:
- ./opt:/root/opt
ports:
- "8000:80"
FROM python:3
USER root
RUN apt-get update
ADD . /code
WORKDIR /code
RUN apt-get install -y vim less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
5.IAMポリシーを少し修正
Lambda関数が作成できたらDynamoDBをトリガーに追加します。部品が揃ったので、IAMポリシーを少し修正していきます。
Lambdaのリソースを修正
IAMポリシーのLambdaリソースを先ほど作成したLambda関数に修正します。入力するのはLambdaの「関数のARN」の値です。「Lambda_functionのARNの設定」に入力すれば下の「Region」「Account」「Function name」欄にも自動的に入力されます。DynamoDBのリソースを修正
▲DynamoDBストリームのARN ▲DynamoDBのARN**IAMポリシーのDynamoDBリソースに「DynamoDBストリームのARN」と「DynamoDBのARN」を入力します。**ARNはそれぞれ上画像の部分です。
6.データを更新してバックアップが作成されることを確認
ここまで出来れば後は確認です。 Lambda関数は簡単にテストができるので確認してみます。テストした際に該当テーブルの「バックアップ」タブに「テーブル名_bk」というファイルができていれば成功です。次は、DynamoDBのデータを更新してバックアップが出来ているか確認します。
バックアップができていない場合はどこかでエラーが起きているので、Lambdaの「モニタリング」タブでログを確認します。
バックアップファイルが確認できたら「バックアップの復元」をクリックして、更新したデータでテーブルができるかどうか確認します。問題なく出来ていれば完了です。
補足
pythonファイル、docker-compose.yml、Dockerfileついて
githubにアップしました。
コンテナ起動後、python3コンテナに入り、下記コマンドを実行してください。
・docker-compose.ymlがあるディレクトリで
docker-compose up -d
・ステータスがupになったら
docker-compose exec python3 bash
・boto3をインストール
pip install boto3 -t ./
boto3はpythonでAWSを操作するためのライブラリです。
Lambdaにzipでアップする必要があるので、現ディレクトリ(/opt内)にインストールするようにしています。
pythonコード内のos.environ['-']
について
プログラム内にはDBの情報を直接記載せず、Lambdaの環境変数で設定しています。環境変数は、Lambdaの「設定」タブ→環境変数から設定ができます。
最後に
DynamoDBのバッグアップには「オンデマンドバックアップ」と「ポイントインタイムリカバリ」の2種類あります。オンデマンドバックアップはバックアップを取得した時点のテーブル状態を保存し、ポイントインタイムリカバリは有効にした時点からテーブルに対する更新処理が記録されます。
今回は「データが更新された際にバックアップを作成したいこと」「無料の範囲で実装したいこと」があったので、これらのバックアップ方法は使わず、DynamoDBストリームとLambdaを組み合わせました。
ポイントインタイムリカバリを有効にしようとすると「追加料金がかかります」というように表示がされるため、『この方法は料金がかかってしまう!これでは実現できない!』と考え、本記事の実装をしました。ですが、ポイントタイムリカバリの説明を改めて見てみると、もしかしたらポイントタイムリカバリでもできるんですかね...?この部分についてはまた改めて調べてみたいと思います。ご存知の方がいたら教えて欲しいです。
それでは!
参考サイト
Amazon DynamoDB
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/dynamodb.html
CreateBackup
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/APIReference/API_CreateBackup.html
DynamoDB StreamをトリガーにしてLambdaを実行する
https://qiita.com/Fujimon_fn/items/1f18360ee9ebf6832617
チュートリアル: DynamoDB Streams と Lambda を使用して新しい項目を処理する
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Streams.Lambda.Tutorial.html#Streams.Lambda.Tutorial.CreateTrigger
DynamoDB テーブルのバックアップ
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Backup.Tutorial.html
AWS SDK for Python (Boto3) の “Client API” と “Resource API” の違いについて調べてみた
https://dev.classmethod.jp/articles/boto3-client-api-and-resource-api/
ステップ 3: Python で項目を作成、読み込み、更新、削除する
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/GettingStarted.Python.03.html
AWS Lambda 実行ロール
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-intro-execution-role.html