現在のインスタンス料金を取得する script: RDS 編

http://qiita.com/bells17/items/5326d11edc6acc4feea2
の RDS 版です

注意点として取得するデータのエンジンを Aurora に絞ってます

rds.rb
require 'json'
require 'bigdecimal'

results = {}
json_data = open(ARGV[0]) {|io| JSON.load(io) }

# product 情報を取得
json_data['products'].keys.each do |skuNo|
    product = json_data['products'][skuNo]

    if (product['productFamily'] == 'Database Instance' and
          product['attributes']['locationType'] == 'AWS Region' and
          product['attributes']['location'] == 'Asia Pacific (Tokyo)' and
          product['attributes']['databaseEngine'] == 'Amazon Aurora') # Aurora だけに絞ってます (エンジンが mysql か postgresql かは無いっぽい??)

        results[product['sku']] = {
            sku: product['sku'],
            location: product['attributes']['location'],
            instanceType: product['attributes']['instanceType'],
            instanceFamily: product['attributes']['instanceFamily'],
            vcpu: product['attributes']['vcpu'],
            physicalProcessor: product['attributes']['physicalProcessor'],
            clockSpeed: product['attributes']['clockSpeed'],
            memory: product['attributes']['memory'],
            networkPerformance: product['attributes']['networkPerformance'],
            currentGeneration: product['attributes']['currentGeneration'],
            price_unit: 'USD'
        }

    end
end


# price

# on demand
json_data['terms']['OnDemand'].keys.each do |skuNo|
    if (results[skuNo])
        results[skuNo][:price_per_hour] = Proc.new {
            skuTerm = json_data['terms']['OnDemand'][skuNo][json_data['terms']['OnDemand'][skuNo].keys[0]]
            priceInfo = skuTerm['priceDimensions'][skuTerm['priceDimensions'].keys[0]]
            BigDecimal(priceInfo['pricePerUnit']['USD']).floor(2).to_f.to_s
        }.call
        results[skuNo][:price_per_day] = (BigDecimal(results[skuNo][:price_per_hour]) * BigDecimal("24")).floor(2).to_f.to_s
        results[skuNo][:price_per_month] = (BigDecimal(results[skuNo][:price_per_day]) * BigDecimal("30")).floor(2).to_f.to_s
    end
end

## reserved 
json_data['terms']['Reserved'].keys.each do |skuNo|
    if (results[skuNo])

        plans = json_data['terms']['Reserved'][skuNo].values.select do |plan|
            plan['termAttributes']['PurchaseOption'] == "All Upfront" # "All Upfront" のものだけ取得したい
        end

        results[skuNo][:price_reserved_1year_purchased_all_upfront] = plans.find { |plan|
            plan['termAttributes']['LeaseContractLength'] == '1yr'
        }['priceDimensions'].values.find {|priceDimension|
            priceDimension['description'] == "Upfront Fee"
        }['pricePerUnit']['USD']

        results[skuNo][:price_reserved_3year_purchased_all_upfront] = plans.find { |plan|
            plan['termAttributes']['LeaseContractLength'] == '3yr'
        }['priceDimensions'].values.find {|priceDimension|
            priceDimension['description'] == "Upfront Fee"
        }['pricePerUnit']['USD']

    end
end

# sort
sorted_result = {}
results.values.each do |row|
    sorted_result[row[:currentGeneration]] ||= {}
    sorted_result[row[:currentGeneration]][row[:instanceFamily]] ||= []
    sorted_result[row[:currentGeneration]][row[:instanceFamily]].push row
end

results = []
['Yes', 'No'].each do |currentGeneration| # 現行世代のものから並べる
    next unless sorted_result[currentGeneration]
    sorted_result[currentGeneration].keys.sort.each do |instanceFamily| # インスタンスファミリー毎に並べる
        results.concat sorted_result[currentGeneration][instanceFamily].sort_by { |row| row[:price_per_hour] }
    end
end

p results.to_json

上記を保存して以下のように実行する

curl https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonRDS/current/index.json > price-AmazonRDS.json
ruby rds.rb price-AmazonRDS.json | sed -e s/^"// | sed -e s/"$// | sed -e 's/\"/"/g' | jq .

以下のような結果が取れる

[
  {
    "sku": "H7JQN46Z6VDZ3K5V",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "db.t2.small",
    "instanceFamily": "General purpose",
    "vcpu": "1",
    "physicalProcessor": "Intel Xeon Family",
    "clockSpeed": "Up to 3.3 GHz",
    "memory": "2 GiB",
    "networkPerformance": "Low to Moderate",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "0.06",
    "price_per_day": "1.44",
    "price_per_month": "43.2",
    "price_reserved_1year_purchased_all_upfront": "403",
    "price_reserved_3year_purchased_all_upfront": "776"
  },
  {
    "sku": "MK8ETWDCPSK52PEV",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "db.t2.medium",
    "instanceFamily": "General purpose",
    "vcpu": "2",
    "physicalProcessor": "Intel Xeon Family",
    "clockSpeed": "Up to 3.3 GHz",
    "memory": "4 GiB",
    "networkPerformance": "Low to Moderate",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "0.12",
    "price_per_day": "2.88",
    "price_per_month": "86.4",
    "price_reserved_1year_purchased_all_upfront": "792",
    "price_reserved_3year_purchased_all_upfront": "1530"
  },
  {
    "sku": "8Z6GS5F6NKX37Q5E",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "db.r3.large",
    "instanceFamily": "Memory optimized",
    "vcpu": "2",
    "physicalProcessor": "Intel Xeon E5-2670 v2 (Ivy Bridge)",
    "clockSpeed": "2.5 GHz",
    "memory": "15.25 GiB",
    "networkPerformance": "Moderate",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "0.35",
    "price_per_day": "8.4",
    "price_per_month": "252.0",
    "price_reserved_1year_purchased_all_upfront": "1704",
    "price_reserved_3year_purchased_all_upfront": "3433"
  },
  {
    "sku": "PQP78BGE4C2HXDQF",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "db.r3.xlarge",
    "instanceFamily": "Memory optimized",
    "vcpu": "4",
    "physicalProcessor": "Intel Xeon E5-2670 v2 (Ivy Bridge)",
    "clockSpeed": "2.5 GHz",
    "memory": "30.5 GiB",
    "networkPerformance": "Moderate",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "0.7",
    "price_per_day": "16.8",
    "price_per_month": "504.0",
    "price_reserved_1year_purchased_all_upfront": "3408",
    "price_reserved_3year_purchased_all_upfront": "6867"
  },
  {
    "sku": "2WTMTR9HDDT7AA73",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "db.r3.2xlarge",
    "instanceFamily": "Memory optimized",
    "vcpu": "8",
    "physicalProcessor": "Intel Xeon E5-2670 v2 (Ivy Bridge)",
    "clockSpeed": "2.5 GHz",
    "memory": "61 GiB",
    "networkPerformance": "High",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "1.4",
    "price_per_day": "33.6",
    "price_per_month": "1008.0",
    "price_reserved_1year_purchased_all_upfront": "6815",
    "price_reserved_3year_purchased_all_upfront": "13733"
  },
  {
    "sku": "VRNJP9SPPRH2KM8M",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "db.r3.4xlarge",
    "instanceFamily": "Memory optimized",
    "vcpu": "16",
    "physicalProcessor": "Intel Xeon E5-2670 v2 (Ivy Bridge)",
    "clockSpeed": "2.5 GHz",
    "memory": "122 GiB",
    "networkPerformance": "High",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "2.8",
    "price_per_day": "67.2",
    "price_per_month": "2016.0",
    "price_reserved_1year_purchased_all_upfront": "13631",
    "price_reserved_3year_purchased_all_upfront": "27466"
  },
  {
    "sku": "NC3BZ293ZJFBVUT5",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "db.r3.8xlarge",
    "instanceFamily": "Memory optimized",
    "vcpu": "32",
    "physicalProcessor": "Intel Xeon E5-2670 v2 (Ivy Bridge)",
    "clockSpeed": "2.5 GHz",
    "memory": "244 GiB",
    "networkPerformance": "10 Gigabit",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "5.6",
    "price_per_day": "134.4",
    "price_per_month": "4032.0",
    "price_reserved_1year_purchased_all_upfront": "27261",
    "price_reserved_3year_purchased_all_upfront": "54932"
  }
]

