AWSをいじり倒す(13-2.lambda+serverless)

再びこちらの記事に戻ってきて

TensorFlowをAWS Lambdaで使って競艇予想APIを爆誕させました!! - Qiita

 Serverlessを使ったlambda関数の定義。

果たしてこれで容量問題を掻い潜って関数が実行できるのか。

まずはやってみよう

 

 

さっそく、Serverlessを動かすためのnode.jsのインストール

MacにNode.jsをインストール - Qiita

 

2箇所エラーがでて詰まった

・1個目

Homebrewで「Warning: The following directories are not writable by your user:」とエラーが出た際の解消法 - Qiita

エラーメッセージに解決方法も書いてあるので、whoamiのところだけ書き換えて

コピペで解消

・2個目

Homebrewで「Warning: The following directories are not writable by your user:」とエラーが出た際の解消法 - Qiita

ディレクトリを作って解消。

 

どちらも記事の記載を淡々とこなせば解消したので特筆することはなし。

 

serverlessをインストール

npm install -g serverless

f:id:Messerarche:20200701174738p:plain

 成功!

 

serverlessからAWSにアクセスするためのIAMアカウントを作成

f:id:Messerarche:20200701175229p:plain

プログラムによるアクセスを選択してユーザを作成。 

 

f:id:Messerarche:20200701175422p:plain

AdministratorAccessを付与。

・・・なんか与えすぎな気もするが一旦作成。

lambdaにデプロイするときにcroud formation使ったりS3バケット作ったりと

いろんなことしまくるようなので、まとめてエイヤ権限ってことなんだろう。

 

ローカル端末から以下のコマンドを実行してserverlessからawsにアクセスできるようにする

serverless config credentials --provider aws --key [アクセスキーID] --secret [シークレットアクセスキー]

 

以下の文が返ってきたら成功 

Serverless: Setting up AWS...

 

プロジェクトを作る。pythonを使うのでtemplateはaws-pythonを選択

serverless create --template aws-python

f:id:Messerarche:20200701180857p:plain

 

コマンドを実行したディレクトリに

handler.py

serverless.yml

の2ファイルができた。

pyにはメインとなるlambda関数を、ymlにはデプロイするサービスの設定を書き込む。

 

lambda-layerとlambda-functionフォルダを作って、handler.pyとserverless.ymlをそれぞれのフォルダにコピーする

mkdir lambda-layer

mkdir lambda-function

cp handler.py lambda-layer/

cp serverless.yml lambda-layer/

cp handler.py lambda-function/

cp serverless.yml lambda-function/

 

lambda-functionの設定

vi lamda-function/serverless.yml

serverless-python-requirementsという機能を使って

追加のパッケージをインストールしてくれるらしいので、その設定を書く。

なお、serverless-python-requirementsを利用する場合、Dockerも必要となる

今回はインストール済みだったので手順は割愛

 

とはいえserverless.ymlのお作法はよくわからないので参考サイト通りに作成。

ただ、結局こっちでもslimPatternsにいらなさそうなファイルをずらずらと列挙して容量削減を図ることになる

service: [サービス名]

plugins:
  - serverless-python-requirements

custom:
  pythonRequirements:
    dockerizePip: true
    zip: true
    slim: true
    slimPatterns:
      - "**/debug"
      - "**/grpc"
      - "**/h5py"
      - "**/markdown"
      - "**/pkg_resources"
      - "**/setuptools"
      - "**/tensorboard/plugins"
      - "**/tensorboard/webfiles.zip"
      - "**/tensorflow_core/contrib"
      - "**/tensorflow_core/examples"
      - "**/tensorflow_core/include"
      - "**/tensorflow_estimator"
      - "**/werkzeug"
      - "**/wheel"
  requirementsService: [サービス名]-layer
  requirementsExport: [サービス名]Layer
  requirementsLayer: ${cf:${self:custom.requirementsService}-${self:provider.stage}.${self:custom.requirementsExport}}

provider:
  name: aws
  runtime: python3.7
  stage: dev
  region: ap-northeast-1
  iamRoleStatements:
      - Effect: "Allow"
        Action:
          - s3:ListBucket
          - s3:GetObject
        Resource:
          - "arn:aws:s3::*"

functions:
  classify:
    handler: handler.classify
    memorySize: 2048
    timeout: 60
    layers:
      - ${self:custom.requirementsLayer}

斜体太字部分は自分の環境に応じて書き換え。

 

