PAYATHON 2026

使用 Serverless Framework 打包 AWS Lambda 可执行文件

支付阿杰

结论

即使 packages/wkhtmltopdf 已经打进部署包,修改 PATH 也不会让它自动成为可执行命令。当前代码加入 PATH 的是 Lambda 代码根目录 LAMBDA_TASK_ROOT,而二进制文件实际位于 LAMBDA_TASK_ROOT/packages/wkhtmltopdf。

建议这样处理:

  1. 按照相对于 serverless.yml 的路径打包二进制文件。
  2. 在代码中使用绝对路径调用,不依赖 PATH。
  3. 确认文件具有 Linux 可执行权限。
  4. 确认二进制文件兼容 AWS Lambda 的 Linux 系统和 CPU 架构。

正确配置打包路径

假设项目结构如下:

serverless.yml
payment/
  module/
    chargePackage.js
packages/
  wkhtmltopdf
node_modules/

使用旧版 Serverless Framework 时,可以这样配置:

service: consult-payment-api

frameworkVersion: ">=1.1.0 <2.0.0"

package:
  individually: true

provider:
  name: aws
  region: us-west-2
  runtime: nodejs8.10
  stage: dev
  timeout: 300

functions:
  UserPackageCharge:
    handler: payment/module/chargePackage.create
    package:
      include:
        - packages/wkhtmltopdf
    events:
      - http:
          path: payment/module/package
          method: post
          cors:
            origin: "*"
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
              - My-Custom-Header

YAML 缩进必须准确。include 中的路径以 serverless.yml 所在目录为基准,不是以 handler 文件所在目录为基准。

新版 Serverless Framework 使用 patterns 代替旧式 include:

package:
  individually: true

functions:
  UserPackageCharge:
    handler: payment/module/chargePackage.create
    package:
      patterns:
        - packages/wkhtmltopdf

题目中的版本范围对应旧版 Framework,因此应使用 include。升级 Framework 后再改用 patterns。

使用绝对路径调用二进制文件

与其只修改 PATH,不如直接将 Node.js 包使用的命令指向部署包内的文件:

const path = require('path');
const wkhtmltopdf = require('wkhtmltopdf');
const MemoryStream = require('memorystream');

wkhtmltopdf.command = path.join(
  process.env.LAMBDA_TASK_ROOT,
  'packages',
  'wkhtmltopdf'
);

exports.create = function (event, context) {
  const memStream = new MemoryStream();
  const htmlUtf8 = Buffer.from(event.html_base64, 'base64').toString('utf8');

  wkhtmltopdf(htmlUtf8, event.options, function (code, signal) {
    if (code !== 0) {
      return context.done(
        new Error(`wkhtmltopdf exited with code ${code}, signal ${signal}`)
      );
    }

    context.done(null, {
      pdf_base64: memStream.read().toString('base64')
    });
  }).pipe(memStream);
};

配置中的 handler 是:

handler: payment/module/chargePackage.create

对应文件必须是:

payment/module/chargePackage.js

导出名称必须是:

exports.create = ...

如果实际文件是 index.js,导出项是 exports.handler,配置也要与真实路径和名称一致,例如:

handler: index.handler

handler 配置与代码导出名称不一致时,还会引发单独的加载错误。

也可以正确扩展 PATH

如果仍希望 wkhtmltopdf 模块通过命令名查找二进制文件,需要将二进制文件所在目录加入 PATH:

const path = require('path');

process.env.PATH = [
  process.env.PATH,
  path.join(process.env.LAMBDA_TASK_ROOT, 'packages')
].join(':');

这样才能通过命令名 wkhtmltopdf 找到 packages/wkhtmltopdf。不过,显式设置 wkhtmltopdf.command 更直观,也不容易受运行环境变量影响。

还可以将二进制文件直接放在部署包根目录:

serverless.yml
wkhtmltopdf
payment/
node_modules/

对应配置为:

package:
  include:
    - wkhtmltopdf

此时把 LAMBDA_TASK_ROOT 加入 PATH,才会与文件的实际位置相符。

确保具有执行权限

打包前,需要为文件设置可执行权限:

chmod 755 packages/wkhtmltopdf

然后只生成部署包,暂不部署:

serverless package

检查 ZIP 中是否包含该文件,以及路径是否正确:

unzip -l .serverless/*.zip | grep wkhtmltopdf

还可以解压后检查权限:

zipinfo -l .serverless/*.zip | grep wkhtmltopdf

部署包中应该出现:

packages/wkhtmltopdf

如果 ZIP 中没有该文件,可依次检查:

  • include 路径是否相对于 serverless.yml。
  • YAML 缩进是否正确。
  • 全局或函数级 exclude 是否与该路径冲突。
  • .serverlessignore 是否排除了 packages 目录。
  • 是否有打包插件重新构建了部署产物。

二进制兼容性同样重要

wkhtmltopdf 必须是为 Lambda 运行环境编译的 Linux 二进制文件,不能直接使用 macOS 或 Windows 版本。它还要与函数的 CPU 架构匹配,例如 x86_64 或 arm64。

即使主程序能够执行,其依赖的动态链接库也必须存在于 Lambda 环境中。可以在兼容的 Linux 构建环境中检查:

file packages/wkhtmltopdf
ldd packages/wkhtmltopdf

如果之后出现以下错误:

Permission denied

通常说明执行权限有问题。

如果出现:

No such file or directory

而文件确定存在,则可能是二进制架构、动态加载器或共享库不兼容。

如果错误提示缺少 libXrender、字体、OpenSSL 等库,需要将相关依赖一同打包,使用专门为 Lambda 构建的静态版本,或者把二进制文件及其依赖放入 Lambda Layer。

nodejs8.10 已经是停止支持的 Lambda 运行时。实际部署时,应迁移到当前受支持的 Node.js 运行时,并在与目标运行时和 CPU 架构一致的环境中重新验证 wkhtmltopdf。核心处理方式不变:确认 ZIP 中包含该文件,再通过 LAMBDA_TASK_ROOT 拼出准确的绝对路径并执行。

备注:内容仅供参考。