【问题标题】:AWS Lambda Function can import a module when run locally, but not when deployedAWS Lambda 函数可以在本地运行时导入模块,但在部署时不能
【发布时间】:2019-05-25 21:44:08
【问题描述】:

我正在尝试扩展 this guide,通过构建 CodePipeline 来获取 GitHub 中的更改、构建它们并将更改部署到我的 Lambda。 sam build --use-container; sam local start-api 允许我在本地成功调用该函数 - 但是当我将该函数部署到 AWS 时,代码无法导入依赖项。

我的代码依赖于requests。我已将其正式包含在我的 requirements.txt 文件中:

requests==2.20.0

我的 buildspec.yml 包含安装依赖项的说明

version: 0.1
phases:
  install:
    commands:
      - pip install -r hello_world/requirements.txt -t .
      - pip install -U pytest
  pre_build:
    commands:
      - python -m pytest tests/
  build:
    commands:
      - aws cloudformation package --template-file template.yaml --s3-bucket <my_bucket>
                                   --output-template-file outputTemplate.yml
artifacts:
  type: zip
  files:
    - '**/*'

当我的包在 CodeBuild 中构建时,即被确认:

[Container] 2018/12/27 23:16:44 Waiting for agent ping 
[Container] 2018/12/27 23:16:46 Waiting for DOWNLOAD_SOURCE 
[Container] 2018/12/27 23:16:46 Phase is DOWNLOAD_SOURCE 
[Container] 2018/12/27 23:16:46 CODEBUILD_SRC_DIR=/codebuild/output/src775882062/src 
[Container] 2018/12/27 23:16:46 YAML location is /codebuild/output/src775882062/src/buildspec.yml 
[Container] 2018/12/27 23:16:46 Processing environment variables 
[Container] 2018/12/27 23:16:46 Moving to directory /codebuild/output/src775882062/src 
[Container] 2018/12/27 23:16:46 Registering with agent 
[Container] 2018/12/27 23:16:46 Phases found in YAML: 3 
[Container] 2018/12/27 23:16:46  PRE_BUILD: 1 commands 
[Container] 2018/12/27 23:16:46  BUILD: 1 commands 
[Container] 2018/12/27 23:16:46  INSTALL: 2 commands 
[Container] 2018/12/27 23:16:46 Phase complete: DOWNLOAD_SOURCE Success: true 
[Container] 2018/12/27 23:16:46 Phase context status code:  Message:  
[Container] 2018/12/27 23:16:46 Entering phase INSTALL 
[Container] 2018/12/27 23:16:46 Running command pip install -r hello_world/requirements.txt -t . 
Collecting requests==2.20.0 (from -r hello_world/requirements.txt (line 1)) 
  Downloading https://files.pythonhosted.org/packages/f1/ca/10332a30cb25b627192b4ea272c351bce3ca1091e541245cccbace6051d8/requests-2.20.0-py2.py3-none-any.whl (60kB)
...

但是当我调用部署的函数时,我得到一个错误:

Unable to import module 'app': No module named 'requests'

这似乎与this question 非常相似,但我没有在我的 Lambda 大楼中使用PYTHONPATH


编辑:我在这个包中的文件中添加了一些调试代码,以尝试了解它们的运行时环境。我还向another package 添加了类似的调试,我通过 CodePipeline 部署到 Lambda(尽管这个不使用 SAM)。调试代码如下:

import os, sys
print('Inside ' + __file__)
for path in sys.path:
    print(path)
    if (os.path.exists(path)):
        print(os.listdir(path))
        for f in os.listdir(path):
          if f.startswith('requests'):
            print('Found requests!')
    print()

此代码尝试确定 requests 模块是否存在于 Lambda 运行时环境的 sys.path 中,如果存在,则在哪里。

对于这个(支持 SAM 的)包,在任何地方都找不到 requests。在未启用 SAM 的包中,requests(以及包的所有其他 requirements.txt 声明的依赖项)在 /var/task 中找到。

看起来 CodeBuild 没有将函数的依赖项与源捆绑在一起,或者 CloudFormation 没有部署这些依赖项。我怀疑这是因为这是一个 SAM 定义的函数,而不是“普通”的 Cloudformation 函数。

This page 说“您还可以使用与 AWS SAM 集成的其他 AWS 服务来自动化您的部署”,但我看不出如何让 CodePipeline 运行 sam deploy 而不是 aws cloudformation deploy(尽管 @ 987654325@ 声称它们是同义词)。