handler.pyには実行したいプログラムを記載。

とりあえず動作確認をしたいので先頭に

try:
    import unzip_requirements
except ImportError:
   pass

import json
import pandas
from keras.models import load_model
import numpy

をつけるだけつけた。

最初の4行はserverlessで必須、後半の4行で動作確認

 

requirements.txtを新たに作る

vi requirements.txt

そこに追加で入れたいパッケージを指定。

tensorflow == 1.14.0

 

 

lambda-layerの設定

service: [サービス名]-layer

plugins:
  - serverless-python-requirements

provider:
  name: aws
  region: ap-northeast-1
  runtime: python3.7
  stage: dev

custom:
  pythonRequirements:
    dockerizePip: true
    layer:
      compatibleRuntimes:
        - python3.7
      slim: true
      strip: false

resources:
  Outputs:
    [サービス名]Layer:
      Value:
        Ref: PythonRequirementsLambdaLayer

インデントとか空白とかその通りにしないとエラーが出るので注意

(これで数時間無駄にした)

 

こちらにも

requirements.txtを新たに作る

vi requirements.txt

そこに追加で入れたいパッケージを指定。

keras == 2.3.1

numpy == 1.18.1

pandas == 1.0.1

 

設定が終わったら以下のコマンドを実行してデプロイ。

cd lambda-layer

serverless deploy

cd ../lambda-function

serverless deploy

 

・・・

  An error occurred: HandlerLambdaFunction - Unzipped size must be smaller than 64198656 bytes (Service: AWSLambdaInternal; Status Code: 400; Error Code: InvalidParameterValueException; Request ID: db8cece7-fd05-4e73-9066-d0921795792c).

というエラーが出て停止。

結局サイズが足りずにだめなんかーい

 

また容量との格闘か・・・

とりあえず組み合わせを変えてみる

具体的にはLayerのrequiementsを

numpy == 1.18.1

 

functionのrequirementsを

tensorflow == 1.14.0
keras == 2.3.1
pandas == 1.0.1

というふうに組み合わせを変えてみる

重複するパッケージがうまいこと作用することを祈って

 

Serverless: Checking Stack update progress...
...............
Serverless: Stack update finished...

 

う、うまくいった・・・。本当にギリギリの戦いだ

AWS lambdaにデプロイした関数ができているので、早速テスト

 

"errorMessage": "[Errno 28] No space left on device",
"errorType": "OSError",

 

はい、エラー

Lambda実行時に 'No space left on device'とエラーが出るときの対処法 - Qiita

 

 tmpを使い切った模様。

果たして、この関数1つで使い切ったのか、トライアンドエラーしているうちに使い切ったのか・・・

とりあえずtmpをrmするコマンドを放り込む。

f:id:Messerarche:20200703162759p:plain

こ、コンソール上でプログラムの変更ができない・・・。

つまり毎回デプロイせなあかんのか!?

デプロイ1回に5分ぐらいかかるのでこれは地味にめんどくさい。

 

しょうがないので再デプロイ

→No space left on device

 

エラーメッセージを読むに、zip解凍した時点でtmp使い切っている疑惑。

これもうだめかもわからんね

 

頭抱えてたらまた新たな突破口を発見

新機能 – Lambda関数の共有ファイルシステム – Amazon Elastic File System for AWS Lambda | Amazon Web Services ブログ

 

いくつかのユースケースではLambda関数によって実装がより容易になります。例えば:

  • /tmpで使用可能な容量(512MB)より大きいデータを処理またはロードする。

ど、ドンピシャ〜

しかも2020/6/17の記事て。最近すぎワロタ 

 

械学習の推論を実装するLambda関数を作成するには、コード内で、必要なライブラリをインポートして機械学習モデルをロードできる必要があります。たいてい、そのようなことをすると、それらの依存関係の全体的なサイズは、デプロイメントパッケージサイズの現在のAWS Lambdaの制限を超えます。これを解決する1つの方法は、ライブラリを適切に最小化して関数コードとともにデプロイし、モデルをS3バケットからメモリ(モデルの処理に必要なメモリを含めて最大3 GB)または/tmp(最大512 MB)に配置します。このようなモデルのダウンロードとカスタムが必要な最小化は、実装するのが簡単ではありませんでした。しかし、これからはEFSを使用できます。

数日間悩んできたことが全部書いてあるしマジかよ。

もうlambdaやめようと思ってたけど、もうちょっと頑張る。

長くなってきたので次回へ