使用 AWS Lambda 的自定义运行环境,在其中运行已经达到生命周期终止的 Node.js v8 等运行环境

首先。

Node.js的版本更新速度相当快对吧。
AWS Lambda的运行时支持期也相应地加快了节奏。

我們知道你們希望我們進行正確的版本升級,但是如果我們不得不繼續使用 Node.js v8.10 作為 Lambda 函數的話,我們將使用自定義運行時來執行已經過時的運行時,以延長其使用壽命。

确认操作环境

    • Arch Linux (2020.04.04)

 

    • Docker 19.03.8-ce

 

    aws-cli 1.18.36

自定义运行时的使用方法。(Zì shí de

有关自定义运行时规范的详细信息,请参考官方文档。

为了使用自定义运行时,您需要在部署软件包或层中包含一个Node执行文件和一个用于启动处理程序函数的引导执行文件。由于本次假设您要使用现有的Lambda函数,所以不需要修改函数的部署软件包,只需在Lambda层中加载运行时即可。

为了创建Lambda Layer,我们将利用以下存储库:
https://github.com/lambci/node-custom-lambda

目前(2020/04/04),这个仓库中包含有关Node v10和v12的文件。
这次我们将fork这个仓库,并添加用于v8.10的文件,以创建一个Layer。

关于Bootstrap,它分别以C语言和JavaScript编写,代码文件分别位于v12.x/bootstrap.c和v12.x/bootstrap.js(v10.x也是如此)。

首先,编译了bootstrap.c并由AWS Lambda启动,接着它使用自定义运行时的Node执行bootstrap.js脚本。
bootstrap.js从AWS Lambda运行时接口接收函数调用事件,执行部署包中的脚本,并进行执行结果的POST操作。

由于上述存储库的bootstrap.js在Node v8.10中运行良好,
所以只需将Lambda Layer中包含的node执行文件更改为v8.10的文件即可完成所需的更改。

创建自定义运行时

从前面介绍的存储库中开始克隆。
需要使用Docker。

$ git clone https://github.com/lambci/node-custom-lambda.git
$ cd node-custom-lambda

基于v12.x目录,创建一个v8.10的目录。

$ cp -r v12.x v8.10
$ cd v8.10

我会删除v12.x的Layer文件。

$ rm layer.zip

在这个项目中,我们将在Docker上进行bootstrap.c的构建和Node的下载。

修改 config.sh 文件,并指定 Node 的版本。

< export NODE_VERSION=12.16.1
---
> export NODE_VERSION=8.10.0

我会进行构建。

$ ./build.sh

解压缩后,您将得到一个名为v8.10/layer.zip的文件夹。解压缩该文件夹后,其中包含以下文件。

layer
├── bin
│   └── node
├── bootstrap
└── bootstrap.js

我会先检查 Node 的版本。

$ ./layer/bin/node -v
v8.10.0

因为有准备好的测试,我将进行执行。

$ ./test.sh

完成了自定义运行时层。

自定义运行环境的部署

在仓库中已准备了publish.sh,但这个脚本会在所有地区部署。
这次我们只需要在ap-northeast-1部署,所以我们将使用AWS CLI手动创建Layer。

首先,将创建的图层文件(layer.zip)上传到任意的S3上。

aws s3api put-object --bucket ${BUCKET_NAME} --key nodejs/8.10.0/layer.zip --body layer.zip --output json

接下来,将创建Lambda层。
在这里我们将使用CloudFormation进行创建。

AWSTemplateFormatVersion: 2010-09-09

Parameters:
  S3BucketName:
    Description: A S3 bucket name contains layer.zip
    Type: String

Resources:
  Nodejs8Runtime:
    Type: AWS::Lambda::LayerVersion
    Properties:
      Content:
        S3Bucket: !Ref S3BucketName
        S3Key: nodejs/8.10.0/layer.zip
      Description: Layer for Node.js 8.10.0 Custom Runtime
      LayerName: custom-runtime-nodejs-8

Outputs:
  Nodejs8RuntimeLayerARN:
    Description: A lambda layer ARN of Node.js 8.10.0 Custom Runtime
    Value: !Ref Nodejs8Runtime
    Export:
      Name: !Sub ${AWS::StackName}-runtime-nodejs8

创建一个栈。

aws cloudformation create-stack --stack-name dev-lambdalayers-nodejs \
--template-body file://template.yml \
--parameter ParameterKey=S3BucketName,ParameterValue=${S3_BUCKET_NAME}

我将确认Layer是否已经创建。

$ aws lambda list-layer-versions --layer-name custom-runtime-nodejs-8
{
  "LayerVersions": [
    {
      "LayerVersionArn": "arn:aws:lambda:ap-northeast-1:123456789012:layer:custom-runtime-nodejs-8:1",
      "Version": 1,
      "Description": "Layer for Node.js 8.10.0 Custom Runtime",
      "CreatedDate": "2020-04-04T16:59:31.629+0000"
    }
  ]
}

创建和测试Lambda函数

我将测试上述创建的自定义运行时。

决定使用Serverless Framework来创建Lambda Function。
通过在provider.runtime中指定“provided”,可以使用自定义的运行时。
Lambda Layer会导入之前的Cloudformation Stack的Output,并指定ARN。

service: test-lambda-function

provider:
  name: aws
  runtime: provided
  stage: dev
  region: ap-northeast-1

functions:
  hello:
    handler: handler.hello
    layers:
      - 'Fn::ImportValue': dev-lambdalayers-nodejs-runtime-nodejs8

函数代码非常简单,只返回正在运行的Node.js版本。

module.exports.hello = async event => {
  return process.version;
};

当你运行它时,如果返回了字符串 v8.10.0,则表示成功。

$ sls invoke -f hello
"v8.10.0"

请注意

    AWS公式のNode.jsランタイムにはaws-sdkが含まれていますが、この方法で作成したカスタムランタイムにはいずれのnpmパッケージも含まれていません。

最后

经过以上措施,现在可以在Lambda函数中使用Node v8延长寿命。
使用相同的方法,也可以运行Node v6和v4。

当然地进行版本升级是最好的选择,但是像node-gyp等本地模块经常会因升级而无法正常运行,所以它们可以用作临时解决措施。

这次使用的代码都已经上传到以下代码库中:https://github.com/uhey22e/node-custom-lambda。