続きを読む

Amazon RDSの情報からmysql_config_editorを一気に設定するスニペット

AWS CLIとJQを使ってmysql_config_editorのコマンドを生成するスニペットです。
PWは自分で入れる感じです。

$ aws rds describe-db-instances | jq -r '.DBInstances[] | ["mysql_config_editor set", "--login_path="+.DBInstanceIdentifier, "--host="+.Endpoint.Address, "--user="+.MasterUsername, "--password"] | join(" ")' | xargs -I{} sh -c 'echo {}; {}'
mysql_config_editor set --login_path=hogedb --host=hogedb.aaaaaaaa.ap-northeast-1.rds.amazonaws.com --user=masteruser --password
Enter password:

続きを読む

現在のインスタンス料金を取得する script: Redshift 編

今までリザーブド購入のために料金をいちいち AWS の料金ページまでアクセスして料金を確認してたんだけど AWS Price List API (http://docs.aws.amazon.com/ja_jp/awsaccountbilling/latest/aboutv2/price-changes.html) を利用して現在の料金の取得を自動化したかったので現在のインスタンス料金を取得する script を書いてみた

1111111料金_-_Amazon_Redshift___AWS.png

とりあえずインスタンスの種類が少なくて楽そうだったので今回は Redshift のものを対象に取得

前提条件として

  • Tokyo リージョンだけわかればよかったので Tokyo リージョンだけに絞ってる
  • 時間あたりの料金がわかる
  • リザーブドインスタンスの時の料金がわかる
  • インスタンスのスペックがわかる

あたりがわかるように書いた

とりあえず動いたものを貼ってるのでコードはきれいじゃない

redshift.rb
require 'json'
require 'bigdecimal'

results = {}
json_data = open(ARGV[0]) {|io| JSON.load(io) }

# product 情報を取得
json_data['products'].keys.each do |skuNo|
    product = json_data['products'][skuNo]

    if (product['productFamily'] == 'Compute Instance' and
          product['attributes']['locationType'] == 'AWS Region' and
          product['attributes']['location'] == 'Asia Pacific (Tokyo)')

        results[product['sku']] = {
            sku: product['sku'],
            location: product['attributes']['location'],
            instanceType: product['attributes']['instanceType'],
            instanceFamily: product['attributes']['instanceType'].split('.')[0],
            vcpu: product['attributes']['vcpu'],
            memory: product['attributes']['memory'],
            storage: product['attributes']['storage'],
            io: product['attributes']['io'],
            ecu: product['attributes']['ecu'],
            currentGeneration: product['attributes']['currentGeneration'],
            price_unit: 'USD'
        }

    end
end


# price

## on demand
json_data['terms']['OnDemand'].keys.each do |skuNo|
    if (results[skuNo])
        results[skuNo][:price_per_hour] = Proc.new {
            skuTerm = json_data['terms']['OnDemand'][skuNo][json_data['terms']['OnDemand'][skuNo].keys[0]]
            priceInfo = skuTerm['priceDimensions'][skuTerm['priceDimensions'].keys[0]]
            BigDecimal(priceInfo['pricePerUnit']['USD']).floor(2).to_f.to_s
        }.call
        results[skuNo][:price_per_day] = (BigDecimal(results[skuNo][:price_per_hour]) * BigDecimal("24")).floor(2).to_f.to_s
        results[skuNo][:price_per_month] = (BigDecimal(results[skuNo][:price_per_day]) * BigDecimal("30")).floor(2).to_f.to_s
    end
end


## reserved 
json_data['terms']['Reserved'].keys.each do |skuNo|
    if (results[skuNo])

        plans = json_data['terms']['Reserved'][skuNo].values.select do |plan|
            plan['termAttributes']['PurchaseOption'] == "All Upfront" # "All Upfront" のものだけ取得したい
        end

        results[skuNo][:price_reserved_1year_purchased_all_upfront] = plans.find { |plan|
            plan['termAttributes']['LeaseContractLength'] == '1yr'
        }['priceDimensions'].values.find {|priceDimension|
            priceDimension['description'] == "Upfront Fee"
        }['pricePerUnit']['USD']

        results[skuNo][:price_reserved_3year_purchased_all_upfront] = plans.find { |plan|
            plan['termAttributes']['LeaseContractLength'] == '3yr'
        }['priceDimensions'].values.find {|priceDimension|
            priceDimension['description'] == "Upfront Fee"
        }['pricePerUnit']['USD']

    end
end


# sort
sorted_result = {}
results.values.each do |row|
    sorted_result[row[:currentGeneration]] ||= {}
    sorted_result[row[:currentGeneration]][row[:instanceFamily]] ||= []
    sorted_result[row[:currentGeneration]][row[:instanceFamily]].push row
end

results = []
['Yes', 'No'].each do |currentGeneration| # 現行世代のものから並べる
    sorted_result[currentGeneration].keys.sort.each do |instanceFamily| # インスタンスファミリー毎に並べる
        results.concat sorted_result[currentGeneration][instanceFamily].sort_by { |row| row[:price_per_hour] }
    end
end

# sorted_result.values.each do |targetGenerationInstances|
#   targetGenerationInstances.keys.sort.each { |instanceFamily|
#       # binding.pry
#       results.concat targetGenerationInstances[instanceFamily].sort_by { |row| row[:price_per_hour]}
#   }
# end

p results.to_json

上記を保存して以下のように実行する

curl https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonRedshift/current/index.json > price-AmazonRedshift.json
ruby redshift.rb price-AmazonRedshift.json | sed -e s/^\"// | sed -e s/\"$// | sed -e 's/\\"/"/g' | jq .

以下のような結果が取れる

[
  {
    "sku": "6REDMMEE7FXXH5Y6",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "dc1.large",
    "instanceFamily": "dc1",
    "vcpu": "2",
    "memory": "15 GiB",
    "storage": "0.16TB SSD",
    "io": "0.20 GB/s",
    "ecu": "7",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "0.31",
    "price_per_day": "8",
    "price_per_month": "240",
    "price_reserved_1year_purchased_all_upfront": "1645",
    "price_reserved_3year_purchased_all_upfront": "2885"
  },
  {
    "sku": "CNP4R2XZ8N7RJJA8",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "dc1.8xlarge",
    "instanceFamily": "dc1",
    "vcpu": "32",
    "memory": "244 GiB",
    "storage": "2.56TB SSD",
    "io": "3.70 GB/s",
    "ecu": "104",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "6.09",
    "price_per_day": "147",
    "price_per_month": "4410",
    "price_reserved_1year_purchased_all_upfront": "33180",
    "price_reserved_3year_purchased_all_upfront": "46160"
  },
  {
    "sku": "YWHTRJBA2KAFS857",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "ds2.xlarge",
    "instanceFamily": "ds2",
    "vcpu": "4",
    "memory": "31 GiB",
    "storage": "2TB HDD",
    "io": "0.40 GB/s",
    "ecu": "14",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "1.19",
    "price_per_day": "29",
    "price_per_month": "870",
    "price_reserved_1year_purchased_all_upfront": "6125",
    "price_reserved_3year_purchased_all_upfront": "7585"
  },
  {
    "sku": "Q8X9U7UKTJV2VGY8",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "ds2.8xlarge",
    "instanceFamily": "ds2",
    "vcpu": "36",
    "memory": "244 GiB",
    "storage": "16TB HDD",
    "io": "3.30 GB/s",
    "ecu": "116",
    "currentGeneration": "Yes",
    "price_unit": "USD",
    "price_per_hour": "9.52",
    "price_per_day": "229",
    "price_per_month": "6870",
    "price_reserved_1year_purchased_all_upfront": "49020",
    "price_reserved_3year_purchased_all_upfront": "60630"
  },
  {
    "sku": "ZURKE2HZ3JZC6F2U",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "ds1.xlarge",
    "instanceFamily": "ds1",
    "vcpu": "2",
    "memory": "15 GiB",
    "storage": "2TB HDD",
    "io": "0.30 GB/s",
    "ecu": "4.4",
    "currentGeneration": "No",
    "price_unit": "USD",
    "price_per_hour": "1.19",
    "price_per_day": "29",
    "price_per_month": "870",
    "price_reserved_1year_purchased_all_upfront": "6125",
    "price_reserved_3year_purchased_all_upfront": "7585"
  },
  {
    "sku": "PDMPNVN5SPA5HWHH",
    "location": "Asia Pacific (Tokyo)",
    "instanceType": "ds1.8xlarge",
    "instanceFamily": "ds1",
    "vcpu": "16",
    "memory": "120 GiB",
    "storage": "16TB HDD",
    "io": "2.40 GB/s",
    "ecu": "35",
    "currentGeneration": "No",
    "price_unit": "USD",
    "price_per_hour": "9.52",
    "price_per_day": "229",
    "price_per_month": "6870",
    "price_reserved_1year_purchased_all_upfront": "49020",
    "price_reserved_3year_purchased_all_upfront": "60630"
  }
]

続きを読む

既存のDynamoDBのテーブルをjsonフォーマットに変換するワンライナー

はじめに

  • AWS cliでテーブル作っていたのですが、jsonで作っておくべきだった・・・。と大変後悔しました。
  • 既存のDynamoDBのテーブルからjsonを出力するスクリプトの記事を見つけたのですが、GlobalSecondaryIndex、LocalSecondaryIndexが無いテーブルの場合はエラーになってしまいますので少し改良したのでご紹介します。
  • このスクリプトをうまく利用することでテーブルの差分をみるのに使えたりするのでオススメです。

スクリプト

  • ここではDynamoDBLocalにあるAssetというテーブルをAsset.jsonというファイルに出力しています。
  • describe-tableでは余計なものがいっぱいついてくるので要素を削除しています。
aws dynamodb describe-table --table-name Asset --endpoint-url http://localhost:8000 |
jq '.Table' |
jq 'del(.TableArn)' |
jq 'del(.GlobalSecondaryIndexes[]?.ItemCount)' |
jq 'del(.GlobalSecondaryIndexes[]?.IndexStatus)' |
jq 'del(.GlobalSecondaryIndexes[]?.IndexArn)' |
jq 'del(.GlobalSecondaryIndexes[]?.IndexSizeBytes)' |
jq 'del(.GlobalSecondaryIndexes[]?.ProvisionedThroughput.NumberOfDecreasesToday)' |
jq 'del(.GlobalSecondaryIndexes[]?.ProvisionedThroughput.LastIncreaseDateTime)' |
jq 'del(.GlobalSecondaryIndexes[]?.ProvisionedThroughput.LastDecreaseDateTime)' |
jq 'del(.LocalSecondaryIndexes[]?.IndexStatus)' |
jq 'del(.LocalSecondaryIndexes[]?.IndexArn)' |
jq 'del(.LocalSecondaryIndexes[]?.ItemCount)' |
jq 'del(.LocalSecondaryIndexes[]?.IndexSizeBytes)' |
jq 'del(.LocalSecondaryIndexes[]?.ProvisionedThroughput.NumberOfDecreasesToday)' |
jq 'del(.LocalSecondaryIndexes[]?.ProvisionedThroughput.LastIncreaseDateTime)' |
jq 'del(.LocalSecondaryIndexes[]?.ProvisionedThroughput.LastDecreaseDateTime)' |
jq 'del(.ProvisionedThroughput.NumberOfDecreasesToday)' |
jq 'del(.ProvisionedThroughput.LastIncreaseDateTime)' |
jq 'del(.ProvisionedThroughput.LastDecreaseDateTime)' |
jq 'del(.TableSizeBytes)' |
jq 'del(.TableStatus)' |
jq 'del(.ItemCount)' |
jq 'del(.IndexArn)' |
jq 'del(.CreationDateTime)' > Asset.json

出力されたスクリプトを利用してcreate-tableする

aws dynamodb create-table --endpoint-url http://localhost:8000 --table-name Asset --cli-input-json file://Asset.json

続きを読む

dynamodb-autoscaling検証

概要

先日のアップデートでdynamodbでautoscalingの機能が実装されました。
https://aws.amazon.com/jp/blogs/news/new-auto-scaling-for-amazon-dynamodb/

NoSQLデータベースのベンチマークツールであるycsbを用いてdynamodb-autoscalingの動作を検証できます。
今回は新規に追加された項目「ターゲット使用率」がどのような影響を及ぼすかについて検証します。

環境準備

環境準備に伴い、下記を参考にさせていただきました。

http://qiita.com/noralife/items/35a956f682b1aca475f6
http://dev.classmethod.jp/cloud/aws/attack_to_dynamodb_using_ycsb/
http://imai-factory.hatenablog.com/entry/2013/04/05/010858

amazon linux version

[ec2-user@dynamo-ec2 ycsb-0.12.0]$ cat /etc/system-release
Amazon Linux AMI release 2017.03

openjdk-develインストール

$ sudo yum -y install java-1.7.0-openjdk-devel

ycsb導入

・ダウンロード
$ wget https://github.com/brianfrankcooper/YCSB/releases/download/0.12.0/ycsb-0.12.0.tar.gz

・展開、移動
$ tar xfz ycsb-0.12.0.tar.gz
$ cd ycsb-0.12.0/

・ファイル用のディレクトリ準備
$ mkdir -p dynamodb/conf/

・ベンチマーク対象テーブル(testtable)のプライマリキーを確認しておく
$ aws dynamodb describe-table --table-name testtable | jq -r '.Table.AttributeDefinitions[].AttributeName'
name

YCSB概要

テストデータをdynamodbに読み込ませて

./bin/ycsb.sh load dynamodb -P workloads/dyamodb -P dynamodb/conf/dynamodb.properties

テストデータを使って負荷をかける。

./bin/ycsb.sh run dynamodb -P workloads/dyamodb -P dynamodb/conf/dynamodb.properties

設定ファイルは下記の3つ

■dynamodb/conf/dynamodb.properties
対象のDynamoDBの情報を設定。変更する部分はプライマリキーくらいかと。
クレデンシャル情報が記載されたファイルもこの中で指定する。

$ cat dynamodb/conf/dynamodb.properties
dynamodb.awsCredentialsFile = dynamodb/conf/AWSCredentials.properties
dynamodb.primaryKey = name
dynamodb.endpoint = http://dynamodb.ap-northeast-1.amazonaws.com

■dynamodb/conf/AWSCredentials.properties
クレデンシャル情報を記載。

$ cat dynamodb/conf/AWSCredentials.properties
accessKey = XXXXXXXXXXXXXXXXXXXX
secretKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

■workloads/(任意のファイル名)
投入するデータ、実施するベンチマークのステータスを設定。
値はテンプレートworkloads/workload_templateを参考。
※operationcountは1,000,000以上が良いかと。10,000で実行したらスケーリングする前に終わってしまいました……

$ cat workloads/dyamodb
workload=com.yahoo.ycsb.workloads.CoreWorkload #デフォルト

recordcount=1000 #テーブルにセットするレコード数
operationcount=2000000 #load時のオペレーション数

insertstart=0 #デフォルト
fieldcount=10 #フィールド数、デフォルト
fieldlength=100 #レコードの長さ、デフォルト

readallfields=true #デフォルト
writeallfields=false #デフォルト

table=testtable #テーブル名

fieldlengthdistribution=constant #デフォルト

#load時のオペレーション比率(read80%, update15%, insert5%)
readproportion=0.8
updateproportion=0.15
insertproportion=0.05

#以下テンプレートの値のまま設定
readmodifywriteproportion=0
scanproportion=0
maxscanlength=1000
scanlengthdistribution=uniform
insertorder=hashed
requestdistribution=zipfian
hotspotdatafraction=0.2
hotspotopnfraction=0.8

measurementtype=histogram
histogram.buckets=1000
timeseries.granularity=1000

検証開始

初期のdynamodbキャパシティ設定

1000レコード書き込むので、書き込み容量ユニット(以下WCU)を10確保。
WCU1だとスロットルが発生して時間がかかります……。

初期設定.PNG

テーブルにテストデータを読み込ませる

1回だけ。10分ほどかかります。

$ ./bin/ycsb.sh load dynamodb -P workloads/dyamodb -P dynamodb/conf/dynamodb.properties

auto-scaling設定

いよいよauto-scaling設定です。
RCU/WCUを下げるオペレーションは最大9回/日になっていますが条件があります。
↓の記事を参考にさせていただきました。
http://qiita.com/mokrai/items/6864b8a723a2728565fc
検証する場合は無駄に下げないように初期値を考慮しましょう。

as1.PNG

・ターゲット使用率
スケーリングの基準になる値です。以降で検証します。

・IAMロール
[新しいロール]を選択すれば問題ありません。
下記の管理ポリシーが付与されたロールが作成されます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DescribeTable",
                "dynamodb:UpdateTable",
                "cloudwatch:PutMetricAlarm",
                "cloudwatch:DescribeAlarms",
                "cloudwatch:GetMetricStatistics",
                "cloudwatch:SetAlarmState",
                "cloudwatch:DeleteAlarms"
            ],
            "Resource": "*"
        }
    ]
}

