俺のアウトプット

調べたこと、試したことを書きます

AWS Cloud9上で作成したLambda関数で環境変数を利用する

Lambdaコード内で、設定値を切り替えたい場合があります。

例えば、本番環境と検証環境で接続先を変えたい、などです。

このような場合、環境変数に値を設定して、コード内から環境変数を参照します。

特定の言語に依存せず参照できますし、コードから定数が消えるので、セキュリティ的にもGoodです。

AWS Serverless Application Model

しかし .bash_profile に環境変数を追加しても、Lambda関数内から取得はできません。

AWS Cloud9 EC2環境には、AWS Serverless Application Model (SAM) がデフォルトでインストールされています。

$ sam --version
A newer version of the AWS SAM CLI is available!
Your version:   0.2.11
Latest version: 0.6.0
See https://github.com/awslabs/aws-sam-local for upgrade instructions

sam version 0.2.11

実は、AWS Cloud9上で作成したLambda関数は、このSAMを利用しており、Lambda関数内から環境変数を利用する場合は、SAM用のテンプレート template.yaml に追記する必要があります。

ローカル上にLambda関数を作成

環境変数を定義して、コードから参照できるか試してみましょう。

まずはLambda関数を作成します。

右側サイドバーにある、[AWS Resources] を選択して [AWSリソース] ウィンドウを開き、[λ+] ボタンをクリックします。

  • 関数名
    • checkSite
  • アプリケーション名
    • CheckSite
  • ランタイム
    • Python 3.6
  • 設計図
    • lambda-canary-python3
      • 指定したサイトを定期的にチェック、テスト失敗時にエラー
  • 関数トリガー
    • none
  • メモリ
    • 128MB
  • ロール
    • 自動生成(Automatically generate role)

AWS Cloud9上に、CheckSiteディレクトリが作成されます。

Lambda関数

作成されたLambda関数です。

デフォルトの状態で、環境変数を参照しています。 このまま利用します。

lambda_function.py

import os
from datetime import datetime
from urllib.request import urlopen

SITE = os.environ['site']  # URL of the site to check, stored in the site environment variable
EXPECTED = os.environ['expected']  # String expected to be on the page, stored in the expected environment variable


def validate(res):
    '''Return False to trigger the canary

    Currently this simply checks whether the EXPECTED string is present.
    However, you could modify this to perform any number of arbitrary
    checks on the contents of SITE.
    '''
    return EXPECTED in res


def lambda_handler(event, context):
    print('Checking {} at {}...'.format(SITE, event['time']))
    try:
        if not validate(str(urlopen(SITE).read())):
            raise Exception('Validation failed')
    except:
        print('Check failed!')
        raise
    else:
        print('Check passed!')
        return event['time']
    finally:
        print('Check complete at {}'.format(str(datetime.now())))
  • 環境変数 siteexpected から値を取得
    • site: チェックするサイトのURL
    • expected: HTMLに含むチェックするキーワード
  • 入力値
    • time: チェックする日時を指定

template.yaml

[環境] ウィンドウから、CheckSiteディレクトリ直下の template.yaml をダブルクリックして、SAM用のテンプレートを [タブ] ウィンドウに表示します。

Propertiesに Environment / Variables キーに、環境変数を追記して保存します。

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Serverless Specification template describing your function.
Resources:
  checkSite:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: checkSite/lambda_function.lambda_handler
      Runtime: python3.6
      Description: ''
      MemorySize: 128
      Timeout: 15
      CodeUri: .debug/
      Environment:
        Variables:
          site: 'https://oreout.hatenablog.com'
          expected: '2018-09-29'

f:id:kitsugi:20180930170054p:plain

ローカル上のLambda関数を実行

正しく動作するか、ローカル上で確認をします。

  1. [Local Functions] 内のLambda関数checkSiteを右クリックしてメニュー表示
  2. [Run] - [Run Local] を選択
  3. [Payload] ペインに入力値を設定して [Run] ボタンをクリック

ブログトップに指定した日付のエントリがあるかどうかを確認します。

見つかった場合

環境変数 expected にトップページに存在するキーワードを設定

