作者: Jim Wang 公众号: 巴博萨船长

摘要:使用Python实现aws lambda函数时,如何解决代码依赖问题?如何在aws lambda中配置并安装Python的依赖包Packages?以pymssql依赖包为例,如何在aws lambda的Python代码中import该依赖?

Abstract: How to solve the problem of code dependency when using Python to implement aws lambda function? How to configure and install Python’s dependency package Packages in aws lambda? This article takes the pymssql dependency package as an example. How to import the dependency in the Python code of aws lambda?

作者: Jim Wang 公众号: 巴博萨船长

任务背景

任务为使用aws的lambda函数从一个MS SQL数据库中读取部分的数据,然后对数据处理后,转存至aws的DyanmoDB上,因为需要从MS SQL数据库中读取信息,代码就需要相关依赖,Python对MS SQL的依赖库为pymssql,初步计划lambda 的函数使用Python来完成,版本为Python 3.6。本文主要介绍,如何在aws lambda函数中使用python依赖库pymssql

所遇问题

aws lambda 的 Python 3.6 的运行环境,是不具有pymssql这个依赖包的,该依赖包并非标准库,又PyPI(Python Package Index)是python官方的第三方库的仓库管理维护,可以通过pip命令获取和安装。aws lambda 函数Python Runtime 运行时与操作系统如下,总的来说lambda函数是运行在Amazon Linux的操作系统上的,Amazon Linux和Amazon Linux2的区别就是内核版本有区别。

Python 运行时
名称 标识符 适用于 Python 的 AWS 开发工具包 操作系统
Python 3.8 python3.8 boto3-1.14.17 botocore-1.17.17 Amazon Linux 2
Python 3.7 python3.7 boto3-1.14.17 botocore-1.17.17 Amazon Linux
Python 3.6 python3.6 boto3-1.14.17 botocore-1.17.17 Amazon Linux
Python 2.7 python2.7 boto3-1.14.17 botocore-1.17.17 Amazon Linux

在测试中,我们使用的lambda函数名为test,Python文件名为lambda_function.py,该文件内容为,lambda函数的范例代码,内容如下。其中第二行为修改内容,主要为了引入pymssql模块。

1
2
3
4
5
6
7
8
9
import json
import pymssql

def lambda_handler(event, context):
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}

由于pymssql不是标准库,所以如果直接在lambda函数中引用该包,则lambda的执行结果为:失败,错误信息为:

1
2
3
{
"errorMessage": "Unable to import module 'lambda_function'"
}

日志输出为,

1
2
3
4
5
START RequestId: d77542a0-d9c0-426a-a050-04948dc8464a Version: $LATEST
Unable to import module 'lambda_function': No module named 'pymssql'

END RequestId: d77542a0-d9c0-426a-a050-04948dc8464a
REPORT RequestId: d77542a0-d9c0-426a-a050-04948dc8464a Duration: 0.32 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 43 MB Init Duration: 1.32 ms

可以看出,程序错误的原因为无法引入pymssql这个模块,是一个依赖问题。基本的共识为:不同版本的Python的相同第三方依赖包的内容是不同的,同样的不同操作系统的相同第三方依赖包的内容也是不同的。所以在调试代码也好,解决依赖也好,就需要创建一个Python版本隔离并且依赖隔离的环境,这部分内容,在python相关的文章里都有介绍,若有需要可以查看相关文章。

解决方法

解决问题的思路有两种:第一种,使用lambda 层(Layer)的方式解决依赖问题,另一种为将依赖内容与lambda函数的py文件整合在一起解决依赖问题。lambda函数代码更新可以通过,网页端,上传zip程序包或Amazone S3中引用,三种方式完成,本文主要讨论的以zip压缩文件作为程序包来更新lambda的相关代码部分。

创建 lambda 程序的依赖项程序包

要创建一个运行在linux系统下,Python 3.6运行时的lambda函数相关依赖项的程序包,就要创建并启动一个Python的隔离运行环境。关于如何创建这样一个隔离环境,也请参考python相关文章。

启动虚拟环境

比如,在Linux系统下,我们有如下的一个名为env3612的python虚拟环境,则可以在终端里输入pyenv activate env3612命令来启动这个虚拟环境,

1
2
3
4
5
6
username@mac:~# pyenv virtualenvs
3.6.12/envs/env3612 (created from /root/.pyenv/versions/3.6.12)
env3612 (created from /root/.pyenv/versions/3.6.12)

username@mac:~# pyenv activate env3612
(env3612) username@mac:~#
创建工作文件夹

在相关虚拟运行环境启动后,就可以创建一个工作目录,下面代码中第一层目录的文件夹pymssql-dependencies这个名字可以自定义,其不对后续操作产生影响,第二层目录的文件夹python,该文件夹名非常重要,请保持一致。

1
2
3
4
username@mac:~# mkdir pymssql-dependencies
username@mac:~# cd pymssql-dependencies/
username@mac:~/pymssql-dependencies# mkdir python
username@mac:~/pymssql-dependencies# cd python/
安装单一Python库