ロールからもわかるように、auto-scalingはcloudwatchの値を評価の基準としています。
auto-scaling設定中は動的なcloudwatchメトリクスが作成され、auto-scaling設定解除後に削除されます。

ターゲット使用率70%の場合

下記設定でcloudwatchの推移を確認します。

auto-scaling設定 RCU WCU
ターゲット使用率(%) 70 70
min(ユニット) 10 10
max(ユニット) 10000 10000

キャパシティの推移

推移は下記の通り

RCU
70rcu.PNG

WCU
70wcu.PNG

ターゲット使用率20%の場合

下記設定でcloudwatchの推移を確認します。

auto-scaling設定 RCU WCU
ターゲット使用率(%) 20 20
min(ユニット) 10 10
max(ユニット) 10000 10000

20rcu.PNG

20wcu.PNG

終わりに

負荷のかけ方が一定の場合はキャパシティーの増減が安定したら変化しないので、
そのあたりの調整が必要だと感じました。
cloudwatchメトリクスが動的な値で作成され、それらが基準になってスケーリングされています。
サポートに確認したところ、閾値を超えてのキャパシティ設定もできるとのことですが、その場合は次のタイミングでスケーリングされてしまうとのことです(未検証)。

続きを読む

aws-cli コマンド一覧(随時追記)

