serverless frameworkを設定する : AWS編

インストール

以下のコマンドでグローバルインストール

sudo npm install -g serverless

Serverless frameworkの設定

serverless config credentials --provider aws --key <ACCESS KEY ID> --secret <SECRET KEY>

クレデンシャルファイルにキー/シークレットを保存しておけば、各コマンドでプロファイルを指定することで、Serverlessコマンドで以下のように使い分けれる。

serverless deploy --aws-profile serverless

プロファイルだけでなく、regionを環境変数にエクスポートすることも可能

export AWS_PROFILE="serverless" && export AWS_REGION=eu-west-1

serverless frameworkでpython packageのバンドルを楽々に

serverless frameworkのpython packageバンドリングプラグイン: serverless-python-requirements

serverless-python-requirementsとはrequirements.txtの依存関係を自動的にバンドルし、PYTHONPATHで利用できるようにするためのServerless v1.xプラグイン

インストール

sls plugin install -n serverless-python-requirements

Serverless Python Requirements

これにより、プロジェクトの package.json と serverless.yml の plugins セクションにプラグインが自動的に追加される。 基本的な使用方法はこれだけです! sls デプロイを実行すると、requirements.txt や Pipfile で指定した python の依存関係がプラグインにバンドルされる。

より詳しくは以下を参照

How to Handle your Python packaging in Lambda with Serverless plugins

もしあなたがMacを使っているのであれば、brewでインストールしたpythonを使う際の注意点を参照のこと。

Serverless Python Requirements

ロスコンパイル

ロスコンパイルしたい場合、Dockerとdocker-lambdaイメージを使うことで、非Linux OS上で可能。 docker を使用できるようにするには、serverless.yml に以下を追加する。

custom:
  pythonRequirements:
    dockerizePip: true

よく使うzipオプション

-f freshen: 変更されたファイルのみ 
-u update: 変更されたか新しいファイルのみ
 -d zipfile 内のエントリを削除 
-m zipfile に移動 (OS ファイルの削除)
  -r ディレクトリへの再帰
 -j ジャンク(記録しない)ディレクトリ名
 -0 ストアのみ 
-l LF を CR LF に変換 (-ll CR LF を LF に変換)
  -1 圧縮が速くなる/ -9 圧縮が良くなる
  -q  silent mode
-v 冗長な操作/バージョン情報をprintする
  -c 一行コメントを追加
 -z zipfile コメントを追加
  -@ stdin から名前を読み込む 
-o zipfile を最新のエントリと同じくらい古いものにする
 -x は以下の名前を除外する
 -i は以下の名前のみを含む
  -F zipfile を修正する (-FF try harder)
 -D ディレクトリエントリを追加しない
  -A adjust self-extracting exe 
-J junk zipfile prefix (unzipsfx)
  -T zipfile の完全性をテストする
 -X eXtra ファイル属性を除外する
  -y シンボリックリンクを参照ファイルの代わりにリンクとして保存する
  -e 暗号化
 -n これらのサフィックスを圧縮しません。
-g 指定した zip アーカイブを新規作成するのではなく、成長させる (追加します)。この操作が失敗した場合、zip はアーカイブを元の状態に復元しようとする。復元に失敗した場合は、アーカイブが破損してしまう可能性があ流ので注意。

  -h2 より多くのヘルプを表示する

Poetryを使ってLambda関数をデプロイしたい

AWSのドキュメントに従ってAWS Lambda用のPythonスクリプト(Poetryの仮想環境内)をパッケージ化してデプロイしたい。

背景

serverless frameworkを使ってPython Lambda関数をデプロイする方法については、すでに多くの記事が存在するが、Poetryと組み合わせてServerlessを使用するための方法についてはあまり記事がないので、この記事を書いた。 

前提条件

  • Poetryをインストール
  • Node.jsをインストール
  • awscliをインストールし、プロファイルで設定
  • グローバルにserverless frameworkをインストール

フォルダ構造

lambda-test/
    handler.py
    pyproject.toml

serverless.ymlの追加

Serverless frameworkを使用するには、この設定ファイルが必要。 serverless.ymlはサービスごとに設定され、このファイルに関数とそのトリガーとなるイベントが定義される YAML ファイル。 このファイルはプロジェクトフォルダのルートに保存されている必要がある。Serverless deploy を実行して Framework でデプロイすると、 serverless.yml (または --config オプションで指定したファイル) のすべてが一度にデプロイされる。

