CodeStarを使ったAWS lambdaの料金通知Botのデプロイ

  • 1
    いいね
  • 0
    コメント

AWS Codestarがリリースされて少し経ちましたが、まだ東京リージョンには来ないですね。待ち遠しい。

Codestar自体は知っていたのですがAWSSummitTokyoで思った以上に便利そうな印象を受けたことと、LambdaのServerlessApplicationModel(SAM)を使ってみたくなったため、社内向けのbotのデプロイ作業をこれで置き換えてみました。

※2017/7/11現在、東京リージョンにはまだ来ていませんのでバージニアリージョンで試しています

これからやること

請求情報をSkypeへ毎日通知するBotを作成して、CodeStarでデプロイする。

前提

  • LambdaFunctionのコード自体の説明はしません。
  • 他のChatツールへの通知もLambdaのコードを多少書きかえればすぐできると思います。

手順

1. 請求情報を取得できるように設定を行う

右上のアカウントのプルダウンから「請求ダッシュボード」を選択。
左側のメニューの「設定」で、「請求アラートを受け取る」にチェックを入れる。

billingreport.JPG

2. Codestarでプロジェクトを作成

a. マネジメントコンソールからCodeStarを開く
b. ポップアップでロールを作るか?という質問が出るので「yes」を選択。
  裏で「aws-codestar-service-role」ロールが作られています。
  (初回やったときにこれが出なくて権限エラーが出てはまりました・・)
c. 「create new project」
d. pythonの新規プロジェクトを選択
codestar.JPG

e. プロジェクト名を「billing-bot」にして「create project」
codestar2.JPG

f. 開発ツールを選択する画面はSkipして完了。
codestar3.JPG

なんかかっこいい画面が出てきましたね。

3. CodeCommitからひな形となるファイルをclone

CodeCommitのgitリポジトリにはsshで接続するものとします。

a. マネジメントコンソールのIAMを開く。
b. ユーザ一覧から利用しているユーザを選択する。
c. 認証情報欄で[SSH公開キーのアップロード]を押して、公開鍵を張り付ける。
d. SSHキーIDが表示されるのでメモっておく。
e. SSHキーIDが覚えにくいのでCodeCommitの場合はこのsshキーを使うということをconfigに書いておく。 <SSH_KEY_ID>は自分のSSHキーIDに置き換えること。

$ cat ~/.ssh/config
Host git-codecommit.*.amazonaws.com
User <SSH_KEY_ID>
IdentityFile ~/.ssh/id_rsa