概要

ついつい忘れがちなので自分で使ったことあるコマンドを随時追記していきます。

※全てのコマンドをまとめているわけではないので、当記事に書いていないコマンドについては公式ドキュメントをご覧ください。
公式ドキュメント

また、aws-cliのレスポンスは基本的にJsonのため(設定で変更可能らしいが)、 jq というJsonフォーマット用ツールを合わせて使うと幸せになれます。

設定ファイル

cliを利用する上では以下のファイルが必要。
aws-cliをインストール後に特定のコマンドを打つことで生成することが出来る。

cofig

cliを使う上での設定を記載したファイル。

~/.aws/config
[default]
output = json
region = ap-northeast-1

credentials

AWSに接続するための認証情報

~/.aws/config
[default]
output = json
region = ap-northeast-1

コマンド

configure

aws-cliを使う上でのファイルを生成する

configure

設定ファイルを生成します。
Document

command
aws configure

set

cliの設定を追加します。
Document

command
aws configure set preview.cloudfront true

iam

IAM関連を操作するコマンド

create-user

IAMユーザーを新規作成します。
Document

引数 概要
user-name IAMユーザー名
command
aws iam create-user \
--user-name sample-user

delete-user

IAMユーザーを削除します。
※ポリシーが設定されている場合はエラーになります。
Document

