FROM amazon/aws-lambda-nodejs:12
COPY app.js package*.json ./
RUN npm install
CMD [ "app.lambdaHandler" ]
Dockerfile 正在将源代码 (app.js
) 和描述软件包和依赖项的文件(package.json
和 packagelock.json
)添加到基础映像中。然后,我运行 npm
来安装依赖项。我将 CMD
设置为函数处理程序,但是也可以稍后在配置 Lambda 函数时作为参数覆盖来完成。
我使用 Docker CLI 在本地构建 random-letter
容器映像:
$ docker build -t random-letter
为了检查是否能够正常运行,我使用 Lambda Runtime Interface Emulator 在本地启动容器映像:
$ docker run -p 9000:8080 random-letter:latest
现在,我使用 cURL 测试函数调用。在这里,我将传递了一个空的 JSON 有效负载。
$ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
如果存在错误,我可以在本地进行修复。假如一切正常,我就可以转到下一步。
为了上传容器映像,我在账户中创建了一个新的 ECR 存储库,然后标记本地映像以将其推送到 ECR。为了识别容器映像中的软件漏洞,我启用了 ECR 映像扫描。
$ aws ecr create-repository --repository-name random-letter --image-scanning-configuration scanOnPush=true
$ docker tag random-letter:latest 123412341234.dkr.ecr.sa-east-1.amazonaws.com/random-letter:latest
$ aws ecr get-login-password | docker login --username AWS --password-stdin 123412341234.dkr.ecr.sa-east-1.amazonaws.com
$ docker push 123412341234.dkr.ecr.sa-east-1.amazonaws.com/random-letter:latest
在这里,我使用 AWS 管理控制台来完成函数的创建。您还可以使用 AWS 无服务器应用程序模型,该模型已更新以添加对容器映像的支持。
在 Lambda 控制台中,单击创建函数。选择容器映像,为函数命名,然后选择浏览映像以在我的 ECR 存储库中查找合适的映像。
选择存储库后,我使用我上传的最新
映像。当我选择映像时,Lambda 会将其转换为底层映像摘要(位于下图中标签的右侧)。您可以使用 docker images --digests
命令在本地查看映像摘要。这样,即使最新
的标签分配给了新的映像,该函数也会使用相同的映像,并且可以免受意外部署的影响。您可以在函数代码中更新要使用的映像。更新函数配置对使用的映像没有影响,即使在此期间将标签重新分配给另一个映像也是如此。
我也可以选择覆盖一些容器映像值。我现在不这样做,但通过这种方式(例如通过覆盖 CMD
值中的函数处理程序),我可以创建可用于不同函数的映像。
我将所有其他选项保留为默认选项,然后选择创建函数。
创建或更新函数的代码时,Lambda 平台会优化新的和更新的容器映像,以便为接收调用做好准备。此优化需要几秒钟或几分钟,具体取决于映像的大小。之后,该函数就可以被调用了。我在控制台中对该函数进行测试。
它可以正常工作! 现在让我们将 API Gateway 添加为触发器。选择添加触发器并使用 HTTP API 添加 API Gateway。为简单起见,我让 API 的身份验证保持开放状态。
现在,我点击几次 API 终端节点,以下载几封随机邮件。
它能够按预期工作! 以下是一些使用 faker.js 模块中的随机数据生成的 PDF 文件。
为 Python 构建自定义映像
有时,您需要使用自定义容器映像,例如为了遵循公司的指导原则,或是为了使用我们不支持的运行时版本。
在此,我想构建一个映像以使用 Python 3.9。我的函数的代码 (app.py
) 非常简单,我只想打个招呼并显示正在使用的 Python 版本。
import sys
def handler(event, context):
return 'Hello from AWS Lambda using Python' + sys.version + '!'
正如之前提到的,我们将与您分享适用于所有受支持运行时的 Lambda Runtime Interface Client(实现 Runtime API)开源实现。在此,我以基于 Alpine Linux 的 Python 映像作为基础。然后,我将适用于 Python 的 Lambda Runtime Interface Client(链接即将发布)添加到映像中。以下是 Dockerfile
的内容:
# 定义全局参数
ARG FUNCTION_DIR="/home/app/"
ARG RUNTIME_VERSION="3.9"
ARG DISTRO_VERSION="3.12"
# 阶段 1 - 捆绑基础映像 + 运行时
# 获取映像的新副本并安装 GCC
FROM python:${RUNTIME_VERSION}-alpine${DISTRO_VERSION} AS python-alpine
# 安装 GCC(Alpine 使用 musl,但我们使用 GCC 进行编译和关联依赖项)
RUN apk add --no-cache \
libstdc++
# 阶段 2 - 构建函数和依赖项
FROM python-alpine AS build-image
# 安装 aws-lambda-cpp 构建依赖项
RUN apk add --no-cache \
build-base \
libtool \
autoconf \
automake \
libexecinfo-dev \
make \
cmake \
libcurl
# 在构建的这个阶段纳入全局参数
ARG FUNCTION_DIR
ARG RUNTIME_VERSION
# 创建函数目录
RUN mkdir -p ${FUNCTION_DIR}
# 复制处理程序函数
COPY app/* ${FUNCTION_DIR}
# 可选 – 安装函数的依赖项
# RUN python${RUNTIME_VERSION} -m pip install -r requirements.txt --target ${FUNCTION_DIR}
# 为 Python 安装 Lambda 运行时接口客户端
RUN python${RUNTIME_VERSION} -m pip install awslambdaric --target ${FUNCTION_DIR}
# 阶段 3 - 最终的运行时映像
# 获取 Python 映像的新副本
FROM python-alpine
# 在构建的这个阶段纳入全局参数
ARG FUNCTION_DIR
# 将工作目录设置为函数的根目录
WORKDIR ${FUNCTION_DIR}
# 复制构建的依赖项
COPY --from=build-image ${FUNCTION_DIR} ${FUNCTION_DIR}
# (可选)添加 Lambda Runtime Interface Emulator 并在 ENTRYPOINT 中使用脚本实现更简单的本地运行
COPY https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie
RUN chmod 755 /usr/bin/aws-lambda-rie
COPY entry.sh /
ENTRYPOINT [ "/entry.sh" ]
CMD [ "app.handler" ]
这次的 Dockerfile 更加清晰,遵循多阶段构建 Docker 最佳实践,分三个阶段构建最终映像。您可以使用这种三阶段方法来构建自己的自定义映像:
阶段 1 是使用运行时(本例中的运行时为 Python 3.9)和 GCC(我们在阶段 2 中用于编译和关联依赖项)构建基础映像。
阶段 2 是安装 Lambda Runtime Interface Client 并构建函数和依赖项。
阶段 3 是将阶段 2 的输出添加到阶段 1 中构建的基础映像,以创建最终映像。这里我还添加了 Lambda Runtime Interface Emulator,但这是可选的,请参阅下文。
我在下面创建了 entry.sh
脚本将其用作 ENTRYPOINT
。它执行适用于 Python 的 Lambda Runtime Interface Client。如果是在本地执行,则 Lambda Runtime Interface Emulator 将包装 Runtime Interface Client。
#!/bin/sh
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
exec /usr/bin/aws-lambda-rie /usr/local/bin/python -m awslambdaric
exec /usr/local/bin/python -m awslambdaric
现在,我可以使用 Lambda Runtime Interface Emulator 在本地检查函数和容器映像是否正常工作:
$ docker run -p 9000:8080 lambda/python:3.9-alpine3.12
不将 Lambda Runtime Interface Emulator 纳入容器映像中
将 Lambda Runtime Interface Emulator 添加到自定义容器映像为可选项。如果不将其纳入其中,则可以按照以下步骤在本地机器上安装 Lambda Runtime Interface Emulato 以进行本地测试:
我删除 Dockerfile 阶段 3 中复制 Lambda Runtime Interface Emulator (aws-lambda-rie
) 和 entry.sh
脚本的命令。在此,我不需要 entry.sh
脚本。
我使用这个 ENTRYPOINT
默认启动 Lambda Runtime Interface Client:
ENTRYPOINT [ "/usr/local/bin/python", “-m”, “awslambdaric” ]
我运行这些命令来在我的本地机器上安装 Lambda Runtime Interface Emulator,例如在 ~/.aws-lambda-rie
下:
mkdir -p ~/.aws-lambda-rie
curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie
chmod +x ~/.aws-lambda-rie/aws-lambda-rie
在 Lambda Runtime Interface Emulator 安装到我的本地机器上后,我可以在启动容器时挂载它。现在在本地启动容器的命令是(假设 Lambda Runtime Interface Emulator 位于 ~/.aws-lambda-rie
:
docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
--entrypoint /aws-lambda/aws-lambda-rie lambda/python:3.9-alpine3.12
/lambda-entrypoint.sh app.handler
测试 Python 自定义映像
无论使用哪种方式,当容器在本地运行时,我都可以使用 cURL 测试函数调用:
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
输出符合我的预期!
"Hello from AWS Lambda using Python3.9.0 (default, Oct 22 2020, 05:03:39) \n[GCC 9.3.0]!"
我将映像推送到 ECR 并像以前一样创建函数。以下是我的测试在控制台中显示的内容:
我的基于 Alpine 的自定义容器映像可以在 Lambda 上运行 Python 3.9 了!
现已推出
您现在可以使用容器映像在以下区域部署 Lambda 函数:美国东部(弗吉尼亚北部)、美国东部(俄亥俄)、美国西部(俄勒冈)、亚太地区(东京)、亚太地区(新加坡)、欧洲(爱尔兰)、欧洲(法兰克福)和南美洲(圣保罗)。我们正在努力尽快在更多区域增加支持。除了 ZIP 存档外,还提供容器映像支持,我们将继续支持 ZIP 打包格式。
使用此功能不会产生额外费用。您需要为 ECR 存储库 和 Lambda(按正常的定价)付费。
您可以将 AWS Lambda 中的容器映像支持与控制台、AWS 命令行界面 (CLI)、AWS 开发工具包、AWS 无服务器应用程序模型以及来自 AWS 合作伙伴的解决方案一起使用,这些解决方案包括 Aqua Security、Datadog、Epsagon、HashiCorp Terraform、Honeycomb、Lumigo、Pulumi、Stackery、Sumo Logic 和 Thundra。
这项新功能创造了新的使用场景,简化了与开发管道的集成,并让您可以轻松使用自定义映像和您喜欢的编程平台构建无服务器应用程序。
了解在 AWS Lambda 中使用容器映像的更多信息并开始使用。
– Danilo