"Runtime.ImportModuleError" 尝试使用层访问 AWS lambda 函数中的 npm 包

"Runtime.ImportModuleError" trying to access npm package in an AWS lambda function using layers

我想在 AWS lambda 函数中使用 npm 包 "request"。

我正在尝试按照本文中概述的步骤操作:https://medium.com/@anjanava.biswas/nodejs-runtime-environment-with-aws-lambda-layers-f3914613e20e

我创建了这样的目录结构:

nodejs
│   package-lock.json
│   package.json
└───node_modules

我的 package.json 看起来像这样:

{
  "name": "my-package-name",
  "version": "1.0.0",
  "description": "whatever",
  "author": "My Name",
  "license": "MIT",
  "dependencies": {
    "request": "^2.88.0"
  }
}

据我从文章中可以看出,上面的内容我应该做的就是运行 npm i,压缩目录,将其作为图层上传,并添加层到我的 lambda 函数。

我已经完成了所有这些,但是当我尝试测试我的功能时我得到的是:

{
  "errorType": "Runtime.ImportModuleError",
  "errorMessage": "Error: Cannot find module 'request'\nRequire stack:\n- /var/task/index.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js",
  "trace": [
    "Runtime.ImportModuleError: Error: Cannot find module 'request'",
    "Require stack:",
    ...

...就像从未添加过图层一样。不管加不加层,报错都是一样的。如果有某种权限问题需要解决,文章中没有任何内容表明。

我尝试了一些不同的方法,例如我的 .zip 文件是否包含顶级目录 "nodejs" 或仅包含其内容。我尝试将 "main": "index.js", 添加到我的 package.json,使用 index.js 文件,如下所示:

export.modules.request = require('request');

...都无济于事。

我错过了什么?

哦,我简直不敢相信只有这个!

.zip 文件的顶级目录必须按字面意思命名为“nodejs”!我使用了不同的名称,只是在 post 的文本中将其改回 "nodejs" 以便更通用,但目录名称一直是真正的问题。

感叹

一般与里面folder/files的名字有关。如果这些文件在别处引用,它也会在那里渗透和抱怨。只要彻底检查文件夹结构,就能抓住小偷。折腾了一天才搞明白,真是个傻逼。

Accessing table data from RDS using lambda function with encrypted key (KMS) and Environment variable

第 1 步:- 首先在 KMS 中启用密钥(密钥管理服务 (KMS))

查看您的关键政策并完成!创建 KMS

{
"Id": "key-consolepolicy-3",
"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "Enable IAM User Permissions",
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::163806924483:root"
        },
        "Action": "kms:*",
        "Resource": "*"
    },
    {
        "Sid": "Allow access for Key Administrators",
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::163806924483:user/User1@gmail.com"
        },
        "Action": [
            "kms:Create*",
            "kms:Describe*",
            "kms:Enable*",
            "kms:List*",
            "kms:Put*",
            "kms:Update*",
            "kms:Revoke*",
            "kms:Disable*",
            "kms:Get*",
            "kms:Delete*",
            "kms:TagResource",
            "kms:UntagResource",
            "kms:ScheduleKeyDeletion",
            "kms:CancelKeyDeletion"
        ],
        "Resource": "*"
    },
    {
        "Sid": "Allow use of the key",
        "Effect": "Allow",
        "Principal": {
            "AWS": [
                "arn:aws:iam::163806924483:user/User1@gmail.com",
                "arn:aws:iam::163806924483:user/User2@gmail.com",
                "arn:aws:iam::163806924483:user/User3@gmail.com"
            ]
        },
        "Action": [
            "kms:Encrypt",
            "kms:Decrypt",
            "kms:ReEncrypt*",
            "kms:GenerateDataKey*",
            "kms:DescribeKey"
        ],
        "Resource": "*"
    },
    {
        "Sid": "Allow attachment of persistent resources",
        "Effect": "Allow",
        "Principal": {
            "AWS": [
                "arn:aws:iam::163806924483:user/User1.dilip@gmail.com",
                "arn:aws:iam::163806924483:user/User2@gmail.com",
                "arn:aws:iam::163806924483:user/User3@gmail.com"
            ]
        },
        "Action": [
            "kms:CreateGrant",
            "kms:ListGrants",
            "kms:RevokeGrant"
        ],
        "Resource": "*",
        "Condition": {
            "Bool": {
                "kms:GrantIsForAWSResource": "true"
            }
        }
    }
]
}