引数 概要
user-name IAMユーザー名
command
aws iam delete-user \
--user-name sample-user

get-user

IAMユーザーを取得します。
Document

引数 概要
user-name IAMユーザー名
command
aws iam get-user \
--user-name sample-user

put-user-policy

IAMユーザーにポリシーを追加します。
Document

引数 概要
user-name IAMユーザー名
policy-name ポリシー名
policy-document ポリシールールが記載されたJsonファイルのパス
command
aws iam put-user-policy \
--user-name sample-user \
--policy-name sample-policy \
--policy-document  file://user_policy.json
user_policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1501770149214",
      "Action": "cloudwatch:*",
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

※Amazonのポリシージェネレーターを使うことで作成可能です。
ポリシージェネレーター

delete-user-policy

IAMユーザーに設定されているポリシーを削除します。
Document

引数 概要
user-name IAMユーザー名
policy-name ポリシー名
command
aws iam delete-user-policy \
--user-name sample-user \
--policy-name sample-policy 

create-access-key

IAMユーザーのアクセスキーを新規作成します。
Document

引数 概要
user-name IAMユーザー名
command
aws iam create-access-key \
--user-name sample-user 

list-users

IAMユーザーの一覧を取得します。
Document

command
aws iam list-usera 

create-role

Roleを新規作成します。
Document

引数 概要
user-name IAMユーザー名
command
aws iam create-role \
--role-name sample-role \
--assume-role-policy-document file://role.json
role.json
{
  "Version": "2012-10-17",
  "Statement": [
     {
       "Action": "sts:AssumeRole",
       "Principal": {
         "Service": "lambda.amazonaws.com"
        },
        "Effect": "Allow",
        "Sid": ""
     }
  ]
}

attach-role-policy

Roleにポリシーを追加します。
Document

引数 概要
user-name IAMユーザー名
policy-arn リソース名
command
aws iam attach-role-policy \
--role-name sample-role \
--policy-arn "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"

s3

S3関連を操作するコマンド

mb

バケットを新規作成します。
Document

command
aws s3 mb s3://sample.bucket

website

バケットを静的Webサイトホスティング化する
Document

引数 概要
index-document ディレクトリに対してリクエストが来た際に自動で追加する接尾辞
command
aws s3 website s3://sample.bucket \
--index-document index.html

s3api

Amazon S3 API関連を操作するコマンド

put-bucket-notification-configuration

バケットにオブジェクトがPutされた時の通知設定
Document

引数 概要
bucket 対象バケット名
notification-configuration 通知設定ファイル
command
aws s3api put-bucket-notification-configuration \
--bucket sample.bucket \
--notification-configuration file://config.json
config.json
{
    "LambdaFunctionConfigurations": [
        {
            "LambdaFunctionArn": "arn:aws:lambda:xxxx",
            "Id": "sample_config",
            "Events": [
                "s3:ObjectCreated:*"
            ]
        }
    ]
}

cloudfront

CloudFront関連を操作するコマンド

create-distribution

CloudFrontを新規作成します。
Document