Payload

{
    "time": "2018/09/30 17:00"
}

Response

"2018/09/30 17:00"

f:id:kitsugi:20180930170112p:plain

見つからなかった場合

環境変数 expected にトップページに存在しないキーワードを設定

Payload

{
    "time": "2018/09/30 17:00"
}

Response

{
    "errorMessage": "Validation failed",
    "errorType": "Exception",
    "stackTrace": [
        [
            "/var/task/checkSite/lambda_function.py",
            24,
            "lambda_handler",
            "raise Exception('Validation failed')"
        ]
    ]
}

f:id:kitsugi:20180930170127p:plain

意図した通りに実行されました。

AWS上でLambda関数を実行

ローカル上のLambda関数をAWS上にデプロイして実行します。

デプロイ

  1. [Local Functions] 内のLambda関数checkSiteを右クリックしてメニュー表示
  2. [Deploy] を選択

Lambda関数のリモート実行

  1. [Local Functions] 内のLambda関数checkSiteを右クリックしてメニュー表示
  2. [Run] - [Run Remote] を選択

AWS Lambdaコンソール画面上で、環境変数が設定されていることが確認できます。

f:id:kitsugi:20180930170141p:plain

参考

AWS Cloud9上で作成したLambda関数でPython3外部モジュールを利用する

Lambda関数でPythonの外部モジュールを利用する場合、Lambda関数デプロイパッケージに含める必要があります。

ローカルの場合は、zipにまとめてAWS Lambdaへアップロードする必要がありますが、AWS Cloud9上からデプロイする場合(つまりSAM)は簡単です。

Lambda関数を作成・デプロイ・実行の仕方は、下記のエントリを参照してください。

ローカル上にLambda関数を作成

ローカル上にLambda関数を作成します。

右側サイドバーにある、[AWS Resources] を選択して [AWSリソース] ウィンドウを開き、[λ+] ボタンをクリックします。

  • 関数名
    • getLastDayOfNextMonth
  • アプリケーション名
    • GetLastDayOfNextMonth
  • ランタイム
    • Python 3.6
  • 設計図
    • empty-python
  • 関数トリガー
    • none
  • メモリ
    • 128MB
  • ロール
    • 自動生成(Automatically generate role)

AWS Cloud9上に、GetLastDayOfNextMonthディレクトリが作成されます。

年月を指定して翌月末日を取得するLambda関数

lambda_function.pyを下記コードに差し替えて、保存 (Command+S) します。

lambda_function.py

import datetime
from dateutil import relativedelta

def lambda_handler(event, context):
    today = datetime.date.today()
    first_day = datetime.date(event.get('year', today.year), event.get('month', today.month), 1)
    last_day = first_day + relativedelta.relativedelta(months=+1, day=99)

    return { 
        'year' : last_day.year,
        'month': last_day.month,
        'day': last_day.day
    }

今回は、日付操作用のdateutilsモジュールを利用します。

  • 入力に年月を指定
  • 省略した場合は当日の年月をデフォルト値に設定
  • 戻り値に翌月末日の年月日を返却
    • 2018年12月なら、2019年1月31日

外部モジュールのインストール

dateutilsモジュールをインストールします。

ターミナルから、GetLastDayOfNextMonthディレクトリに移動してインストールします。

$ cd GetLastDayOfNextMonth/
$ pip-3.6 install dateutils -t .

-tはどこにインストールするかのターゲットを、.はカレントディレクトリを意味します。

必要なモジュールがインストールされました。

f:id:kitsugi:20180929171723p:plain

ローカル上のLambda関数を実行

正しく動作するか、ローカル上で確認をします。

  1. [Local Functions] 内のLambda関数getLastDayOfNextMonthを右クリックしてメニュー表示
  2. [Run] - [Run Local] を選択
  3. [Payload] ペインに入力値を設定して [Run] ボタンをクリック

Payload

{
  "year": 2018,
  "month": 12
}

Response

{
    "year": 2019,
    "month": 1,
    "day": 31
}

正しく表示されました。

AWS上でLambda関数を実行

