Lambda Python3.6でTweetしてみた[with Serverless Framework]

こんにちは、臼田です。

皆さん、Lambdaしてますか?

今回はLambdaでPython3.6を利用してツイッターにTweetしてみます。

私の目的としては、下記Step Functionsを利用したCVE Listのアップデート確認ですが、

Tweetの機能だけなら汎用的に利用できると思うので、汎用的な手順として書いていきます。

Step FunctionsにLambdaからデータを渡してみた

目次

  1. Twitterでアプリケーションを登録
  2. スクリプト作成
  3. Serverless Framework環境作成
  4. デプロイ

1. Twitterでアプリケーションを登録

ツイッターへの投稿には、利用するアカウントでアプリケーションを作成して、アプリケーション経由でAPIをリクエストする必要があります。

アプリケーション作成

ツイートしたいアカウントでログインして、TwitterのDeveloperサイトにアクセスします。

ページ最下部のTools「Manage my apps」からアプリケーションの管理ページへ移動します。

001_manage_app

「Create New App」をクリックしてアプリケーションを作成します。

002_create_app

適当に情報を入力し「Create your Twitter application」をクリックします。

003_create

これでアプリケーションの作成が完了しました。

API Keyの取得

アプリケーション管理画面から「Keys and Access Tokens」タブに移動し、「Consumer Secret (API Secret)」及び「Consumer Secret (API Secret)」を控えます。

004_api_key

Access Tokenの取得

「Keys and Access Tokens」タブ下方へスクロールし、「Create my access token」をクリックします。

005_create_token

作成された「Access Token」及び「Access Token Secret」を控えます。

006_token

これでTwitter側の準備は完了です。

2. スクリプト作成

ローカル環境でスクリプトを作成します。

環境準備

今回はPython 3.6で用意しますので、私の場合は作業用に作成したフォルダでpyenv localで個別の環境を割り当てています。

$ pyenv virtualenv 3.6.1 tweet_3.6.1
$ pyenv local tweet_3.6.1
$ python --version
Python 3.6.1

パッケージがglobalの環境と混ざらないように分けたい場合には、下記を参考に専用の環境を作成することをおすすめします。

AWS LambdaがPython3.6に対応したのでpyenvとpyenv-virtualenvで環境構築してみた

Tweepyインストール

Twitter APIを利用するには、OAuth2.0を利用した認可が必要になります。

OAuth2.0のパッケージを利用してもいいのですが、Twitter APIに最適化したパッケージがいくつかあるのでそちらを利用します。

今回は、Tweepyを利用してみます。

$ pip install tweepy

スクリプト作成

今回は下記のように作成しました。

# tweet.py
# -*- coding: utf-8 -*-

import os
import tweepy

def lambda_handler(event, context):
	consumer_key = os.environ['TW_CONSUMER_KEY']
	consumer_secret = os.environ['TW_CONSUMER_SECRET']
	access_token = os.environ['TW_ACCESS_TOKEN']
	access_token_secret = os.environ['TW_ACCESS_TOKEN_SECRET']

	auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
	auth.set_access_token(access_token, access_token_secret)

	api = tweepy.API(auth)
	r = api.update_status(event['message'])
	return 0

# call lambda_handler
if __name__ == "__main__":
	import sys
	import json
	lambda_handler(json.loads(sys.argv[1]), {})

本当はエラー処理とか入れたほうがいいですが、とりあえずこれで動きます。

API Key等はハードコーディングしないように環境変数から取るようにしています。

Lambdaは環境変数に対応しているので、このやり方でもいいですし、引数で渡したりS3のjson読みに行ったりしてもいいですが、環境変数だとServerless Frameworkの中で値を管理できます。

tweetする内容は引数で取るようにしています。

末尾はローカルのターミナルから実行する際に引数をlambda_handlerに渡して実行するように指定しています。

環境変数の設定

ローカルでテストするために環境変数の設定をします。

export TW_CONSUMER_KEY=xxxxxxxxxxxxxxxx
export TW_CONSUMER_SECRET=xxxxxxxxxxxxxxxx
export TW_ACCESS_TOKEN=xxxxxxxxxxxxxxxx
export TW_ACCESS_TOKEN_SECRET=xxxxxxxxxxxxxxxx