f. git cloneしてパスフレーズを入力するとcloneが実行される。
 (cloneするURLはCodeCommitのページに行けば参照可能です。

$ git clone ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos/<REPOSITORY_NAME>
Enter passphrase for key '*****/.ssh/id_rsa':

httpsでの接続の場合は下記手順 c の下のあたりに認証情報(ユーザ名、パスワード)を生成する手順がありましたので、それでできると思います

初期ファイルはこのようになっていました

$ cd <REPOSITORY_NAME>
$ find -type f | grep -v .git
./buildspec.yml
./index.py
./README.md
./template.yml

READMEに説明が書かれています(英語ですが)

  • buildspec.yml - コードをパッケージングする方法を記載したCodeBuild用設定ファイル
  • index.py - Lambdaの実行コード
  • template.yml - LambdaとAPIGatewayにデプロイするためのSAMの設定ファイル

非常にわかりにくかったのですが、buildspec.ymlの中の

aws cloudformation package --template template.yml --s3-bucket $S3_BUCKET --output-template template-export.json

にてtemplate.ymlをtemplate-export.jsonに変換して出力されます。
そして、ServerlessApplicationModelでデプロイする際はtemplate-export.jsonを使用しているようです。

template.ymlではGetEvent/PostEventなどからも想像できるようにApiGatewayの設定も行われています。

4. 実行コードの作成

lambdaの実行コードを書いていきます。作成対象は以下の★がついている3つのファイルです。
index.pyはいらないので消しておいてもOKです

$ find  -type f | grep -v .git
./buildspec.yml
./README.md
./requirements.txt   ★
./src/billing_bot.py  ★
./src/skype_adapter.py ★
./template.yml
requirement.txt
requests
pytz
src/billing_bot.py
# -*- coding: utf-8 -*-

from skype_adapter import SkypeAdapter
from datetime import datetime, timedelta

import boto3
import pytz
import os

def post_current_charges():

    startDate = datetime.today() - timedelta(days = 1)
    endDate = datetime.today()

    session = boto3.Session()
    client = session.client('cloudwatch')   
    response = client.get_metric_statistics (
        MetricName = 'EstimatedCharges',
        Namespace  = 'AWS/Billing',
        Period   = 86400,
        StartTime  = startDate,
        EndTime = endDate,
        Statistics = ['Maximum'],
        Dimensions = [
            {
                'Name': 'Currency',
                'Value': 'USD'
            }
        ]
    )

    maximum = response['Datapoints'][0]['Maximum']
    date = response['Datapoints'][0]['Timestamp'].strftime('%Y/%m/%d')

    roomid = os.environ['ROOM_ID']
    message = date + "時点でのAWS利用料は" + str(maximum) + "ドルです" 
    print message

    SkypeAdapter().postConversation(roomid, message)

def lambda_handler(event, context):
    """Lambda使う場合のエントリポイント"""
    post_current_charges()


if __name__ == "__main__":
    """コマンド実行のエントリポイント"""
    post_current_charges()
src/skype_adapter.py
# -*- coding: utf-8 -*-

import requests
import json
import os

class SkypeAdapter:
    """ Skypeに投稿するためのAdapterClass """

    def postConversation(self, roomid, message):
        """ Skypeへメッセージを投稿する """
        token = self.__auth()
        self.__post( token, roomid, message )

    def __auth(self):
        """ MicrosoftBotFrameworkのOAuthClient認証を行いaccess_tokenを取得する """

        headers = { 'Content-Type' : 'application/x-www-form-urlencoded' }
        data = {
            'grant_type' : 'client_credentials',
            'client_id' : os.environ['CLIENT_ID'],
            'client_secret' : os.environ['CLIENT_SECRET'],
            'scope' : 'https://graph.microsoft.com/.default'
        }

        access_token_response = requests.post( 'https://login.microsoftonline.com/common/oauth2/v2.0/token', headers=headers, data=data )

        if access_token_response.status_code != 200 :
            print access_token_response.headers
            print access_token_response.text
            raise StandardError('Skype OAuth Failed')

        tokens = json.loads(access_token_response.text)
        return tokens['access_token']

    def __post(self, token, roomid, message):
        """ MicrosoftBotFrameworkのチャット投稿用RESTAPIを叩く """

        headers = { 
            'Authorization' : 'Bearer ' + token,
            'Content-Type' : 'application/json'
        }

        data = {
            'type' : 'message/text',
            'text' : message
        }

        url = 'https://api.skype.net/v3/conversations/' + roomid + '/activities/'

        response = requests.post( url, headers=headers, json=data)

        if response.status_code != 201 :
            print response.status_code
            print response.headers
            print response.text
            raise StandardError('Skype Post Failed')

        return

5. buildspec.yml

CodeBuildでの処理内容を定義するbuildspec.ymlを編集していきます。
installフェーズを追加して、pipを使って関連ライブラリの取得を行います。

version: 0.1

phases:
  install:
    commands:
      - pip install -r requirements.txt -t ./src
  build:
    commands:
      - aws cloudformation package --template template.yml --s3-bucket $S3_BUCKET --output-template template-export.json
artifacts:
  type: zip
  files:
    - template-export.json

ただ、これをCodeCommitにpushしてBuildを実行しても落ちました・・

CodeBuildを詳しく見てみると、「ビルド環境」というもので実行環境のイメージを選択する部分があり、なぜか nodejs のイメージが選択されている!!!

codebuild2.JPG

怒りを抑えて、python用のイメージに変えておきましょう。

codebuild3.JPG

6. template.ymlの設定

最後はLambdaをデプロイするためのtemplate.ymlです。
環境変数の部分は自分の環境にあったものを指定してください。アカウント取得方法は過去記事を参考に。

AWSTemplateFormatVersion: 2010-09-09
Transform:
- AWS::Serverless-2016-10-31
- AWS::CodeStar
Description: AWS利用料を通知するbot

Parameters:
  ProjectId:
    Type: String
    Description: CodeStar projectId used to associate new resources to team members

Resources:
  billingbot:
    Type: AWS::Serverless::Function
    Properties:
      Handler: billing_bot.lambda_handler
      Runtime: python2.7
      CodeUri: src
      Description: AWS利用料を通知するbot
      MemorySize: 128
      Timeout: 60
      Role: arn:aws:iam::860559588436:role/CodeStarWorker-billing-bot-Lambda
      Events:
        Schedule1:
          Type: Schedule
          Properties:
            Schedule: cron(0 1 * * ? *)
      Environment:
        Variables:
          CLIENT_SECRET: ********
          ROOM_ID: '19:*******@thread.skype'
          CLIENT_ID: *******

7. IAMロールに足りていない権限を付与

今回の用途に応じた権限をIAMのロールの設定画面から付与します。

  • CloudFormationのロール:スケジュール実行用トリガ作成用にCloudWatchEventFullAccessが必要
  • Lambdaのロール:請求情報を参照するためにCloudWatchReadOnlyAccessが必要

これらがないとビルドが当然ビルドは落ちます。
ここもはまりポイントでした。落ちてからじゃないとロールが作られていることにすら気づかなかったです。

ここも怒りを抑えてポチポチ設定。

8. CodeCommitにコードをpush

最後にこれらのコードをpushするとCodePipelineが動き出します。あとは、じっと待ちましょう。

9. テスト

Lambdaの画面を開いて、テスト!(パラメータは適当でいいです)

Skypeに通知が届けばテスト成功です!
bot.JPG

あとは毎日朝10時に自動投稿してくれます。

感想

なんか遅い

  • コードをCodeCommitにプッシュしてからのPipelineの起動に少し間が空く
  • CodeCommitがInProgress状態になる(単にpushされただけなのに何が動いているのか不明)

逆にCodeBuildはDockerベースなのに思ったより早い。

完成度がまだ微妙

  • CodeCommitはプルリクができなかったり
  • CodeBuild用のDockerイメージがなぜかnodeJSで作られていたり
  • 裏で勝手に作られているIAMロールに対して権限をつけることがわかりにくかったり
  • (Cloudformationの機能ですが)失敗したときのデバッグ・リトライがつらかったり

と微妙なところも多いですが、トータルで見れば今後に期待が持てるサービスですね。