ローカル上のLambda関数をAWS上にデプロイして実行します。

デプロイ

  1. [Local Functions] 内のLambda関数getLastDayOfNextMonthを右クリックしてメニュー表示
  2. [Deploy] を選択

Lambda関数のリモート実行

  1. [Local Functions] 内のLambda関数getLastDayOfNextMonthを右クリックしてメニュー表示
  2. [Run] - [Run Remote] を選択

AWS Lambdaコンソール画面上で、モジュールがデプロイされていることが確認できます。

f:id:kitsugi:20180929171742p:plain

参考

AWS Cloud9からPython3で作成したLambda関数を実行する

AWS Cloud9上で、Lambda関数を作成して実行します。

大まかな流れは下記になります。

  1. ローカル上にLambda関数を作成
  2. ローカル上のLambda関数を(デバッグ)実行
  3. AWS上にLambda関数をデプロイして実行

ここでいうローカルとは、ブラウザで起動したCloud9環境を指します。

Lambda関数はPython3で作成します。
Python3の構築は下記エントリを参照してください。

AWS Lambdaの連携先リージョン

その前にLambdaの連携先リージョンを変更します。

EC2環境のデフォルトでは、Lambdaの連携先リージョンは、AWS Cloud9を起動したリージョンと同じになります。

AWS Cloud9 EC2環境をシンガポールリージョンで作成した場合、Lambdaや実行ロール、それを作成するCloudFormationは、同じシンガポールリージョンに作成されます。

今回は、Lambdaの連携先をシンガポールリージョンから東京リージョンに変更します。

  1. ツールバー右側の歯車アイコンを選択して、プロジェクト設定を開く
  2. [AWS SETTINGS] を選択
  3. [AWS Region] をAsia Pacific (Tokyo)に変更

f:id:kitsugi:20180927075616p:plain

特に設定反映ボタンはありません。

ローカル上にLambda関数を作成

ローカル上、つまりAWS Cloud9 環境上に、Lambda関数を作成します。

右側サイドバーにある、[AWS Resources]を選択して [AWSリソース] ウィンドウを開きます。

ウィンドウ右上に、プロジェクト設定で選択した東京リージョン ap-northeast-1 が表示されています。

それではLambda関数を作成しましょう。 オレンジ色の [λ+] ボタンをクリックします。

f:id:kitsugi:20180927075625p:plain

サーバーレスアプリケーションの作成

関数名アプリケーション名を入力します。

f:id:kitsugi:20180927075632p:plain

ここで入力した関数名アプリケーション名は、Cloud9環境内のディレクトリ名、AWS Lambda関数名に使われます。

f:id:kitsugi:20180928060910p:plain

f:id:kitsugi:20180928062411p:plain

ランタイム(言語とバージョン)とテンプレートとなる設計図を選択します。

  1. ランタイムからPython 3.6を選択
  2. 設計図としてhello-world-python3を選択
  3. [Next] ボタンをクリック

f:id:kitsugi:20180927075650p:plain

関数トリガーでは、API Gateway と連携するかを設定します。
今回はデフォルトのnoneのまま、[Next] ボタンをクリックします。

f:id:kitsugi:20180927075657p:plain

メモリロールを選択します。
今回はデフォルトのまま、[Next] ボタンをクリックします。 ロールは自動生成されます。

f:id:kitsugi:20180927075706p:plain

確認画面です。
問題がなければ、[Finish] ボタンをクリックします。

f:id:kitsugi:20180927075714p:plain

ローカル上にいくつかのファイルが作成され、指定したリージョンにAWS Lambda関数(とロール)が作成されます。

Lambdaコードの修正

環境ウィンドウ内のlambda_function.pyをダブルクリックして、タブウィンドウにLambda関数を表示します。

f:id:kitsugi:20180928064947p:plain

下記コードに差し替えて、保存 (Command+S) します。

lambda_function.py

import json

print('Loading function')

def lambda_handler(event, context):
    message = 'Hello {} {}!'.format(event['first_name'], event['last_name'])

    return {
        'message' : message
    }

入力された名前と文字を連結して返すだけのシンプルなコードです。