ローカルからの投稿

試しにローカルから実行してみます。

メッセージはjsonの"message"キーに入れて渡します。

$ python tweet.py '{"message":"test tweet from local"}'

007_tweet_local

無事投稿できました。

3. Serverless Framework環境作成

スクリプトが作成できたので、Serverless Frameworkを利用してLambdaにデプロイできるようにします。

Serverless Frameworkの導入については下記をご参考にして下さい。

なお、今回のバージョン(1.18.0)ではnode.jsは「v6.5.0」が求められます。(参考)

今から始めるServerless Frameworkで簡単Lambda開発環境の構築

serverless-python-requirementsの導入

LambdaでPythonの外部モジュールを利用する場合には、Zipでまとめてアップロードする必要がありますが、serverless-python-requirementsというプラグインを利用すると、デプロイ時に自動的に必要なプラグインをまとめてアップロードしてくれます。

詳しくは下記を参照して下さい。

Serverless Frameworkのプラグインを利用した外部モジュールの管理

私の環境は以下のようになっています。

$ node -v
v6.11.1
$ sls --version
1.18.0
$ npm ls | grep serverless-python-requirements
└─┬ serverless-python-requirements@2.4.1

サービスの作成

Serverless Frameworkでサービスを作成します。

既存のフォルダで作成するので下記のようになります。

$ sls create -t aws-python3 -n tweet

serverless.ymlを変更します。

service: tweet

provider:
  name: aws
  runtime: python3.6

# you can overwrite defaults here
  region: us-west-2
  memorySize: 128
  timeout: 180

  plugins:
  - serverless-python-requirements

functions:
  tweet:
    handler: tweet.lambda_handler
    environment:
      TW_CONSUMER_KEY: xxxxxxxxxxxxxxx
      TW_CONSUMER_SECRET: xxxxxxxxxxxxxxx
      TW_ACCESS_TOKEN: xxxxxxxxxxxxxxx
      TW_ACCESS_TOKEN_SECRET: xxxxxxxxxxxxxxx

custom:
  pythonRequirements:
    pythonBin: python

pluginとしてserverless-python-requirementsを読み込ませています。

また、関数名を適切に変更し、「environment」要素で環境変数を定義しています。

custom部分は次で説明します。

index.jsの編集

serverless-python-requirementsの2.4.1では、残念ながらpyenv環境での実行に対応してません。

具体的には、serverless.ymlで設定したprovicer.runtimeの値をpythonのエイリアスとして使用しようとします。

したがって、LambdaでPython3.6を利用するには、環境上のPythonもpython3.6で呼び出されないとserverless-python-requirementsの2.4.1では正常に動作しません。

対応策として、実際に./node_modules/serverless-python-requirements/index.jsprovicer.runtimeを読み込む部分(80行目ぐらい)を下記のように変更します。

// const runtime = this.serverless.service.provider.runtime;
const runtime = this.serverless.service.custom.pythonRequirements.pythonBin;

これで、serverless.ymlで追記したcustomの設定を読み込むようになります。

※pyenvで動作しない仕様についてはserverless-python-requirementsに修正を依頼しています

requirements.txtの作成

serverless-python-requirementsでまとめる外部モジュールを指定します。

# requirements.txt
tweepy

4. デプロイ

それではsls deploy -vでデプロイします。

デプロイが完了するとマネジメントコンソールのLambdaの画面で作成されていることが確認できます。

008_deploy

sls invoke localコマンドで動作を確認してもいいですが、今回はマネジメントコンソールからテストしてみます。

テストの設定から下記のようにmessageを設定します。

009_test

実行すると、投稿が確認できました。

010_tweet

さいごに

これで、LambdaのPython3.6からツイッターにtweetできました。

serverless-python-requirementsの問題は少しありましたが、それ以外は比較的簡単に実行できる手順かと思います。

さて、次回はStep Functionsでこれまで作成したLambdaをつなげてみます。