AWS Lambda(Python)を使ってGoogle APIにアクセスしようとしたらライブラリではまった話です。
(Python初心者なので誤った記述があるかもしれません)
環境
- 開発環境用にEC2 Amazon Linux2 + Python 3.7
- Google APIにはサービスアカウントでアクセスする
- Google API利用に必要なPythonパッケージ用にLambda Layerを作成する
参考にした記事
- メインのコード自体は「補足:サービスアカウントを使ったやり方」を
ほぼそのまま活用させていただきました。
- 必要なパッケージについてはGoogleの「Python quickstart」を参照しています。
事象
pip -t
でインストールしたディレクトリを圧縮・Layer化したのに、Lambdaが以下のエラーを吐く。
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function': No module named 'google.oauth2' Traceback (most recent call last):
※Layer用のzipは以下の手順で作成(Layerの作り方は、いつもお世話になっているクラスメソッドさん記事参照)
$ mkdir python
$ pip3 install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib -t python/
$ zip -r layer.zip python/
切り分け
1. ローカルで同じようにパッケージをインストールしコードを動かしたときはエラーが出なかった
→Layerの問題?
2. google.oauth2
はgoogle-auth-oauthlib
の前提パッケージ
→一緒にインストールされているはず。
3. -tで指定したパッケージのインストール先にgoogle.oauth2
が存在するかどうか
→なぜかprotobufしか存在しない
$ ls python/google/
protobuf _upb
4. ローカル(-t 指定なし)のパッケージインストール先を見てみる
→oauth2はある。逆にprotobufが無い。(つまりprotobufは別のところにインストールされている?)
$ ls /usr/local/lib/python3.7/site-packages/google
api api_core auth cloud gapic logging longrunning oauth2 rpc type
5. protobufのインストール先を探す
→/usr/local/lib/
ではなく /usr/local/lib64/
にインストールされている。
/usr/local/lib64/
を確認すると3と同じようにprotobufしか存在しない状態。
$ pip3 show protobuf
Name: protobuf
Version: 4.21.7
Summary:
Home-page: https://developers.google.com/protocol-buffers/
Author: protobuf@googlegroups.com
Author-email: protobuf@googlegroups.com
License: 3-Clause BSD License
Location: /usr/local/lib64/python3.7/site-packages
Requires:
Required-by: google-api-core, googleapis-common-protos
$ ls /usr/local/lib64/python3.7/site-packages/google/
protobuf _upb
推測
-t を使うことでlib/ と lib64/ に分かれるはずのパッケージが同じディレクトリにインストールされてしまい、上書きされている?
原因
どうやら pip の --upgrade オプションと -t を併用した場合、同じ名前で異なるプラットフォーム(32bit と 64bit)のパッケージがあると追加ではなく、上書きされてしまうようです。
(upgradeだから他のパッケージには影響がないかと思いきや、ディレクトリが同じだと置き換えになる)
githubでも議論されていて、closedにはなっているもののズバリの解決策がありません。
You cannot pip install a single set of requirements that have mixed pure lib and platform lib installations with the same name, on a platform where the pure lib and platform lib folders are different, as pip will not merge these together.
--upgradeの説明を追加したほうがいいんじゃない?とも言われていますね。
- WARNING: Target directory /path/to/target/namespace already exists. Specify --upgrade to force replacement.
+ WARNING: Target directory /path/to/target/namespace already exists. Specify --upgrade to force replacement (existing 'namespace' will be replaced completely, not merged)
回避策
pip -t
によるディレクトリ指定のインストールは利用できないので、pythonの仮想環境を利用し
Layer用zipファイルを作成することにします。
$ python3 -m venv env
$ source env/bin/activate
$ pip3 install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
$ cp -pr env/lib/python3.7/site-packages python
$ zip -r layer.zip python/
仮想環境は lib64/ が lib/へのシンボリックリンクになっており、パッケージが同じディレクトリに共存できます。
(googleディレクトリにprotobufと他のパッケージが存在している)
$ ls -l env/
total 8
drwxrwxr-x 2 ec2-user ec2-user 4096 Oct 17 16:41 bin
drwxrwxr-x 2 ec2-user ec2-user 6 Oct 17 16:41 include
drwxrwxr-x 3 ec2-user ec2-user 23 Oct 17 16:41 lib
lrwxrwxrwx 1 ec2-user ec2-user 3 Oct 17 16:41 lib64 -> lib
-rw-rw-r-- 1 ec2-user ec2-user 70 Oct 17 16:41 pyvenv.cfg
$ ls env/lib/python3.7/site-packages/google
api api_core auth cloud gapic logging longrunning oauth2 protobuf rpc type _upb
結果
仮想環境で作成したlayer.zipを読み込ませたところ、エラーなくGoogle APIが実行できるようになりました。
START RequestId: ee65c39b-6d0d-44b5-81e4-xxxxxxxxxx Version: $LATEST
Getting the upcoming 10 events
No upcoming events found.
{'kind': 'calendar#events', 'etag': '"xxxxxxxxxxxxxxx"', 'summary': 'Work', 'updated': '2022-10-18T01:11:51.814Z', 'timeZone': 'Asia/Tokyo', 'accessRole': 'reader', 'defaultReminders': [], 'nextSyncToken': '---------------------------------', 'items': []}
END RequestId: ee65c39b-6d0d-44b5-81e4-xxxxxxxxxx
REPORT RequestId: ee65c39b-6d0d-44b5-81e4-xxxxxxxxxx Duration: 3485.31 ms Billed Duration: 3486 ms Memory Size: 128 MB Max Memory Used: 87 MB Init Duration: 565.76 ms