ローカル上のLambda関数を実行

作成したLambda関数をローカル上で実行します。
操作は [AWSリソース] ウィンドウから行います。

  1. [Local Functions] 内のLambda関数myHelloWorldを右クリックしてメニュー表示
  2. [Run] - [Run Local] を選択

f:id:kitsugi:20180927075740p:plain

[実行] タブが表示されます。

[実行] タブ内の [Payload] ペインで、JSON形式で入力データを登録します。

{
  "first_name": "太郎",
  "last_name": "山田"  
}

[Run] ボタンをクリックすると、結果が下に表示されます。

f:id:kitsugi:20180927075748p:plain

ローカル上でLambda関数をデバッグする

今度はデバッグ実行してみましょう。

行番号の横をクリックして、ブレークポイントを貼ります。 赤い円が表示されます。

f:id:kitsugi:20180928052935p:plain

[Run] ボタン右側の [バグ] アイコンをクリックします。アイコンが灰色から緑に切り替わります。
この状態で、[Run] ボタンをクリックします。

f:id:kitsugi:20180928052947p:plain

ブレークポイントでコードが一時停止され、[デバッガー] ウィンドウに現在の値が表示されます。

f:id:kitsugi:20180928052956p:plain

AWS上でLambda関数を実行

ローカル上のLambda関数をAWS上にデプロイして実行します。
操作は [AWSリソース] ウィンドウから行います。

AWS上にデプロイ

  1. [Local Functions] 内のLambda関数myHelloWorldを右クリックしてメニュー表示
  2. [Deploy] を選択

指定したリージョンへLambda関数がデプロイされます。

f:id:kitsugi:20180927075804p:plain

Lambda関数のリモート実行

  1. [Local Functions] 内のLambda関数myHelloWorldを右クリックしてメニュー表示
  2. [Run] - [Run Remote] を選択

f:id:kitsugi:20180927075811p:plain

[実行] タブ内の接続先が、Lambda (remote)になっていることを確認して、[Run] ボタンをクリックします。

f:id:kitsugi:20180928074312p:plain

[結果] ペインにステータスコード200が表示されていれば成功です。

削除

作成したLambda関数を削除する場合、AWS側のリソースとローカル上の両方を削除する必要があります。

AWS側のリソース

CloudFormationから削除します。

[サービス] - [管理ツール] - [CloudFormation] からスタックを削除します。

f:id:kitsugi:20180927075756p:plain

ローカル上

AWS Cloud9 [環境] パネル上のアプリケーションディレクトリを右クリックして削除します。

まとめ

AWS Cloud9を利用すれば、Lamba関数を簡単にデバッグすることができます。
PC上にローカル環境(aws-sam-cli)を構築する必要はありません。

今回は触れていませんが、既存のLamba関数をAWS Cloud9上にインポートすることもできます。

気軽に試してみましょう。

参考

AWS Cloud9 IDE内でWebアプリケーションを表示する

AWS Cloud9 IDE内で、実行中のWebアプリケーションを表示することができます。

今回は、Flaskを利用して確認します。
FlaskはPythonの軽量なWebアプリケーションフレームワークです。

Python3の構築は下記エントリを参照してください。

Flaskのインストール

Flaskをpip経由でインストールします。

$ sudo pip-3.6 install flask
Collecting flask
  Downloading
略
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24

コードの作成

確認用のテストコードを作成します。
メニューバー [File] - [New From Template] - [Python File] を選択します。

f:id:kitsugi:20180926064259p:plain

タブウィンドウに新規エディタが表示されるので、下記のコードを貼り付けて保存します。
ポートは8080を指定します。

test.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run(debug=True, port=8080)

IDE内からWebアプリケーションを表示するには、ポート8080を指定する必要があります。
それ以外のポート番号だとエラーが表示されます。

f:id:kitsugi:20180926073923p:plain

※ドキュメントだとポート番号80818082も許可してるっぽいけど、エラーになる。なぜ?

