DynamoDBのお手軽バックアップ
アプリの状態だけ保持しているようなあまり大きくないDynamoDBのテーブルを
バックアップするのにDataPipeline使うのもどうかと思うのでツール作ろうかと考えていたら
既にあった。
使ってみたら結構便利だったので使い方をまとめます。
AmazonLinuxへインストール
このツールはAWSのpython向けSDKであるboto
を利用しますが、
AmazonLinuxにはpython + botoが初めから入っています。
dynamodumpをgitからチェックアウトするだけで使う準備は完了です。
$ yum -y install git $ git clone https://github.com/bchew/dynamodump.git $ cd dynamodump
バックアップ
バックアップ対象のDynamoDBテーブルへの読み込み権限があれば、
以下の3つのオプションのみでバックアップが取得できます。
パラメータ名 | 値 |
---|---|
-m (Mode) | backup |
-r (Region) | ap-northeast-1 |
-s (SrcTable) | 対象のテーブル名 |
$ python dynamodump.py -m backup -r ap-northeast-1 -s my-table INFO:root:Starting backup for my-table.. INFO:root:Dumping table schema for my-table INFO:root:Dumping table items for my-table INFO:root:Backup for my-table table completed. Time taken: 0:00:00 $ tree . |-- LICENSE |-- README.md |-- dump | `-- my-table | |-- data | | `-- 0001.json | `-- schema.json |-- dynamodump.py `-- requirements.txt
リストア
リストアはバックアップ時と同じパラメータでも実行可能です。
しかしそのまま実行すると前処理でテーブルを削除してしまうので、
データだけを戻したい場合は--dataOnly
オプションを指定します。
パラメータ名 | 値 |
---|---|
-m (Mode) | restore |
-r (Region) | ap-northeast-1 |
-s (SrcTable) | 対象のテーブル名 |
--dataOnly | スキーマの作成/削除を行わない |
$ python dynamodump.py -m restore -r ap-northeast-1 -s my-table INFO:root:my-table table is being deleted.. INFO:root:my-table table deleted! INFO:root:Starting restore for my-table to my-table.. INFO:root:Creating my-table table with temp write capacity of 25 INFO:root:Waiting for my-table table to be created.. [CREATING] INFO:root:my-table created. INFO:root:Restoring data for my-table table.. INFO:root:Processing 0001.json of my-table INFO:root:Updating my-table table read capacity to: 1, write capacity to: 1 INFO:root:Restore for my-table to my-table table completed. Time taken: 0:00:21
$ python dynamodump.py -m restore -r ap-northeast-1 -s my-table --dataOnly INFO:root:Starting restore for my-table to my-table.. INFO:root:Restoring data for my-table table.. INFO:root:Processing 0001.json of my-table INFO:root:Restore for my-table to my-table table completed. Time taken: 0:00:01
S3へ定期バックアップするように設定する
cronでも良いと思いますが今回はJenkinsを使用してジョブを定義しました。
Git:RepositoryURL
にhttps://github.com/bchew/dynamodump.git
を指定して、
シェルの実行
で以下のように定義するのがオススメです。
対象のバケット名やテーブルはお好みで変更してください。
BUCKET_NAME=yustam-dynamo-backup ARCHIVE_NAME=dump_`date +%Y%m%d%H%M`.tgz python dynamodump.py -m backup -r ap-northeast-1 -s my-table tar czf $ARCHIVE_NAME ./dump rm -rf ./dump aws s3 cp $ARCHIVE_NAME s3://$BUCKET_NAME/dynamo/$ARCHIVE_NAME rm -f $ARCHIVE_NAME exit 0;
ビルドトリガ
を定期的に実行
にして好きな時間に動かしたらいいと思います。
AWS CLIを使用してEC2のバックアップを定期的に取得する
AWS CLIとjqを使用した実装です。
設定した世代数分保持して古いバックアップから消していきます。
設定値 | 説明 |
---|---|
MAX_IMAGE_NUM | バックアップのイメージを保持する世代数 |
INSTANCE_ID | バックアップを取得する対象のEC2インスタンスID |
AMI_NAME | 取得するAMIの名前。実行毎に重複しないようにする |
DESCRIPTION | バックアップの種類を識別するために使用します |
cronやJenkinsのジョブに設定して定期的に実行します。
再起動を許容する場合は--no-reboot
オプションを外します。
EBS付きのインスタンスは追加のオプションが必要なので注意。
#!/bin/sh MAX_IMAGE_NUM=2 REGION=ap-northeast-1 INSTANCE_ID=i-00000000 AMI_NAME=ec2-backup-$(date +%m%d%H%M) DESCRIPTION="daily_backup" IMAGES=$(aws ec2 describe-images\ --region ap-northeast-1\ --owners self\ --filters Name=description,Values=$DESCRIPTION\ | jq .Images) # Delete oldest image if [[ $(echo $IMAGES | jq length) -ge $MAX_IMAGE_NUM ]]; then OLDEST_IMAGE_ID=$(echo $IMAGES | jq 'min_by(.CreationDate)' | jq -r .ImageId) echo "Delete: $OLDEST_IMAGE_ID" aws ec2 deregister-image\ --region $REGION\ --image-id $OLDEST_IMAGE_ID fi # Create image echo "Create: $AMI_NAME" aws ec2 create-image\ --region ap-northeast-1\ --instance-id $INSTANCE_ID\ --name $AMI_NAME\ --no-reboot\ --description $DESCRIPTION exit 0
Packer + Jenkins(on EC2)で自動AMI作成
Packerを使用してAMIの作成を自動化が出来たのでJenkinsでCIしてみようと思います。
Packerの使い方やJenkinsの使い方は割愛して簡単にポイントのみまとめます。
Jenkinsの設定
Gitリポジトリを指定
こちらのようなリポジトリを作成します。
template.json
がPackerのテンプレートファイルです。
ジョブの設定
シェルの実行
で以下のようにシェルを設定します。
packerをインストールしてしまえば毎回ダウンロードする必要はありません。
mkdir packer cd packer wget -o - https://dl.bintray.com/mitchellh/packer/packer_0.8.0_linux_amd64.zip unzip packer_0.8.0_linux_amd64.zip cd ../ ./packer/packer build template.json rm -rf ./packer exit 0;
設定は以上です。あとは好きなタイミングでジョブが動くように設定します。
注意する点
Jenkinsからpackerを使用してsshするとsudoが使用できない
packerを使用するとEC2にsshで接続してコマンドを実行しようとしますが、
スクリプト内でsudoを使用するとエラーが出て失敗してしまいます。
sudo: sorry, you must have a tty to run sudo
解決法
そのため、ここではEC2のUserDataにて/etc/sudoers
を編集するようにしました。
packerではAMI作成用のEC2にUserDataを指定できるため、そこでsudoの設定を変更します。
#!/bin/bash -ex sed -i 's/Defaults requiretty/Defaults !requiretty/g' /etc/sudoers
その他、sudoで実行したいコマンドがあればここで実行しておくと良いです。
ビルダーのuser_data_file
にファイル名を指定すると起動時にUserDataへ設定されます。
{ "builders": [{ "type": "amazon-ebs", "region": "ap-northeast-1", "source_ami": "ami-cbf90ecb", "instance_type": "t2.micro", "ssh_username": "ec2-user", "ami_name": "packer-ami-{{timestamp}}", "user_data_file": "files/user-data.txt" }], "provisioners": [{ "type": "shell", "scripts": [ "scripts/create-user-dirs.sh", "scripts/install-packages.sh" ] }] }
AmazonSNS通知の内容を電話で通知する
Twilioという電話通知を行えるAPIサービスを使用して、
SNS通知 -> Lambda -> Twilio -> 携帯電話
という流れで電話通知を行う仕組みを作ってみます。
使用するサービス
Twilioのアカウントを作成する
- サインアップからアカウントを作成します。
- アカウントを作成したらこちらでTwilioの電話番号を取得します。
Show API Credentials
をクリックしAccountSid
とAuthToken
をメモします。
Lambdaアプリの作成
アプリはこちらからダウンロードします。 gitとnpmを使用しますので入ってない場合はインストールしてください。
git clone https://github.com/y-matsuki/sns-phone-call.git cd sns-phone-call/
設定ファイルconfig/default.yaml
を編集します。
twilio: accountSid: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' authToken: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' language: 'ja-jp' voice: 'woman' toNumber: '+81xxxxxxxxxx' fromNumber: '+81xxxxxxxxx'
accountSid
,authToken
は先ほどメモした値を設定します。fromNumber
には先ほど取得したTwilioの電話番号を設定します。toNumber
には認証済みの電話番号(アカウント登録時の電話番号)を設定します。
設定ファイルを修正したら以下のコマンドでzipパッケージを作成します。
npm install npm run build
SnsPhoneCall.zip
が出来たら成功です。
Lambdaアプリのデプロイ
AWS Management ConsoleにログインしAWS Lambdaのページへ移動します。 現在(2015/6)はまだ東京リージョンに作成できませんが夏には来るそうです。
項目名 | 設定値 |
---|---|
Name | sns-phone-call(任意) |
Runtime | NodeJS |
Code entry type | Upload a .ZIP file |
Handler | SnsPhoneCall.handler |
Role | Basic execution role |
Upload
ボタンから先ほど作成したSnsPhoneCall.zip
をアップロードして、
Create Lambda Function
ボタンをクリックします。
SNS通知の作成
Amazon SNSのページへ移動してCreate new topic
からトピックを作成します。
作成したらActions > Suscribe to topic
から配信先を登録します。
Protocol
にAWS Lambda
を選択すると先ほど作成したLambdaアプリが表示されます。
Create Subscription
をクリックしたら準備完了です。
動作確認
先ほど作成したSNSトピックを選択しPublish to toipc
をクリックします。
Message
に喋らせたい内容を記述してPublish message
をクリックします。
設定ファイルのtoNumber
に設定した電話番号宛に電話が届いたら成功です。
まとめ
Lambdaを使用することでEC2なしでTwilioAPIをコールする仕組みができました。 Lambdaアプリの処理はnode.jsで記述しているためSNS通知の内容を読んで キーワードでフィルタしたり宛先を振り分けたりカスタマイズすると面白いかなと思います。
他のアカウントへS3バケットへのアクセスを許可する
クロスアカウントと外部ID
このあたりに書いてあることを実践してみます。
外部 ID について - AWS Security Token Service
一時的なセキュリティ認証情報を使用して AWS リソースをリクエストする - AWS Security Token Service
準備
アクセス元のアカウントIDを調べます。
ここではアクセス元を999999999999
としS3バケットがある方を111111111111
とします。
S3バケットを持つアカウントの方にIAMにロールを作成
- ロール名を入力
ここではcross-account
としました。
- アクセス元のアカウントIDを入力
- ポリシーを選択
ここではSelectPolicyTemplateからAmazon S3 Read Only Access
を選択し、
そのまま進んでロールを作成しました。
アクセス元のアカウントからアクセスしてみる
- アクセス元のアカウントにEC2を立ててそこからアクセスしてみます。
sshログインしたら試しにS3のバケット一覧を取得してみます。
$ ssh ec2-user@ec2-54-65-xxx-xxx.ap-northeast-1.compute.amazonaws.com -i key.pem [ec2-user@ip-172-31-24-163 ~]$ aws s3 ls 2014-12-23 07:25:37 cf-templates-xxxxxxxxxxxxx-ap-northeast-1 2015-01-11 03:32:18 elasticbeanstalk-ap-northeast-1-999999999999 2015-01-05 14:42:54 yustam-artifact-repository 2014-11-07 15:21:07 yustam-docker-registry 2014-12-11 09:52:14 yustam-lambda-sample 2014-11-14 12:26:45 yustam-private
ロールを付与しておくとここではアクセス元アカウントのバケットが確認できます。
- STSのAssumeRoleでセッションを作成
ここではaws-cli
を使用してクロスアカウントアクセスを利用します。
sts assume-role
を使用してS3バケットアクセス用のセッションを作成します。
[ec2-user@ip-172-31-24-163 ~]$ aws sts assume-role --role-arn arn:aws:iam::111111111111:role/cross-account --role-session-name hoge { "AssumedRoleUser": { "AssumedRoleId": "AROAJWSTZ4KKB2EXAMPLE:hoge", "Arn": "arn:aws:sts::111111111111:assumed-role/cross-account/hoge" }, "Credentials": { "SecretAccessKey": "EmlBUyGcOKPI/Sdsf4RCE56sHnbo6XfJkEXAMPLE", "SessionToken": "AQoDYXdzECUagAKpFP2OknLKXsPLl/801n...(省略)", "Expiration": "2015-02-07T05:19:53Z", "AccessKeyId": "ASIAJ2GNOUN4ZEXAMPLE" } }
- 作成したセッションを利用してS3バケットにアクセスしてみる
先ほどのレスポンスに記載されたアクセスキー/シークレットキー/トークンを使用します。
[ec2-user@ip-172-31-24-163 ~]$ export AWS_ACCESS_KEY_ID=ASIAJ2GNOUN4ZEXAMPLE [ec2-user@ip-172-31-24-163 ~]$ export AWS_SECRET_ACCESS_KEY=EmlBUyGcOKPI/Sdsf4RCE56sHnbo6XfJkEXAMPLE [ec2-user@ip-172-31-24-163 ~]$ export AWS_SECURITY_TOKEN=AQoDYXdzECUagAKpFP2OknLKXsPLl/801n...(省略) [ec2-user@ip-172-31-24-163 ~]$ aws s3 ls 2013-02-15 08:04:27 private.yustam.jp 2013-01-21 05:53:07 www.robotjohn.jp 2013-07-10 01:08:47 www.yustam.jp 2013-10-27 02:28:07 yustam.convert
S3バケットの一覧を取得するとアクセス先アカウントのバケット一覧が取得できます。
Javaから利用する場合は以下のようにAssumeRoleしてcom.amazonaws.auth.BasicSessionCredentials
を作成すればいいみたいです。
package jp.yustam.s3.ca; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicSessionCredentials; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.securitytoken.AWSSecurityTokenService; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.securitytoken.model.AssumeRoleRequest; import com.amazonaws.services.securitytoken.model.AssumeRoleResult; import com.amazonaws.services.securitytoken.model.Credentials; import org.junit.Test; public class CrossAccountTest { static final String ROLE_ARN = "arn:aws:iam::111111111111:role/cross-account"; static final String ROLE_SESSION_NAME = "hoge"; @Test public void testAccessS3BucketOnAnotherAccount() { AWSSecurityTokenService sts = new AWSSecurityTokenServiceClient(); AssumeRoleResult result = sts.assumeRole(new AssumeRoleRequest() .withRoleArn(ROLE_ARN) .withRoleSessionName(ROLE_SESSION_NAME)); Credentials credentials = result.getCredentials(); AWSCredentials sessionCredentials = new BasicSessionCredentials(credentials.getAccessKeyId(), credentials.getSecretAccessKey(), credentials.getSessionToken()); AmazonS3 s3 = new AmazonS3Client(sessionCredentials); for (Bucket bucket : s3.listBuckets()) { System.out.println(bucket); } } }
Javaからもっと簡単に使う(3/5更新)
Credentialsを毎回生成するのもイケてないし有効期限があるから管理めんどうだなと
思ってたらSTSAssumeRoleSessionCredentialsProvider
という便利なものが既に用意されていた。
AWSCredentialsProvider cp = new STSAssumeRoleSessionCredentialsProvider(ROLE_ARN, ROLE_SESSION_NAME); AmazonS3 s3 = new AmazonS3Client(cp);
re:Invent 2014スライドのメモ
re:Invent 2014のスライドが少しずつ登録されているようなので気になるものをメモ。
サービス別
Amazon Key Management Service
(SEC301) Encryption and Key Management in AWS | AWS re:Invent 2014
(SEC406) NEW LAUNCH: Building Secure Applications with AWS Key Manage…
Amazon Aurora
(SDD415) NEW LAUNCH: Amazon Aurora: Amazon’s New Relational Database …
CloudFormation
(APP306) Using AWS CloudFormation for Deployment and Management at Sc…
EC2
(SDD406) Amazon EC2 Instances Deep Dive | AWS re:Invent 2014
IAM
(SEC305) IAM Best Practices | AWS re:Invent 2014
Netflix
(DEV309) From Asgard to Zuul: How Netflix’s Proven Open Source Tools …
(BDT403) Netflix's Next Generation Big Data Platform | AWS re:Invent …
Hybrid
(ARC203) Expanding Your Data Center with Hybrid Infrastructure | AWS …
(ENT401) Hybrid Infrastructure Integration | AWS re:Invent 2014
その他
(DEV302) Tips, Tricks, and Best Practices for the AWS SDK for Java | …
(BAC304) Deploying a Disaster Recovery Site on AWS: Minimal Cost with…
(ARC306) IoT: Small Things and the Cloud | AWS re:Invent 2014
(ARC307) Infrastructure as Code | AWS re:Invent 2014
(SDD409) Amazon RDS for PostgreSQL Deep Dive | AWS re:Invent 2014
整理中...
(SDD408) Amazon Route 53 Deep Dive: Delivering Resiliency, Minimizing…
(PFC403) Maximizing Amazon S3 Performance | AWS re:Invent 2014
(PFC402) Bigger, Faster: Performance Tips for High Speed and High Vol…
(PFC306) Performance Tuning Amazon EC2 Instances | AWS re:Invent 2014
(PFC303) Milliseconds Matter: Design, Deploy, and Operate Your Applic…
(MED303) Secure Media Streaming and Delivery | AWS re:Invent 2014
(MBL202) NEW LAUNCH: Getting Started with AWS Lambda | AWS re:Invent …
(DEV308) Automating Your Software Delivery Pipeline | AWS re:Invent 2…
(ARC318) Continuous Delivery at a Rate of 500 Deployments a Day! | AW…
(ARC402) Deployment Automation: From Developers' Keyboards to End Use…
(ARC311) Extreme Availability for Mission-Critical Applications | AWS…
(ARC304) Designing for SaaS: Next-Generation Software Delivery Models…
(APP310) Scheduling Using Apache Mesos in the Cloud | AWS re:Invent 2…
(ARC203) Expanding Your Data Center with Hybrid Infrastructure | AWS …
(APP313) NEW LAUNCH: Amazon EC2 Container Service in Action | AWS re:…
(APP303) Lightning Fast Deploys with Docker Containers and AWS | AWS …
(APP308) Chef on AWS: Deep Dive | AWS re:Invent 2014
(SPOT303) Building Mission Critical Database Applications: A Conversa…
(WEB301) Operational Web Log Analysis | AWS re:Invent 2014
Enterprise Security Considerations
[(ARC311) Extreme Availability for Mission-Critical Applications | AWS…
(APP204) NEW LAUNCH: Introduction to AWS Service Catalog | AWS re:Inv…
(APP304) AWS CloudFormation Best Practices | AWS re:Invent 2014
(APP307) Leverage the Cloud with a Blue/Green Deployment Architecture…
(ARC312) Processing Money in the Cloud | AWS re:Invent 2014
(ARC202) Real-World Real-Time Analytics | AWS re:Invent 2014
(ENT209) Netflix Cloud Migration, DevOps and Distributed Systems | AW…
(SDD407) Amazon DynamoDB: Data Modeling and Scaling Best Practices | …
(SDD405) Amazon Kinesis Deep Dive | AWS re:Invent 2014
(SDD401) Amazon Elastic MapReduce Deep Dive and Best Practices | AWS …
(SDD424) Simplifying Scalable Distributed Applications Using DynamoDB…
(SDD416) Amazon EBS Deep Dive | AWS re:Invent 2014
(SDD413) Amazon S3 Deep Dive and Best Practices | AWS re:Invent 2014
(SPOT305) Event-Driven Computing on Change Logs in AWS | AWS re:Inven…
(SDD418) Amazon CloudWatch Deep Dive | AWS re:Invent 2014
(SDD423) Elastic Load Balancing Deep Dive and Best Practices | AWS re…
CloudFormationのテンプレートをVelocityで生成する
CloudFormationのテンプレートは変数を定義したり他のコンポーネントを参照させたり結構いろいろなことが出来ますが汎用的に書こうとすると複雑になってしまい慣れてない人だと読みづらいテンプレートになってしまうため、 テンプレートのテンプレートを作成してApache VelocityでAMIやスナップショットIDなど
注入する方がメンテナンスも楽かなと思い試してみました。
要するにMappingsとParametersを使用せずにVelocityのテンプレートを書いてJavaから変数を渡します。
Velocityに限らずのテンプレートエンジンだとコメントも書き放題なのでメンテナンスを考えると何かしらテンプレートエンジンを使用する方がいい気がします。
他にCloudFormationのテンプレートに向いているテンプレートエンジンがあれば教えて欲しいです。
テンプレート
セキュリティグループにEC2インスタンス×1+EBSボリューム×1(ファイル用)+EBSボリューム×N(DB用)みたいな簡単な構成です。
velocity-cfn-template-sample.template.vm
Javaコード
VelocityContext context = new VelocityContext(); context.put("description", new String("this is my sample template.")); context.put("availability_zone", new String("ap-notrheast-1")); context.put("instance", new Instance("ami-00000000", "SampleInstance", "t2.micro")); context.put("volume", new Snapshot(null, "File", "standard", "/dev/sdf", "/mnt/file", 10)); List<Ingress> ingress = new ArrayList<>(); ingress.add(new Ingress("tcp", 22, 22, "XXX.XXX.XXX.XXX")); ingress.add(new Ingress("tcp", 5432, 5432, "XXX.XXX.XXX.XXX")); context.put("security_group", new SecurityGroup("SampleSG", "this is a sample.", ingress)); List<Snapshot> snapshots = new ArrayList<>(); snapshots.add(new Snapshot("snap-00000001", "DB01", "standard", "/dev/sdh", "/var/lib/pgsql9/data1", 10)); snapshots.add(new Snapshot("snap-00000002", "DB02", "standard", "/dev/sdi", "/var/lib/pgsql9/data2", 10)); snapshots.add(new Snapshot("snap-00000003", "DB03", "standard", "/dev/sdj", "/var/lib/pgsql9/data3", 10)); context.put("snapshots", snapshots); Velocity.addProperty("input.encoding", "UTF-8"); Velocity.addProperty("output.encoding", "UTF-8"); Velocity.addProperty("resource.loader", "class"); Velocity.addProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); Velocity.init(); Template template = Velocity.getTemplate(name); StringWriter sw = new StringWriter(); template.merge(context, sw); System.out.println(sw.toString());