EDIT2 - 我相信我找到了问题所在。对于上下文,请回想一下,我有两个包正在通过 CodePipeline(或尝试)部署 Lambda - 这个问题中提到的一个,将 Lambda 称为AWS::Serverless::Function,另一个使用AWS::Lambda::Function。第一个函数的代码被定义为相对位置(即,对我的包中的目录的引用:CodeUri: main/),而第二个函数的 Code 是对 S3 位置的引用(在 CodePipeline 中使用 @987654348 获取@或...BucketName"]})

以下是第一个包的 CodeBuild 输出示例:

[Container] 2018/12/30 19:19:48 Running command aws cloudformation package --template-file template.yaml --s3-bucket pop-culture-serverless-bucket --output-template-file outputTemplate.yml 

Uploading to 669099ba3d2258eeb7391ad772bf870d  4222 / 4222.0  (100.00%) 
Successfully packaged artifacts and wrote output template to file outputTemplate.yml. 
Execute the following command to deploy the packaged template 
aws cloudformation deploy --template-file /codebuild/output/src110881899/src/outputTemplate.yml --stack-name <YOUR STACK NAME> 

与第二个包的 CodeBuild 输出中的相同输出进行比较:

....
[Container] 2018/12/30 16:42:27 Running command aws cloudformation package --template-file template.json --s3-bucket {BUCKET_NAME} --output-template-file outputTemplate.yml 

Successfully packaged artifacts and wrote output template to file outputTemplate.yml. 
Execute the following command to deploy the packaged template 
aws cloudformation deploy --template-file /codebuild/output/src282566886/src/outputTemplate.yml --stack-name <YOUR STACK NAME>

这表明第一个包的 aws cloudformation package 调用会导致将文件 (669099ba3d2258eeb7391ad772bf870d) 上传到 S3,这基于 template.yaml 的内容,而“第二个包的 CodePipeline 的 Build 阶段的“输出”是 CodeBuild 一直在运行的目录的zipin - 其中包括依赖项(因为调用了pip install)。 p>

可以通过简单地将我的 SAM 模板函数的 template.yaml 更改为引用 S3 位置来解决此问题 - 但这意味着我将无法在本地测试对函数的更新(例如,sam local start-api)而不编辑模板,因为它会引用 S3 位置,因此不会受到本地更改的影响。

理想情况下,我想找到一种方法将代码的依赖项包含在打包和上传的 S3 文件中。从本地测试来看,运行sam package/aws cloudformation package 而不首先运行sam build 似乎只包含源代码(无依赖项)。但是,我无法在 CodeBuild 中运行 sam build,因为那里没有安装 SAM。

(这也表明我无意中部署了我的第二个包的测试依赖项 - 因为需要在 CodeBuild 中安装它们(以便运行测试))

【问题讨论】:

  • 部署 lambda 函数时,所有不属于 AWS 环境的依赖项都必须添加到部署包中。 https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html。我确实使用像pip install requests -t 这样的命令。在我看来,CodeBuild 依赖于 AWS Serverless,它依赖于带有 lambda 的标准 zip 文件。所以我认为你仍然必须通过本地安装来做到这一点。但是您可以将 setup 命令放在构建脚本中。

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


【解决方案1】:

CodeBuild 构建环境(特别是在使用托管映像时)基于 Ubuntu 基础映像 - 在 Lambda 上运行时这些依赖项可能不兼容。这是因为 Lambda 容器环境基于 Amazon Linux - https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html

您可以尝试通过将依赖项打包到源包中并从“requirements.txt”文件中跳过它来解决此问题。

如果我没记错的话,类似的问题在 - Using moviepy, scipy and numpy in amazon lambda

【讨论】:

  • 嗨迪帕克!感谢您的回复。不幸的是,我认为这不是解决方案 - 我有 another package,我将其部署为 Lambda,similarly has a dependency on requests,这没有问题。我怀疑这是因为该软件包的 CodeBuild 安装到 lib 而不是 .。我会尝试,而不是。
【解决方案2】:

我找到了一个“解决方案”,将我的代码的依赖项安装在main 目录而不是根目录中。但是,我认为更好的选择是使用layers 来保存依赖项。

【讨论】:

    【解决方案3】:

    您的 lambda 执行在实际 AWS Lambda 执行环境中运行时显示 "Unable to import module" 的原因是因为您的 lambda 部署包(通过 aws cloudformation package 命令上传到 S3)缺少在您的 中指定的必需依赖项requirements.txt.

    aws cloudformation packagesam package 之类的命令将通过压缩所有内容(无论是源代码、依赖项还是任何内容)与 CloudFormation 模板中的 AWS::Serverless::Function 资源一起使用其他东西)在通过CodeUri 属性指定的目录中,然后它将生成的 zip 文件上传到 S3 存储桶,为您提供转换后的 CloudFormation 模板,其中部署包的 S3 存储桶路径替换指定的本地计算机中源代码的路径在CodeUri 属性中。

    查看您的 buildspec.yml,我认为问题来自您在 install 阶段的pip install -r hello_world/requirements.txt -t . 命令中指定的-t . 选项。这会将依赖项安装在当前目录(通常是项目的根目录)中,而不是 hello_world lambda 函数的源代码所在的目录中。因此,在后面的aws cloudformation package 步骤中,依赖项不会与源代码一起压缩。

    一般来说,当您创建 lambda 函数部署包(无论是支持 SAM 还是普通 Lambda)时,您需要捆绑应用中使用的所有内容(源代码、依赖项、资源等)。你通常这样做:-

    1. 如果它是支持 SAM 的 CloudFormation 模板,请使用 sam build 命令。该命令会自动找到你的requirements.txt,并将指定的依赖安装到.aws-sam目录中,为上传到S3做准备。

    2. 手动运行pip install -r requirements.txt 到正确的目录,其中内容被压缩为部署包以部署 lambda 函数。这适用于支持 SAM 或普通 Lambda CloudFormation 模板。

    【讨论】:

      【解决方案4】:

      如果您的CodeUri 指向/main,则在运行aws cloudformation package 时,此文件夹的内容将被压缩并上传到 S3,但没有依赖关系。

      运行sam package时的不同之处在于它会为您安装requirements.txt中的依赖项并将其输出到.aws-sam/build/&lt;functionname&gt;文件夹。

      因此,为了打包依赖项,您需要访问函数文件夹并在本地安装依赖项,例如。

      • pip install -r requirements.txt -t .
      • 然后运行aws cloudformation package --s3-bucket &lt;YOUR_BUCKET&gt; --template-file &lt;YOUR TEMPLATE YAML&gt; --output-template-file &lt;OUTPUT TEMPLATE NAME YAML&gt;

      【讨论】:

        猜你喜欢
        • 2020-02-04
        • 2021-04-02
        • 2020-06-07
        • 1970-01-01
        • 1970-01-01
        • 2014-03-13
        • 2020-02-17
        • 1970-01-01
        • 2013-04-17
        相关资源
        最近更新 更多