步骤:- 2 在 IAM 中为 KMS 创建策略分配给您的每个 lambda 函数

"StringEquals": {
            "kms:EncryptionContext:LambdaFunctionName": [
                "LambdaFunction-1",
                "LambdaFunction-2",
                "LambdaFunction-3"
            ]
        }

步骤 3:- 将在步骤 2 中创建的策略分配给您的默认 lambda 角色(需要创建第一个 Lambda 以获得默认的 lambda 角色)

步骤 4:- 创建 lambda 函数

Node.js lambda 函数代码

const mysql = require('mysql');
const aws = require("aws-sdk");


const functionName = process.env.AWS_LAMBDA_FUNCTION_NAME;
let res;
let response={};
exports.handler = async(event) => {
reset_globals();

// load env variables
const rds_user = await kms_decrypt(process.env.RDS_USERNAME);
const rds_pwd = await kms_decrypt(process.env.RDS_PASSWORD)

// setup rds connection
var db_connection = await mysql.createConnection({
    host: process.env.RDS_HOSTNAME,
    user: rds_user,
    password: rds_pwd,
    port: process.env.RDS_PORT,
    database: process.env.RDS_DATABASE
});

var sqlQuery = `SELECT doc_id from documents`;
await getValues(db_connection,sqlQuery);

}

async function getValues(db_connection,sql) {
await new Promise((resolve, reject) => {
    db_connection.query(sql, function (err, result) {
        if (err) {
            response = {statusCode: 500, body:{message:"Database Connection Failed", 
             error: err}};
            console.log(response);
            resolve();
        }
        else {
           
            console.log("Number of records retrieved: " + JSON.stringify(result));
            res = result;
           
            resolve();
        }
    });
    });
}

async function kms_decrypt(encrypted) {
const kms = new aws.KMS();
const req = { CiphertextBlob: Buffer.from(encrypted, 'base64'), EncryptionContext: { 
LambdaFunctionName: functionName } };
const decrypted = await kms.decrypt(req).promise();
let cred = decrypted.Plaintext.toString('ascii');
return cred;
}


function reset_globals() {
res = (function () { return; })();
response = {};
}

现在您应该在 Lambda 中看到 KMS。

第五步:-设置环境变量并加密。

Lambda -> 函数 -> 配置 -> 环境变量 -> 编辑

RDS_DATABASE 文档

RDS_HOSTNAME docrds-library.c1k3kcldebmp.us-east-1.rds.amazonaws.com

RDS_PASSWORD root123

RDS_PORT3306

RDS_USERNAME管理员

在 Lambda 函数中解密加密的环境变量使用下面的代码

function kms_decrypt(encrypted) {
const kms = new aws.KMS();
const req = { CiphertextBlob: Buffer.from(encrypted, 'base64'), EncryptionContext: { 
LambdaFunctionName: functionName } };
const decrypted = await kms.decrypt(req).promise();
let cred = decrypted.Plaintext.toString('ascii');
return cred;
}

我的 RDS 文档 table 看起来像:-

我正在使用 lambda 函数中的 sqlQuery 访问列 doc_id

var sqlQuery = `SELECT doc_id from documents`;

测试 lambda 函数后,我得到以下输出。

如果你得到SQL import Error,那么必须添加一层。

errorType": "Runtime.ImportModuleError",
"errorMessage": "Error: Cannot find module 'mysql'\nRequire stack:\n- 
/var/task/index.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js",
 "trace": [
"Runtime.ImportModuleError: Error: Cannot find module 'mysql'",

You can configure your Lambda function to use additional code and content in the form of layers. A layer is a ZIP archive that contains libraries, a custom runtime, or other dependencies. With layers, you can use libraries in your function without needing to include them in your deployment package.

To include libraries in a layer, place them in the directory structure that corresponds to your programming language.

Node.js – nodejs/node_modules

Python – python

Ruby – ruby/gems/2.5.0

Java – java/lib

首先创建一个包含 mysql archieve 的 zip archieve。

First create a react-project

Then in terminal $project-path > npm init

Then $project-path > npm install mysql

You should see node_modules folder created.

Zip node_modules that folder and upload on layer as shown below.

然后,Goto Lambda--> Layer-->Create layer.

对我来说,导致这些问题的原因是 package.json 的一个版本仍然在旧版本的 .build 文件夹中,该文件夹也已部署。一旦我删除它,软件包就会按预期安装。