AWS Lambda(Python)を手っ取り早く高速化する方法【cloudpack 大阪 BLOG】
aws lambdaでは、CPUの使用時間に対し100ミリ秒単位で課金されるため、処理を高速化できるとその分料金も下がります。今回は簡単にLambda(Python)を高速化する方法を紹介します。
方法
処理系をJITコンパイル機能を持つPyPyに変更します。
これだけです。特にソースを見なおすとかではないので手軽に試せます。
PyPyとは
PyPy(パイパイ)は、プログラミング言語Pythonの実装の1つであり、Pythonで記述されたPythonの処理系であることが特徴の1つである(セルフホスティング)。PyPyは、実行速度と効率、およびオリジナルのPython実装であるCPythonとの互換性に重点を置いている。 |
環境構成
OS:Amazon Linux
PyPy:5.1.1 x86_64:
PyPyのインストール
公式サイトではredhat系のバイナリが提供されていませんので、以下ページで提供されているportable-pypyをダウンロードします。今回は、「PyPy 5.1.1 x86_64」をダウンロードしました。
GitHub - squeaky-pl/portable-pypy: Portable 32 and 64 bit x86 PyPy binaries for many Linux distributions.
ファイル構成
ダウンロードしたpypy-5.1.1-linux_x86_64-portable.tar.bz2を解凍してpypyにリネームしておきます。
ファイル構成は以下のとおりです
├── lambda_function.py ・・・lambdaから呼び出されてrun.shを実行する ├── run.sh ・・・main_functionをpypyで実行する ├── main_function.py ・・・実際の処理が書かれたpython └── pypy ・・・portable-pypy
lambda_function.py
run.shを実行するだけのlambda関数です
# coding: utf-8 import commands def _(cmd): return commands.getoutput(cmd) def lambda_handler(event, context): main() def main(): print _('sh ./run.sh')
main_function.py
実際に実行したい処理をここに記述します。
今回はパフォーマンス測定用に大量のリストを追加する処理としました。
# coding: utf-8 def main(): rangelist = range(1,100000) for var in range(0, 100): func(rangelist) print('main func finish') def func(rangelist): list = [] for var in rangelist: list.append(var) if __name__=='__main__': main()
ファイルを全て準備できれば、ディレクトリごとzipで固めてlambdaへアップします。pypy-portableの容量が大きいのでs3経由でないとアップできませんでした。
パフォーマンス結果
以下測定結果です。上記のmain_function.pyをデフォルトで実行した時とPyPyで実行した時とで、それぞれ5回測定して平均を取りました。PyPyの場合初回が遅くなるかと思いましたがあまり変わりませんでした。
PyPy | デフォルト | |
---|---|---|
1 | 5085.81 | 13523.69 |
2 | 5655.01 | 13497.07 |
3 | 4984.38 | 15315.06 |
4 | 4931.47 | 14630.05 |
5 | 4981.22 | 13615.74 |
平均 | 5127.57 | 14116.32 |
単位:ms
まとめ
今回のケースの場合、処理時間が約1/3になったのでかなりの高速化の効果がありました。
lambdaで重い処理を実行している場合など、PyPyでパフォーマンス改善を試しててみるのもいいかもしれません。
ただし、全てのケースで早くなるわけではなく処理が軽いlambdaで試した場合は、PyPyで実行した時のほうが逆に遅かったです。また、デフォルトのCpythonとは互換性がない部分もありますので、実際に切り替える場合はしっかりとテストが必要になると思います。今後、パフォーマンス改善が必要になった場合に、1つの手法として試してみたいと思います。
pythonのimaplibでメールのAND/OR検索【cloudpack 大阪 BLOG】
pythonでimaplibを使ってGmailを取得しようとして対象メールの検索をしてみたのですが、AND検索やOR検索でうまく検索できずに少しハマりましたので備忘録として残しておきます。
事前設定
まずは対象のGmailアカウントで以下を参考にアプリパスワードを取得します。通常のパスワードではログインできません。また、imapを有効にしておく必要があります。
support.google.com
実行コード
今回実行するコードは以下
import imaplib,email,email.Header GMAIL_USER=YOUR_GMAIL_ACCOUNT GMAIL_APP_PASSWORD=YOUR_GMAIL_APP_PASSWORD #取得したアプリパスワード def decode(src): result = '' decodefrag = email.Header.decode_header(src) for frag, enc in decodefrag:↲ if enc: result += unicode(frag, enc) else: result += unicode(frag) return result if __name__ == "__main__": gmail = imaplib.IMAP4_SSL(host=HOST) gmail.login(GMAIL_USER, GMAIL_APP_PASSWORD) gmail.select(LABEL) search_option = 'ここに検索条件をセット' typ, data = gmail.search(None, search_option) for num in data[0].split(): typ, data = gmail.fetch(num, '(RFC822)') msg = email.message_from_string(data[0][1]) subject = decode(msg.get('Subject'))
検索条件
件名で検索
search_option = '(SUBJECT "testmail01")'
AND 検索
件名と日付のFROM-TOでAND検索
search_option = '(SUBJECT "testmail01" SENTSINCE "09-Apr-2016" SENTBEFORE "10-Apr-2016")'
OR 検索
件名とFromでOR検索
search_option = '(OR (SUBJECT "testmail01") (FROM "test@gmail.com"))'
AND・OR 検索
件名でOR検索し日付のFROM-TOでAND検索
search_option = '((OR (SUBJECT "testmail01") (FROM "test@gmail.com")) SENTSINCE "01-May-2016" SENTBEFORE "03-May-2016")'
検索条件についてはRFCを読みましたが、実際の表記形式がよく分からなかったので試行錯誤しました。また、検索方法には括弧を付与する方法と付与しない方法がありましたが、付与しない方法ではうまく検証できませんでした。
注意するポイント
NewRelicでEC2をモニタリング【cloudpack 大阪 BLOG】
NewRelicでEC2にWordPressをセットアップしてモニタリングしてみました。リソース状況が視覚的によく分かるので、パフォーマンス分析に使いやすいです。また、単にCPU使用率だけでなくプロセスごとのCPU使用率・メモリの推移やアプリケーションのレスポンスタイムの内訳などもグラフで表示されておりシステムのボトルネックを調査するのにお勧めです。
導入方法
初期設定
導入方法についてそれぞれ以下のページを参照してください。
qiita.com
AWSユーザーは以下ページからNew Relicのユーザー登録をすることでStandard版が無料で使えます。
New Relic AWS | AWS Performance Monitoring | New Relic | New Relic Partner
EC2の鍵を入れ替える手順【cloudpack 大阪 BLOG】
別のアカウントからAMIを共有して起動した際に、OSによっては公開鍵をうまく変更できていないことがあるので、公開鍵を入れ替える方法についての備忘録
手順概要
- EC2からrootボリュームをデタッチ
- 別のEC2にrootボリュームをアタッチ
- rootボリュームをマウント
- 公開鍵の修正
- rootボリュームをアンマウント
- rootボリュームをデタッチ
- 元のEC2にrootボリュームをアタッチ
手順詳細
① EC2からrootボリュームをデタッチ
まずは、対象のEC2からrootボリュームをデタッチしますが、再度アタッチする際にデバイス名が必要となるのでデバイス名を控えておきます。
デバイス名を確認
対象ボリュームをデタッチする
② 他のEC2に対象のボリュームをアタッチする
③ rootボリュームをマウント
別のEC2(起動していなかったら起動する)にログインし下記コマンドを実行する
ボリュームの確認
# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvdf 202:80 0 50G 0 disk └─xvdf1 202:81 0 50G 0 part ←対象ボリュームが認識されている xvda1 202:1 0 50G 0 disk /
マウント用のディレクトリを作成する
# mkdir -p /mnt/ebs/0
マウントする
# mount /dev/xvdf1 /mnt/ebs/0
③ 対象ユーザーの公開鍵(authorized_key)を修正
ログインするための公開鍵をセットする
# vim /mnt/ebs/0/home/target_user/.ssh/authorized_keys
必要に応じてsshd_configを修正
# vim /mnt/ebs/0/etc/ssh/sshd_config
③ 対象ボリュームをアンマウント
# umount /mnt/ebs/0/
④ 対象ボリュームをデタッチ
手順①と同様にデタッチする
⑤ 元のEC2にアタッチ
元のインスタンスを選択し、①で控えておいたデバイス名をセットする
AWS Lambdaの簡易コマンド(lambda-controller)を作ってみた【cloudpack 大阪 BLOG】
やりたかったこと
外部ライブラリを使ってlambdaを開発しているとローカル環境で開発してアップロードしてAWSコンソールから実行という手間がすごく面倒だったので、開発時の簡単な実行確認については、ローカル環境のコマンドラインで全て完結できないかと思っていました。
AWSへのアップロードについては、lambda-uploaderを使っており、後はlambdaの実行がコマンドラインから簡単にできれば何とかなると思っていました。CLIは手軽に実行するには少し面倒なのと結果もjson形式となり見づらかったので、簡易実行できるコマンドを作ってみました。
ただし、まだまだ出来ることは限られているのと不具合もあると思うので今後改善予定です。
インストール
pip install lambda-controller
使い方
前提としてaccesskey,secretkeyのcredentialへの設定が必要になります。
lambda関数の実行
lambda-controller --invoke(or -i) FunctionName
lambda関数のリスト表示
lambda-controller --detail(or -d) FunctionName
lambda関数の詳細情報表示
lambda-controller --list(or -l)
profileを指定して実行する場合
lambda-controller --profile=yourprofile --invoke(or --list or --detail)
Route53にドメインをコマンドラインで登録する【cloudpack 大阪 BLOG】
Route53にドメインを大量に登録する必要があったので、cliでのやり方を調べてみるとインプットにjsonファイルが必要なことがわかりました。ちょっと面倒だったので別の方法を探してみるとcli53というツールを見つけました。使い勝手はかなりいい感じだったので、インストール方法と使い方を紹介したいと思います。
インストール方法
ダウンロード
以下のリンクから自分の環境に応じて、パッケージをダウンロードしてください。
今回はMac版(cli53-mac-amd64)をダウンロードしました。
Release 0.7.0 · barnybug/cli53 · GitHub
インストール方
リネームして実行権を付与します。
# sudo mv cli53-mac-amd64 /usr/local/bin/cli53 # sudo chmod +x /usr/local/bin/cli53
使い方
事前に~/.aws/credentialsにシークレットキーとアクセスキーを設定しておいてください。
もしくは実行時に以下のように変数にセットしてください
aws_access_key_id = AKID1234567890 aws_secret_access_key = MY-SECRET-KEY
hosted zoneの作成
cli53 create example.com
Aレコード
Zone Apex
cli53 rrcreate example.com '@ 300 A 192.168.1.2'
サブドメインがwww
cli53 rrcreate example.com 'www 60 A 192.168.0.1'
CNAMEレコード
cli53 rrcreate example.com 'mail CNAME ghs.googlehosted.com.'
MXレコード
cli53 rrcreate example.com '@ MX "10 192.168.0.1" "20 192.168.0.2"'
AWS LambdaでGmail APIの呼び出し 第2回【cloudpack 大阪 BLOG】
前回でGmailAPIにアクセスする準備は整いましたので、引き続きLambdaでアクセスしてみたいと思います。
2.Lambdaの設定
① Lambda用プロジェクトの作成
プロジェクトのディレクトリを作成します。また、GmailAPIにアクセス用のライブラリなども利用するためvirtualenvで環境を分けておきます。
mkdir gmailapi_for_lambda cd gmailapi_for_lambda/ virtualenv .venv
② 秘密鍵をp12からpemに変換
ダウンロードしたp12形式のファイルをpem形式に変換
openssl pkcs12 -in xxxxxxx.p12 -nodes -nocerts > privatekey.pem
③ Lambdaの作成
以下の4つのファイルを準備する。
gmailapi_for_lambda ├── gmail_access.py ・・・ lambda関数本体 ├── lambda.json ・・・ labda-uploader用設定ファイル ├── privatekey.pem ・・・ APIアクセス証明書 └── requirements.txt ・・・ ライブラリリスト
◆gmail_access.py
GmailにアクセスするLambda関数本体となります。
####################################################################################### # This is a sample to get the message list from Gmail ####################################################################################### import httplib2 import imaplib,email,email.Header import base64 from apiclient.discovery import build from oauth2client.file import Storage from oauth2client.client import SignedJwtAssertionCredentials import pprint def lambda_handler(event, context): try: main() except Exception: traceback.print_exc() sys.stderr.flush() sys.exit(1) def main(): pp = pprint.PrettyPrinter(indent=4) SERVICE_ACCOUNT_ID="your service account id" ← サービスアカウントIDをセット KEY_SECRET="your secret key" ← 秘密鍵のパスワード GMAIL_ACCOUNT="your gmail account" ← gmailのアカウント f = open("privatekey.pem","rb") key = f.read() f.close() credentials = SignedJwtAssertionCredentials( SERVICE_ACCOUNT_ID, key, scope="https://www.googleapis.com/auth/gmail.readonly", sub=GMAIL_ACCOUNT) http = httplib2.Http() http = credentials.authorize(http) service = build("gmail", "v1", http=http) message_list = service.users().messages().list(userId='me', #labelIds=label_ids, #pageToken=page_token, maxResults=10, q='is:unread' ).execute() for message in message_list['messages']: message_detail = service.users().messages().get(userId='me', id=message['id'],format='metadata').execute() for element in message_detail['payload']['headers']: if element['name'] == 'Subject': print element['value'] if __name__ == '__main__': main()
◆lambda.json
lambda-uploder用の設定ファイルとなります。名称は任意です。roleは環境に合わせて変更してください。
{ "name": "GmailTest", "description": "GmailTest", "region": "ap-northeast-1", "handler": "gmail_access.lambda_handler", "role": "arn:aws:iam::000000000000:role/lambda_basic_execution", "timeout": 300, "memory": 128 }
◆requirements.txt
利用するpythonのライブラリを記載します。Lambdaへのアップロード時にはこのファイルを参照してライブラリがアップロードファイルへ固められます。
pyCrypto google-api-python-client
virtualenv環境をactive化する
source .py27/bin/activate
pythonライブラリのインストール
pip install -r requirements.txt
lambda-uploaderのインストール
pip install lambda-uploader
lambda-uploaderを実行
事前にaws configrueでアップロード先のアカウントのアクセスキー及びシークレットキーをセットしておく
lambda-uploader