AWS Lambda 不导入 LXML
AWS Lambda not importing LXML
我正在尝试在 AWS Lambda 中使用 LXML 模块,但没有成功。我使用以下命令下载了 LXML:
pip install lxml -t folder
下载到我的lambda函数部署包中。我像处理所有其他 lambda 函数一样压缩了我的 lambda 函数的内容,并将其上传到 AWS Lambda。
但是,无论我尝试什么,当我 运行 函数时都会出现此错误:
Unable to import module 'handler': /var/task/lxml/etree.so: undefined symbol: PyFPE_jbuf
当我在本地 运行 时,我没有遇到问题,只是当我在 Lambda 上 运行 时出现了这个问题。
AWS Lambda 使用特殊版本的 Linux(据我所知)。
通常使用 "pip install a_package -t folder" 是一件好事,因为它有助于将您的依赖项打包到将发送到 Lambda 的存档中,但是库,尤其是二进制库必须与OS 和 Python 在 lambda 上的版本。
您可以使用 Python 中包含的 xml 模块:https://docs.python.org/2/library/xml.etree.elementtree.html
如果您确实需要 lxml,此 link 提供了一些有关如何为 Lambda 编译共享库的技巧:
http://www.perrygeo.com/running-python-with-compiled-code-on-aws-lambda.html
我遇到了同样的问题。
Raphaël Braud 发布的 link 很有帮助,还有这个:
https://nervous.io/python/aws/lambda/2016/02/17/scipy-pandas-lambda/
使用两个 links 我能够成功导入 lxml 和其他所需的包。以下是我遵循的步骤:
- 使用 Amazon Linux ami
启动 ec2 机器
运行下面的脚本来积累依赖:
set -e -o pipefail
sudo yum -y upgrade
sudo yum -y install gcc python-devel libxml2-devel libxslt-devel
virtualenv ~/env && cd ~/env && source bin/activate
pip install lxml
for dir in lib64/python2.7/site-packages \
lib/python2.7/site-packages
do
if [ -d $dir ] ; then
pushd $dir; zip -r ~/deps.zip .; popd
fi
done
mkdir -p local/lib
cp /usr/lib64/ #list of required .so files
local/lib/
zip -r ~/deps.zip local/lib
创建 link 中指定的处理程序和工作程序文件。示例文件内容:
handler.py
import os
import subprocess
libdir = os.path.join(os.getcwd(), 'local', 'lib')
def handler(event, context):
command = 'LD_LIBRARY_PATH={} python worker.py '.format(libdir)
output = subprocess.check_output(command, shell=True)
print output
return
worker.py:
import lxml
def sample_function( input_string = None):
return "lxml import successful!"
if __name__ == "__main__":
result = sample_function()
print result
- 将处理程序和工作程序添加到 zip 文件。
经过上述步骤后 zip 文件的结构如下:
deps
├── handler.py
├── worker.py
├── local
│ └── lib
│ ├── libanl.so
│ ├── libBrokenLocale.so
| ....
├── lxml
│ ├── builder.py
│ ├── builder.pyc
| ....
├── <other python packages>
- 确保在创建 lambda 函数时指定正确的处理程序名称。在上面的例子中,它将是- "handler.handler"
希望对您有所帮助!
扩展一下 Mask 的回答。特别是在安装 lxml 的情况下,libxslt 和 libxml2 库已经安装在执行 AWS lambda 的 AMI 上。因此,不需要像答案 那样使用不同的 LD_LIBRARY_PATH 启动子进程,但是有必要 运行 pip install lxml 在 AMI 图像上(也可以交叉编译,但我不知道怎么做)。
Launch an ec2 machine with Amazon Linux ami
Run the following script to accumulate dependencies:
set -e -o pipefail
sudo yum -y upgrade
sudo yum -y install gcc python-devel libxml2-devel libxslt-devel
virtualenv ~/env && cd ~/env && source bin/activate
pip install lxml
for dir in lib64/python2.7/site-packages \
lib/python2.7/site-packages
do
if [ -d $dir ] ; then
pushd $dir; zip -r ~/deps.zip .; popd
fi
done
请注意,Marks 回答的最后几步被省略了。您可以直接从包含处理程序方法的 python 文件中使用 lxml。
扩展这些答案,我发现以下方法很有效。
这里的重点是 python 使用静态库编译 lxml,并安装在当前目录而不是站点包中。
这也意味着您可以像往常一样编写您的 python 代码,而不需要不同的 worker.py 或摆弄 LD_LIBRARY_PATH
sudo yum groupinstall 'Development Tools'
sudo yum -y install python36-devel python36-pip
sudo ln -s /usr/bin/pip-3.6 /usr/bin/pip3
mkdir lambda && cd lambda
STATIC_DEPS=true pip3 install -t . lxml
zip -r ~/deps.zip *
将其提升到一个新的水平,使用无服务器和 docker 来处理所有事情。这是一个博客 post 证明这一点:
https://serverless.com/blog/serverless-python-packaging/
我已经使用 serverless 框架及其内置的 Docker 功能解决了这个问题。
要求:您的 .aws 文件夹中有一个可以访问的 AWS 配置文件。
首先,按照 here 所述安装无服务器框架。然后,您可以使用命令 serverless create --template aws-python3 --name my-lambda
创建一个配置文件。它将创建一个 serverless.yml 文件和一个带有简单 "hello" 函数的 handler.py。您可以检查它是否适用于 sls deploy
。如果可行,就可以使用无服务器了。
接下来,我们需要一个名为 "serverless-python-requirements" 的附加插件来捆绑 Python 要求。您可以通过 sls plugin install --name serverless-python-requirements
.
安装它
这个插件是我们解决丢失的 lxml 包所需的所有魔法发生的地方。在 custom->pythonRequirements 部分,您只需添加 dockerizePip: non-linux
属性。您的 serverless.yml 文件可能如下所示:
service: producthunt-crawler
provider:
name: aws
runtime: python3.8
functions:
hello:
# some handler that imports lxml
handler: handler.hello
plugins:
- serverless-python-requirements
custom:
pythonRequirements:
fileName: requirements.txt
dockerizePip: non-linux
# Omits tests, __pycache__, *.pyc etc from dependencies
slim: true
这将 运行 在预配置的 docker 容器中捆绑 python 需求。在此之后,您可以 运行 sls deploy
看到奇迹发生,然后 sls invoke -f my_function
检查它是否有效。
当您使用无服务器进行部署并稍后添加 dockerizePip: non-linux
选项时,请确保使用 sls requirements clean
清理您已经构建的需求。否则,它只是使用已经构建的东西。
我可以按照 this 页面上的自述文件进行操作:
- 安装 docker 后,运行 此命令(将
python3.8
替换为您用于 lambda 函数的 python 版本,以及 lxml
与您要使用的 lxml 版本)
$ docker run -v $(pwd):/outputs -it lambci/lambda:build-python3.8 \
pip install lxml -t /outputs/
- 这将在您的工作目录中创建一个名为
lxml
的文件夹,可能还会创建一些您可以忽略的其他文件夹。将 lxml
文件夹移动到与您用作 lambda 处理程序的 .py
文件相同的目录。
- 使用 lxml 文件夹压缩
.py
文件,如果您使用的是 virtualenv,则压缩任何包。我的 site-packages 文件夹中已经有一个 virtualenv 和 lxml,所以我必须先删除它。这是我 运行 的命令(请注意,我的 virtualenv v-env 文件夹与我的 .py
文件位于同一目录中):
FUNCTION_NAME="name_of_your_python_file"
cd v-env/lib/python3.8/site-packages &&
rm -rf lxml &&
rm -rf lxml-4.5.1.dist-info &&
zip -r9 ${OLDPWD}/${FUNCTION_NAME}.zip . &&
cd ${OLDPWD} &&
zip -g ${FUNCTION_NAME}.zip ${FUNCTION_NAME}.py &&
zip -r9 ${FUNCTION_NAME}.zip lxml
- 如果你没有 virtualenv 或任何其他依赖项,你可以 运行
FUNCTION_NAME="name_of_your_python_file"
zip -g ${FUNCTION_NAME}.zip ${FUNCTION_NAME}.py &&
zip -r9 ${FUNCTION_NAME}.zip lxml
- 将 ${FUNCTION_NAME}.zip 上传到您的 lambda 函数并正常使用。
更多关于使用 virtualenv 为 lambda 创建 .zip 文件的信息 here
lxml 库是 os 依赖的,因此我们需要预编译副本。以下是步骤。
创建一个 docker 容器。
docker run -it lambci/lambda:build-python3.8 bash
创建一个名为 'lib' 的目录(任何你想要的)并将 lxml 安装到其中。
mkdir lib
pip install lxml -t ./lib --no-deps
打开另一个cmd然后运行
docker ps
复制containerid
将容器中的文件复制到 host。
mkdir /home/libraries/opt/python/lib/python3.8/site-packages/
docker cp <containerid>:/var/task/lib /home/libraries/opt/python/lib/python3.8/site-packages/
现在你有从 amazonlinux box 编译的文件的 lxml 副本,如果你喜欢 lxml 作为 Lambda 层。导航到 /home/libraries/opt
并压缩名为 python
的文件夹。将 zip 命名为 opt
。现在您可以将 zip 作为图层附加到您的 lambda 中。
如果你想要 lambda 中的 lxml 库。导航至 /home/libraries/opt/python/lib/python3.8/site-packages/
并将 lxml
文件夹复制到您的 lambda 中。
LXML 对其 运行 环境非常敏感。
我通过在 python:3.x-slim 容器中构建 zip Lambda 包解决了这个问题:
pip install --target=. lxml
zip -r lambda.zip lambda.py lxml
图像容器版本必须与 Lambda
中使用的 python 引擎版本相同
使用 python 3.6、3.7 和 3.8 成功测试
您可以在此处找到此项目和大多数其他项目的 whl 文件;
https://pypi.org/project/lxml/#files
我正在尝试在 AWS Lambda 中使用 LXML 模块,但没有成功。我使用以下命令下载了 LXML:
pip install lxml -t folder
下载到我的lambda函数部署包中。我像处理所有其他 lambda 函数一样压缩了我的 lambda 函数的内容,并将其上传到 AWS Lambda。
但是,无论我尝试什么,当我 运行 函数时都会出现此错误:
Unable to import module 'handler': /var/task/lxml/etree.so: undefined symbol: PyFPE_jbuf
当我在本地 运行 时,我没有遇到问题,只是当我在 Lambda 上 运行 时出现了这个问题。
AWS Lambda 使用特殊版本的 Linux(据我所知)。
通常使用 "pip install a_package -t folder" 是一件好事,因为它有助于将您的依赖项打包到将发送到 Lambda 的存档中,但是库,尤其是二进制库必须与OS 和 Python 在 lambda 上的版本。
您可以使用 Python 中包含的 xml 模块:https://docs.python.org/2/library/xml.etree.elementtree.html
如果您确实需要 lxml,此 link 提供了一些有关如何为 Lambda 编译共享库的技巧: http://www.perrygeo.com/running-python-with-compiled-code-on-aws-lambda.html
我遇到了同样的问题。
Raphaël Braud 发布的 link 很有帮助,还有这个: https://nervous.io/python/aws/lambda/2016/02/17/scipy-pandas-lambda/
使用两个 links 我能够成功导入 lxml 和其他所需的包。以下是我遵循的步骤:
- 使用 Amazon Linux ami 启动 ec2 机器
运行下面的脚本来积累依赖:
set -e -o pipefail sudo yum -y upgrade sudo yum -y install gcc python-devel libxml2-devel libxslt-devel virtualenv ~/env && cd ~/env && source bin/activate pip install lxml for dir in lib64/python2.7/site-packages \ lib/python2.7/site-packages do if [ -d $dir ] ; then pushd $dir; zip -r ~/deps.zip .; popd fi done mkdir -p local/lib cp /usr/lib64/ #list of required .so files local/lib/ zip -r ~/deps.zip local/lib
创建 link 中指定的处理程序和工作程序文件。示例文件内容:
handler.py
import os
import subprocess
libdir = os.path.join(os.getcwd(), 'local', 'lib')
def handler(event, context):
command = 'LD_LIBRARY_PATH={} python worker.py '.format(libdir)
output = subprocess.check_output(command, shell=True)
print output
return
worker.py:
import lxml
def sample_function( input_string = None):
return "lxml import successful!"
if __name__ == "__main__":
result = sample_function()
print result
- 将处理程序和工作程序添加到 zip 文件。
经过上述步骤后 zip 文件的结构如下:
deps
├── handler.py
├── worker.py
├── local
│ └── lib
│ ├── libanl.so
│ ├── libBrokenLocale.so
| ....
├── lxml
│ ├── builder.py
│ ├── builder.pyc
| ....
├── <other python packages>
- 确保在创建 lambda 函数时指定正确的处理程序名称。在上面的例子中,它将是- "handler.handler"
希望对您有所帮助!
扩展一下 Mask 的回答。特别是在安装 lxml 的情况下,libxslt 和 libxml2 库已经安装在执行 AWS lambda 的 AMI 上。因此,不需要像答案 那样使用不同的 LD_LIBRARY_PATH 启动子进程,但是有必要 运行 pip install lxml 在 AMI 图像上(也可以交叉编译,但我不知道怎么做)。
Launch an ec2 machine with Amazon Linux ami
Run the following script to accumulate dependencies:
set -e -o pipefail
sudo yum -y upgrade
sudo yum -y install gcc python-devel libxml2-devel libxslt-devel
virtualenv ~/env && cd ~/env && source bin/activate
pip install lxml
for dir in lib64/python2.7/site-packages \
lib/python2.7/site-packages
do
if [ -d $dir ] ; then
pushd $dir; zip -r ~/deps.zip .; popd
fi
done
请注意,Marks 回答的最后几步被省略了。您可以直接从包含处理程序方法的 python 文件中使用 lxml。
扩展这些答案,我发现以下方法很有效。
这里的重点是 python 使用静态库编译 lxml,并安装在当前目录而不是站点包中。
这也意味着您可以像往常一样编写您的 python 代码,而不需要不同的 worker.py 或摆弄 LD_LIBRARY_PATH
sudo yum groupinstall 'Development Tools'
sudo yum -y install python36-devel python36-pip
sudo ln -s /usr/bin/pip-3.6 /usr/bin/pip3
mkdir lambda && cd lambda
STATIC_DEPS=true pip3 install -t . lxml
zip -r ~/deps.zip *
将其提升到一个新的水平,使用无服务器和 docker 来处理所有事情。这是一个博客 post 证明这一点: https://serverless.com/blog/serverless-python-packaging/
我已经使用 serverless 框架及其内置的 Docker 功能解决了这个问题。
要求:您的 .aws 文件夹中有一个可以访问的 AWS 配置文件。
首先,按照 here 所述安装无服务器框架。然后,您可以使用命令 serverless create --template aws-python3 --name my-lambda
创建一个配置文件。它将创建一个 serverless.yml 文件和一个带有简单 "hello" 函数的 handler.py。您可以检查它是否适用于 sls deploy
。如果可行,就可以使用无服务器了。
接下来,我们需要一个名为 "serverless-python-requirements" 的附加插件来捆绑 Python 要求。您可以通过 sls plugin install --name serverless-python-requirements
.
这个插件是我们解决丢失的 lxml 包所需的所有魔法发生的地方。在 custom->pythonRequirements 部分,您只需添加 dockerizePip: non-linux
属性。您的 serverless.yml 文件可能如下所示:
service: producthunt-crawler
provider:
name: aws
runtime: python3.8
functions:
hello:
# some handler that imports lxml
handler: handler.hello
plugins:
- serverless-python-requirements
custom:
pythonRequirements:
fileName: requirements.txt
dockerizePip: non-linux
# Omits tests, __pycache__, *.pyc etc from dependencies
slim: true
这将 运行 在预配置的 docker 容器中捆绑 python 需求。在此之后,您可以 运行 sls deploy
看到奇迹发生,然后 sls invoke -f my_function
检查它是否有效。
当您使用无服务器进行部署并稍后添加 dockerizePip: non-linux
选项时,请确保使用 sls requirements clean
清理您已经构建的需求。否则,它只是使用已经构建的东西。
我可以按照 this 页面上的自述文件进行操作:
- 安装 docker 后,运行 此命令(将
python3.8
替换为您用于 lambda 函数的 python 版本,以及lxml
与您要使用的 lxml 版本)$ docker run -v $(pwd):/outputs -it lambci/lambda:build-python3.8 \ pip install lxml -t /outputs/
- 这将在您的工作目录中创建一个名为
lxml
的文件夹,可能还会创建一些您可以忽略的其他文件夹。将lxml
文件夹移动到与您用作 lambda 处理程序的.py
文件相同的目录。 - 使用 lxml 文件夹压缩
.py
文件,如果您使用的是 virtualenv,则压缩任何包。我的 site-packages 文件夹中已经有一个 virtualenv 和 lxml,所以我必须先删除它。这是我 运行 的命令(请注意,我的 virtualenv v-env 文件夹与我的.py
文件位于同一目录中):FUNCTION_NAME="name_of_your_python_file" cd v-env/lib/python3.8/site-packages && rm -rf lxml && rm -rf lxml-4.5.1.dist-info && zip -r9 ${OLDPWD}/${FUNCTION_NAME}.zip . && cd ${OLDPWD} && zip -g ${FUNCTION_NAME}.zip ${FUNCTION_NAME}.py && zip -r9 ${FUNCTION_NAME}.zip lxml
- 如果你没有 virtualenv 或任何其他依赖项,你可以 运行
FUNCTION_NAME="name_of_your_python_file" zip -g ${FUNCTION_NAME}.zip ${FUNCTION_NAME}.py && zip -r9 ${FUNCTION_NAME}.zip lxml
- 将 ${FUNCTION_NAME}.zip 上传到您的 lambda 函数并正常使用。
更多关于使用 virtualenv 为 lambda 创建 .zip 文件的信息 here
lxml 库是 os 依赖的,因此我们需要预编译副本。以下是步骤。
创建一个 docker 容器。
docker run -it lambci/lambda:build-python3.8 bash
创建一个名为 'lib' 的目录(任何你想要的)并将 lxml 安装到其中。
mkdir lib
pip install lxml -t ./lib --no-deps
打开另一个cmd然后运行
docker ps
复制
containerid
将容器中的文件复制到 host。
mkdir /home/libraries/opt/python/lib/python3.8/site-packages/
docker cp <containerid>:/var/task/lib /home/libraries/opt/python/lib/python3.8/site-packages/
现在你有从 amazonlinux box 编译的文件的 lxml 副本,如果你喜欢 lxml 作为 Lambda 层。导航到
/home/libraries/opt
并压缩名为python
的文件夹。将 zip 命名为opt
。现在您可以将 zip 作为图层附加到您的 lambda 中。如果你想要 lambda 中的 lxml 库。导航至
/home/libraries/opt/python/lib/python3.8/site-packages/
并将lxml
文件夹复制到您的 lambda 中。
LXML 对其 运行 环境非常敏感。
我通过在 python:3.x-slim 容器中构建 zip Lambda 包解决了这个问题:
pip install --target=. lxml
zip -r lambda.zip lambda.py lxml
图像容器版本必须与 Lambda
中使用的 python 引擎版本相同使用 python 3.6、3.7 和 3.8 成功测试
您可以在此处找到此项目和大多数其他项目的 whl 文件; https://pypi.org/project/lxml/#files