AWS Lambda@Edge Nodejs:“环境变量不受支持。”

30

最初采用这种方法的动机来自于亚马逊: https://aws.amazon.com/blogs/compute/resize-images-on-the-fly-with-amazon-s3-aws-lambda-and-amazon-api-gateway/ (在他们添加“update”之前……)

在我们的AWS Lambda调整大小函数中,它会调整图像大小并将新图像存储到S3中。

const s3_bucket = process.env.s3_bucket;
S3.putObject({
  Body: buffer,
  Bucket: s3_bucket,
  ContentType: contentType,
  CacheControl: 'max-age=31536000',
  Key: key,
  StorageClass: 'STANDARD'
}).promise()
现在我们希望这个功能在所有测试/暂存环境以及生产环境中都能够使用。因此,我找到了“环境变量”,我认为很好!但是当我尝试部署新版本时,我只得到了以下信息: Environment variables not supported。我们在CloudFront中设置了错误的内容吗?我们正在使用Node版本6.10。如果我们必须硬编码桶并保留不同版本的代码来处理此问题,那么我很难相信这一点。如果是这种情况,那么我们浪费了很多时间来使用AWS Lambda...编辑:我们所做的是接受像“media/catalog/product/3/0/30123/768x/lorem.jpg”这样的图像请求,然后将其调整为位于“media/catalog/product/3/0/30123.jpg”的原始图像,将其调整为768px和webp(如果浏览器支持),然后返回新图像(如果尚未缓存)。

4
为什么会被踩?管理员选项里有这个功能,直到点击部署之前都没有显示它无效,那时我已经在我的代码中实现了它,上传到了S3...至少花费了一个小时。 - OZZIE
6个回答

44

使用自定义起源标头的解决方法

根据限制文档,Lambda@Edge不支持环境变量。

但是,如果您在起源请求或起源响应上使用Lambda@Edge,则可以使用CloudFront Origin Custom Headers来绕过此限制。

基本上,您可以在CloudFront起源中设置自定义标头来代替环境变量。然后将这些“静态”标头传递到您的起源请求/响应Lambda@Edge中。

enter image description here

然后您可以通过以下方式在Lambda@Edge函数代码中访问它们:

const foo = request.origin.custom.customHeaders["x-env-foo"][0].value;

当使用S3作为源时:

const foo = request.origin.s3.customHeaders["x-env-foo"][0].value;

另请参阅https://medium.com/@mnylen/lambda-edge-gotchas-and-tips-93083f8b4152


很棒的想法!我尝试了一下,但是不知何故我的customHeaders为空,即使我在cloudfront中设置了它们。我还尝试将它们添加到缓存策略中,但我无法让它工作:lambda事件对象上的customHeaders始终为空,无论我的设置如何。 - victor israe
我真的找不到在CloudFront分发中添加自定义标头的位置。有什么建议吗?AWS文档也没有帮助。 - Jonny
我找到了它 - 在分发中,点击“来源”选项卡,然后编辑您拥有的源。里面有一个名为“添加自定义标头 - 可选”的部分。 - Jonny

5

使用 Lambda 函数名称的解决方法

如果您有针对开发和生产环境的不同函数,您也可以检查 process.env.AWS_LAMBDA_FUNCTION_NAME

const getIsDev = () => {
    // lambda at edges cant set environment variables in the console.
    if (!process.env.AWS_LAMBDA_FUNCTION_NAME) {
        return true // << double check this is the behavior you want to fall back to.
    }
    if (process.env.AWS_LAMBDA_FUNCTION_NAME.indexOf("-dev") => 0) {
        // does the function name contain the word -dev?  
        return true
    }
    return false
}

5
作为选择答案的替代方案...
可以使用Serverless FrameworkWebpackserverless-webpack插件来实现相同的效果。
您可以使用以下选项访问用于无服务器操作的选项:
const slsw = require('serverless-webpack');
const stage = slsw.lib.options.stage;

或者,您可以使用以下方式访问serverless.yml文件中的信息:

const serverless = slsw.lib.serverless;

然后只需将以下内容添加到您的webpack.config.js文件的插件中:

plugins: [new EnvironmentPlugin({ VAR1: var1, VAR2: var2, STAGE: stage })]

这种方法可以为您提供一种简单的方式来管理Lambda@Edge函数中的环境变量。

2
根据此 CloudFront Lambda 限制文档,不支持环境变量。可以使用 SSM Parameter Store 来管理函数的变量。您可以通过控制台或编程方式编辑 Parameter Store 变量,并且可以使用ssm.getParameter() 函数获取这些变量。最初的回答是: 不支持环境变量,使用 SSM Parameter Store 来管理函数的变量。

