Boto3のヘルパークラスを作った【cloudpack大阪BLOG】
これまでBoto3を使う時にコピペで流用していたコード部分をライブラリ化しました。
ついでに、PyPIに登録して世にリリースしてみました。
以下で利用可能になります。
# pip install boto3helper
機能の紹介
いろいろ機能は作っていく予定ですが、今回は、各リソースに対してイテレート対策を紹介します。
Strategyパターン風にイテレートするよう実装しました。
Throttlingによるリトライも考慮しています。
これをコーディングすると、結構なボリュームになります。
例えば、各EC2インスタンスに対し処理(インスタンスIDを出力)を行う場合は、以下のようになります。
#!/usr/bin/python # coding:utf-8 import boto3 from boto3.session import Session import botocore session ={"accessKey" : "hogehoge...", "secretKey" : "fugafuga...", "region" : "ap-northeast-1など"} client = session.client('ec2') def describe_instances_force_1st(): try: response = client.describe_instances() except botocore.exceptions.ClientError: if not 'ThrottlingException' in sys.exc_info(): raise time.sleep(1.0) response = describe_instances_force_1st() return response def describe_instances_force(next_token): try: response = client.describe_instances( NextToken = next_token ) except botocore.exceptions.ClientError: if not 'ThrottlingException' in sys.exc_info(): raise time.sleep(1.0) response = describe_instances_force(next_token) return response if __name__ == "__main__": first_call = True result = True while True: if first_call: resources = describe_instances_force_1st() first_call = False else: resources = describe_instances_force(next_token) for reservation in resources['Reservations']: instances = reservation['Instances'] for instance in instances: print('InstanceId'), print(instance['InstanceId']) # 次のページなし if not resources.has_key('NextToken'): break # 次のページへのポインタを取得 next_token = resources['NextToken']
boto3helperを使うことで、以下のようになります。
#!/usr/bin/python # coding:utf-8 import boto3helper # 各インスタンスに対して実行する処理 def test_func(instance, args): print(args['arg1']), print(instance[args['arg2']]) if __name__ == "__main__": credential ={"accessKey" : "hogehoge...", "secretKey" : "fugafuga...", "region" : "ap-northeast-1など"} o = boto3helper.ec2_helper.EC2_Helper(credential) o.exec_func_each_instances(test_func, arg1 = 'InstanceId: ', arg2 = 'InstanceId')
テストコードが適当だったり、対応サービスも少なかったりまだまだ未熟なライブラリですが、コツコツとアップデートしておきますので、気になった方は使ってみてください。
おさらい!AWS Route53【cloudpack大阪BLOG】
Route53について復習したので、もろもろまとめてみました。
目次
Hosted Zone
Public Hosted Zone
普通に使う分にはこっち。
Private Hosted Zone
Publicに書いたものは、グローバルに伝搬しますが、こちらはVPC内でのみでしか名前解決できません。
用途は以下のような感じかと。
- インターネットに出ない名前解決
- RDSのエンドポイントとかに短いエイリアスを付けることができる
検証
VPC内のEC2から名前解決
[ec2-user@ec2-in-vpc ~]$ dig hoge.pict3.net +short 10.0.0.xxx
VPC外のEC2から名前解決
[ec2-user@ec2-out-of-vpc ~]$ dig hoge.pict3.net +short 52.yy.yy.yy
Publicと重複した場合はPrivateが優先されるようです。
ルーティングポリシー
シンプルルーティング
重み付けルーティング
複数の同一名称のレコードを登録し、割振る割合を設定できます。
ブルー・グリーン・デプロイメントに最適!
設定例
検証
キャッシュも入っていると思うので、あまり正確なものではないですが、なんとなく動いていそうです。
$ for i in {1..10}; do dig test.pict3.net +short; sleep 60; done; 10.0.0.2 10.0.0.2 10.0.0.2 10.0.0.2 10.0.0.1 10.0.0.1 10.0.0.2 10.0.0.2 10.0.0.2 10.0.0.2
レイテンシルーティング
AWSに蓄積されたレイテンシ情報をもとに、最適なリージョンにふり分けます。
設定例
検証
オレゴンリージョンと東京リージョンにローンチしたEC2から名前解決してみて確認してみます。
[ec2-user@ec2-at-usa ~]$ dig test.pict3.net +short 10.0.0.1 [ec2-user@ec2-at-japan ~]$ dig test.pict3.net +short 10.0.0.2
いい感じ!
Failoverルーティング
必ずヘルスチェックとセットで使います。
ヘルスチェックに失敗した際に、S3上のSorryページや別リージョンのELBにアクセスを向けることができます。
ヘルスチェックの設定は後述。
設定例
加えて、S3にSorryページのHTMLをアップロードしておきました。
検証
OKのとき、Route53に設定したドメインに対して解決されるIPは、ELBのものとなります。
$ curl -LI hoge-elb-xxxxxxxx.ap-northeast-1.elb.amazonaws.com -o /dev/null -w '%{http_code}\n' -s 200 $ dig hoge-elb-xxxxxxxx.ap-northeast-1.elb.amazonaws.com +short 54.xxx.xxx.xxx 52.yyy.yyy.yyy $ dig test.pict3.net +short 54.xxx.xxx.xxx 52.yyy.yyy.yyy
NGのとき、Route53に設定したドメインに対して解決されるIPは、ELBのものとなりません。
(きっとS3のwebホスティング用インスタンス)
$ curl -LI hoge-elb-xxxxxxxx.ap-northeast-1.elb.amazonaws.com -o /dev/null -w '%{http_code}\n' -s 403 $ dig test.pict3.net +short 54.zzz.zzz.zzz
ブラウザからもフェイルオーバー時に、S3に置いたsorryページが表示されることが確認できました。
Geolocationルーティング
アクセス元の位置情報を元にルーティングします。
レイテンシベースのものと似通った結果になりそうですが、より厳密に設定することができます。
設定例
検証
ここでもオレゴンリージョンと東京リージョンにローンチしたEC2から名前解決してみて確認してみます。
[ec2-user@ec2-at-usa ~]$ dig test.pict3.net +short 10.0.0.1 [ec2-user@ec2-at-japan ~]$ dig test.pict3.net +short 10.0.0.3
アクセス元の国のレコードセットがなかったら。。。
名前解決しませんでした。USで州が異なる場合も同様に名前解決しません。
ヘルスチェック
「エンドポイント」または「その他のヘルスチェック」の状態をチェックします。
「エンドポイント」にはIPアドレスやドメイン名を指定できます。 ELBのヘルスチェックのように対象プロトコルなどが選択できます。
設定例
ELBのエンドポイントで設定してみました。
検証
エンドポイントのヘルスチェックではレスポンスコードが200以上かつ400未満かどうかをチェックします。
従って、
$ curl -LI hogehoge.ap-northeast-1.elb.amazonaws.com -o /dev/null -w '%{http_code}\n' -s 200
のときは、Healty
となります。
ここで、webサーバーのindex.htmlを消してレスポンスコードが403になるようにしてみます。
$ curl -LI hogehoge.ap-northeast-1.elb.amazonaws.com -o /dev/null -w '%{http_code}\n' -s 403
このときは、Unhealty
となります。
まとめ
思っていたよりも簡単に機能的なことができるので、Route 53を好きになりました!
今後、Traffic flowとかも触ってみます。
AWS LambdaのログがCloudWatchLogsに出力されない(?)【cloudpack大阪BLOG】
自分自身もハマりましたが、周りでも同じような声を聞くので手順を展開しておきます。
Lambdaを利用していて、イベントソースによりちゃんと発火したかどうか、Cloud Watch Logsを確認することが多いと思います。
ただし、このログ、出力されないことがあるんじゃないか?との声をたまに聞きます。
そんなときにチェックする方法を紹介いたします。
まずは最初に。。。
本当に実行されたのか? CloudWatchから「Invocation count」を確認します。
まぁここは大丈夫かと思いますが、念のため。
Cloud Watch Logsを見るときの落とし穴
次にCloud Watch Logsを見ますが、この時に見落としが発生することが多いです。
ストリーム内には複数ページのログが出力されていることがあります。 同じような出力内容が続き、一見、末端まで見たかなと思っても、 ページ送りをポチポチすると、目的のログが出てくることがあります。
しかし見づらい(´Д`)
そんなわけで、ローカル環境(mac)にログをダウンロードして、目的のログを探すことにします。
ローカル環境でログを確認する手順
S3へエクスポート
まずは、Cloud Watch LogsをS3にエクスポートします。
エクスポートはロググループ単位で行うことができます。 期間やプレフィックスで絞り込むことも可能です。
出力先のS3のバケットは事前に準備ください。
S3からダウンロード
エクスポートしたログは現物さながら、ストリーム単位でフォルダ階層化されています。
マネジメントコンソールからペチペチとダウンロードするのは至極面倒なので、CLIを使ってダウンロードしちゃいます。
$ aws s3 --profile hoge cp s3://fuga ~/Documents/temp/ --recursive
ダウンロードしたファイルから検索
落としてきたログファイル群から、特定のキーワードで検索します。
find ~/Documents/temp/exportedlogs -type f -name "*.gz" | xargs gunzip -c | grep piyo
上記の手順は、Lambdaログに限らず有効です。
Cloud Watch Logsを探りたい時にお試しください。
AWS CloudWatchのカスタムメトリクスをDataDogで確認【cloudpack大阪BLOG】
DataDogとは、監視のSaaSで、様々な形式でグラフ表示できたりします。 タグなどでグルーピングもできるので、簡単なデータ解析にも役立ちます。
AWSとの親和性も高く、簡単な設定でCloudWatchのデータを監視することができちゃいます。 そして、CloudWatchでは最大2週間しかデータを保持しませんが、 DataDogに登録することでより以前のデータをみることもできます。
詳しくは本家サイトで。
今回は、CloudWatchのカスタムメトリクスをDataDogで表示してみようと思います。 以下、手順です。
カスタムメトリクスを設定する
メモリ監視を追加してみます。 導入環境はAmazon Linuxで。
$ uname -a Linux ip-10-0-0-194 4.1.10-17.31.amzn1.x86_64 #1 SMP Sat Oct 24 01:31:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
AWSがスクリプトを公開してくれているのでそちらを拝借。 http://aws.amazon.com/code/8720044071969977
perlのライブラリが諸々必要なのでインストール。
$ wget http://aws-cloudwatch.s3.amazonaws.com/downloads/CloudWatchMonitoringScripts-1.2.1.zip $ unzip CloudWatchMonitoringScripts-1.2.1.zip $ cd aws-scripts-mon/ $ sudo yum -y install perl-core perl-URI perl-DateTime-TimeZone perl-Bundle-LWP
問題なくインストールができたかを確認!
$ ./mon-put-instance-data.pl --mem-util --verify --verbose MemoryUtilization: 11.7104374428861 (Percent) No credential methods are specified. Trying default IAM role. Using IAM role <ec2-basic> Endpoint: https://monitoring.ap-northeast-1.amazonaws.com Payload: {"MetricData":[{"Timestamp":1454679988,"Dimensions":[{"Value":"i-0b1dfb233a07e0d4f","Name":"InstanceId"}],"Value":11.7104374428861,"Unit":"Percent","MetricName":"MemoryUtilization"}],"Namespace":"System/Linux","__type":"com.amazonaws.cloudwatch.v2010_08_01#PutMetricDataInput"} Verification completed successfully. No actual metrics sent to CloudWatch.
cronに登録して、CloudWatchにデータが積まれることを確認します。
$ crontab -l */5 * * * * ~/aws-scripts-mon/mon-put-instance-data.pl --mem-util --mem-used --mem-avail --from-cron
OK!!
DataDogを設定する
アカウントを登録します。
ご覧のとおり、たくさんのリソースに対応しています。
今回はcredentialで登録します。 全体的にカスタムメトリクスを受けてもいいですが、 アカウントを限定してカスタムメトリクスを取ることもできます。
以上! 実に簡単。
しばらくすると、Metrics ExplorerからDataDogでグラフ表示可能に。
DataDog、いろいろ試していこうと思います!
AWS IoTでインターフォン【cloudpack大阪BLOG】
cloudpack大阪オフィスのインターフォンが鳴らなくなりました。
なんともみすぼらしいので、なんとかしたい(; ´Д`)
こんなときにもAWSで解決するのがcloudpack!
うってつけのサービス、AWS IoTを使います。
システム構成はこんな感じです。
呼び鈴側のデバイスは、AWS IoT ボタンを使いました。
受け口側のデバイスは、固定電話を使います。LambdaからTwilioでコールします。
実現
AWS IoT
今回はAWS IoT ボタンを使いましたが、Rule以外のリソースはデフォルトのままで進めます。
ちなみに、AWS IoTには4種のリソースがあります。
それぞれの役割はかなりざっくりと言うと、以下のような感じです。(詳しくは別の機会に)
・Certificate ... 証明書。PolicyとThingに紐付けます。
・Policy ... アクセス許可の設定を行います。
・Rule ... デバイスから受けたデータをどう処理するかを定義します。
・Thing ... デバイスにあたります。
今回はピンポンダッシュ対策として、ボタンの長押し(LONG Click : 1.5sec以上)を拾うようにします。
Ruleはこんな感じになりました。
・Name aws_iot_button_rule_long ・State Enabled ・Description aws_iot_button_rule_long ・Query string SELECT * FROM 'iotbutton/+' WHERE clickType = "LONG"
actionには後述のLambdaファンクションを当てがいます。
Lambda
twilioを使ったお電話Lambda、以前のBlogではnode.jsを使ったので今回はPython版を例示します。
$ mkdir caller $ cd caller/ $ pip install twilio -t . $ vi lambda_function.py $ zip -r upload.zip *
TwilioのサイトでPython版コードも例示してくれるので、lambda_function.pyは割愛します。
あとは、Lambdaにupload.zipをアップロードするだけ。
結果
あっという間に応急処置Done!!
AWS IoTもLambdaもTwilioも使い易い!
インターフォンの故障対応もcloudpackまで(嘘)
※ 本記事はフィクションです。AWS IoTボタンは日本国内では技適通っていないので使えません。 実現の際は技適の通ったデバイスをご使用ください。
AWSのIPアドレスレンジ変更をBacklogに通知する(SNS + Lambda + S3)【cloudpack大阪BLOG】
やりたいこと
AWSのリソースが使うIPアドレスレンジは、以下に公開されています。
https://ip-ranges.amazonaws.com/ip-ranges.json
ちょくちょく更新されますが、FireWallやWAFの設定に反映させる必要があるシステムをお持ちのプロジェクト担当者へ、正確かつ迅速に通知することを目標に自動化システムを構築しました。
実現
AWSのリソースが使うIPアドレスレンジの変更は、SNSで受信することができるようです。 また、担当者への連絡はBacklogを使うこととします。
以下のような構成で実現します。
S3
S3に通知を受ける直前のIPアドレスレンジを保管しておきます。 バケット名は適当に。
Lambdaファンクション
IAMロールには、S3の読み書き権限を付けておきます。
#!/usr/bin/python # -*- coding: utf-8 -*- import httplib import json import difflib import boto3 s3 = boto3.resource('s3') import pprint pp = pprint.PrettyPrinter(indent=4) API_KEY = "<Backlogより発行>" BACKLOG_HOST = "<Backlogスペース + ドメイン>" S3_BACKET_NAME = "<バケット名>" S3_LAST_IP_RANGE_OBJ = "last-ip-range.json" def get_project_id(project_code): uri = "/api/v2/projects/%s?apiKey=%s" % (project_code, API_KEY) connect = httplib.HTTPSConnection(host = BACKLOG_HOST, port = 443) connect.request("GET", uri) response = connect.getresponse() body = response.read() connect.close() data = json.loads(body) project_id = data['id'] return project_id def post_issue(project_id, summary, issue_type_id, priority_id, description): data = { "projectId" : project_id, "summary" : summary, "issueTypeId" : issue_type_id, "priorityId" : priority_id, "description" : description } params = json.dumps(data) headers = { "Accept":"application/json", "Content-Type":"application/json", } uri = "/api/v2/issues?apiKey=%s" % (API_KEY) connect = httplib.HTTPSConnection(host = BACKLOG_HOST, port=443) connect.request("POST", uri , params, headers) response = connect.getresponse() connect.close() def download_ip_range(file): uri = "/ip-ranges.json" connect = httplib.HTTPSConnection(host = "ip-ranges.amazonaws.com", port = 443) connect.request("GET", uri) response = connect.getresponse() body = response.read() f = open(file, "w") f.write(body) f.close() connect.close() return def construct_description(diff): description = u"AWSの使用するIPアドレスレンジに更新がありました。" description += u"変更内容は以下です。" description += '\n' description += "{code}\n" for buf in diff: description += buf description += "{/code}\n" print(description) return description def lambda_handler(event, context): last_ip_range_file = '/tmp/' + S3_LAST_IP_RANGE_OBJ + '_last' current_ip_range_file = '/tmp/' + S3_LAST_IP_RANGE_OBJ + '_current' download_ip_range(current_ip_range_file) s3.meta.client.download_file(S3_BACKET_NAME, S3_LAST_IP_RANGE_OBJ, last_ip_range_file) last_ip_range = open(last_ip_range_file, "r") current_ip_range = open(current_ip_range_file, "r") diff = difflib.context_diff(last_ip_range.readlines(), current_ip_range.readlines(), fromfile='before', tofile='after') description = construct_description(diff) summary = "[連絡] AWSリソースのIPアドレスレンジに更新がありました" issue_type_id = 3 priority_id = 3 # 通知したいプロジェクトを追加 project_ids = [] project_ids.append(get_project_id("HOGE")) project_ids.append(get_project_id("FUGA")) for project_id in project_ids: post_issue(project_id, summary, issue_type_id, priority_id, description) response = s3.meta.client.upload_file(current_ip_range_file, S3_BACKET_NAME, S3_LAST_IP_RANGE_OBJ)
SNS
こちらのサイトを参考にTopic ARNを設定します。 ProtocolはLambda、Endpointは先ほど作ったファンクションを選択します。
Lambdaのイベントソースに反映されます。
結果
ちゃんとBacklogに投稿されました! Lambdaの恩恵、デカすぎです!!
AMIバックアップを自動取得するLambdaファンクション(python版)【cloudpack大阪BLOG】
今回はシンプルなLambda。
今秋のre:InventでアップデートされたScheduled Eventを利用して、AMIバックアップをとるファンクションを作成しました。
仕様
仕様は以下のような感じ。
- AMIバックアップをとりたいEC2に、タグ'Backup--Generation : 世代数'を設定しておく
- 'Backup-Generation'には、保持したい世代数を設定する
- AMIおよびSnapShotには、元となるEC2のタグ群(*)を設定する (*)Backup-Generationを除く
- LambdaのScheduled Eventをトリガーに実行する
- AMI取得後、本ファンクションで作成したAMIのうち、保持したい世代数よりも古いイメージおよびスナップショットを削除する
- ファンクションで作成したAMIを区別するため、AMIおよびsnapshotにタグ'Backup-Type : auto'を付与する
EC2設定
まずは、EC2にタグ付け。
ここでは、2世代のバックアップをとることとします。
Lambdaファンクション
次は肝心かなめのLambdaファンクション。
コード
コードは、長くなるのでQiitaを参照ください。
AMIバックアップを取るLambdaファンクション(python) - Qiita
IAM Role
roleには以下を設定しています。
- AmazonEC2FullAccess
- AWSLambdaBasicExecutionRole
Event Source
毎朝2時に設定しました。
公式ドキュメントがわかりやすいです。
結果
こんな感じで、意図通り動いていそうです!
まとめ
EC2と1ヶ月のコスト比較をしてみます。コストは超概算ですので悪しからず。
EC2(*)の運用コストは以下です。
(*) 東京リージョン、オンデマンド、t2.micro、Linux
0.02 (/hr) * 24 (hr) * 31 (days) = $14.88 / 月
Lambda(*)の運用コストを計算してみます。
(*) 東京リージョン、128MB
リクエスト回数は誤差の範囲になるので無視します。
実行時間は上述のログから引っ張ってきました。
0.000000208 (/msec) * 43,889.93 (msec) * 31 (days) = $0.283 / 月
コストはなんと、52分の1に!!
Lambdaは今のところ無料枠に期間制限がないので、こちらも考慮すると更なるコスト削減になるかもです。
可用性のメリットもお忘れなく!
Scheduleイベントを使うことで、バッチ処理的に使っていたサーバーを捨てることができそうです!
今回はさらっと作ったLambdaファンクションですが、wait処理をSNSとかで無駄時間を減らすよう改善していきたいところです。