【问题标题】:AWS Lambda not importing LXMLAWS Lambda 不导入 LXML
【发布时间】:2016-07-23 16:06:20
【问题描述】:

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

【问题讨论】:

  • lxml 需要安装系统级软件包,这必须在 Amazon linux AMI 上完成并包含在您的 lambda 包中

标签: python amazon-web-services lxml aws-lambda


【解决方案1】:

我遇到了同样的问题。

Raphaël Braud 发布的链接很有帮助,这个也是: https://nervous.io/python/aws/lambda/2016/02/17/scipy-pandas-lambda/

使用这两个链接,我能够成功导入 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”

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    扩展这些答案,我发现以下方法效果很好。

    这里的重点是让 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 来处理所有事情。这是一篇博客文章,展示了这一点: https://serverless.com/blog/serverless-python-packaging/

    【讨论】:

      【解决方案3】:

      我已经使用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 清理您已经构建的需求。否则,它只会使用已经构建的东西。

      【讨论】:

      • 你需要安装 Docker 才能让它工作。关于这方面的好文章:serverless.com/blog/serverless-python-packaging.
      • 我最终使用了无服务器(甚至复制无服务器创建的包,制作一个 zip 并上传它对我不起作用)
      • 这是一个很好的解决方案。这里有一个重要的警告让我转动了我的轮子:如果您在将 dockerizePip 选项添加到您的 yaml 之前一直在运行 sls deploy ,则 sls 可能会继续使用您的要求的缓存版本!这将导致对 yaml 的更改无效!检查您的 sls deploy 输出中是否有类似 Using static cache of requirements... 的内容,如果它显示,您必须在此解决方案起作用之前对引用的缓存文件进行核对。
      • @OliverHolmberg 很棒的评论!这一点非常重要,因为大多数人在编译后当然会发现这一点。
      • 这是迄今为止解决问题的更简单、更快捷的方法!!应该被投票到顶部。
      【解决方案4】:

      扩展了 Mask 的答案。特别是在安装 lxml 的情况下,libxslt 和 libxml2 库已经安装在执行 AWS lambda 的 AMI 上。因此,无需像该答案中那样使用不同的 LD_LIBRARY_PATH 启动子进程,但是有必要在 AMI 映像上运行 pip install lxml (也可以进行交叉编译但我不知道怎么做)。

      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 answer 的最后一步被省略了。您可以直接从包含处理程序方法的 python 文件中使用 lxml。

      【讨论】:

        【解决方案5】:

        AWS Lambda 使用特殊版本的 Linux(据我所知)。

        使用“pip install a_package -t 文件夹”通常是一件好事,因为它有助于将您的依赖项打包到将发送到 Lambda 的存档中,但是库,尤其是二进制库必须兼容在 lambda 上使用 OS 和 Python 的版本。

        您可以使用 Python 中包含的 xml 模块:https://docs.python.org/2/library/xml.etree.elementtree.html

        如果你真的需要 lxml,这个链接提供了一些关于如何为 Lambda 编译共享库的技巧: http://www.perrygeo.com/running-python-with-compiled-code-on-aws-lambda.html

        【讨论】:

          【解决方案6】:

          按照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,还可以压缩任何包。我的站点包文件夹中已经存在一个 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 here 为 lambda 创建 .zip 文件

          【讨论】:

            【解决方案7】:

            lxml 库依赖于操作系统,因此我们需要预编译副本。以下是步骤。

            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. 将文件从容器复制到主机。
              mkdir /home/libraries/opt/python/lib/python3.8/site-packages/
              docker cp &lt;containerid&gt;:/var/task/lib /home/libraries/opt/python/lib/python3.8/site-packages/

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

            7. 如果你想在 lambda 中使用 lxml 库。导航到 /home/libraries/opt/python/lib/python3.8/site-packages/ 并将 lxml 文件夹复制到您的 lambda 中。

            【讨论】:

            • 那么你上传为Layer的ZIP文件里面应该有如下结构吧? * python/lib/python3.9/site-packages/lxml * python/lib/python3.9/site-packages/lxml-4.6.4.dist-info 呃,格式无法正常工作。
            【解决方案8】:

            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 测试成功

            【讨论】:

              【解决方案9】:

              您可以在此处找到此项目和大多数其他项目的 whl 文件; https://pypi.org/project/lxml/#files

              【讨论】:

              • 这如何回答问题或改进例如the accepted answer。我可以理解为什么这个线程上的人知道在哪里可以找到最新版本的 LXML 文件可能很有用。但由于它没有直接回答问题,因此应该作为评论发布。
              猜你喜欢
              • 2019-11-11
              • 1970-01-01
              • 2020-04-20
              • 1970-01-01
              • 1970-01-01
              • 2020-04-03
              • 2021-01-09
              • 1970-01-01
              • 2022-06-22
              相关资源
              最近更新 更多