AWSを使い倒す(15.Step Functions)
lambdaとAPI Gatewayの組み合わせが動いたと思った矢先、
思わぬところで問題発生。
API Gatewayってタイムアウト29秒までしか設定できないのね。
lambdaで普通に1分ぐらいかかる処理仕込んでたので
API Gatewayが待ちきれない状態になってしまった・・・。
【API Gatewayタイムアウト対策】Step Functionsを組み合わせて非同期処理にしてみる | Developers.IO
ということでこのStep Functionsを使って解決を試みる。
記事を読むにStep Functions(のステートマシン)を介してlambdaを実行することで、
lambda実行中なら実行中、終わったら結果をステートマシンが代わりに「応答」してくれるようになるので、API Gatewayのタイムアウトが回避できる。
代わりにAPI叩く人は結果返ってくるまで何回かAPIを叩かないといけないって感じ。
大変なのが
APIを叩いた時にユーザが渡してくるパラメータを
一度ステートマシンが受け取って
それをlambdaに渡す
ってしないといけないのでややこしい。
ここの理解にめちゃめちゃ時間がかかった。
まずは適当なlambda関数を作成する。
eventとしてStep Functionsから引数を受け取れるか、の確認するだけなので
本当にシンプルな作り。
次にStep Functionのステートマシンの作成
作成画面
コードスニペットで作成、タイプは標準を選ぶ
定義を書く。
ここにどう書くか、を理解するのが大変だった
今回書いたのはずばりこのコード
大事なのは、ResourceとParameters
Resourceには、lambda関数のarnを記載する。
Parametersは記法に戸惑うが、API Gatewayから変数として受け取るのでこう書く。
具体例を書くと、以下のようなフォーマットでAPI Gatewayから情報が来た時に
"input": {
"bucket: "nameofbucket",
"folder": "nameoffolder"
}
"s3bucket.$": "$.bucket"の意味は
s3bucketは変数です、変数の中身は来た情報の中のbucketが指し示す値です
ということになる
上記のような解釈で今度はステートマシンが
"event": {
"s3bucket": "nameofbucket",
"s3folder": "nameoffolder"
}
と情報を再構築してlambdaに渡してくれる。
これを元にlambdaは
event["s3bucket"]
として値を受け取ることができる。
$.だの.$だのをつけないと、変数ではなく定数と解釈されて受け取れなくなる。
この辺の何がどうなるはこちらで試されている
StepFunctionsの入力方法サンプルメモ集 - Qiita
作成すると、ARNが表示されるのでメモっておく。
API Gatewayの前にAPI Gateway用のIAMロールを作成しておく。
具体的には、StepFunctionsの実行権限を持ったロールが必要。
API Gateway を使用して Step Functions API を作成する - AWS Step Functions
ロールを作成したら、API Gatewayに移動
な形でメソッドとリソースを作成する。
executionがlambdaをキックするAPI、
statusがlambdaの進捗を確認するAPI。
メソッドの作成時に、StepFunctionsを選択する
重要なのはアクションのところ。
LambdaをキックするAPIにはStartExecution
進捗を確認するAPIにはDescribeExecution
と入力する。
実行ロールはさっき作ったIAMロールのarnを入力。
出来上がったら早速テストをする。
まずはlambdaの実行、つまりexecutionのPOSTから、テストの画面を開いた後
リクエスト本文に以下を入力
黒塗りのところにステートマシンのarnを記載する。
また、ステートマシンに渡したい変数を図のように記載する。
そしてテスト実行。
実行されたら
こんなレスポンスが返ってくる。
このexecutionArnを使って進捗確認を行うのでメモしておく。
次に進捗確認のほうのメソッド(/status)にうつる
リクエスト本文に、先ほどのexecutionArnを図のように記載する
これで実行すると、
先ほどキックしたlambdaが実行中であれば実行中、
実行完了していたら処理結果が返ってくる。
その進捗具合はステートマシンのログから確認ができる。
うまくいっているとこんな感じで
1.ExcecutionStartedにはAPIGatewayから送られてきた情報が
3でステートマシンがlamdaに渡した情報が
5でlambdaの実行結果が見える
一応この通りやれば動くハズ。
ここからは追加で
リクエスト本文にいちいちステートマシンのarn情報を記載しない方法と、
パスパラメータをステートマシン越しにlambdaへ渡す方法を記す。
まずは、実行したいステートマシンをあらかじめ記載しておく方法
この画面の統合リクエストのところをクリック
開いた画面の一番下、マッピングテンプレートを開く
ここにいろいろ書くことで、リクエスト本文の内容とかを書き換えることができる。
公式ドキュメントを参考に
API Gateway を使用して Step Functions API を作成する - AWS Step Functions
こんな感じで記載する。
stateMachineArnはもちろん自分のステートマシンに合わせて書き換える
右下の保存を押す。これでAPI実行の度にステートマシンの情報を書かなくてもよくなる。
次はパスパラメータ
パスパラメータを使うためのリソースとメソッドを作成する。
テストを押した時にここに
(めちゃ適当に)値を入れて、これがlambdaまで伝われば成功
こちらも統合リクエストでマッピングテンプレートの設定を行うことで実現できる
ずばり書くとこんな感じ。
参考にしたのは以下
API GatewayでStepFunctionsと統合する時のTips - Qiita
API Gateway マッピングテンプレートとアクセスのログ記録の変数リファレンス - Amazon API Gateway
これをテストすると、API Gatewayからの出力はこうなる
"input": {
"input": {},
"InputParams": {
"bucket": "a",
"folder": "b"
}
},
パスパラメータで引数を渡すし、実行時のステートマシン設定も不要になったので
テスト時にリクエスト本文に何も書く必要がなくなったため、
"input": {}、つまり記事の前半で参照していたinputには何も入らない。
代わりに、InputParams配下に自分の指定したパラメータが入っている。
ステートマシンにも変更が必要。
今まで受け取っていたinputの中身が空っぽになって、
代わりにInputParams配下に中身が入るので、そのような宣言がいる。
InputPathで宣言してやる。以下が参考
InputPath およびパラメータ - AWS Step Functions
lambdaも実態に合わせて変更("s3bucket"とかをただの"bucket"にしただけ)
これで、パスパラメータからステートマシンに、ステートマシンからlambdaに変数を渡していく仕組みが動く。
動きは確認できたので、あとは実装だ・・・!
AWSをいじり倒す(14.API Gateway)
API Gatewatを使って、lambdaに変数を渡せるAPIを作ってみる
今回の目標は、
s3の指定したバケットに指定した名前のフォルダができるもの」を作る
先にlambda関数を作成する。
pathParametersでバケット名とフォルダ名を受け取るところが肝。
ちなみにバケットを新しく作るコードではない。
あくまで指定したバケットにフォルダを作るだけなので、バケットは先に作っておく。
まずはテストをしてみる。テストイベントの設定から
イベントテンプレートのAmazon API Gateway AWS Proxyを指定する
出てきたイベント内容の、pathParametersにbucketとfolderの情報を書き込む。
API叩いた時に飛んでくるリクエストの中身をここに書いて模擬できるというわけだ。
変更する必要がある箇所は2箇所
まずはresouceとpathのところ。
https://example.com/slp/[バケット名]/[フォルダ名]
みたいな感じでAPIを作りたいのでそう書く。
pathのところには[バケット名]と[フォルダ名]を実際の値で埋めたものを書く
もう一つはここ、pathParameters
lambdaに渡す変数を指定する。さっきのコードで
bucket=event['pathParameters']['bucket']
folder==event['pathParameters']['folder']
のところで受け取る値となる
作り終えたらテスト実行。
確認しに行くと・・・できてるが、フォルダじゃなくてファイルになってる・・・
フォルダを作るには、
test-folder-12345/ みたいに最後にスラッシュを付けないといけないようだ。
ということで
をくっつけてリトライ
うむ。
参考:ゼロから作りながら覚えるAPI Gateway環境構築 | Developers.IO
Websocketはチャットみたいな頻繁にやり取りするものに使うとして
HTTPとRESTはどっちがいいのか
Amazon API Gatewayは「HTTP API」と「REST API」のどちらを選択すれば良いのか? #reinvent | Developers.IO
勉強という意味では機能の多いRESTで十分に慣れたほうがよさそうかな。
早速設定してみる
REST、新しいAPIを選択。
API名も適当につける。
いろいろ設定できそうな画面ができてきた。
リソース=ディレクトリみたいなもの。
例えばtestリソースを作るとAPI叩く時のURLが
になる。
アクションからリソースの作成を選択した画面
リソース名とパス名で別のものを付けられるようだけど、別にすると混乱しそう
なお、 階層構造で作ることができる。
次に、作りたいフォルダ名をlambdaに変数として渡したいので
パスパラメータの設定をする
AWS Lambda + API Gateway で/hoge/{group}/{user}のように階層構造のREST APIでパスパラメータの受け渡し – 或る阿呆の記
設定自体は{}でくくるだけ
こんな感じ。バケット名とフォルダ名を受け取るつもり
これでhttps://example.com/slp/mf/[バケット名]/[フォルダ名]
な感じのURLを叩けばバケット名とフォルダ名がlambdaに渡されるようにする
次にメソッドの作成
メソッドでHTTPのメソッドを指定できる。
やりたい処理に対してどれを選べばいいんだよ?ってなるかもしれないが
大まかに書くと、
GETがリソースの取得
POSTがリソースの新規登録
PUTが既存リソースの更新
DELETEがリソースの削除
という感じ。
とは言ってもAPIは多種多様でこれはこれと定めるのは難しい。
有名サービス間でも使い方が微妙に違っていて
こう使うべしみたいな論で本が一冊出てるレベル。
O'Reilly Japan - Web API: The Good Parts
今回は新しいフォルダを作る、なのでPOSTかな。
POSTを確定すると連携先のlambda関数の指定が出てくる
統合タイプはもちろんLambda関数
Lambdaプロキシ統合の使用にチェック。
これでパスパラメータが使えるようになる
Lambda関数に事前に作っておいた関数を指定する
設定するとこんな画面が出てくる
テストを押すと
おー、lambdaでやったテストと同じことができそう。
早速、パスのところのfolderとbucketに値を入れてテスト
他の場所に値を入れる必要はなし
結果・・・怒られた。
API Gateway コンソールを使用してメソッドをセットアップする - Amazon API Gateway
バックエンドが返したレスポンスに対応するメソッドレスポンスが定義されていない場合、API Gateway はクライアントにレスポンスを返しません。代わりに、
500 Internal server error
エラーレスポンスを返します。
確かに、lambdaから応答を返す設定は何もしてないので、怒られて当然か。
S3を確認するとちゃんとfolderはできていた。
lambda関数はちゃんと動いているようだ
というわけでlambda関数にレスポンスのコードを書く。
Amazon API Gateway + AWS Lambda でのレスポンス形式 – サーバーワークスエンジニアブログ
基本的にはstatusCodeをreturnするだけでよいみたい。簡単〜
応答処理を入れたバージョン。
うまくいったらput_objectのresponseに書かれているステータスコードを(200だろうけど)をstatusCodeとしてreturn。
失敗したらstatusCodeを500と
エラーメッセージを取り出して返却するようにしてみた。
ではテスト
まずは成功事例、実在するバケットを指定して実行
うむ。
次に失敗事例として、バケット名を存在しないものにして実行
思惑通り動作した。
最後にデプロイを行う。
デプロイを行うと外部に公開されることとなるが、
誰からでもアクセスできてしまうので認証を入れ込む
APIキーを作る。
作成画面はいたってシンプル
できた。表示のところを押すとキーが表示される
API設定に戻ってきて、メソッドリクエストを選択
APIキーの必要性をtrueに設定
終わったらいよいよデプロイ
ステージ名devで作成
devステージにURLが出現。デプロイはこれで完了。
次に使用量プランを通じてAPIキーを設定する
スロットリングとクォータのチェックを外す。
どちらもAPIの呼び出し制限に関する設定だ
APIステージとの関連付け。さっきのdevのこと
追加するとこんな感じ。
次でAPIキーの紐付け
さっき作ったAPIキーの名前を入力して追加。
ではいよいよ実行。
どのURLにアクセスすればいいかはlambdaの画面から確認ができる。
キーまで全部丸見え。
では手元のPCから実行してみる
$ curl -X POST -H 'x-api-key:[APIキー]' https://[URL]/dev/slp/mf/[bucket名]/[folder名]
"Success to make folder"
できてるできてる。
APIキーのところを抜いて叩くとエラー
{"message":"Forbidden"}
ちゃんと機能してる。
ということで無事完成!
lambda完全に理解した
今までの軌跡。
構成図をアイコンも使ってカッコよくしてみた
2020年春アニメおすすめ
NETFLIXもしくはAmazonPrimeVideoでみることができるアニメオンリーで
個人的におすすめと思ったものまとめ。
2020/4〜6放送分。
絶対みるべき
久米田さんといえば世の中の風刺で有名だが
いつものキレはそのままにほんのり感動を混ぜた良作。
序盤から意味深な伏線をふわっと残すので、気になって最後まで楽しく見れた。
当然1期から見るのをおすすめ。
安心して見られるギャグ+ラブコメ。見る清涼剤。
声優の声色の使い分けっぷりもすごい
3期もあるらしいという噂
www.ghostintheshell-sac2045.jp
見た目でめちゃめちゃ損してると思った。
ガチガチの3Dを我慢して見ると、やはりストーリーは攻殻で引き込まれる。
ネットリンチとか割と身近に問題になってる話も織り交ぜつつ
キャラ同士の掛け合いは相変わらずの面白さ。
3Dならではのカチカチなアクションも、
ある意味AIにサポートされた機械的な動きにマッチしていてアリかなとすら思えてくる
見ても良いと思う
乙女ゲーが舞台ということでどうにも最初は気乗りしなかったが、
見ていくうちに慣れてきて、だんだん面白くなり、最後も綺麗に終わった。
悪役令嬢というジャンルを理解するため教養として見ておくべき、という側面もある
恋愛のヤキモキ感も見ていてもどかしい、そのもどかしさを楽しむアニメ。
キャラもよく、人間臭い悩みも身に滲みる。
ただ、アニメにすると会話の中で大事な「間」がどうにもテンポの悪さに感じてしまった。
ネタとして
OPだけは聞いた方がよい。
サムネがすでにヤバい
AWSをいじり倒す(13-3.lambda+EFS)
あらすじ
lambdaを使おうとするもtensorflowがデカすぎて容量問題に苦しむ
そこに現れた一筋の光、新機能EFSとの接続
新機能 – Lambda関数の共有ファイルシステム – Amazon Elastic File System for AWS Lambda | Amazon Web Services ブログ
さっそく試してみることにする。
VPCを作成
まずはVPCを作成。インターネットゲートウェイの設定も作成。
後々s3も使おうと思っているのでエンドポイントも作成。
この辺は過去記事で一回やっているのでサクサク作れた
サブネットは2個作っておくと良いらしいので2個作った。
セキュリティグループを作成
1つはインバウンドやアウトバウンドになにも書いていないlambda-sg
もう一つはEFSにアタッチするためのlambda-efs-sg
こちらはインバウンドでNFSの2049を許可する
あとはEC2にアタッチするSSHが許可されたSG
まあこれはおそらく既存のものがあるはず
次にEFSを準備
さっき作ったVPCを指定し
セキュリティグループはlambda-efs-sgを指定
もう一つ設定が必要なのはアクセスポイントのところ
lambdaにEFSの操作権限を与える。
このまま作成を完了
lambdaの設定
次にlambdaの関数画面へ
アクセス権限を設定する
ロール名をクリックしてIAMの画面を開き、以下の二つのポリシーをアタッチ
次にlambda関数のVPCの設定へ
ここのサブネットで作った2つともを登録
セキュリティグループはlambda-sg
で作成。
ちょっと時間がかかって、できましたのメッセージ。
lambdaが裏で動かすEC2にネットワークインタフェースを作ったりなんやりしてたみたい
次にいよいよファイルシステムとの接続
さて・・・
EFSを選んだら、マウントするターゲットポイントが出てきた。
さっきEFSで設定したアクセスポイントだ。
ローカルマウントパスには適当に
/mnt/slp
と設定。
EFSへのパッケージインストール
さて、ここからが問題
新しいVPCのパブリックサブネットでAmazon Linux EC2インスタンスを起動します。インスタンスの詳細で、EFSマウントポイントがあるアベイラビリティーゾーンの1つを選択してから、[Add file system ]を選択して、関数に使用しているのと同じEFSを自動的にマウントします。 EC2インスタンスのセキュリティグループには、
default
のセキュリティグループ(EFSをマウントできるようにする)とSSHへのインバウンドアクセスを与える(インスタンスに接続できるようにする)セキュリティグループを選択します。
ほほー、EC2を立ち上げて、EFSをマウントしろと。面白いな
AMIに関してなにも書いてないが、python3.8で試しているようなので
EC2作成手順の途中で、ファイルシステムにEFSを追加・・・したのだが
これは後々なぜかうまくいかなかったのでやめた。
普通にEC2を起動して、EFSをマウントしたほうがよい。
なんでうまくいかなかったのか原因を探る元気がないので、うまくいく手順を記すことに力を使う
立ち上がったEC2にて
sudo mkdir efslambda
sudo mount -t efs -o tls,accesspoint=[アクセスポイント名] [ファイルシステムID] efslambda
してマウントする。詳細は過去記事をみてください。
ちなみに今後のインストール作業に割と時間がかかるので
EC2はメモリ4GBぐらいのを選んだ方がいい。無料枠じゃないけど。
今回必要なパッケージ情報は以下。
vi requirements.txtしてリストアップする。
tensorflow
keras
numpy
pandas
ちなみに、最新を入れずに過去のバージョン
つまりpython3.7だとか、tensorflow1.14.0だとかを入れようとすると
lambdaの実行がまったくうまくいかなくて心折れそうになった。
少しでも容量の節約を・・・と過去バージョンに拘っていたけど
EFSがきた以上、容量気にせず最新で入れてしまった方が良いみたいだ
EFSは、EC2によって
/mnt/efs/fs1
の下に自動的にマウントされます。そこで、/ml
ディレクトリを作成し、パスの所有者を、現在接続(ec2-user
)しているユーザーとグループに変更します。sudo mkdir /mnt/efs/fs1/ml
sudo chown ec2-user:ec2-user /efslambda
手動でマウントしたのでmkdirは無視して(斜線済)
sudo chown のほうだけ実行
まずはpythonのインストール
(2022/12/20更新)
以下の通り設定すればスマートにできた。
今入っているバージョンを確認
python3 -V
sudo yum install python3.8 -y
echo 'alias python=python3.8' >> ~/.bashrcsource ~/.bashrc
python -V
(2022/12/20更新ここまで)
無事、pythonのインストールが終わったら
pip3.8 install -t /efslambda/lib38 -r requirements.txt
で各種パッケージをインストール。
結構時間がかかる
無事終わったら
最後に、EFSアクセスポイントに使用したユーザーとグループに
/ml
パス全体の所有権を与えます。
以下を実行。
sudo chwon -R 1001:1001 /efslambda
lambdaの動作環境設定
次はlambda関数に戻ってきて、環境変数の書き換え
使用するランタイムに応じて、依存関係が展開パッケージに含まれていないか、レイヤーに含まれていない場合に、依存関係を探す場所を指示する方法を見つける必要があります。 Pythonの場合、
PYTHONPATH
環境変数を/mnt/inference/lib
に設定しています。
だそうなので、PYTHONPATHをさっきパッケージを詰め込んだ/mnt/slp/lib38に割当
ガイドではモデルもEFSに入れているみたいだけど、まずは動くところを確認したいのでこれで。
次に基本設定もいじる
ランタイム=Python3.8
メモリ=1024MB
タイムアウト=3分
実行してみる
いよいよ必要なパッケージを全部importして実行してみる
実行!
やった〜!
ブログだと1本道で成功を歩んでいるが、
実際はいろいろあって半日以上費やし、もう目がしぱしぱ。
後日談
EFS配下にモデルとインプットファイルを置いて、機械学習の推論を実行してみた。
ソース全部は載せられないが、一部抜粋でこんな感じ
結果・・・成功!気持ちいい〜
おわりに
まあでもserverlessだのレイヤーだのzipで固めるだの言う話よりは断然わかりやすいし
コードもコンソール上でいじれて扱いやすいし
設定さえうまくできれば良いことしかない。最高だ。
ということでようやく下地が整ったので、
モデルファイル食わせたり、トリガーの設定したりができる。
それはまた今度で。
おまけ1
ubuntu使っているので完全には参考にならなかったが
動画でセットアップする手順公開してる人がいたので貼っておく
おまけ2
ちなみに
python3.7以下の環境でtensorflowを動かそうとすると
インストールまではうまくいくが、lambda実行時に以下のようなメッセージが出て壁にぶち当たる。
ImportError: /lib64/libm.so.6: version `GLIBC_2.23' not found (required by /mnt/slp/lib/tensorflow/python/_pywrap_tensorflow_internal.so)
Failed to load the native TensorFlow runtime.
amazon web services - [AWS Lambda]: How to fix "version GLIBC 2.27 Not found" - Stack Overflow
無理やりglibを移植すれば動くらしいけど、自分には無理だった
せっかく調べた関連コマンドものせとく
$ ls -l | grep libm.so
-rw-r--r-- 1 root root 141 1月 17 06:34 libm.so
lrwxrwxrwx 1 root root 12 5月 27 00:46 libm.so.6 -> libm-2.26.so
strings libm-2.26.so | grep GLIBC
GLIBC_2.2.5
GLIBC_2.4
GLIBC_2.15
GLIBC_2.18
GLIBC_2.23
GLIBC_2.24
GLIBC_2.25
GLIBC_2.26
GLIBC_PRIVATE
GLIBC_2.15
python3.8で快調に動いたので、こっちはもう追わない。
AWSをいじり倒す(13-2.lambda+serverless)
再びこちらの記事に戻ってきて
TensorFlowをAWS Lambdaで使って競艇予想APIを爆誕させました!! - Qiita
Serverlessを使ったlambda関数の定義。
果たしてこれで容量問題を掻い潜って関数が実行できるのか。
まずはやってみよう
さっそく、Serverlessを動かすためのnode.jsのインストール
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
成功!
serverlessからAWSにアクセスするためのIAMアカウントを作成
プログラムによるアクセスを選択してユーザを作成。
AdministratorAccessを付与。
・・・なんか与えすぎな気もするが一旦作成。
lambdaにデプロイするときにcroud formation使ったりS3バケット作ったりと
いろんなことしまくるようなので、まとめてエイヤ権限ってことなんだろう。
ローカル端末から以下のコマンドを実行してserverlessからawsにアクセスできるようにする
serverless config credentials --provider aws --key [アクセスキーID] --secret [シークレットアクセスキー]
以下の文が返ってきたら成功
Serverless: Setting up AWS...
プロジェクトを作る。pythonを使うのでtemplateはaws-pythonを選択
コマンドを実行したディレクトリに
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-requirementscustom:
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:
passimport 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-requirementsprovider:
name: aws
region: ap-northeast-1
runtime: python3.7
stage: devcustom:
pythonRequirements:
dockerizePip: true
layer:
compatibleRuntimes:
- python3.7
slim: true
strip: falseresources:
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するコマンドを放り込む。
こ、コンソール上でプログラムの変更ができない・・・。
つまり毎回デプロイせなあかんのか!?
デプロイ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やめようと思ってたけど、もうちょっと頑張る。
長くなってきたので次回へ
AWSをいじり倒す(13.lambda)
食わず嫌いしてたlambdaを使ってみる。
今回は目的があって、
s3にファイルが入ったタイミングで
tensorflowで作成したモデルを使って推論をするプログラムを動かしたい、というもの。
まずは下調べしてみたけど、やっぱりちょっとめんどくさい。
今回は手元にあるpythonのコードを動かすのが目的なんだけど、
import numpyとかpandasとかしてたら、
事前にそれらパッケージをzipでアップロードしてあげないといけないようだ
pandasをLambdaのLayerとして追加する - Qiita
そのパッケージも、ローカルで作ってもだめで、指定したEC2で作る必要がある
まず最初にややこしいのが、使うpythonのバージョンによって
アップロードするパッケージの生成元となるEC2のAMIを選ばないといけない点
今回使おうとしているpython3.7のOSはAmazon Linux(2ではない)
のでこのAMIを選んでインスタンスを起動。
次に、EC2のデフォルトpythonのバージョンが2.7だかなんだかで古いので
目的のバージョンのpythonをインストール。
現時点でyumに対応したpythonは3.6系までしかないので、
3.7以降はソースからインストールするしかない
今回はPython3.7.6を使う。
具体的な手順はここが参考になった
AWS Lambdaでpandasを利用する(Lambda Layers編) - marmarossa’s blog
まず目的のpythonをダウンロード
curl https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tgz -o Python-3.7.6.tgz
解凍
tar xzf Python-3.7.6.tgz
ディレクトリに移動
cd Python-3.7.6
必須ライブラリをインストール
yum install gcc openssl-devel bzip2-devel libffi-devel
コンパイルしてインストール
./configure --enable-optimizations
sudo make altinstall
パッケージをインストールするディレクトリを作成
mkdir python/lib/python3.7/site-packages/
pandasのインストール
pip3.7 install pandas -t ./python/lib/python3.7/site-packages/
kerasのインストール
pip3.7 install keras -t ./python/lib/python3.7/site-packages/
zipに固める
zip pandaskeras.zip python/ -r
S3にアップロード(EC2へのS3アクセス権限付与など忘れずに)
aws s3 mv pandaskeras.zip s3://[s3bucket名]/module/pandaskeras.zip
S3のコンソール画面からアップロードされたことを確認して、
オブジェクトURLをメモっておく
lambdaの画面に移動
Additional Resourcesからレイヤーを選択、レイヤーの作成
レイヤーの作成。
S3からアップロードを選択し
リンクURLに先ほどメモしたURLを貼る
互換性のあるランタイムオプションはPython3.7を選択
次に関数の作成
まずはHello Worldから。
ロールは特に用意していないので作ってもらう
関数の作成を実行。
ほどなくして作成が完了
何かいろいろできそう(小並感)
ともあれ関数を実行してみる。
右上のテストボタンを押す
こんな画面が出てくるので、イベント名に適当にhelloと入力、作成
再度テストを実行
結果がずらずらとでてきた。とりあえずは動いたようだ。
さて、問題のimport pandasができるのかを試す
レイヤーを選択
お客様のレイヤーにさっき登録したレイヤーが出てきた。
numpyとscipyならデフォルトで用意してくれとるのね。
ちなみに、余談だが試行錯誤してるときはこんな怒られ方をした
どうも、追加パッケージは合計262MBまでしかだめらしい。
AWS LambdaでTensorFlow 2.0を使った画像分類 - ユニファ開発者ブログ
この辺にいかにやりくりするか、みたいなテクニックが書いてあった。
ええ、そこって気を使わないかんとこなの・・・。
いろんなものimportして大きめのプログラム動かそうと思ったらlambdaは諦めて
EC2で普通に動かすしかないのかも。
とりあえず追加は完了。
プログラムにimport pandasとかを追加してテストしてみる。
ソースを変更したら、Saveしたのちにテストを実行
どうやらうまくいったらしい。
import kerasも試してみる
ぐえ、tensorflowもいりますよ、と(凡ミス)。
しかしtensorflowは馬鹿デカ容量なので
上に書いたように262MBなんて余裕で超えて、そのままlambdaには持ってこれない。
一応EC2にインストールしてみる
pip3.7 install tensorflow -t ./python/lib/python3.7/site-packages/ --no-cache-dir
(--no-cache-dirについては以下)
AWS EC2でMemoryErrorとNo space left on deviceで詰まった話 - Qiita
ずらずら、とインストールされた中からダメ元で重そうなのを消してみる。
tensorboardとかは確かにいらんよなぁ
容量を確認するコマンドを打ってみる
du -sh ./*/
本体が1.5GB〜!!
犯人探ししていこう
これか。
これ消しちゃまずそうなやつ・・・え、みんなどうやってんの?
Running TensorFlow on AWS Lambda using Serverless - Mike Moritz - Medium
slim
— Removes unneeded files and directories such as *.so
, *.pyc
, dist-info
, etc.
まじ?これ消していいの?(わかってない
どうみてもプログラムの本体だから無理だよなぁ
物は試しってことで除外してzipしてみる
[ec2-user@ip-172-31-14-167 ~]$ zip pkt.zip python/ -r -x python/lib/python3.7/site-packages/tensorflow/python/_pywrap_tensorflow_internal.so
できたのは164MBのzip
レイヤーに登録しようとすると
これでもでかすぎてだめか。
tensorflowさえ使わなければ万事うまくいきそうなんだけど、
使うんだよな〜〜
次回、どうも成功してる事例っぽいserverlessからデプロイするやり方をダメ元で試してみる・・・。
あと、s3に格納されたファイルを引数としたり、
結果をs3に格納したりする方法を探してみると
【AWS Lambdaの基本コードその2】 S3へのファイル保存 | ナレコムAWSレシピ
これをみると普通にpythonのコードでs3にアクセスする感じなのね。
てっきり、lambdaの機能として提供されているのかと思った。
そうなってくると、あとはimportがうまくいけば
この関数のトリガーイベントの設定と、
成功時・失敗時ログをどこに流すかって設定するぐらいか。
なんだかlambdaの全体像は見えてきたが、超えるべき山がまだ高い
AWSをいじり倒す(番外編.LightSailのredmineにgit設定)
LightSailで建てたRedmineの中にGitを入れ込むという設定。
別にLightSailがEC2(redmineの入ったAMI利用)であってもよい。設定は一緒。
これらの特徴はbitnamiのRedmineを利用するという点にある。
bitnami式Redmineは色々制約があるので、大半のネット記事を鵜呑みにするとうまくいかず、死ぬほど紆余曲折したのでメモしておく。
最後は妥協しているので参考程度に。
bitmani式ではなくて自前で構築する場合は、この記事は逆に参考にならんので注意
環境は
Ubuntu 16.04.6
Bitnami Redmine 4.1.0-8
gitのインストール
bitnami式redmineにgitは最初から入っているので不要。
リポジトリ を準備。
作業ディレクトリは適当に以下とした。
/home/bitnami/git/repos
リポジトリ を作成
なお、pushするたびにupdate-server-infoしないといけないらしいので
それを回避する設定もいれておく
実行権限も付与
bitnamiで建てたRedmineの、Apacheやwww-なんとかみたいなユーザ相当はdaemon。
それはこのサイトをみて知った。
Amazon Lightsailの諸々ファイル置場 | It works for me
他にもログの場所など有益な情報がまとまっていてありがたい
gitでやり取りする時に、必ずユーザとパスワードを要求したいので、その準備。
-bをつけて1ラインで登録してしまわないとバグるみたいな記載をみたので念の為書いておく
Apache用コンフィグの作成
bitnami式redmineの裏で動いているのはApache。
主にこのディレクトリ 配下にいろんなconfigが入っている
/opt/bitnami/apache2/conf/
この配下で作業を行っていく
git.confを新たに作成。
まずは最小構成の内容をgit.confに記載する。
このgit.confを読み込んでもらうために、同一ディレクトリにあるhttpd.confの最終行に
を追加
これで最小構成が完成。システムのリスタートを行う。
bitnami式redmineのリスタートコマンドは以下。
いったんここまででテスト。
ローカル環境にて、cloneを試す。
ユーザ名入力
パスワード入力
Cloning into 'test'...
warning: You appear to have cloned an empty repository.
この返答がくればOK
ちなみに、まだcloneはできてもpushはできない。
[master 452f009] first commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 testfile
error: Cannot access URL https://example.com/git/test.git/, return code 22
fatal: git-http-push failed
error: failed to push some refs to 'https://example.com/git/test.git'
pushするための設定
pushを行うためには、git.confに追加の設定が必要でそのやり方は2通りある
1.git-http-backendを使う
2.WebDavを使う
のどちらかを行う必要がある。
なお、SSHは使わずにhttpでなんとかしたい、というのが前提。
(鍵管理したくないので)
さて、git-http-backendを有効化する手順は巷にたくさん書いてあるが、
それをそのまま入れると失敗するのが最大のハマりポイント。
具体的にはこういうやつだ
上3行を追加し、取り消し線のところを削除
他にもなんだか装飾されたコンフィグが公開されているが、最低限これだけでいい。
リスタートしてconfファイルを読み込む
すると、ローカルで実行した結果がこうなる
fatal: repository 'https://example.com/git/test.git/' not found
ちなみに、cloneも失敗するようになる。
これが何故なのかわからず死ぬほど時間を使った。
結論はbitnami式redmine環境特有のものだった。
自分なりに調べた結果を言葉にすると
bitnami式は動作するための最小限のモジュールしかインストールしていない
かつ、通常インストールされる場所とは別のディレクトリにモジュールを置いているのでapt-get等で追加インストールしてもそれだけでは反映されない。
なので、普通に動かないし、追加でpkgインストールしても動かないという二重苦。
ちなみに2番目のDAVの設定にも手を出してみたがこれまた沼が深そうで諦めた。
また、手探りでa2enmod cgiコマンド打ったあと再起動したらredmineが立ち上がらなくなり、ubuntuのデフォルトページが表示されるようになってしまった。クソ焦って慌てて復旧させたが、configのいかんところが書き変わってしまったんだろうか・・・。若干トラウマ
さて、試行錯誤を経てたどり着いたのがこのブログ
bitnami redmine と同じホストで git 連携する - criticablog
完全ではないが、動くところまではもっていくことができた。
以下にコピペな部分もあるが手順を書いておく。
fcgiwrapインストール
ユーザグループ設定
git.confを大改造。Subversionとかは使わないので自分用に多少アレンジ
モジュールの配置
そして
すると、無事clone/pushどちらも動いた。
先人の知恵さまさま・・・!
これで、http通信+password要求でgitが使える環境が完成。
redmineとgitの連携は
あたりをみながら設定してなんなく動いた。
で
ここからはうまくいっていなのでただの参考記事
どうもredmineで作ったID/PASSをgitの認証に使うことができるとのこと
これは便利と思って試そうとして死んだ。
先人様のサイトにあるこのconfigをgit.confに追加でぶちこむ。
なお、DbPassは
/opt/bitnami/apps/redmine/htdocs/config/database.yml
に書いてある。
これでclone実行するとこんな感じのエラー
Cloning into 'test'...
fatal: unable to access 'https://example.com/git/test.git/': The requested URL returned error: 500
/opt/bitnami/apache2/logs/error_logをみるとこんなことが書いてある
[Mon May 25 14:36:04.034645 2020] [perl:error] [pid 21399:tid 139890079483648] [client 118.2.90.3:54909] install_driver(mysql) failed: Can't locate DBD/mysql.pm in @INC (@INC contains: /opt/bitnami/git/lib/site_perl/5.16.3 /opt/bitnami/perl/lib/5.16.3/x86_64-linux-thread-multi /opt/bitnami/perl/lib/5.16.3 /opt/bitnami/perl/lib/site_perl/5.16.3/x86_64-linux-thread-multi /opt/bitnami/perl/lib/site_perl/5.16.3 /opt/bitnami/perl/lib/5.16.3/x86_64-linux-thread-multi /opt/bitnami/perl/lib/site_perl/5.16.3/x86_64-linux-thread-multi /bitnami/ruby25stack-linux-x64/output/perl/lib/site_perl/5.16.3/x86_64-linux-thread-multi /bitnami/ruby25stack-linux-x64/output/perl/lib/site_perl/5.16.3 /bitnami/ruby25stack-linux-x64/output/perl/lib/5.16.3/x86_64-linux-thread-multi /bitnami/ruby25stack-linux-x64/output/perl/lib/5.16.3 . /opt/bitnami/apache2) at (eval 6) line 3.\nPerhaps the DBD::mysql perl module hasn't been fully installed,\nor perhaps the capitalisation of 'mysql' isn't right.\nAvailable drivers: DBM, ExampleP, File, Gofer, Proxy, Sponge.\n at /opt/bitnami/perl/lib/site_perl/5.16.3/Apache/Authn/Redmine.pm line 557.\n
太字のところに着目
DBD/mysql.pmが見つからねーよ、と。
これが先に説明したbitnami特有のモジュール足りてないアレ。
これについて戦っている記事を2つ見つけた。
git - install_driver(mysql) failed: Can't locate DBD/mysql.pm - Stack Overflow
なんでこのエラーがでるかの仕組みがしっかりと書いてあるが、
悲しいかなこのサイトの結論は
「サポートに繰り返し言ってくしかないね!」
こっちは気合で解決しましたと書いてある
過去サーバから設定ファイルをコピーしてきて、config書き換えたらうまくいったんだと。
試しにファイルをいくつかそれっぽいコピーしてみたけど、
そこまで知識がないので泥沼化して諦めた。つら。
どうも昔のredmineにはちゃんと入ってたモジュールっぽいんだよなぁ。
現時点の最新(4.1.0-8)には含まれてないのは謎