# serverless.yml

service: lambda-test

provider:
  name: aws
  runtime: python3.6

functions:
  test:
    handler: handler.main

plugins:
  - serverless-python-requirements

custom:
  pythonRequirements:
    dockerizePip: non-linux

serverless-python-requirementsについては以下も参考に。

datablogger.hatenablog.com

npm を起動して python-requirements プラグインをインストール

Serverlessはnode製パッケージであり、現在のセットアップにserverless-python-requirementsプラグインをインストールする。 プロジェクトフォルダのルートにあるnpm initnpm install -save serverless-python-requirementsを実行する。そうすると、以下のようなフォルダ構造になる。

lambda-test/
    node_modules/
        ....
    handler.py 
    package.json 
    package-lock.json 
    pyproject.toml

依存関係をrequirements.txtとしてエクスポート

プロジェクトの依存関係をrequirements.txtとしてエクスポートする。そうしないとServerless: Generating requirements.txt from pyproject.tomlとログがでるが、なぜかうまくいかないので、デプロイ前にrequirements.txtをエクスポートする。

poetry export -f requirements.txt > requirements.txt --without-hashes

lambdaのデプロイ

ここまででLambda関数をデプロイする準備ができた。デプロイする前に、正しいPython環境(プロジェクトのもの)が有効になっていることを確認しよう(poetry shell)。また、poetry run python handler.pyを実行して機能をテストすることもできる。それが正常に動作していれば、以下のコマンドでLambdaをデプロイする。

serverless deploy

ラムダを呼び出す

そして、次のコマンドを使用してデプロイされたLambda関数を呼び出すことができる。

serverless invoke -f test --log

Python、distutilsの削除提案

Pythonではdistutils モジュールのかわりにsetuptools パッケージの使用を推奨してきたた。setuptools は最近 distutils の完全なコピーを統合し、この標準ライブラリに依存しなくなった。Pip はすでに長い間、パッケージをインストールする際に黙って distutils を setuptools に置き換えてきた。また distutils のドキュメントには 2014 年以降 (またはそれ以前) に段階的に廃止されていると記載されている。Standards Track(標準化過程) PEP 632ではdistutilsを標準ライブラリから削除する提案がなされた。

PEP 632 -- Deprecate distutils module | Python.org

Pythonのライブラリ・フレームワークにおけるloggingの設計基礎、4つのキワードを理解する

Pythonのログイングを理解する上で重要な4つのキーワードを解説する

  1. Loggers  : loggingシステムにメッセージを送信するためにアプリケーションコードによって使用される
  2. Formatters : 出力のためにメッセージをフォーマットする
  3. Filters : 細かい出力制御を提供
  4. Handlers : フォーマットされた出力をファイルなどの宛先に送る。

アプリケーション、フレームワーク、ライブラリの中で、これらのタイプのそれぞれをいつ、どこで、どのように使うかを知ることが重要だ。どんな完全なスタンドアロンのアプリケーションでも、適切なロギング出力を得るためには、4つのうち少なくとも3つを使用すべきだといわれている。標準ライブラリのドキュメントには、これを行う方法がかなり明確に記載されている。 これら4つのタイプのうち、フィルタはおそらくあまり使ったことがない人がおおいのではないか。フィルタは、例えば、マルチスレッドネットワーク指向のコードでは、文脈に沿った情報をログに取得するのに便利な手段となる。 (例: リモートIPアドレスやユーザ名、データベース接続固有の情報など)。ほとんどの場合、ライブラリやフレームワークの作者は、Logger オブジェクトだけを使うことになる。Loggerを取得して、それを使ってログイベントを公開する。Loggingレベルは安易に設定しないほうがいいし、フォーマッタやハンドラも設定には慎重になったほうがいいだろう。ライブラリやフレームワークがロギングパッケージを使用する際には、ロギングパッケージを適切に使用することが不可欠だ。不適切な使用は、ライブラリやフレームワークをより大きなアプリケーションに組み込むことを困難にする。

基本的には

log = logging.getLogger(__name__)

