目录

关键词:aws、lambda、云函数、python3.6、运行时、依赖包

背景

有个用户在上传背景图,下次登录后背景图消失。

经过排查发现上传背景图,上传s3走的上传图片服务接口:lambda.9ong.com/uploadimg

而上传接口lambda.9ong.com/uploadimg 是aws上的lambda云函数服务。

问题

将代码调整后,在本地执行上传s3,但s3提示没有权限:

S3UploadFailedError: Failed to upload /tmp/jm-cqj.jpg to img6.9ong.com/jm-cqj_1642065228.jpg: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

经过确认比对,发现代码中的access_key与aws控制台的access_key不一致。判定是key被删导致。

理论上直接改代码,更新最新的密钥即可,但由于原云函数使用python2.7实现的功能,aws出于安全原因,不允许直接改python2.7的云函数,需要将python环境升级到3.6以上,而第三方包boto、PIL都需要更新,甚至io包的使用方式有所调整,需要在原来的基础之上进行调整修改。

支持python3.6的代码调整

这里忽略具体代码,这里默认代码测试是ok的。

代码与运行时依赖分离

旧版本代码上传是直接把逻辑代码文件与依赖包一起打到同一个zip包。详见:使用 .zip 文件归档部署 Python Lambda 函数 - AWS Lambda

但我们尝试过几次没有成功,一直报找到handler(handler.handler是入口文件handler.py的handler入口方法),后来发现其实就是异常错误了,比如:

其中有一次测试发现日志里报一个错误:

Unable to import module 'handler': libjpeg.so.8: cannot open shared object file: No such file or directory

结合AWS LambdaでPillow-SIMDを使用した際に起きたエラーの解決方法 - Qiita这篇文章,尝试将逻辑代码与依赖的python库和lib库分离。

看官方文档,关于lambda层的定义与创建:创建和共享 Lambda 层 - AWS Lambda

Lambda 层提供了一种方便的方法来打包库和其他可与 Lambda 函数搭配使用的依赖项。使用层可以缩小上传的部署存档的大小,并加快代码的部署速度。层还可促进代码共享和责任分离,以便您可以更快地迭代编写业务逻辑。

层是可以包含其他代码或数据的 .zip 文件存档。层可以包含库、自定义运行时、数据或配置文件。层可促进代码共享和责任分离,以便您可以更快地迭代编写业务逻辑。

层是可以包含库、自定义运行时、数据、配置文件的,但需要放在一个Lambda云函数能识别的path上:

运行时 路径
Node.js nodejs/node_modules
Node.js nodejs/node14/node_modules (NODE_PATH)
Python python
Python python/lib/python3.9/site-packages(站点目录)
所有运行时 bin (PATH)
所有运行时 lib (LD_LIBRARY_PATH)

通过上表(来自上面的链接)我们就知道我们要构造的层的zip包结构是:

其中lib目录可以放比如前面缺失的libjpeg.so.8等文件,python目录用于存放依赖的第三方包 ,详见:如何生成依赖包的zip包?

这样就有了层的zip包:layer.zip。

然后对逻辑代码进行打包,打完的imageDeal.zip包如下:

如何生成运行时依赖的zip包

使用 .zip 文件归档部署 Python Lambda 函数 - AWS Lambda

我们是有依赖关系的部署程序包,参考上面文档,假设在mac或linux上:

我们先安装对应第三方包,创建一个package目录,将需要安装的第三方包下载到package目录:

mkdir lambda

# 准备python依赖包
cd lambda
mkdir python
pip3 install --target ./python qiniu
pip3 install --target ./python boto3
pip3 install --default-timeout=100 --target ./python Pillow

# 准备lib依赖
cd ..
mkdir lib
cp /usr/lib/x86_64-linux-gnu/libjpeg.so.8 lib/

# 压缩打包 ,在lambda目录下
zip -r layer.zip .

–default-timeout=100 用于解决read timeout问题,如果存在这种情况的话。

如果有必要时使用 sudo

运行时依赖zip包结构如下:

上传代码和依赖

创建层及上传

前面我们将python第三方依赖包和运行时依赖文件都打包到layer.zip,紧接着就是创建lambda层,上传这个zip包:

上传代码zip包

第三方依赖包与运行时依赖文件剥离到层后,剩下的就是我们的逻辑代码了(也就是gitlab上的代码文件),我们将前面压缩打包的imageDeal.zip上传:

进行一次简单的测试,保证lambda能正常调用到入口方法(详见运行时配置:handler.handler):

日志排查

测试过程中,并不是一直顺利,比如可能会因为依赖文件缺失报错等(一般语法的错误在本地都能解决),Lambda云函数支持了监控-日志:

Lambda 会记录云函数处理的所有请求,并通过 Amazon CloudWatch Logs 自动存储代码生成的日志。我们也可以在代码中插入自定义日志记录语句来验证代码。

自定义日志记录:

print("## s3 upload reponse:")
print(response)

Python 中的 AWS Lambda 函数日志记录 - AWS Lambda

参考

Python 中的 Lambda 函数处理程序 - AWS Lambda

python: boto3 客户端线程安全吗?

Clarify multithreading documentation · Issue #1246 · boto/botocore

Session — Boto3 Docs 1.20.37 documentation

Low-level clients — Boto3 Docs 1.20.37 documentation