作者: 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 , 转载文章请务必以超链接形式标明文章出处,作者信息及本版权声明。