Webアプリケーションの表示

  1. [Run]ボタンを選択
  2. Webアプリケーションが実行 (Running on http://127.0.0.1:8080/)
  3. [Preview]ボタンを選択
  4. [Preview Running Application] を選択

f:id:kitsugi:20180926061901p:plain

IDE上にアプリケーションプレビュータブが開かれ、Hello World! と表示されました。

f:id:kitsugi:20180926061908p:plain

アプリケーションプレビュータブ

  1. プレビューをリロードします。
  2. アドレスバー。URLを入力します。
  3. プレビューを新しいウィンドウで開きます。

f:id:kitsugi:20180926070705p:plain

参考

AWS Cloud9でPython3の環境を構築する

EC2環境を作成した直後はPython2系を利用できます。
Python3系を利用するためにはプロジェクト設定を変更する必要があります。

EC2環境の構築は下記エントリを参照してください。

プロジェクト設定

Python用の設定項目を表示します。

  1. メニューバー右側の歯車アイコンを選択
  2. プロジェクト設定画面 [PROJECT] - [Python Support] を選択

f:id:kitsugi:20180925055741p:plain

Python Version

使用するPythonのバージョンを指定します。 Python3 に変更します。

注意点として、ここで設定したPythonのバージョンは、IDE上で実行したコード用(ランナー)であって、ターミナル上のPythonとは関係ありません。

ターミナル上のPythonは、エイリアス変更しない限り2.7系のままです。

$ python -V
Python 2.7.14

Enable Python code completion

ONの場合、Pythonコード補完を有効にします。
デフォルトではONです。

この項目は PYTHONPATH のパスを参照します。 コード補完が効いていない場合、パスが間違っている可能性があります。

デフォルト値

/usr/local/lib/python2.7/dist-packages:/usr/local/lib/python3.4/dist-packages:/usr/local/lib/python3.5/dist-packages

パスをコロン(:)で連結します。

デフォルトでは2.73.43.5を参照していますが、インストールされているPythonを確認すると2.73.6です。(2018年9月25時点)

$ ls /usr/local/lib -l
total 8
drwxr-xr-x 3 root root 4096 Jan 15  2018 python2.7
drwxr-xr-x 3 root root 4096 Sep 13 11:57 python3.6

Python3系でコード補完をするためには、PYTHONPATH値を修正します。

修正した値

/usr/local/lib/python2.7/dist-packages:/usr/local/lib/python3.6/dist-packages

プロジェクト設定の保存場所

プロジェクト設定画面で設定した値は、JSON形式でproject.settingsファイルに保存されます。

  1. 環境ウィンドウにある歯車アイコンを選択
  2. [Show Hidden Files]をチェックして隠しファイルを表示
  3. .c9フォルダ内のproject.settingsファイルをダブルクリック
  4. エディタタブ上に表示された、設定ファイルpython項目を確認

f:id:kitsugi:20180925064120p:plain

自分の環境では、プロジェクト設定からPythonのバージョンを変更したにも関わらず、反映されないケースがありました。 (リロードすると元に戻る)

その場合は、直接project.settingsファイルを編集して保存しました。

Pythonバージョン確認

新規にファイルを作成してバージョンを確認してみます。
コード補完が効いているか確認をしてください。

test.py

import sys

print(sys.version_info)

ファイルを保存して[Run]ボタンをクリックして実行します。
ランナーウィンドウにバージョンが表示されます。

Python3の場合

f:id:kitsugi:20180925070013p:plain

Python2の場合

f:id:kitsugi:20180925070021p:plain

ランナーウィンドウのRunner:言語ボタンから直接変更もできます。

ターミナル

ターミナルからPythonのバージョンを確認します。
エイリアス変更はご自由に。

$ python -V
Python 2.7.14

$ pip -V
pip 9.0.3 from /usr/lib/python2.7/dist-packages (python 2.7)

$ python3 -V
Python 3.6.5

$ pip-3.6 -V
pip 9.0.3 from /usr/lib/python3.6/dist-packages (python 3.6)

参考

  • AWSドキュメント
    • AWS Cloud9 Integrated Development Environment (IDE) でプロジェクト設定を操作する

AWS Cloud9とCodeCommitを統合する

AWS Cloud9 内から AWS CodeCommit リポジトリでコード管理を行うことができます。

AWS Cloud9 EC2環境を起動します。
AWS CodeCommitリポジトリは事前に用意しておいてください。

AWS Cloud9 EC2環境の構築は、下記エントリを参照してください。

AWS CodeCommitの前準備は、下記エントリを参照してください。

Gitの設定

AWS Cloud9内のターミナルウィンドウから、gitがインストールされていることを確認します。

$ git --version
git version 2.14.4

git configコマンドで、Gitコミットと関連付けるユーザー名メールアドレスを設定します。

$ git config --global user.name "Hoge Fuga"
$ git config --global user.email hoge-fuga@example.com

AWS CLIの認証情報ヘルパー設定

ターミナルウィンドウから、下記のコマンドを入力し、HTTPS接続用のAWS CLIの認証情報ヘルパーを設定します。
認証ヘルパーを設定すると、毎回ユーザーとパスワードを入力する手間がなくなります。

$ git config --global credential.helper '!aws codecommit credential-helper $@'
$ git config --global credential.UseHttpPath true

設定した値は、git config -l で確認できます。

$ git config -l
core.editor=/usr/bin/nano
user.name=Hoge Fuga
user.email=hoge-fuga@example.com
credential.helper=!aws codecommit credential-helper $@
credential.usehttppath=true

CodeCommitリポジトリの作成

リポジトリを作成します。

$ aws codecommit create-repository --repository-name SampleRepo
{
    "repositoryMetadata": {
        "repositoryName": "SampleRepo", 
        "cloneUrlSsh": "ssh://git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/SampleRepo", 
        "lastModifiedDate": 1536974158.19, 
        "repositoryId": "905607e6-13f3-4a0e-698b-b1d0c6bd6146", 
        "cloneUrlHttp": "https://git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/SampleRepo", 
        "creationDate": 1536974158.19, 
        "Arn": "arn:aws:codecommit:ap-southeast-1:123456789012:SampleRepo", 
        "accountId": "123456789012"
    }
}

Cloud9を起動したリージョン上にリポジトリが作成されます。

別のリージョンに作成する場合は、--regionオプションを指定します。

$ aws codecommit create-repository --repository-name SampleRepo --region ap-northeast-1

CodeCommitリポジトリのクローンを作成

ターミナルウィンドウから、git cloneコマンドを実行します。

$ git clone https://git-codecommit.<リージョン>.amazonaws.com/v1/repos/<リポジトリ名>

URLは、 CodeCommitのダッシュボードのURL列のアイコンから HTTPS を選択することでコピーできます。

f:id:kitsugi:20180915095453p:plain

参考

技術ブログ始めて2ヶ月たちました

早いもので、ブログを立ち上げてから2ヶ月が経ちました。

当初は、週に1〜2エントリを目標にしていましたが、気がつけば 30エントリ。
2日に1エントリのペース。 内容は別にしても、まずまずです。

「ブログ面倒だからやめるかぁ、どうせ無料だし」
と、言い訳するのを防ぐため、すぐにPro(1年払い)に切り替えました。

技術ブログを書く目的

大きく2つあります。

インプットだけでは忘れやすい

自分はインプットはそこそこしている方だと思います。

電車の中でスマホで調べたり、自宅で気になる技術を調査したり。。

それでも、使わないと段々と忘れてしまいます。 誰だってそうだと思うんですよね。年をとるとなおさらです。

そこで、ブログでアウトプットです。

このブログを見ている人はあまりいませんが、インプットした情報をまとめて、文章構成を考えて、間違いがないかを確認する。

この工程があるから、断然インプットだけよりかは、忘れにくくなります。

まぁ、使わなかったら、結局忘れるんですけどねw

文書をまとめる力をつけたい

文章をまとめる力がつけば、普段の仕事の質も上がるし、プレゼンや人に教える時にもメリットがあると思います。

まだまだ、まとめる力がついていないのが残念です。
でも継続していけば、少しずつまとめる力が付いてくる、と思っています。

量は質に変化するとポジティブに考えてます。