20
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【ServerlessFramework】LambdaがCode storage limit exceededになってしまった時の対処法

Last updated at Posted at 2019-09-04

ServerlessFrameworkでデプロイしようとしたら、Lambdaの上限に達してしまい、さらにはCloudformationのスタックのロールバックに失敗してどうにもならなくなってしまいました。
この問題を解決するために手こずってしまったので、覚書です。

初めに

この記事は己への戒めです

事の経緯

ある日突然、ServerlessFrameworkでデプロイしようとしたら、

An error occurred: 関数名 – Code storage limit exceeded. (Service: AWSLambda; Status Code: 400; Error Code: CodeStorageExceededException; Request ID: hogehoge).

というメッセージが返ってきて、deployをお断りされてしまいました。
さらに、

Stack:arn:aws:cloudformation:ap-northeast-1:8515373650097:stack/ReceptionROBO-receptionDev/e71e9510-475a-11e8-b902-503a6feb6ef is in UPDATE_ROLLBACK_FAILED state and can not be updated.

なんてことまで言われてしまいました。

この時点で発生している問題は、
・アカウント単位で設定されているLambdaのコードストレージ上限(75GB)に達してしまった。
・CloudFormationのスタックのロールバックに失敗してしまった。
の2点です。

Lambdaの問題
Lambdaには様々な制限があり、Lambdaのコードストレージの上限は75GBとなっています。
コードストレージにはLambda関数のバージョンが含まれており、Serverlessframeworkで特に何も指定していない場合、現在の一番新しいバージョン($LATEST)以外に、歴代の古いバージョンが蓄積されていきます。これがコードストレージを圧迫していき、ついに75GBに達してしまったようです。

CloudFormationの問題
CloudFormationのステータスですが、公式によると、UPDATE_ROLLBACK_FAILEDは「スタックの更新の失敗後、1 つ以上のスタックを前の動作ステートに戻すことに失敗」した状態です。
Lambdaのコードストレージが上限に達し、デプロイに失敗したことでスタックの更新にも失敗してしまった。しかし、Lambdaがどうにもならない状態になっているので、ロールバックもできない、ということでしょうか・・・(間違っていたら教えてください)

とりあえず、
CloudFormationのスタックを正常にロールバックし、ステータスをUPDATE_ROLLBACK_COMPLETEにして、かつLambdaのコードストレージから不要なバージョンを削除すれば良さそうです。

対処法

結論を先に言ってしまうと、この問題に対する対処法は
・CloudFormationのスタックを正常にロールバックし、ステータスをUPDATE_ROLLBACK_FAILED→UPDATE_ROLLBACK_COMPLETEにする。
・Lambdaの不要なバージョンを消す。
・再発を防ぐため、Lambdaのバージョン管理するプラグインを入れる。
です。

やったこと

1.CloudFormationのスタックをロールバックする。

ロールバックが既に失敗している時点で、一筋縄ではいかないことは容易に想像がつきます・・・
公式のトラブルシューティングを確認すると、更新のロールバックを強制的に続行することができるようです(失敗することもあります。というか、失敗することの方が多いようですが・・・)。
それでダメなら失敗の原因になっているリソースをスキップしてロールバックするように書いてありました。
とりあえずやってみます。

CloudFormationのコンソール画面に行き、左側のスタック一覧から、問題を起こしているスタックを選択します(スタックの名前はエラーに書いてあります)。
この時、状況の理由欄に、なぜロールバックが失敗してしまったのかが書いてあります。ロールバックがうまくいかない原因になっているリソース名が書いてあるので、確認しておきます。

スクリーンショット 2019-08-30 20.14.59.png 上の方にあるスタックアクションをクリックし、更新のロールバックを続行を選択します(画像はクリックできないようにグレーアウトになってしますが、ステータスがUPDATE_ROLLBACK_FAILEDの時はクリックできます)。 スクリーンショット 2019-08-29 10.36.19.png ロールバック時にスキップするリソースの一覧が出てきます。 スキップするリソースにチェックを入れて、左下の更新ロールバックを続けるをクリックします。 スクリーンショット 2019-08-29 10.40.17.png

しばらく待って成功すれば、ステータスがUPDATE_ROLLBACK_COMPLETEに変わります。
UPDATE_ROLLBACK_COMPLETEは「スタックの更新の失敗後、1 つ以上のスタックを前の動作状態に正常に戻した状態」です。
これでCloudFormationの問題の方は解決・・・?

2.Lambdaの不要なバージョンを消す。

CloudFormationのステータスがUPDATE_ROLLBACK_COMPLETEになったからといって、この段階でデプロイできるようになるわけではありません。
根本的な原因=Lambdaのコードストレージに空きがない問題を解決していないからです。

試しにLambdaのコンソール画面から様子を伺ってみると、
スクリーンショット 2019-08-28 19.34.29.png
コードストレージがバッチリ100%になっちゃってます。
ちなみにバージョンの方も確認してみると、
スクリーンショット 2019-08-29 10.35.56.png
うわっ・・・(引)
こんな状態の関数がいっぱいあるわけですから、75GBあっても足りなくなってしまうわけです・・・
いくら一つ一つの関数のサイズが小さくても、大量のバージョンをぶら下げていたら意味ないですね・・・

