amazon-web-services - AWS Lambda 函数可以在本地运行时导入模块,但在部署时不能

标签 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 非常相似,但我没有使用 PYTHONPATH在我的 Lambda 大楼里。

编辑:我在这个包中的文件中添加了一些调试代码,以尝试了解它们的运行时环境。我还在 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模块存在于 sys.path Lambda 的运行时环境——如果是,在哪里。

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

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

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

EDIT2 - 我相信我已经找到了问题。对于上下文,回想一下,我有两个包正在通过 CodePipeline(或尝试)部署 Lambda - 这个问题中提到的那个,它将 Lambda 称为 AWS::Serverless::Function ,第二个,它使用 AWS::Lambda::Function .第一个函数的代码被定义为相对位置(即,对我的包中的目录的引用: CodeUri: main/ ),而第二个函数的 Code是对 S3 位置的引用(在 CodePipeline 中使用 Fn::GetArtifactAtt": ["built", "ObjectKey"]}...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 阶段的“输出”是 zip运行 CodeBuild 的目录 - 其中包括依赖项(因为调用了 pip install )。

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

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

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

最佳答案

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

命令如 aws cloudformation packagesam package将与 合作AWS::无服务器::函数 通过将所有内容(无论是源代码、依赖项还是任何其他内容)压缩到通过 CodeUri 指定的目录中,从而在 CloudFormation 模板中添加资源属性,然后它将生成的 zip 文件上传到 S3 存储桶,为您提供转换后的 CloudFormation 模板,其中您的部署包的 S3 存储桶路径替换了 CodeUri 中指定的本地计算机中源代码的路径属性(property)。

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

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

  • 使用sam build命令(如果它是启用 SAM 的 CloudFormation 模板)。这个命令会自动找到你的requirements.txt并将指定的依赖项安装到 .aws-sam准备上传到 S3 的目录。
  • 手动运行pip install -r requirements.txt到正确的目录,其中内容被压缩为用于部署 lambda 函数的部署包。这适用于支持 SAM 的或普通的 Lambda CloudFormation 模板。
  • 关于amazon-web-services - AWS Lambda 函数可以在本地运行时导入模块,但在部署时不能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53952204/

    相关文章:

    aws-lambda - Terraform 在 zip 准备好之前创建 lambda

    带有 entry_point 控制台脚本的 Python 包分发

    Python 打包 - 名称错误

    ios - aws dynamodb 如何在 ios 中使用对象映射器和批处理获取

    amazon-web-services - 为云构建 Erlang 应用程序

    ios - 如何在DynamoDB(iOS)中查询StringSet值?

    amazon-web-services - 调用 Lambda 函数的 aws boto3 unittest 函数

    aws-lambda - 我无法通过VPN将mysql客户端连接到RDS

    javascript - NodeJs : How can I see the log of a NodeJs server running by PM2 in a EC2 instance

    python - 如何使用 python setup.py 上传?