引数 概要
cli-input-json 新規作成用Jsonファイルのパス ※file://${パス}にすること
command
aws cloudfront create-distribution \
--cli-input-json file://distribution.json
distribution.json
{
  "Id": "",
  "IfMatch": "",
  "DistributionConfig": {
    "Comment": "${コメント}",
    "CacheBehaviors": {
      "Quantity": 0
    },
    "IsIPV6Enabled": true,
    "Logging": {
      "Bucket": "",
      "Prefix": "",
      "Enabled": false,
      "IncludeCookies": false
    },
    "WebACLId": "",
    "Origins": {
      "Items": [
        {
          "S3OriginConfig": {
            "OriginAccessIdentity": ""
          },
          "OriginPath": "",
          "CustomHeaders": {
            "Quantity": 0
          },
          "Id": "${ID}",
          "DomainName": "${ドメイン名}"
        }
      ],
      "Quantity": 1
    },
    "DefaultRootObject": "index.html",
    "PriceClass": "PriceClass_All",
    "Enabled": true,
    "DefaultCacheBehavior": {
      "TrustedSigners": {
        "Enabled": false,
        "Quantity": 0
      },
      "LambdaFunctionAssociations": {
        "Quantity": 0
      },
      "TargetOriginId": "${S3のバケット名}",
      "ViewerProtocolPolicy": "allow-all",
      "ForwardedValues": {
        "Headers": {
          "Quantity": 0
        },
        "Cookies": {
          "Forward": "all"
        },
        "QueryStringCacheKeys": {
          "Quantity": 0
        },
        "QueryString": true
      },
      "MaxTTL": 300,
      "SmoothStreaming": false,
      "DefaultTTL": 300,
      "AllowedMethods": {
        "Items": [
          "HEAD",
          "GET"
        ],
        "CachedMethods": {
          "Items": [
            "HEAD",
            "GET"
          ],
          "Quantity": 2
        },
        "Quantity": 2
      },
      "MinTTL": 300,
      "Compress": false
    },
    "CallerReference": "${一意となる文字列}",
    "ViewerCertificate": {
      "CloudFrontDefaultCertificate": true,
      "MinimumProtocolVersion": "SSLv3",
      "CertificateSource": "cloudfront"
    },
    "CustomErrorResponses": {
      "Quantity": 0
    },
    "HttpVersion": "http2",
    "Restrictions": {
      "GeoRestriction": {
        "RestrictionType": "none",
        "Quantity": 0
      }
    },
    "Aliases": {
      "Items": [
        "${エイリアス名}"
      ],
      "Quantity": 1
    }
  }
}

ec2

EC2関連を操作するコマンド

create-security-group

セキュリティグループを新規作成します。
Document

引数 概要
group-name セキュリティグループ名
description 説明
command
aws ec2 create-security-group \
--group-name sample-sg-group \
--description 'サンプル用のセキュリティグループ'

authorize-security-group-ingress

セキュリティグループのルールを定義します。
Document

引数 概要
group-name セキュリティグループ名
description 説明
port 対象のポート番号
cidr 許可するIPアドレス
command
aws ec2 authorize-security-group-ingress \
--group-name sample-sg-group \
--protocol tcp \
--port 22 \
--cidr "0.0.0.0/0" 

allocate-address

ElasticIPを新規作成します。
Document

引数 概要
domain vpsを使う際には「vpc」を指定する
command
aws ec2 allocate-address \
--domain vpc

run-instances

インスタンスを新規作成し起動します。
Document

引数 概要
image-id インスタンスのベースとなるマシンイメージ
count 起動するインスタンスの数
instance-type 作成するインスタンスの種別
key-name 使用するキーペア名
security-groups 紐付けるセキュリティブループ名(複数可)
command
aws ec2 run-instances \
--image-id ami-3bd3c45c \
--count 1 \
--instance-type t2.micro \
--key-name sample_key_pair \
--security-groups sample-sg-group

create-tags

対象のインスタンスにタグを新規追加します。
Document

引数 概要
resources 対象のインスタンスID
tags Key/Valueの形式で追加していく
instance-type 作成するインスタンスの種別
key-name 使用するキーペア名
security-groups 紐付けるセキュリティブループ名(複数可)
command
aws ec2 create-tags \
--resources i-xxxx \
--tags Key=Name,Value=sample-instance

associate-address

ElasticIPとインスタンスを紐付けます。
Document

引数 概要
allocation-id 対象のElasticIP ID
instance 対象のインスタンスID
command
aws ec2 associate-address \
--allocation-id eipalloc-xxxx \
--instance i-xxxx

release-address

ElasticIPを解放します。
Document

引数 概要
allocation-id 対象のElasticIP ID
command
aws ec2 release-address \
--allocation-id eipalloc-xxxx

modify-instance-attribute

インスタンスの属性を更新します。
Document

引数 概要
groups セキュリティブループID(複数可)
instance-id 対象のインスタンスID
command
aws ec2 modify-instance-attribute  \
--groups sample-sg-group  \
--instance-id i-xxxx

terminate-instances

インスタンスを停止して削除します。
Document

引数 概要
instance-ids 対象のインスタンスID(複数可)
command
aws ec2 terminate-instances \
--instance-ids i-xxxx

lambda

Lambda関連を操作するコマンド

create-function

Lambda関数を新規作成します。
Document

引数 概要
function-name Lambda関数名
zip-file zipパス
role 他のAWSサービスに接続するためのロール
handler 実行するためのコード内関数
runtime タイムアウトの定義(秒)
timeout アップロードするzipへのパス
memory-size Lambda関数実行時のメモリ割り当て
command
aws lambda create-function \
--function-name SampleFunction \
--zip-file fileb://SampleFunction.zip \
--role arn:aws:iam::xxxx:role/sample-role \
--handler SampleFunction.handler \
--runtime python2.7 \
--timeout 10 \
--memory-size 1024

delete-function

Lambda関数を削除します。
Document

引数 概要
function-name Lambda関数名
command
aws lambda delete-function \
--function-name SampleFunction 

add-permission

Lambda関数への権限を追加います。
Document

引数 概要
function-name Lambda関数名
statement-id 一意となるステートメント名
action 許可させるLambdaアクション
principal プリンシパルの設定
source-arn 関数呼び出しを許可するAmazonリソース名
command
aws lambda add-permission \
--function-name "sample-function" \
--statement-id "sample-statement" \
--action "lambda:InvokeFunction" \
--principal "s3.amazonaws.com" \
--source-arn "arn:aws:s3:::sample.bucket"

続きを読む

AnsibleでAWS操作 ②EC2インスタンス作成編

AnsibleでAWS操作シリーズ

  1. aws-cliインストール編
  2. EC2インスタンス作成編

やりたかったこと

  • EC2インスタンスに紐付けるSecurityGroupの作成/設定
  • EC2インスタンスに紐付けるElastic IPの作成/設定/削除
  • EC2インスタンスの作成/削除
  • Elastic IPの関連付け
  • GUIを使わずに黒い画面でコマンドを「ッターーン!」してかっこつけたい

やったこと

前提

  • CIサーバー(ansible実行サーバー)構築済み
  • CLIサーバー(aws-cli実行サーバー)構築済み
  • Ansibleインストール済み
  • aws-cliインストール済み
  • jqインストール済み
  • 各サーバーへのSSH接続設定済み

${~}は各環境に合わせて値を設定してください。