単純にこれらのバージョンを消していけばいいだけなのですが、数が膨大すぎるので手作業ではやりたくありません。
どうしようかと思って調べていたところ、こちらにいい解決策がありました。
この記事で紹介しているclear-lambda-storageを使うと、溜まりに溜まったLambda関数の古いバージョンを一気に消してくれます。やってみましょう。

やるといっても、本当にシンプルで、

git clone https://github.com/epsagon/clear-lambda-storage
cd clear-lambda-storage/
pip install -r requirements.txt
python clear_lambda_storage.py --token-key-id <access_key_id> --token-secret <secret_access_key>

これだけなのですが、

An error occurred (UnrecognizedClientException) when calling the ListFunctions operation: The security token included in the request is invalid.

エラーです。

Scanning ap-east-1 region

の出力があった後に失敗していたので、リージョンと認証情報が噛み合ってない・・・?
tokenの有効期限も確認しましたが、問題なし。
Lambdaは東京リージョンを使っていたので、東京だけみてくれたらいいのになーと思い、ソースコードをみてみることにしました。

clear_lambda_storage.py
def list_available_lambda_regions():
    """
    Enumerates list of all Lambda regions
    :return: list of regions
    """
    session = Session()
    return session.get_available_regions('lambda')

ここでLambdaが使用可能なリージョン一覧をリストで取ってきて、

clear_lambda_storage.py
def remove_old_lambda_versions(args):
    """
    Removes old versions of Lambda functions
    :param args: arguments
    :return: None
    """
    regions = list_available_lambda_regions()
    total_deleted_code_size = 0
    total_deleted_functions = {}
    num_to_keep = 2

    if args.num_to_keep:
        num_to_keep = args.num_to_keep

    for region in regions:
        print('Scanning {} region'.format(region))
        ##以下省略

regionsの中にあるリージョン分削除を繰り返す、というような動きのようです。
東京だけやれたらいいので、

clear_lambda_storage.py
    #regions = list_available_lambda_regions()
    regions = ['ap-northeast-1']

こうしました(最低)。

もう一度やってみます。

$ clear-lambda-storage ootaizumi$ python clear_lambda_storage.py --token-key-id <access_key_id> --token-secret <secret_access_key>
Scanning ap-northeast-1 region
Detected ReceptionROBO-receptionDev-Get_ReservationInfo_Date with an old version 1
Detected ReceptionROBO-receptionDev-Get_ReservationInfo_Date with an old version 2
Detected ReceptionROBO-receptionDev-Get_ReservationInfo_Date with an old version 3
Detected ReceptionROBO-receptionDev-Get_ReservationInfo_Date with an old version 4
......長すぎるので省略

古いバージョンの検出が始まりました。
あまりにも多すぎて、手に汗を握りながら眺めていたのですが、多い関数で400くらいありました。ひええ・・・

----------
Deleted 6424 versions from 34 functions
Freed 83916 MBs

終わったようです。
コンソール画面を確認してみると、
スクリーンショット 2019-08-29 20.37.52.png
すごい減ってる・・・!

ここまでやって、ようやくdeployできるようになりました。長かった・・・

3.Lambdaのバージョン管理を行うプラグインを導入する。

何もしなければ、いつか同じ問題が再発してしまうので、このタイミングで対策しておきます。

Lambda関数のバージョンは、Serverlessframeworkでデプロイした時に溜まっていきます。
必要位以上に古いバージョンを溜め込まないように、Serverless Prune Pluginを使います。
Serverless Prune Pluginは、デプロイする度に溜まっていく古いバージョンを適宜削除してくれるプラグインです。
早速使ってみます。
Serverlessと同じ階層で

$ sls plugin install -n serverless-prune-plugin

これだけです。
Serverless Prune Pluginがインストールされ、serverless.ymlに

serverless.yml
plugins:
  - serverless-prune-plugin
custom:
  prune:
    automatic: true
    number: 3

この記述が追加されます。
automaticをtrueにしておくと、Serverlessでデプロイした時にこのプラグインが自動的に実行されます。
numberは残しておくバージョンの数です。デフォルトは3になっているようです。

この状態でデプロイしていると、
スクリーンショット 2019-09-04 10.21.52.png
こんな感じで、最新のバージョン($LATEST)と3つ前のバージョンまで残るようになります。
関数の数にもよりますが、この程度ならコードストレージを圧迫してしまうこともないでしょう。きっと。

終わりに

今回のことは、事前にきちんと対策をしていれば防げたことだと思います。
サーバーレスってとても便利で、サクサクできるので開発していてすごく楽しいのですが、やることはきちんとやっておかないといけないなと思いました。反省・・・
もっと勉強しよう・・・

参考

How to Free AWS Lambda Code Storage when Exceeded
Serverless Frameworkを本番運用する際にやっておいたほうが良い事

20
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?