3
我真的很讨厌lambda.. 我从未见过如此不直观、难以使用且耗时的工具.. 但还是谢谢,我会尝试的!我刚刚花了一周多时间来实现这个.. 我真是够累的了.. - OZZIE
3
如果不支持,为什么还要在界面中显示环境变量?是为了浪费人们的时间吗? - OZZIE
2
环境变量仅在Lambda@Edge中受支持。在常规函数中,它们通常是被支持的。您真的需要使用Lambda@Edge吗?普通的Lambda不能胜任吗? - Thales Minussi
我的意思是,这是一个由S3事件触发的调整大小函数,其本质上是异步和最终一致的。为什么要使用Lambda@Edge来实现这个功能呢? - Thales Minussi
1
我不确定我完全理解这个答案 :( 好的,我可以在那里存储一些东西,然后我可以获取它.. 但是它如何帮助确定当前请求是来自暂存还是生产环境呢?他们也可能有并发请求.. - OZZIE
从技术上讲,如果你只有这两个环境(staging和production),你应该有2个单独的Lambda。然而,如果你想只保留一个Lambda,如果你通过API网关(或任何请求服务)访问Lambda,那么你可以从Lambda接收到的事件中简单地查看所请求的路径(/staging vs /production),并相应地采取行动。同样,你应该为两个环境设置参数存储值。 - Deiv

1
我曾经将这作为评论,但我认为将其添加为答案是值得的。
你为什么需要使用Lambda@Edge?我理解你的沮丧,但Lambda@Edge的设计是为了实现完全不同的一组事情。请查看一些用例here 在您的用例中,您将对象上传到S3,PUT对象事件将触发Lambda函数,这本质上是异步和最终一致的。由于您只会获得几百毫秒的优化缩略图生成执行时间,因此您的用户实际上并不需要它。在他们需要缩略图时,无论如何它已经存在。
在常规Lambda函数中,您绝对可以利用环境变量,从而非常容易地将不同的设置应用于不同的环境(dev、test、prod)。
您可以查看如何在常规Lambda函数中设置环境变量here

说实话,我不知道。我们所做的是接收一个图片请求,例如“media/catalog/product/3/0/30123/768x/lorem.jpg”,然后使用位于“media/catalog/product/3/0/30123.jpg”的原始图片,将其调整大小为768像素并转换为WebP格式(如果浏览器支持),然后返回新的图片(如果尚未缓存)- 难道我们不需要@Edge来完成这个任务吗? - OZZIE
我认为你的应用程序在这里可以更加主动而不是被动。为什么不在上传时触发调整大小?然后你的应用程序在请求生命周期中不需要处理任何东西,使最终用户体验更快。请问您能告诉我图片上传的时间点吗?是用户决定缩略图的大小还是预定义的应用程序规则?如果是用户决定,我建议异步处理调整大小并返回类似于“正在处理您的请求。它很快就会可用”的信息,然后你的前端可以轮询它。 - Thales Minussi
2
一般来说,根据用户请求进行图像处理通常不是一个好主意,因为这通常需要相当长的时间。如果我说的有意义,请告诉我。希望能对你有所帮助! - Thales Minussi
1
如果您的目标是基于用户请求进行图像处理,那么您也可以只使用API网关-> Lambda,这样您就可以使用环境变量。然而,在Lambda Edge中缺少环境变量并不是一个缺点,因为如前所述,您可以使用Parameter Store来实现此目的(并且可以使用诸如node-cache之类的模块来实现缓存这些参数以节省处理时间)。 - Deiv
与在上传时处理和调整图像以适应所有必要大小的方法不同,即时处理图像具有以下几个优点:增加了灵活性 降低了存储成本 具备故障恢复能力 - OZZIE
这就是我问它是用户定义的规则还是应用程序定义的规则的原因。如果用户选择尺寸,则不可能预测他们想要的每个可能的尺寸。如果这是应用程序规则,我坚持认为应该在上传后立即完成。 - Thales Minussi

1

我通过在bash构建脚本中将s3_bucket前置到js文件中来解决了这个问题。因此,我指定build.sh [s3_bucket] [environment-name]

if [ ! $# -eq 2 ]; then
    echo 'You need to provide two parameters: [s3_bucket] [environment]'
    echo 'example: build.sh imagetest-us-east-1 next'
    echo 'example: build.sh [s3_bucket_to_be_defined] production'
    exit 1
fi

filename='index.js'
setCurrentEnvironment() {
    jsEnv="const s3_bucket='$1';"
    mv "$filename" "$filename".orig && cp "$filename".orig "$filename"
    echo -e "$jsEnv\n\n$(cat ${filename})" > "$filename"
}
restoreDefault() {
    rm -rf "$filename"
    mv "$filename".orig "$filename"
}

setCurrentEnvironment $1
zip -FS -q -r "../../dist/resize__$2.zip" *
restoreDefault

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接