作業フロー

  1. create-aws-ec2-ssh-security-group.ymlを実行

    • 戻り値のGroupIdの値をvars/all.ymlのSECURITY_GROUP_IDに指定
  2. create-aws-ec2-instance.ymlを実行

    • 戻り値のInstanceIdAllocationIdをそれぞれINSTANCE_IDELASTIC_IP_IDに指定
  3. setup-aws-ec2-instance.ymlを実行

jqで取得した各種IDを使って一つのtaskでやろうと思いましたが、Instance作成直後はElasticIPの紐付けが出来ないっぽいので、playbookを分けています。

ディレクトリ構成


├── ansible.cfg
├── create-aws-ec2-instance.yml
├── create-aws-ec2-ssh-security-group.yml
├── inventory
│   └── production
│       └── inventory
├── roles
│   ├── create-aws-ec2-elastic-ip
│   │   └── tasks
│   │       └── main.yml
│   ├── create-aws-ec2-instance
│   │   └── tasks
│   │       └── main.yml
│   ├── create-aws-ec2-ssh-security-group
│   │   └── tasks
│   │       └── main.yml
│   ├── setup-aws-ec2-elastic-ip
│   │   └── tasks
│   │       └── main.yml
│   └── setup-aws-ec2-instance
│       └── tasks
│           └── main.yml
├── setup-aws-ec2-instance.yml
├── vars
│   └── all.yml
└── view-aws-ec2-instance.yml

Ansible構成ファイル

inventory

inventory/production/inventory
[ciservers]
${CIサーバーホスト}

[cliservers]
${CLIサーバーホスト}

[all:vars]
ENV=production

vars

vars/all.yml
AWS_EC2:
  SECURITY_GROUP_ID: ${セキュリティグループID}
  SECURITY_GROUP_NAME: ${セキュリティグループ名}
  SECURITY_GROUP_DESCRIPTION: ${セキュリティグループ概要}
  IP_ADDRESS: ${IPアドレス}
  KEY_PAIR_NAME: ${キーペア名}
  AMI_ID: ${AMI ID}
  INSTANCE_TYPE: ${インスタンスタイプ}
  INSTANCE_ID: ${インスタンスID}
  INSTANCE_NAME: ${インスタンス名}
  ALLOCATION_ID: ${Elastic IP ID}

playbook

create-aws-ec2-ssh-security-group.yml
- hosts: cliservers
  roles:
    - create-aws-ec2-ssh-security-group
  vars_files:
    - vars/all.yml
create-aws-ec2-instance.yml
- hosts: cliservers
  roles:
    - create-aws-ec2-instance
    - create-aws-ec2-elastic-ip
  vars_files:
    - vars/all.yml
setup-aws-ec2-instance.yml
- hosts: cliservers
  roles:
    - setup-aws-ec2-instance
    - setup-aws-ec2-elastic-ip
  vars_files:
    - vars/all.yml

tasks

role/create-aws-ec2-ssh-security-group/tasks/main.yml
- name: "Create SSH Security Group"
  shell: |
    aws ec2 create-security-group \
    --group-name {{ AWS_EC2.SECURITY_GROUP_NAME }} \
    --description '{{ AWS_EC2.SECURITY_GROUP_DESCRIPTION }}'
  register: result
  changed_when: False

- debug: var=result.stdout_lines
  when: result | success
  tags:
    - always

- name: "Setup SSH Security Group"
  shell: |
    aws ec2 authorize-security-group-ingress \
    --group-name {{ AWS_EC2.SECURITY_GROUP_NAME }} \
    --protocol tcp \
    --port 22 \
    --cidr "{{ AWS_EC2.IP_ADDRESS }}/0" \
    | jq '.'
  tags:
    - always
role/create-aws-ec2-instance/tasks/main.yml
- name: "Create Instanse"
  shell: |
    aws ec2 run-instances \
    --image-id {{ AWS_EC2.AMI_ID }} \
    --count 1 \
    --instance-type {{ AWS_EC2.INSTANCE_TYPE }}  \
    --key-name {{ AWS_EC2.KEY_PAIR_NAME }} \
    --security-groups {{ AWS_EC2.SECURITY_GROUP_NAME }} \
    | jq -r '.Instances[] | { InstanceId }'
  register: create_instance_result
  changed_when: False

- debug: var=create_instance_result.stdout_lines
  when: create_instance_result | success
  tags:
    - always
role/create-aws-ec2-elastic-ip/tasks/main.yml
- name: "Create Elastic IP"
  shell: |
    aws ec2 allocate-address \
    --domain vpc \
    | jq -r '{ AWS_EC2.ALLOCATION_ID }'
  register: result
  changed_when: False

- debug: var=result.stdout_lines
  when: result | success
  tags:
    - always
role/setup-aws-ec2-instance/tasks/main.yml
- name: "Create Tags"
  shell: |
    aws ec2 create-tags \
    --resources {{ AWS_EC2.INSTANCE_ID }} \
    --tags Key=Name,Value={{ AWS_EC2.INSTANCE_NAME }}
  register: result
  changed_when: False

- debug: var=result.stdout_lines # stdout => stdout_lines
  when: result | success
  tags:
    - always

role/setup-aws-ec2-elastic-ip/tasks/main.yml
- name: "Setup Elastic IP"
  shell: |
    aws ec2 associate-address \
    --allocation-id {{ AWS_EC2.ALLOCATION_ID }} \
    --instance {{ AWS_EC2.INSTANCE_ID }}
  register: result
  changed_when: False

- debug: var=result.stdout_lines # stdout => stdout_lines
  when: result | success
  tags:
    - always

実行コマンド

command
ansible-playbook -i inventory/production create-aws-ec2-ssh-security-group.yml
command
ansible-playbook -i inventory/production create-aws-ec2-instance.yml
command
ansible-playbook -i inventory/production setup-aws-ec2-instance.yml

正常に処理が終了したらインストール編で実行したplaybookを実行してみてください。
インスタンス一覧取得playbook

新しいインスタンスの情報が取得出来るはずです。

おまけ インスタンス及びElasticIPの削除

delete-aws-ec2-instance.yml
- hosts: cliservers
  roles:
    - delete-aws-ec2-instance
    - delete-aws-ec2-elastic-ip
  vars_files:
    - vars/all.yml

tasks

role/delete-aws-ec2-instance/tasks/main.yml
- name: "Delete EC2 Instanse"
  shell: |
    aws ec2 terminate-instances \
    --instance-ids {{ AWS_EC2.INSTANCE_ID }}
  register: result
  changed_when: False

