当在本地运行时,AWS Lambda Function可以导入模块,而在部署时则不能导入模块

时间:2018-12-28 00:02:07

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

我正在尝试在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


编辑:我向此软件包中的文件添加了一些调试代码,以试图了解其运行时环境。我还为通过CodePipeline部署到Lambda的another package添加了类似的调试功能(尽管该调试器不使用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()

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

对于此(启用SAM的)程序包,在任何地方都找不到requests。在未启用SAM的程序包中,在requests中发现了requirements.txt(以及该程序包的所有其他/var/task声明的依赖项)。

似乎CodeBuild没有将函数的依赖项与源代码捆绑在一起,或者CloudFormation没有部署这些依赖项。我怀疑这与以下事实有关:这是SAM定义的函数,而不是“原始的” Cloudformation函数。

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


EDIT2-我相信我已经找到了问题。对于上下文,回想一下,我有两个通过CodePipeline部署Lambda的程序包(或试图这样做)–这个问题中提到的一个程序包,将Lambda称为aws cloudformation deploy,第二个程序包使用{{1 }}。第一个功能的代码定义为相对位置(即,对我的软件包中的目录的引用:AWS::Serverless::Function),而第二个功能的Code是对S3位置的引用(在CodePipeline中获取) ,带有AWS::Lambda::FunctionCodeUri: main/

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

Fn::GetArtifactAtt": ["built", "ObjectKey"]}

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

...BucketName"]}

这表明第一个包的[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> 调用导致将文件(.... [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> )上载到S3,该文件仅基于{{1 }},而第二个软件包的CodePipeline的Build阶段的“输出”是CodeBuild已在 in 中运行的目录的aws cloudformation package-包括依赖项(由于调用到669099ba3d2258eeb7391ad772bf870d)。

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

理想情况下,我想找到一种在打包并上传的S3文件中包含代码依赖项的方法。从本地测试看来,运行pip install / template.yaml而不先运行sam local start-api只会导致仅包含源代码(不存在依赖项)。但是,由于未在其中安装SAM,因此无法在CodeBuild中运行sam package

(这也表明我无意中部署了第二个程序包的测试相关性,因为需要将它们安装在CodeBuild中(以便运行测试))

4 个答案:

答案 0 :(得分:1)

通过在main目录而不是根目录中安装我的代码的依存关系,我找到了解决方案。但是,我相信一个更好的选择是使用layers来保存依赖项。

答案 1 :(得分:0)

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

解决类似的问题

答案 2 :(得分:0)

您的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 阶段在-t .命令中指定的pip install -r hello_world/requirements.txt -t .选项。这会将依赖项安装在当前目录(通常是项目的根目录)中,而不是 hello_world lambda函数的源代码所在的目录中。因此,在以后的aws cloudformation package步骤中,依赖项不会与源代码一起压缩。

通常,当您创建lambda函数部署包(无论是启用SAM的Lambda还是原始版本的Lambda)时,您需要捆绑应用中使用的所有内容(源代码,依赖项,资源等)。通常,您可以通过以下方式做到这一点:-

  1. 如果它是启用SAM的CloudFormation模板,请使用sam build命令。此命令将自动找到您的requirements.txt并将指定的依赖项安装到.aws-sam目录中,以准备上传到S3。

  2. 手动运行pip install -r requirements.txt到正确的目录,将内容压缩为用于部署lambda函数的部署程序包。既可以在启用SAM的Lambda CloudFormation模板中使用,也可以使用。

答案 3 :(得分:0)

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

运行sam package的不同之处在于,它为您安装了requirements.txt中的依赖项,并将其输出到.aws-sam/build/<functionname>文件夹中。

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

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