だけでできないかをまず考えよう。 これは、特定のロギングイベントがどのモジュールから発生したかを正確に示す名前のロガーを取得する。 (このロガーが定義されているモジュールでのみ使用されると仮定している)。一般的に、ログを取る必要のあるすべてのモジュールは、nameインスタンス化されたモジュールレベルのロガーを持つべきであり、そのモジュールのコードはそのロガーを使用します。より細かい制御が必要な場合は、そのモジュールロガーの子ロガーを持つこともできる。しかし、ロガーはシングルトンなので、クラス インスタンスでロガーへの参照を保持する必要はない。ロガーがシングルトンであるのには、それなりの理由があります。Loggerがシングルトンでない場合、これはできない。

ライブラリではロギングレベル、フォーマッタ、またはハンドラを設定する必要は基本的にない。もしあなたがライブラリの作者で、ユーザの利便性のためにこれらのうちの1つ以上を行いたいと思っているならば、本当にそうなのか慎重に考えよう。ユーザーの助けになるどころか、ユーザーをイライラさせるだけになるかもしれないからだ。

一方Djangoのようなフレームワークでロギングを使うときには、健全なプラクティスに従うことが重要だ。健全なプラクティスとはフレームワークが特定のハンドラやフォーマッタを設定する場合、それを簡単に無効にできる方法で行い、それを無効にする方法を明確に文書化するといったことを意味する。無効にするということは、フィルタ、ハンドラ、フォーマッタがインスタンス化されたり、ロギングレベルが設定されたり(setLevelで)、ハンドラがロガーに追加されたり(addHandlerで)するコードブランチをすべて無効にすることを意味します。たとえば、ほとんどのユーザーが非常に特定のタイプのロギング出力を必要とすることが予想されるため、特定のハンドラとフォーマッタのセットをセットアップしたい場合がある。この例としては、CherryPy のようなウェブフレームワークがあり、Apache によって生成されたものと同様の標準のアクセスログやエラーログを生成するためにロギングシステムを使用することができる。これは、単純なウェブアプリケーションを立ち上げて実行するときに便利だ。しかしながら、より高度な使用例では、フレームワーク内で行われるロギング設定を上書きしたり無効にしたりする簡単な方法がない場合、非常にライブラリユーザーをイラつかせる結果となるだろう。

アプリケーションのロギングは、ほとんどの場合、起動時に一度設定される。この設定プロセスは、アプリケーション開発者によって行われた設定に加えて、フレームワークやライブラリが独自のロギング設定を行うことによって、カオスになりがちだ。最初の設定が勝つ場合もあれば (例えば logging.basicConfig)、2つの競合する設定 (例えば、2つのハンドラが同じファイルに出力を送って重複したメッセージになるなど)、最後の1つが勝つ場合もあります (例えば、与えられたロガーやハンドラのロギングレベルを設定するなど)。ロギングがインポート時に設定されている場合(一般的には避けるべきことです)、モジュールがインポートされる順序は、奇妙で直感的でない方法でロギングの動作を変更することができる。これはすべて、不適切に使用された場合、ロギングパッケージの動作を追うのがとても難しくなることを意味する。これは、フレームワークやライブラリがロギングを設定してはいけないときにロギングを設定してしまうと目も当てられない。

フレームワークやライブラリの開発者としては、ロギングレベル、フィルタ、フォーマット、ハンドラのための単一の設定ポイントを常に許可したり、提供したりするべきだ。ライブラリを開発するときは、ロガーを取得してログを記録するだけにすべきだ。フレームワークを開発するときは、logging.config.dictConfig で設定できるものを上書きしたり、無効にしたりする方法を提供するようにすべきだ。これらのシンプルなルールに従うことで、あなたのライブラリやフレームワークは、高度なロギング設定を設定しようとしている人にとって、より便利になるだろう。

https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library

aws s3にあるファイルサイズなどを取得するaws-cliコマンド

取得予定のファイルサイズを知りたいことってあるよね。 aws s3にあるファイルサイズなどを取得するaws-cliコマンドを以下に示します

aws-cliコマンド

aws s3 ls s3://[バケット名]/[フォルダ名]/  --recursive --human --sum

オプションの説明:

  • --human-readable(--human):ファイルサイズに単位をつけ,human redableにしてくれるオプション

  • --summarize (--sum):合計サイズとオブジェクトの数を表示

  • --recursive:再帰的に実行

なおaws s3 ls コマンドはwird card searchには対応していない。(aws cp は対応している)

https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-event-notification-filter-wildcard/