使用无服务器和 chrome-aws-lambda 节点包在 AWS Lambda 上找不到 Chrome 二进制文件

Posted

技术标签:

【中文标题】使用无服务器和 chrome-aws-lambda 节点包在 AWS Lambda 上找不到 Chrome 二进制文件【英文标题】:Chrome Binary Not Found on AWS Lambda Using Serverless and chrome-aws-lambda Node package 【发布时间】:2020-05-16 03:10:38 【问题描述】:

我创建了一个接受 URL 并将其转换为 PDF 的简单应用程序。它将生成的 PDF 存储在 S3 存储桶中并返回 PDF 的 URL。它使用 Chrome(无头运行)将 URL 转换为 PDF。我使用了无服务器框架、AWS Lambda 和 chrome-aws-lambda npm 包。当我使用无服务器在本地执行此设置时,一切都很好。我可以使用邮递员通过 URL 发出请求,它会返回结果 PDF 的 URL。当我将此设置部署到 AWS Lambda 时,它会返回 502 内部服务器错误响应。当我查看我的应用程序的 AWS 日志时,我看到以下内容:


    "errorType": "Error",
    "errorMessage": "ENOENT: no such file or directory, open '//../bin/chromium.br'",
    "code": "ENOENT",
    "errno": -2,
    "syscall": "open",
    "path": "//../bin/chromium.br",
    "stack": [
        "Error: ENOENT: no such file or directory, open '//../bin/chromium.br'"
    ]

这是应用程序的主要处理程序:

import AWS from 'aws-sdk'
import middy from 'middy'
import chromium from 'chrome-aws-lambda'
import 
  cors,
  doNotWaitForEmptyEventLoop,
  httpHeaderNormalizer,
  httpErrorHandler
 from 'middy/middlewares'

const handler = async (event) => 
  // Request body is passed in as a JSON encoded string in 'event.body'
  const data = JSON.parse(event.body)

  const executablePath = event.isOffline
    ? './node_modules/puppeteer/.local-chromium/linux-706915/chrome-linux/chrome'
    : await chromium.executablePath

  const browser = await chromium.puppeteer.launch(
    args: chromium.args,
    defaultViewport: chromium.defaultViewport,
    executablePath: executablePath,
    headless: true
  )

  const page = await browser.newPage()

  await page.goto(data.url, 
    waitUntil: ['networkidle0', 'load', 'domcontentloaded']
  )

  const pdfStream = await page.pdf()

  var upload = new AWS.S3.ManagedUpload(
    params: 
      Bucket: 'bucketname',
      Body: pdfStream,
      Key: `$Date.now()-result.pdf`,
      ACL: 'public-read'
    
  )

  var promise = upload.promise()

  return promise.then(
    function (data) 
      console.log(data.Location)
      return 
        statusCode: 200,
        body: data.Location
      
    ,
    function (err) 
      console.log('Error', err)
      return 
        statusCode: 500,
        body: err
      
    
  )


export const generate = middy(handler)
  .use(httpHeaderNormalizer())
  .use(cors())
  .use(doNotWaitForEmptyEventLoop())
  .use(httpErrorHandler())

这里是无服务器框架配置文件:

service: print-pdf

package:
  individually: true

provider:
  name: aws
  runtime: nodejs12.x
  region: us-east-2
  stage: prod

plugins:
  - serverless-bundle # Package our functions with Webpack
  - serverless-offline

# Create our resources with separate CloudFormation templates
resources:
  # API Gateway Errors
  - $file(resources/api-gateway-errors.yml)
  # S3
  - $file(resources/s3-bucket.yml)

# 'iamRoleStatements' defines the permission policy for the Lambda function.
# In this case Lambda functions are granted with permissions to access S3.
iamRoleStatements:
  - Effect: Allow
    Action:
      - s3:GetObject
      - s3:PutObject
    Resource: "arn:aws:s3:us-east-2:*:*"

functions:
  give-me-the-pdf:
    handler: handler.generate
    events:
      - http:
          path: pdf
          method: post
          cors: true
          authorizer: aws_iam

这是 package.json:


  "name": "print-pdf",
  "version": "1.0.0",
  "main": "handler.js",
  "author": "Dean Andreakis <dean@deanware.com>",
  "license": "MIT",
  "private": true,
  "scripts": 
    "test": "serverless-bundle test"
  ,
  "dependencies": 
    "chrome-aws-lambda": "^1.20.4",
    "middy": "^0.28.4",
    "puppeteer-core": "^1.20.0"
  ,
  "devDependencies": 
    "aws-sdk": "^2.597.0",
    "jest": "^24.9.0",
    "puppeteer": "^2.0.0",
    "serverless": ">=1.48.1",
    "serverless-bundle": "^1.2.5",
    "serverless-dotenv-plugin": "^2.1.1",
    "serverless-offline": "^5.3.3"
  

为什么 Chrome 在部署到 AWS 时找不到,而不是在本地运行?

【问题讨论】:

这可能是由无服务器打包应用程序的方式引起的。您是否尝试过使用serverless-webpack 并将chrome-aws-lamdba 配置为外部?有一个类似的问题here。 jpetty 您对使用 serverless-webpack 等的建议最终解决了我的问题。如果你能把它变成一个答案,我很乐意奖励你。 【参考方案1】:

您可以使用serverless-webpack 并将chrome-aws-lamdba 配置为外部。

有一个类似的问题here。

将此添加到您的 webpack 配置中:

externals: ['aws-sdk', 'chrome-aws-lambda']

【讨论】:

【参考方案2】:

serverless-bundle 仅包含您在处理程序中使用的 JS 代码,并剥离其他所有内容以最小化您的包。这意味着 chrome 二进制文件被排除在外。

要包含这些二进制文件,请将以下内容添加到您的 serverless.yml

custom:
  bundle:
    copyFiles:
      - from: 'node_modules/chrome-aws-lambda/bin/*'
        to: './'

【讨论】:

它似乎有所帮助,但现在我收到以下错误:“错误:无法启动 chrome!spawn ./EACCES” 嗨,Dean,您能检查一下捆绑包是否复制了所有必需的文件。还有一些公共层包含在 lambda 上运行 chrome 所需的模块(github.com/shelfio/chrome-aws-lambda-layer),您可以使用其中之一或查看创建自己的步骤。 好的,我为 chrome-aws-lambda 使用了公共层,但我仍然收到“错误:无法启动 chrome!spawn /opt/nodejs/node_modules/chrome-aws-lambda/bin/chromium. br EACCES" 嗨,Dean,我检查了我的 lambda 以进行打印,我使用 puppeteer 来启动这样的 chrome...gist.github.com/pepoviola/3316771686bd1e7d43c6ddeb068d2e45

以上是关于使用无服务器和 chrome-aws-lambda 节点包在 AWS Lambda 上找不到 Chrome 二进制文件的主要内容,如果未能解决你的问题,请参考以下文章

Azure基础:何时使用Azure Functions无服务器计算(11)

使用 Retrofit 和 MockWebServer 模拟无网络

浅谈无状态和有状态服务的区别

.htaccess、YSlow 和“使用无 cookie 域”

了解 Azure 中的无服务器计算

使用无服务器部署 AWS Lambda 函数在需要外部模块时不会部署