进入最终工作目录python之后,即可使用pip命令pip install pymssql -t ./ ,就可以单独安装pymssql这个依赖库安装到这个python文件夹内,安装之后,文件夹内仅有pymssql相关内容。

1
(env3612) username@mac:~/pymssql-dependencies/python# pip install pymssql -t ./
压缩程序包

完成pymssql之后,需要从python这个文件夹返回至上一级目录,再使用zip -r ~/tmp/pymssql-dependencies.zip . 将python目录内的所有内容压缩成zip文件。该文件可以理解为lambda函数的程序包。

1
2
3
4
5
6
7
(env3612) username@mac:~/pymssql-dependencies/python# cd ..
(env3612) username@mac:~/pymssql-dependencies# mkdir ~/tmp
(env3612) username@mac:~/pymssql-dependencies# zip -r ~/tmp/pymssql-dependencies.zip .
adding: python/ (stored 0%)
adding: python/_mssql.cpython-36m-x86_64-linux-gnu.so (deflated 73%)
...
adding: python/pymssql.libs/libsybdb-98ddd287.so.5.1.0 (deflated 64%)

一、通过 lambda 层解决依赖问题

该方法解决问题是,首先要创建一个lambda 函数的层,配置信息请参考下方图片。在这里层的名称可以自定义,因为其只做为索引,在lambda函数引入层时,被用于索引。与之后要上传至层的zip文件名,和zip文件内的文件都无关系。

创建lambda层

Lambda Create Layer

然后选择之前创建的zip文件,上传后点击创建,就可以完成相关lambda层的创建。再在lambda函数的配置页面点击Layers,然后选择添加层,为test这个lambda函数的指定一个layer。

aws-lambda-0203

然后在如下页面选择自定义层,选择层的名称,点击添加即可完成配置。

aws-lambda-0204

然后就可以再次测试该lambda函数。这时候lambda的执行结果为:成功,详细信息为:

1
2
3
4
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}

日志输出为,

1
2
3
START RequestId: ee1f059b-8288-4463-8501-8f36df9a6d67 Version: $LATEST
END RequestId: ee1f059b-8288-4463-8501-8f36df9a6d67
REPORT RequestId: ee1f059b-8288-4463-8501-8f36df9a6d67 Duration: 0.33 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 48 MB Init Duration: 29.24 ms

如上的信息表明,上述方法已经通过lambda函数的层的方式解决了依赖问题,通过该方法也可以解决其他相关依赖的相似问题。

二、通过 lambda 代码解决依赖问题

该方法前期准备工作与方法一相同,都需要创建Python的隔离运行环境,并安装pymssql至python文件夹内,在安装以后,python文件夹的具体情况如下,

1
2
3
4
5
6
7
8
(env3612) username@mac:~/pymssql-dependencies/python# ls -al
total 7392
drwxr-xr-x@ 7 username staff 224 9 27 16:23 .
drwx------+ 145 username staff 4640 9 27 16:24 ..
-rwxr-xr-x@ 1 username staff 2287480 9 27 11:07 _mssql.cpython-36m-x86_64-linux-gnu.so
drwxr-xr-x@ 9 username staff 288 9 27 11:07 pymssql-2.1.5.dist-info
-rwxr-xr-x@ 1 username staff 1490808 9 27 11:07 pymssql.cpython-36m-x86_64-linux-gnu.so
drwxr-xr-x@ 4 username staff 128 9 27 16:20 pymssql.libs

这时候需要将lambda函数的py文件,即lambda_function.py拷贝至该目录,然后在python目录下,运行zip -r ~/Downloads/test.zip .命令,将python文件内的所有文件压缩成为一个名为test.zip的压缩文档,如下,

1
2
3
4
5
6
7
8
(env3612) username@mac:~/pymssql-dependencies/python#  zip -r ~/Downloads/test.zip . 
updating: _mssql.cpython-36m-x86_64-linux-gnu.so (deflated 73%)
updating: pymssql.cpython-36m-x86_64-linux-gnu.so (deflated 72%)
updating: lambda_function.py (deflated 24%)
adding: pymssql-2.1.5.dist-info/ (stored 0%)
adding: pymssql-2.1.5.dist-info/RECORD (deflated 39%)
...
adding: pymssql.libs/lambda_function.py (deflated 24%)

在这里,zip的文件名最好与lambda函数保持一致,文件名称字母大小写不敏感。然后点击lambda函数中函数代码部分的操作按钮,然后选择上传.zip文件项,接着选择test.zip压缩文件,点击保存即可。

aws-lambda-0204

完成上述内容,然后点击测试,就可以得到与方法一一样的成功执行结果。

小结

上述两个方法中,程序包的部署,可以通过网页端完成,也可以通过aws-cli来实现。zip的文件包也可以先上传至Amazone S3存储内,然后在页面端选择使用。在尝试两个方法的时候,要注意打包zip程序包时,命令行的当前目录,方法一需要在python上一级目录。方法二需要确保在python目录内。上述两个方案仅为一家之言,并非最优,若读者有别的思路也欢迎关注我的个人微信公众号,一起讨论学习。


版权声明:
文章首发于 Jim Wang's blog , 转载文章请务必以超链接形式标明文章出处,作者信息及本版权声明。