Lambda の Advent Calender にタッチの差で参加できなかったので、#0的に11月30日を狙って(勝手に)投稿してみます!!
今年の頭から EC2 上で Twitter retrobot を動かしていたのですが、先月 Lambda が Python 対応&スケジュール実行されたので、今回その retrobot を Lambda に移行してみました。
最初は Lambda の勉強のために移行しようと思ったのですが、移行後金額を見積もってみるとまさかの無料枠内(しかもかなり余裕があるように見えます。)に収まったので、経費削減にもつながりました。(計算間違ってるかもしれないので、最後に見積もりを載せてみました。間違っていたらご指摘ください!)
前提/環境
- 10分に1回、約1年前のツイートをつぶやきなおすbotになります。
- Lambda 歴トータル1週間くらいです。(4月のハンズオン以来。。)
- Python も初心者です。(運用スクリプト少し書いたことある程度)
- Yosemite 10.10.4 で作業しています。
こんな人向け
- Lambda function を初めて書く方
- 安価に retrobot 運用してみたい方
Twitter アプリの登録とアクセスキーの取得
こちらのページを参考にして進めてください。
Consumer Key(CK)
Consumer Secret(CS)
Access Token(AT)
Access Token Secret(AS)
の4つが必要になります。
過去1年分のツイートファイル(json形式/月ごと)を取得し、S3にアップロードする
過去のツイートは tweet.zip という形でダウンロードできます。
設定画面を開いてください。
設定画面の一番下に「全ツイート履歴」という項目がありますので、そこからメールを送信してください。
(私は最近履歴をダウンロードしているので「再送信」となっていますが、みなさんの画面では「送信」になっているかと思います。)
次に、S3 に retrobot 用のバケットを作成してください。
S3 > Create Bucket で作成することができます。
名前は全世界でユニークにしないとなりませんので、(twitterアカウント名).retro とでもすればよいと思います。
そして、先ほどのzipファイルを展開し、直近1年分の js ファイルを upload してください。
こんな感じになるはずです。↓
Pythonスクリプトの作成
やっていることは下記の通りです。
(Python 素人なのでもっといい書き方あればぜひコメントください!)
- 1年前同月のツイートファイルを S3 から取得
- 1年前の同じ時刻〜10分後までのツイートをフィルタする
- フィルタされた結果をつぶやく
# * enter your hogehoge
となっている5箇所だけ変更すれば動くはずです。
from requests_oauthlib import OAuth1Session
import commands, boto3, re, json
CK = '***' # * enter your Consumer Key *
CS = '***' # * enter your Consumer Secret *
AT = '***' # * enter your Access Token *
AS = '***' # * enter your Accesss Token Secert *
UPDATE_URL = 'https://api.twitter.com/1.1/statuses/update.json'
AWS_S3_BUCKET_NAME = "***" # * enter your backet name *
def _(cmd):
return commands.getoutput(cmd)
# get "keyName" obj from S3
def _getTweetList(keyName):
s3 = boto3.resource('s3', region_name='ap-northeast-1')
bucket = s3.Bucket(AWS_S3_BUCKET_NAME)
obj = bucket.Object(keyName)
response = obj.get()
body = response['Body'].read()
return body.decode('utf-8')
# tweet text
def _tweet(text):
params = {"status": text }
twitter = OAuth1Session(CK, CS, AT, AS)
req = twitter.post(UPDATE_URL, params = params)
if req.status_code == 200:
return text
else:
return req.status_code
def handler(event, context):
# yyyy_mm.js
GET_OBJECT_KEY_NAME = _('date +"%Y_%m" --date "1 year ago"') + ".js"
tweetList = _getTweetList(GET_OBJECT_KEY_NAME)
# delete first line ("Grailbird.data.tweets_yyyy_mm =")
tweetListJson = re.sub(r"^.*\n", "", tweetList)
tweets = json.loads(tweetListJson)
# yyyy-mm-dd hh:m
targetDateTime = _('date +"%Y-%m-%d %H:%M" --date "1 year ago"')[:-1]
targetTweetList = []
for tweet in tweets:
if tweet["created_at"].startswith(targetDateTime):
# replace mention and hashtag mark
text = tweet["text"].replace('@','*').replace('#','+')
targetTweetList.append(text)
# desc -> asc
targetTweetList.reverse()
for text in targetTweetList:
_tweet(text)
デプロイパッケージの作成
外部ライブラリを使用しているため、zipファイルにまとめてアップロードする必要があります。
※ここでは手順のみ記載しますが、こちらに詳細がありますので、詳しく知りたい方はご確認ください。
→https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html
Lambda ファンクション用のディレクトリを作成してください。
また、今回 requests_oauthlib モジュールを使用しますので、こちらをインストールしてください。
mkdir lambda_function
cd lambda_function/
pip install requests_oauthlib -t ./
pip がなければインストールしてください。(sudo 権限が必要です)
easy_install pip
Python ファイルのアップロード方法は下記の3つがありますが、
- ソースを Management Console 上に記入する
- zip ファイルを作成し直接 upload する
- zip ファイルを作成し、S3 に upload し、Lambda function から参照する
今回のように外部モジュールを使用するときは、zip ファイルを作成する必要があります。
今回は2番目の方法で進めます。
cp (pyのあるdir)/ketancho_retro.py ./
zip -r ketancho_retro.zip ./
Lambda ファンクションの作成
Lambda のトップ画面から Create Lambda function を選択します。
blue print は hello-world-python を選択してください。
Configure function では、下記の項目を設定してください。
- Name:Lambda function の名前を入力してください。
- Lambda function code:"Upload a .ZIP file" を選択し、先ほど作成した zip ファイルを選択してください。
- handler:[先ほど作成した .py のファイル名].[最初に実行するメソッド名]になります。私の場合は、ketancho_retro.handler となります。
- Role:今回は S3 バケットにアクセスが必要なので、Create New Role から "S3 execution role" を選択してください。すると、別タブで IAM 画面が開かれますが、何も変更せずAllowとしてください。元の画面に戻ってきて、今作成したロールが設定されます。
最後にAdvanced sttingsですが、タイムアウト値が3秒ではたまにタイムアウトしてしまうので、10秒としています。
設定できたらNextを押してください。
Event sources の設定
Lambda ファンクションが作成できたら、次に Event sources を設定します。
何をトリガーにファンクションを実行するかの定義になります。
「Event source」タブを押してください。
「Add event source」を押してください。
今回はcronライクに10分に1回起動するようにしたいので、
「Event source type」は Scheduled Event を、
「Schedule expression」は cron(0/10 * ? * * *) としてください。
ここまでで設定完了です。
うまく動くかテストしたい場合は、「Test」ボタンからテストできます。
インプットは何にするか聞かれますが、そのまま Save and Test としてください。
(このインプットは使わないのでなんでもOKです。)
ただし、1年前の同じ時間から10分後までに何もつぶやいていない場合は、botの方も何もつぶやかれません。
すぐにテストしたい場合は、targetDateTime
を適当な値に変更してzipファイルをアップロードしなおしてテストしてみてください。
見積もり
最初に「タダかも!」と書きましたが、私は下記のように見積もっています。
AWS公式ページに「Lambda では 1 か月に 1,000,000 件の無料リクエストおよび 400,000 GB/秒のコンピューティング時間が無料利用枠となっています。 」と書かれています。
まず、リクスト数ですが、
10分に1回 x 6 x 24時間 x 31日分で最大でも4,464回/月となり、無料枠の1%にも満たないです。
次に、コンピューティング時間ですが、何日か運用したのですが、
のように、せいぜい3,000msで処理が完了します。(青がMax、緑がavg、橙がminです。)
これを月換算(x4,464回分)すると、13,392秒/月になります。
Lambdaでは何GBメモリおを占有したかで決まるので、これに設定しているメモリ128MBをかけると1,674GB、、無料枠のこちらも1%にも届きません。
なのでタダ!と書いた次第です!(間違っていてら教えてください。)
まとめ
retrobot は Lambda を学ぶ材料として、ちょうどいい規模だと思います。
スムーズにいけば1時間程度で完了すると思いますので、サーバレスこと始めとしてやってみてもらえればと思います。
次は、AMI の自動取得を Lambda に移行しようと思います。
また、retrobot は1年前に自分がやろうとしてたこと/やりたかったこと、でもやってなかったことを思い出すのにちょうどいいです。
タダで運用できそうですので、備忘録がてら(?)運用してみてください!