みるめも

Lambdaレイヤーを実際のプロジェクトで使ってみてとても不便に感じたこと3つ

Lambdaレイヤーを実際のプロジェクトで使ってみてとても不便に感じたこと3つ
0
1
214
2
4
みるみ
Follow Me!
みるみ

ブロガー、エンジニア。

文章を書くのが好きです。

Lambda同士で共通の処理を切り出せる LambdaLayers は一見とても便利に見えますが、実際のプロジェクトで使ってみたらかなり不便に感じました。

その結果工数をかけてまでわざわざレイヤー化したものをもとに戻したほどです(これは他の要因もあったけど)

備忘録を兼ねて、デメリットを3つでまとめます。

Lambda Layersのここが微妙だった

今回は

  • 運用は SAM
  • 言語はPython
  • けっこう大規模なアーキテクチャ(ネストスタックなどあり)
  • このアーキを触る主な開発人員は3~4人

という感じでした。

1.ポリシーや権限などをレイヤーを呼ぶFunctionやスタックに持たせないといけない

これは「不便」や「面倒」といった僕らの精神的側面とは全く違う問題だと思っていて、本来必要としていないリソースに無駄な権限付与や環境変数付与をしないといけなくなるのがかなり気持ち悪かったんですよね。

図にするとこう。

lambda-layers-demerit-policy

この図だけだとあまり伝わらない気がするのですが、実際はスタックで分かれていたりするのでもっと大変な感じでした。

レイヤーはLambdaと名前こそつくものの、実態としてはインポートするモジュールのようなものに過ぎません。つまりそれを行使するLambda Functionに実際のパラメータを付与しないといけなくなり、SAMのテンプレートもだいぶめちゃくちゃなことになります。わりと地獄を見た。

これはネストスタックを使っているとさらに混乱することになります。「全然必要としていないヤツにもVPCのセキュリティグループとかが付与されている!」とかなるわけで、真面目に設計するならこの時点で何かを変えないといけません。

最後にちょろっと書いたんですが、たぶんレイヤーの想定用途としてこういうシーンは考えられていないんじゃないかなーと今は考えています。

2.マネジメントコンソールからLambda関数として気軽にさわれない

実際の実装はローカルの好きなエディタでやるとはいえ、

  • デプロイしたあとにちょっとprint文を足したい
  • 動作確認のためにデプロイ環境上でだけ特定範囲をコメントアウトしておきたい

みたいな需要は絶対ありますよね。

そんなとき、サクッとマネジメントコンソールのLambdaを開いてソースを直で触れるのは便利でした。どうせそのあとデプロイしたりCI/CDが動いたりしたらちゃんと正常状態に上書きしてくれるわけだから気も楽だし。

ところがレイヤー化してしまった部分は単一のLambda Functionとしては触れなくなるので、こういったGUI上での気軽なデバッグはできなくなります。

 

あと、これに絡んでもうひとつ言うとするならCloudWatchでログを見たいときに単体の関数として閲覧できないというのも挙げられると思います。

当然レイヤーを呼んだLambdaのログストリームとして探しに行くことになるので、そのアプリケーションならでは処理をレイヤーに切り出したときは「これなんか思ってたんと違くね…?」ってなりがち(DB接続みたいな本当の汎用的な処理を切り出す分にはこの限りではないと思います)

3.他のFunctionから参照するレイヤーのバージョンを常に最新のものに保つ(現実的な)方法がない

これは解決策を知らないだけの可能性があるんですが、めっっっちゃ調べたけど良い方法が見つからなかったので書く。

SAMでtemplate.yamlを記述するにあたり、FunctionにLayerを持たせる部分はこんな感じになるかと思います。

Function:
  Type: AWS::Serverless::Function
  Properties:
    FunctionName: my-function
    CodeUri: ../modules/functions/
    Handler: app.lambda_handler
    Runtime: python3.8
    Layers:
      - arn:aws:lambda:ap-northeast-1:123456789012:layer:MyLayer:1

最後を見ると分かる通り、レイヤーにはバージョンの概念があり、バージョンが違うものはARNも違う扱い→別リソースということになる性質があります。

困ったことにこいつはデプロイするたびに勝手にバージョン番号をインクリメントさせていくので、レイヤーの内容を更新するたびに参照しているLambda Function側のテンプレート内容も更新しないといけないということになります。

Layers:
  # AWS SDKとかでは使える $LATEST という表現はここでは使えないらしい
  - arn:aws:lambda:ap-northeast-1:123456789012:layer:MyLayer:$LATEST
  # 無意味な抵抗
  - arn:aws:lambda:ap-northeast-1:123456789012:layer:MyLayer:{$LATEST}
  # SubやRefしようにもParametersには純粋な文字列しか置けないのでどうにもならない
  - !Sub arn:aws:lambda:ap-northeast-1:123456789012:layer:MyLayer:{$hoge}

こんな感じで思いつく記述は色々試してみたり…

Outputs:
  MyLayerArn:
    Value: !GetAtt MyLayer
    Export: 
      Name: !Sub ${AWS::StackName}-MyLayer

Outputs:を利用して他のスタックで使うクロススタック参照、およびDynamic Referencesのも試みたりもしましたが、なんか色々あってダメだった記憶があります(詳細忘れた)。

その他ネストスタックの構造を上手く使うとなんとかなったりするにはするのだけど、レイヤー単体でこれをちゃんと解決する方法はついぞ見つけられませんでした。

なんでや~~~

Serverless を使用すればわりと簡単に記述できそうなことは分かりました。が、これだけのためにデプロイ環境に新しい要素を持ち込むのは、ねぇ…。

 

レイヤーにこんな面倒なバージョンという仕組みがあることからも、頻繁に更新されるものではない→アプリケーションロジックを記述するような場所ではなさそうという感じがやっぱりしますね。

使ってみると思想も色々見えてくるもんですな。

おわりに

まあ書いてて思ったわけですが、たぶんレイヤーの想定されている用途は本当に汎用的な処理だけを書くものなんでしょうね。アプリケーション全体で使われるから~みたいな感じで関数として切り出すイメージだとたぶんデメリットばかり気になる感じ。

とはいえわざわざ共通化を検討していたということはそこそこな内容のものではあるはずだし、全部が全部DB接続とか通信処理とかではないよなーとも思う。

ベストプラクティス知りたい。

0
1
214
2
4
みるみ
Follow Me!
みるみ

ブロガー、エンジニア。

詳しいプロフィールはこのページで色々書いてます。もやってます。

みるめも