【Lambda】突然「Runtime.ImportModuleError」が発生!

Blog-Thumbnail_KUDs-01

はじめに

こんにちは、KUDs です。

先日、Lambda 関数で以下のようなエラーが発生しました。

[ERROR] Runtime.ImportModuleError: Unable to import module 'MODULE_NAME': cannot import name 'DEPRECATED_SERVICE_NAMES' from 'botocore.docs' (/opt/python/botocore/docs/__init__.py)

特に関数に手を加えたわけでもありません。
そう、ある日突然関数が正常に実行されなくなったのです。

今回の記事では、この事象の原因と解決策について解説します。

エラーの原因

概要

結論、原因はランタイムの自動更新によるものです。

もう少し言うと、以下のような流れで発生します。

  1. Lambda ランタイムが更新される
  2. 同梱の boto3 のバージョンが更新される
  3. 更新された boto3 とデプロイパッケージに含まれる botocore の互換性がなくなる
  4. エラー!

boto3 の GitHub に Issue も上がってました。

boto3-1.26.102 introduces import issue · Issue #3648 · boto/boto3
Describe the bug We are trying to run a tool using boto3 that stopped working with the most recent patch version (1.26.1...

色んなケースがありますね。
「botocore のバージョンを 1.29.102 にすると解決した」という記述もあります。
自分の事象とも当てはめると、botocore のバージョンが古い (1.29.102 未満?) かつ、boto3 が新しい (1.26.102 以上?) 場合にエラーが起きてそうな気がします。

調査

手元で検証・調査する中で、ランタイムの更新に依るものであることがわかりました。

以下は実際の確認内容です。

ランタイムのバージョン確認

まず、エラーが発生した時の Lambda 関数ログからランタイムを確認しました。

INIT_START Runtime Version: python:3.8.v48 Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:830ab1165f1cdc47b8ae1e516f3936d8a176aec50d0699efd25a8bfeabb797b4

ログから、エラー発生時のランタイムバージョンは python:3.8.v48 であることがわかります。

次に、以前正常に動作していた時のログを確認しました。

INIT_START Runtime Version: python:3.8.v43 Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:b20481443932830049531ab20faaf09295061aff7a67a0b0f40496e5e1ddec59

ログから、正常動作時のランタイムバージョンは python:3.8.v43 であることがわかります。

同梱の boto3 / botocore バージョン確認

boto3 / botocore のバージョンを確認しましょう。

関数内で以下のように記述しバージョンをチェックしましょう。

response = {
    'boto3 version is ': (boto3.__version__),
    'botocore version is ': (botocore.__version__)
}

print でも大丈夫です。

まず、ランタイム python:3.8.v43 の同梱をチェックします。

INIT_START Runtime Version: python:3.8.v43 Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:b20481443932830049531ab20faaf09295061aff7a67a0b0f40496e5e1ddec59
...
{'boto3 version is ': '1.26.90', 'botocore version is ': '1.29.90'}

次に、python:3.8.v48 の同梱をチェックします。

INIT_START Runtime Version: python:3.8.v48 Runtime Version ARN: arn:aws:lambda:ap-northeast-1::runtime:830ab1165f1cdc47b8ae1e516f3936d8a176aec50d0699efd25a8bfeabb797b4
...
{'boto3 version is ': '1.34.42', 'botocore version is ': '1.34.42'}

バージョンが上がっていることが確認できます。

対応策

対策はいくつかあります。
以下では、暫定的な対応と恒久的な対応を記載します。

暫定対応

とにかく早く復旧する必要がある場合は、とりあえずロールバックしましょう。
特に、本番環境でエラーが発生している場合はまず復旧優先です。

手順としては先ほどと同じような調査の流れになります。

  1. CloudWatch Logs から 対象の Lambda 関数ログを選択
    • INIT_START ログから正常動作時のランタイムバージョン ARN を確認
  2. 対象の Lambda 関数を選択
    • 「ランタイム管理設定を編集」
    • 「手動」を選択
    • ランタイムバージョン ARN にて arn:aws:lambda:{region}::runtime:{id} を指定

ランタイムバージョンを固定したら、とりあえずは復旧できるはずです。
落ち着いたら、以下の恒久対応を行っていきましょう。(ランタイムを完全に固定するとセキュリティイシューに繋がりますので)

恒久対応

恒久対応は大きく二つあるかと思います。

デプロイパッケージ / Lambda レイヤーで boto3 / botocore のバージョンを固定

boto3 / botocore を問題なく動いていた時のバージョンで固定する方法です。

Lambda レイヤーは Linux 等で Zip ファイルを作成し、カスタムレイヤーとしてアップロードしましょう。

ざっくりとした手順は以下のような感じになります。

$ mkdir botocore-layer
$ cd botocore-layer
$ mkdir -p python/lib/python3.8/site-packages
$ pip install boto3==1.26.90 botocore==1.29.90 -t python/lib/python3.8/site-packages/
$ zip -r botocore-layer.zip python

※事前にランタイムに合わせて python, pip を準備しておきましょう

後は Lambda レイヤー作成でアップロードし、適応すれば完了です。

Lambda ランタイムが提供する boto3 / botocore のバージョンを使用

Lambda ランタイムが提供する boto3 / botocore を使用できればそもそもエラーは発生しません。
また、ランタイム更新に応じてバージョンも更新されるため、セキュリティイシューに繋がる懸念も減ります。

但し、デプロイパッケージや Lambda レイヤーから boto3 の依存関係を完全に除外する必要があります。
完全に人(関数)に依るので、手順はありません。

さいごに

本番環境の場合はランタイムバージョンの自動更新をオフにしておいた方がいいでしょう。
検証環境のみ自動更新にして、正常性確認のために定期実行しておくのもいいかもしれませんね。

マネージドサービスは自動アップデートしてくれるのはありがたいですが、こういう所は注意したいです。

以上です。

コメント