サーバーレスでEvernoteに登録したブックマークをランダムにSlackへ通知するアプリを作る②
前回はEvernoteAPIを利用して、Bookmarkの一覧を取得するところまでを解説しました。今回は、AWS SAMを使ってサーバーレスなインフラを構築していきます。
参考
AWS CLIの準備
まずはこれがないと始まりません。pipでインストールしましょう。
pip install awscli
AWS CLIの初期設定を行います。 ※予めAWSコンソールからsam-sampleというユーザーを作成し、アクセスキーを取得しておきます。
$ aws configure --profile sam-sample AWS Access Key ID [None]: A**** AWS Secret Access Key [None]: U**** Default region name [None]: ap-northeast-1 Default output format [None]: json
S3バケットの準備
SAMでのデプロイまでの流れは以下の通りです。
- SAMテンプレートファイルをパッケージ化しS3に保存。
- パッケージ化したテンプレートを使用するよう記述したCloudFormationテンプレートファイルを作成。
- CloudFormationテンプレートファイルに基づいてデプロイ。
パッケージ化したSAMテンプレートファイルを保存するためのS3バケットをあらかじめ作成しておきます。さきほど作成したsam-sampleユーザーにバケット作成権限を持たせている場合には下記コマンドで作成可能です。バケット名はグローバルでユニークである必要がありますので、被らない名前をつけてください。
aws s3 mb s3://sam-bucket --profile sam-sample
SAMテンプレートの作成
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Create Lambda function by using AWS SAM. # 共通設定 Globals: Function: Runtime: python2.7 Timeout: 15 MemorySize: 256 # リソース定義 Resources: GetBookmarkLambda: Type: AWS::Serverless::Function Properties: CodeUri: 'get-bookmark/lambda_function.zip' Handler: handler.handler Events: Timer: Type: Schedule Properties: Schedule: rate(5 minutes)
AWSTemplateFormatVersionは現在のところ'2010-09-09'しかありませんので、これを指定してください。
TransformではAWS::Serverless-2016-10-31を指定するとyaml形式でテンプレートを記述することができます。元々はjsonで記述するしかなかったのですが、現在ではより可読性の高いyamlで記述するのがおすすめです。
Resourcesで実際に使用するリソースを定義していきます。SAMで使用できるリソースはLambda(AWS::Serverless::Function)、APIGateway(AWS::Serverless::Api)、DynamoDB(AWS::Serverless::SimpleTable)などがあります。今回の構成ではLambdaしか使いません。
Lambda functionの作成
# coding:utf-8 import random import logging from HTMLParser import HTMLParser from evernote.api.client import EvernoteClient from evernote.edam.notestore.ttypes import NoteFilter, NotesMetadataResultSpec import settings class LinkParser(HTMLParser): # ノート内のURLを抽出するパーサー def __init__(self): HTMLParser.__init__(self) self.url = "" def handle_starttag(self, tag, attrs): if tag == "a": # 開始タグがaであるかどうか判定 attrs = dict(attrs) # タプルを辞書に変換する if 'href' in attrs: # キー値(属性名)がhrefであるか判定 self.url = attrs['href'] @property def link(self): return self.url def handler(event, context): # Evernoteクライアントを初期化する(1) client = EvernoteClient(token=settings.AUTH_TOKEN, sandbox=False) note_store = client.get_note_store() # ノートブック一覧の取得(2) notebooks = note_store.listNotebooks() logger = logging.getLogger() logger.setLevel(logging.INFO) for notebook in notebooks: # bookmarksという名前のノートブックにブックマークを保存している。 if notebook.name == 'bookmarks': # 検索条件設定:指定ノートブックにひも付くノート(3) filter = NoteFilter() filter.notebookGuid = notebook.guid # 検索結果設定:ノートタイトルのみ取得(4) resultSpec = NotesMetadataResultSpec() resultSpec.includeTitle = True # ノートメタデータの検索(5) metalist = note_store.findNotesMetadata(filter, 0, 10000, resultSpec) parser = LinkParser() # ノートGUIDを検索条件にノートデータの取得(6) # ランダムに5件のbookmarkを抽出 for n in random.sample(metalist.notes, 5): content = note_store.getNoteContent(n.guid) parser.feed(content) logger.info('Note title: {}\tLink: {}'.format(n.title, parser.link)) return 'OK!'
前回書いたコードをLambda用に少し書き換えました。NOTEのタイトルとブックマークURLをログに出力するようにしています。
デプロイ
今回はLambdaでPython外部モジュールのevernoteを使うので、Lambda functionのコードとモジュールそのものを一緒にzipで固めてデプロイする必要があります。 まずは外部モジュールをカレントディレクトリにインストールします。
pip install evernote -t ./
次にzip圧縮
zip -r lambda_function.zip ./
テンプレートをパッケージ化させてさきほど作成したS3バケットにアップロードします。
aws cloudfront package \ --template-file template.yaml \ --s3-bucket sam-backet \ --output-template-file packaged-template.yaml \ --profile sam-sample
このコマンドでtemplate.yamlの内容をパッケージ化し、それをインポートしたpackaged-template.yamlという名称のCloudFormation用テンプレートファイルができあがります。後はこのテンプレートを基にCloudFormationでデプロイします。
aws cloudformation deploy \ --template-file packaged-template.yaml \ --stack-name get-bookmark-stack \ --capabilities CAPABILITY_IAM \ --profile sam-sample
CloudFormationでは、テンプレートで定義されたひと塊のリソース群をスタックと呼びます。今回は、get-bookmark-stack
というスタック名をつけてデプロイしました。あとは自動的にリソースが作製されてデプロイ完了です。5分ごとにLambdaが実行されているログが確認出来れば成功です。
まとめ
今回はLambdaを用いたサーバーレスなインフラ構成をAWS SAMを使ってデプロイするところまで紹介しました。次回はslackへの通知機能を実装していきます。