0. はじめに
ここ1年はStackstormばかり扱っているのですが、年末だし他の技術も触るかー!と思いたち、色々自分の作業ディレクトリを漁っていたところ、Twitterbotなるものを発掘しました。
Stackstorm???
という方はこちらをご参照ください。(自演)
Dockerで始めるStackstorm再入門1/3(環境構築からOrquestaで書いたWorkflowの結果をslackに通知するところまでのチュートリアル)
話を戻します。
そのTwitterbotですが、私はvpsを使って運用していました。
ただ、そんなに頻繁に動かさないので、また勉強も兼ねて、このたびAWS Lambda(以下、lambda)移行
にチャレンジした次第です。
lambdaってなに?
という方は、AWSがオフィシャルなハンズオンを公開しているので、そちらをご参照ください。
AWS Hands-on for Beginners の新コンテンツ紹介記事を書きました!AWS はじめの一歩にぜひ!😀|“スケーラブルウェブサイト構築編” を公開しました!- Monthly AWS Hands-on for Beginners 2019年12月号https://t.co/6QZIOARKDC
— ketancho 🙂|Kei Kanazawa (@ketancho) December 24, 2019
本記事では、サンプルコードも用意しているので、ぜひお試しください。
1. 目次
- 2. 環境/バージョン情報
- 3. Serverless Frameworkとは
- 4. ローカル開発環境の準備
- 5. serverless.ymlを編集
- 6. lambdaの実行ファイルを編集
- 7. lambdaをローカルから実行
- 8. lambdaをデプロイ
- 9. 参考
2.環境/バージョン情報
ローカル開発環境
- Ubuntu: 19.04 (Disco Dingo)
- npm: 6.13.4
Python: 3.7.3
- tweepy: 3.8.0
- oauthlib: 3.1.0
- requests: 2.22.0
- requests-oauthlib: 1.3.0他
serverless
- Framework Core: 1.60.4
- Plugin: 3.2.6
- SDK: 2.2.1
- Components Core: 1.1.2
- Components CLI: 1.4.0
AWS Lambda
- python3.7
3. Serverless Frameworkとは
そもそもServerless Frameworkとはなんなのか。
公式ドキュメントでは、このように紹介されています。
The Serverless Framework consists of an
open source CLI
thatmakes it easy to develop, deploy and test
serverless appsacross different cloud providers
, as well as a hosted Dashboard that includes features designed to further simplify serverless development, deployment, and testing, and enable you to easily secure and monitor your serverless apps.
参考: Serverless Framework Documentation
個人的に感じた特徴は以下のとおりです。
node.js製
フレームワークローカルで開発
したファンクションをserverlessコマンドを使って任意のプロバイダープラットフォーム(AWS, GCPなど)
にデプロイ- デプロイの設定は
serverless.yml
で定義
4. ローカル開発環境の準備
- サンプルリポジトリをクローン
- AWSクレデンシャルの設定
- Serverless Frameworkのインストールとプロジェクトの作成
- プラグインのインストール
サンプルリポジトリをクローン
gkz@localhost ~$ git clone https://github.com/gkzz/lambda_twbot.git \
&& cd lambda_twbot
AWSクレデンシャルの設定
gkz@localhost ~$ aws configure
[default]
region = ap-northeast-1
output = json
[default]
aws_access_key_id = xxxxxxxxxxx
aws_secret_access_key = yyyyyyyyyyyyy
Serverless Frameworkのインストールとプロジェクトの作成
gkz@localhost ~$ sudo npm install -g serverless
gkz@localhost ~$ serverless create \
> --template aws-python3 \
> --name src \
> --path src
Serverless: Generating boilerplate...
(略)
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v1.60.4
-------'
Serverless: Successfully generated boilerplate for template: "aws-python3"
プラグインのインストール
今回は以下の3つのプラグインをインストールしました。
プラグインの名称 | 目的 |
---|---|
serverless-python-requirements | ライブラリをインストールするため |
serverless-dotenv-plugin | 環境構築を読み込むため |
serverless-offline | ローカル開発環境でlambdaを実行するため |
参考までにserverless-python-requirements
をインストールした際のコマンドを貼りますが、他の2つも同様にインストールしています。
gkz@localhost ~$ sudo npm install --save serverless-python-requirements
プロジェクト構成
serverless,ymlを編集する前にディレクトリ構成を確認しておきましょう。
特に確認したい点は以下の2点です。
- serverless.ymlからみた.envの配置場所です。
- Twitterのtokenは
./config/.env
から読み取ります。
- Twitterのtokenは
requirements.txtはserverless.ymlと同じ階層
に配置します。
gkz@localhost ~$ cat src/handler.py.tmpl > src/handler.py
gkz@localhost ~$ cat config/.env.tmpl > config/.env
gkz@localhost ~$ tree -L 2
.
└── src # プロジェクトフォルダ
├── 37 # python -m venv $nameで作ったpython3.7の仮想環境の名称
├── config # .envなど変数が記載されたファイルの親ディレクトリ
├── handler.py # lambdaの実行関数
├── node_modules
├── package.json
├── package-lock.json
├── __pycache__
├── requirements.txt # tweepyなど必要なライブラリが記載
├── serverless.yml # lambdaをローカル開発環境からデプロイするのに使う
└── serverless.yml.org # プロジェクトフォルダを作る際に生成されたserverless.ymlのサンプルファイル
5 directories, 6 files
5. serverless.ymlを編集
service:srccustom:dotenv:basePath:./config/# ./lambda_twbot/src/.config/stage:${env:STAGE}# basepath/.envから環境変数STAGEを読み込むregion:${env:REGION} # 同様に環境変数REGIONを読み込むpythonRequirements:dockerizePip:non-linuxprovider:name:awsruntime:python3.7stage:${self:custom.stage}region:${self:custom.region}plugins:-serverless-python-requirements-serverless-dotenv-plugin-serverless-offlinefunctions:rtweet:handler:handler.rtweet# $filename.$function(handler.pyのrtweet関数を実行)memorySize:256timeout:90sevents:-schedule:cron(0/20 * * * ? *)# 毎日20分おきfav:handler:handler.favmemorySize:256timeout:90sevents:-schedule:cron(0/600 * * * ? *) # 毎日600分おき
6. lambdaの実行ファイルを編集
# coding: UTF-8
try:importunzip_requirementsexceptImportError:passimporttweepyimporttimeimportosimportrandomimporttracebackdef_set_token():""" set twitter token """CK=os.environ['CONSUMER_KEY']CS=os.environ['CONSUMER_SECRET']AT=os.environ['ACCESS_TOKEN']AS=os.environ['ACCESS_TOKEN_SECRET']auth=tweepy.OAuthHandler(CK,CS)auth.set_access_token(AT,AS)returntweepy.API(auth)def_find_tweet(token):found=[]kws=['#python 本','#lambda 楽しい',]forkwinkws:fortweetintweepy.Cursor(token.search,kw).items(5):found.append(tweet)returnfounddefrtweet(event,context):""" entry point of rtweet """counter=0token=_set_token()tweets=_find_tweet(token)fortweetintweets:try:#print('\nRetweet Bot found tweet by @' + tweet.user.screen_name + '. ' + 'Attempting to retweet.')
tweet.retweet()logger.info(tweet.retweet())counter+=1#print('Retweet published successfully.')
# Where sleep(10), sleep is measured in seconds.
# Change 10 to amount of seconds you want to have in-between retweets.
# Read Twitter's rules on automation. Don't spam!
time.sleep(random.randint(2,5))# Some basic error handling. Will print out why retweet failed, into your terminal.
excepttweepy.TweepErroraserror:print('''Retweet not successful.
Reason: {r}
'''.format(r=error.reason))exceptStopIteration:print(traceback.format_exc())breakreturn"RtweetConts: {num}".format(num=counter)deffav(event,context):""" entry point of fav """counter=0token=_set_token()tweets=_find_tweet(token)fortweetintweets:try:tweet.favorite()# Where sleep(10), sleep is measured in seconds.
# Change 10 to amount of seconds you want to have in-between retweets.
# Read Twitter's rules on automation. Don't spam!
time.sleep(random.randint(2,5))# Some basic error handling. Will print out why retweet failed, into your terminal.
excepttweepy.TweepErroraserror:print('''Fav not successful.
Reason: {r}
'''.format(r=error.reason))exceptStopIteration:print(traceback.format_exc())breakreturn"FavConts: {num}".format(num=counter)#if __name__ == "__main__":
# rtweet('', '')
certifi==2019.11.28
chardet==3.0.4
idna==2.8
oauthlib==3.1.0
PySocks==1.7.1
requests==2.22.0
requests-oauthlib==1.3.0
six==1.13.0
tweepy==3.8.0
urllib3==1.25.7
7. lambdaをローカルから実行
実行する前にrequirements.txtからライブラリをインストール
することと、-f
あるいは-function
で実行関数を指定することを忘れないでください。
gkz@localhost ~$ python3.7 -m venv 37 && \
> source 37/bin/activate \
> pip install -r requirements.txt
gkz@localhost ~$ serverless invoke local --function rtweet
gkz@localhost ~$ serverless invoke local --f fav
8. lambdaをデプロイ
デプロイは以下のコマンドで実行できます。
gkz@localhost ~$ serverless deploy -v
gkz@localhost ~$ sls deploy -v
ファイルを編集した際には一度削除してから改めてデプロイします。
やりかたはdeployをremoveに変える
だけです。
gkz@localhost ~$ serverless remove
gkz@localhost ~$ sls remove
9. 参考
- 公式ドキュメント
- サンプルコード
- 環境変数の取り扱い
サンプルリポジトリ