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 上 运行 时出现了这个问题。

A​​WS 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 页面上的自述文件进行操作:

  1. 安装 docker 后,运行 此命令(将 python3.8 替换为您用于 lambda 函数的 python 版本,以及 lxml与您要使用的 lxml 版本)
    $ docker run -v $(pwd):/outputs -it lambci/lambda:build-python3.8 \
          pip install lxml -t /outputs/
    
  2. 这将在您的工作目录中创建一个名为 lxml 的文件夹,可能还会创建一些您可以忽略的其他文件夹。将 lxml 文件夹移动到与您用作 lambda 处理程序的 .py 文件相同的目录。
  3. 使用 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
    
  4. 如果你没有 virtualenv 或任何其他依赖项,你可以 运行
    FUNCTION_NAME="name_of_your_python_file"
    zip -g ${FUNCTION_NAME}.zip ${FUNCTION_NAME}.py && 
    zip -r9 ${FUNCTION_NAME}.zip lxml
    
  5. 将 ${FUNCTION_NAME}.zip 上传到您的 lambda 函数并正常使用。

更多关于使用 virtualenv 为 lambda 创建 .zip 文件的信息 here

lxml 库是 os 依赖的,因此我们需要预编译副本。以下是步骤。

  1. 创建一个 docker 容器。
    docker run -it lambci/lambda:build-python3.8 bash

  2. 创建一个名为 'lib' 的目录(任何你想要的)并将 lxml 安装到其中。
    mkdir lib
    pip install lxml -t ./lib --no-deps

  3. 打开另一个cmd然后运行
    docker ps

  4. 复制containerid

  5. 将容器中的文件复制到 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/

  6. 现在你有从 amazonlinux box 编译的文件的 lxml 副本,如果你喜欢 lxml 作为 Lambda 层。导航到 /home/libraries/opt 并压缩名为 python 的文件夹。将 zip 命名为 opt。现在您可以将 zip 作为图层附加到您的 lambda 中。

  7. 如果你想要 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