- debug: var=result.stdout_lines # stdout => stdout_lines
  when: result | success
  tags:
    - always
role/delete-aws-ec2-instance/tasks/main.yml
- name: "Delete Elastic IP"
  shell: |
    aws ec2 release-address \
    --allocation-id {{ AWS_EC2.ALLOCATION_ID }}
  register: result
  changed_when: False

- debug: var=result.stdout_lines # stdout => stdout_lines
  when: result | success
  tags:
    - always
command
ansible-playbook -i inventory/production delete-aws-ec2-instance.yml

終わりに

インスタンスの作成や関連する設定もCLIから実行することが出来ます。
さらに各種設定値等もファイル内に残るので管理がしやすいです!

次は、S3バケットの作成をAnsible+aws-cliでやってみようと思います。

じゃあの。

続きを読む

【メモ】Amazon EC2で既存のインスタンスに利用されているKeyPairを確認する

やりたかったこと

AWSのアカウントを運用していると増えるKeyPair・・・

そのうち、利用してないKeyPairも増えてきて、「この秘密鍵、必要なん?」ってなると思います。

で、棚卸ししました。

そのときに利用したコマンドをメモしておきます。

EC2インスタンスで利用されているKeyPairを調べる

以下のコマンドを利用しました。配列が出力されます。

aws ec2 describe-instances --query "Reservations[].Instances[].KeyName" | jq -r unique

現状のjmespathでは配列の重複排除はできないようです。

https://stackoverflow.com/questions/37022753/how-can-i-get-unique-values-in-array-in-a-jmespath-query

KeyPairの一覧を調べる

以下のコマンドを利用しました。配列が出力されます。

aws ec2 describe-key-pairs --query "KeyPairs[].KeyName"

不要なKeyPairを削除する

例えば、「現在利用されていないKeyPairは不要である」として対処します。

上記の要件はあくまでも一例です。組織・プロジェクトなどによっては必要な場合も当然あり得ますので、個別に判断してください。

続きを読む

AWSCLIでdescribe-instancesする時は–output textを指定するといいです

awscliのデフォルトの出力形式はJSONなので、そのままだとjqなどのJSONパーサを使わないと他コマンドでの転用が厳しいですが、--output textを使うといい感じに整形してくれます。

例えば

aws ec2 describe-instances --filter "Name=tag:Name,Values=YourInstanceName" --query "Reservations[].Instances[].InstanceId"

のようにすると、[["YourInstanceId"]]のように、JSON形式で返ってきてしまうので

aws ec2 describe-instances --filter "Name=tag:Name,Values=YourInstanceName" --query "Reservations[].Instances[].InstanceId" --output text

のように、--output textを付け加えると、YourInstanceIdのようにブラケットやダブルクォーテーションが外れた状態で戻ってきます。

それだけです。

続きを読む

AnsibleでRoute53のレコード情報をいい感じにエクスポートする

はじめに

Route53でDNSを運用している方は多いと思いますが、ゾーンごとのレコード一覧とかサクっと調べたいことがあるかと思います。
マネコンだとエクスポートできないし、AWS CLIでやるにしてもjqなどで適切にパースしないと見づらいです。

今回は、Ansibleを利用してレコード一覧をYAML形式で見やすくファイル出力してみます。

前提

  • AWS関連のモジュール実行にはbotoが必要です。
  • credential情報は環境変数かaws configureでセットしてある必要があります。

sample

やること

取得したレコードセット情報を、ゾーンごとにworkディレクトリにYAML形式で保存する

ディレクトリ構成

ディレクトリ構成
site.yml
roles/
|--route53_export/
|  |--tasks/
|  |  |--main.yml
|  |--templates/
|  |  |--save_record_sets.j2
hosts/aws                  #inventory
work/                      #ゾーン単位でレコード情報が出力される
|--testdomain.com.txt        #出力されたレコード情報

inventory

AWSリソース関連モジュールはすべてlocalhostで実行するので、下記のようなインベントリファイルを用意します。

hosts/aws
[aws]
localhost

Role

roles/route53_export/tasks/main.yml
---
- name: Create directory
  file:
    path: "{{ inventory_dir | dirname }}/work/route53_export"
    state: directory
    mode: '0775'
    group: wheel
  delegate_to: localhost
  check_mode: no
  changed_when: no
  become: yes

- name: Get all hosted zones
  route53_facts:
    query: hosted_zone
  register: hosted_zones
  check_mode: no

- name: Get record sets in a given hosted zone
  route53_facts:
    profile: "{{ lookup('env', 'AWS_DEFAULT_PROFILE') }}"
    query: record_sets
    hosted_zone_id: "{{ item.Id | regex_replace('/hostedzone/','') }}"
  check_mode: no
  register: record_sets
  with_items: "{{ hosted_zones.HostedZones }}"

- name: Save record sets
  template:
    src: save_record_sets.j2
    dest: >-
      {{ inventory_dir | dirname }}/work/route53_export/{{ item.item.Name | regex_replace('(^.*).$','1') }}.txt
    mode: '0664'
    group: wheel
  delegate_to: localhost
  changed_when: no
  become: yes
  with_items: "{{ record_sets.results }}"
  when: not ansible_check_mode

template

roles/route53_export/templates/save_record_sets.j2
{{ item.ResourceRecordSets | to_nice_yaml(indent=2) }}

site.yml

site.yml
---
- name: route53_export
  hosts: localhost
  connection: local
  roles:
    - role: route53_export

実行

Command
$ ansible-playbook -i hosts/aws -l localhost site.yml

出力イメージ

work/testdomain.com.txt
- Name: cname.testdomain.com.
  ResourceRecords:
  - Value: www.example.com
  TTL: 300
  Type: CNAME
- AliasTarget:
    DNSName: testelb-xxxxxxxxx.ap-northeast-1.elb.amazonaws.com.
    EvaluateTargetHealth: false
    HostedZoneId: Z14GRHDCWA56QT
  Name: elb.testdomain.com.
  Type: A
- Name: text.testdomain.com.
  ResourceRecords:
  - Value: '"var"'
  TTL: 300
  Type: TXT
- Name: www.testdomain.com.
  ResourceRecords:
  - Value: 10.1.1.1
  TTL: 7200
  Type: A

まとめ

いかがでしょうか。結構見やすいと思います。
このファイルをgit管理するのもアリかと。

参考

AnsibleでAWSリソースを管理するシリーズ

続きを読む