AWS Lambda Container 方式部署 Flask 应用并通过 API Gateway 提供访问

前言

一年前写过一篇 Lambda 运行 Flask 应用的博文:
https://lpwmm.blog.csdn.net/article/details/139756140

当时使用的是 ZIP 包方式部署应用代码, 对于简单的 API 开发用起来还是可以的, 但是如果需要集成到 CI/CD pipeline 里面就有点不太优雅. 本文将介绍使用容器方式部署 Flask 应用到 Lambda, 并实现通过 API Gateway 进行访问.

开发一个简单的 Flask 应用

使用 uv 作为项目管理工具, 如果你还不了解 uv, 可以参考之前的这篇文章:
https://lpwmm.blog.csdn.net/article/details/146774376

完整的项目代码开源在 Gitee:
https://gitee.com/lpwm/flask-on-lambda

主要涉及到以下常用的场景:

  • 静态文件访问, 模板中引入了自定义的 CSS 样式文件
  • 表单处理
  • 路由重定向

实现效果:
在这里插入图片描述

容器化封装

Dockerfile

# 使用 ECR 提供的 Alpine 环境的 Python 3.12
FROM public.ecr.aws/docker/library/python:3.12-alpine
# [重要] 添加 Lambda Web Adapter (LWA)
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter# 使用清华源安装 uv
RUN sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories \&& apk add --no-cache uv# [重要] 配置 uv 的缓存文件夹路径, Lambda 中只有 /tmp 具有 RW 权限
ENV UV_CACHE_DIR="/tmp"
# 配置 uv 使用清华源
ENV UV_DEFAULT_INDEX="https://pypi.tuna.tsinghua.edu.cn/simple"WORKDIR /var/task# 先将 uv 项目相关的文件复制并初始化 .venv 和依赖
COPY pyproject.toml uv.lock .python-version ./
RUN uv sync# 再将其他文件复制, 这样可以有效减少后面代码发生更新时重新 build 镜像所需要的操作时间
COPY static ./static
COPY templates ./templates
COPY app.py ./# Lambda 执行时只能在一个运行环境中跑一个 Worker, 所以注意加参数 -w=1, 监听端口直接用 LWA 默认的 8080, 不用再改 LWA 的参数了
CMD ["uv", "run", "gunicorn", "-b=:8080", "-w=1", "app:app"]

测试容器

docker build -t flask-on-lambda .
docker run -it --rm -p 8080:8080 flask-on-lambda

AWS 资源创建

ECR & Lambda

REPO_NAME=flask-on-lambda
# 创建 ECR repository
aws ecr create-repository --repository-name $REPO_NAME# 将 ECR repository 的 URI 存入变量, 方便后面调用
REPO_URI=$(aws ecr describe-repositories --repository-names $REPO_NAME --query 'repositories[0].repositoryUri' --output text)# 从 URI 拆分出来 ECR 的主域名, 用于 Docker 登录访问
ECR_HOST=$(echo $REPO_URI | awk -F'/' '{print $1}')# Docker 登录 ECR
aws ecr get-login-password --region cn-northwest-1 | docker login --username AWS --password-stdin $ECR_HOST# 推送 Docker image 到 ECR
docker tag $REPO_NAME:latest $REPO_URI:latest
docker push $REPO_URI:latest# [可选] 获取最新 Image 的哈希值
LATEST_DIGEST=$(aws ecr describe-images --repository-name $REPO_NAME --query 'sort_by(imageDetails,& imagePushedAt)[-1].imageDigest' --output text)# [可选] 更新 Lambda
aws lambda update-function-code --function-name $REPO_NAME --image-uri $REPO_URI@$LATEST_DIGEST --no-cli-pager# 创建 IAM Role
aws iam create-role \--role-name lambda-execution-role-$REPO_NAME \--assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}' \
&& aws iam attach-role-policy \--role-name lambda-execution-role-$REPO_NAME \--policy-arn arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole# 获取 Role ARN
ROLE_ARN=$(aws iam get-role --role-name lambda-execution-role-$REPO_NAME --query 'Role.Arn' --output text)# 创建和 REPO 相同名称的 Lambda
aws lambda create-function \--function-name $REPO_NAME \--package-type Image \--code ImageUri=$REPO_URI:latest \--role $ROLE_ARN

测试 Lambda 调用

aws lambda invoke \--function-name flask-on-lambda \--payload '{"httpMethod": "GET","path": "/","headers": {"Host": "example.com","User-Agent": "curl/7.68.0"},"requestContext": {"resourcePath": "/","httpMethod": "GET"},"body": null,"isBase64Encoded": false}' \--cli-binary-format raw-in-base64-out \/dev/stdout

预期响应:

{"statusCode": 200,"headers": {},"multiValueHeaders": {"server": ["gunicorn"],"date": ["Sun, 13 Jul 2025 12:02:04 GMT"],"connection": ["close"],"content-type": ["text/html; charset=utf-8"],"content-length": ["585"]},"body": "<html>\n\n<head>\n    <title>Flask on Lambda</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/style.css\">\n</head>\n\n<body>\n    <section>\n        <h1>Welcome to the Flask on Lambda</h1>\n        <p>This is a simple Flask application powered by Lambda.</p>\n    </section>\n    <section>\n        <form action=\"\" method=\"post\">\n            <label for=\"name\">Name:</label>\n            <input type=\"text\" id=\"name\" name=\"name\" required placeholder=\"Enter your name\">\n            <br>\n            <button type=\"submit\">Submit</button>\n        </form>\n    </section>\n</body>\n\n</html>","isBase64Encoded": false
}

后面关于 API Gateway 的配置用 CLI 会很麻烦, 就都在 Console 操作了

API Gateway - HTTP API

  • 创建 HTTP API
    在这里插入图片描述
    在这里插入图片描述
  • 添加 Lambda 集成
    在这里插入图片描述
  • 修改路由:
    Method: ANY
    Resource path: /{proxy+}
    在这里插入图片描述
  • 使用默认 Stage
    在这里插入图片描述
  • 完成创建
    在这里插入图片描述
  • 在 Deploy > Stages 中找到 Invoke URL
    在这里插入图片描述
  • 使用浏览器访问测试, 受到 Lambda 的 Cold start 机制的影响, 首次加载和交互的速度会有点慢.
    在这里插入图片描述
    后面刷新后再次交互速度就很快了.
    在这里插入图片描述

性能优化

为了保证用户能在首次访问的时候也有友好的体验, 我们可以为 Lambda 配置 Provisioned concurrency (额外收费的哟)

  • 首先为 Lambda function 创建 Version
    在这里插入图片描述
    在这里插入图片描述
  • 在 Version 视图中编辑 Provisioned concurrency
    在这里插入图片描述
    在这里插入图片描述
  • 此时 Status 为 In progress, 需要等几分钟
    在这里插入图片描述
    状态变成 Ready 就好了
    在这里插入图片描述
  • 复制当前 Version 界面的 Function ARN
    在这里插入图片描述
  • 回到 HTTP API 控制台修改 Integration, 将 Lambda function 对应的 ARN 更新为上面复制的带有 Version 信息的
    在这里插入图片描述
  • 确认目前使用的集成设置中 Lambda 包含了版本信息(后面多了 :1)
    在这里插入图片描述
    因为 HTTP API 默认开启了 Auto deploy 的选项, 所以这种修改都不需要手动重新 Deploy 操作. 再次使用浏览器访问测试, 速度嘎嘎的~

当然, 我们前面配置的 Provisioned concurrency = 1, 对于生产环境业务负载较高的场景, 可以酌情提升.

结尾

至此, 我们成功使用 Docker 容器的方式将一个 Flask 应用部署到了 Lambda 上, 并通过 API Gateway (HTTP API) 对外提供了可访问的 URL 地址, 实现了 Serverless 部署传统 Web 应用. 🎉🎉🎉
由于应用全部都封装在了 ECR 镜像, 所以在实际项目中, 也可以很方便的融入到 CI/CD pipeline 中.

关于之前撰稿期间使用 REST API 踩坑的经历, 有兴趣可以继续阅览. 😂

REST API 踩坑记录

由于 REST API 生成的 Stage URL 中必然会包含 stage 名称, 而经过 LWA 转发到后面的 Lambda 在进行路由地址生成的时候, 并不会包含这个 stage 的名称. 例如: stage = default
第一次请求的地址: https://api.com/default/, 页面中 Flask 跳转后本来应该是定向到 https://api.com/default/success/abc 但是实际跳转后的地址是 https://api.com/success/abc, 由于缺少了 stage 信息, 所以就 4XX 了. 如果 stage 名称是固定的, 那么其实也可以在 Flask 应用里面直接写死, 跟 REST API 传来的保持一致, 理论上应该也能解决. 不过懒得折腾了…下面是之前配置 REST API 的记录, 归档了.

  • 添加 Trigger
    在这里插入图片描述
  • 创建新的 REST API
    在这里插入图片描述
  • 打开自动创建好的 API
    在这里插入图片描述
  • 删除自动创建的资源路径
    在这里插入图片描述
  • 在根路径下创建资源
    在这里插入图片描述
  • 创建 Proxy 资源
    在这里插入图片描述
  • 编辑集成
    在这里插入图片描述
  • Execution role 可以留空
    在这里插入图片描述
  • 测试 GET 方法
    在这里插入图片描述
    在这里插入图片描述
  • 部署 API
    在这里插入图片描述
  • 继续返回 Lambda function 设置, 添加环境变量 AWS_LWA_REMOVE_BASE_PATH, Value 值为 REST API 中的 Stage 名称
    在这里插入图片描述

REST API 存在问题

完成上面的配置后, 如果从浏览器直接访问 Stage URL 根路径报错:
在这里插入图片描述
访问子路径 success/变量 可以加载出来页面
在这里插入图片描述
但是静态 CSS 文件加载失败, 因为请求路径中并没有包含 stage 的名称
在这里插入图片描述
先来解决直接访问 Stage 根路径报错的问题. 这是因为前面只给 /{proxy+} 创建了 ANY 方法和集成, 对于 / 来说, 还是空的设置. 再单独选中 / 资源路径, 创建 ANY 方法, 相同的方式配置 Lambda proxy 集成
在这里插入图片描述

重新部署后就可以访问到了:
在这里插入图片描述
当提交表单后, 重新定向的 URL 又出现了和 CSS 加载相同的问题, Stage 名称丢失了:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88886.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88886.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React虚拟DOM的进化之路

引言 在Web前端开发中&#xff0c;用户交互的流畅性和页面性能一直是核心挑战。早期&#xff0c;开发者直接操作真实DOM&#xff08;Document Object Model&#xff09;时&#xff0c;频繁的重排&#xff08;reflow&#xff09;和重绘&#xff08;repaint&#xff09;导致性能…

(7)机器学习小白入门 YOLOv:机器学习模型训练详解

— (1)机器学习小白入门YOLOv &#xff1a;从概念到实践 (2)机器学习小白入门 YOLOv&#xff1a;从模块优化到工程部署 (3)机器学习小白入门 YOLOv&#xff1a; 解锁图片分类新技能 (4)机器学习小白入门YOLOv &#xff1a;图片标注实操手册 (5)机器学习小白入门 YOLOv&#xff…

初识MySQL(三)之主从配置与读写分离实战

主重复制 主重复制原理master开启二进制日志记录slave开启IO进程&#xff0c;从master中读取二进制日志并写入slave的中继日志slave开启SQL进程&#xff0c;从中继日志中读取二进制日志并进行重放最终&#xff0c;达到slave与master中数据一致的状态&#xff0c;我们称作为主从…

RabbitMQ面试精讲 Day 2:RabbitMQ工作模型与消息流转

【RabbitMQ面试精讲 Day 2】RabbitMQ工作模型与消息流转 开篇 欢迎来到"RabbitMQ面试精讲"系列的第2天&#xff0c;今天我们将深入探讨RabbitMQ的工作模型与消息流转机制。这是面试中最常被问到的核心知识点之一&#xff0c;90%的RabbitMQ面试都会涉及消息流转流程…

基于SpringBoot3集成Kafka集群

1. build.gradle依赖引入 implementation org.springframework.kafka:spring-kafka:3.2.02. 新增kafka-log.yml文件 在resource/config下面新增kafka-log.yml&#xff0c;配置主题与消费者组 # Kafka消费者群组 kafka:consumer:group:log-data: log-data-grouptopic:log-data: …

wpf Canvas 导出图片

在WPF中将Canvas导出为图片主要涉及以下关键步骤和注意事项: ‌核心实现方法‌使用RenderTargetBitmap将Canvas渲染为位图,再通过PngBitmapEncoder保存为PNG文件。需注意临时移除Canvas的布局变换(LayoutTransform)以避免渲染异常‌1。示例代码片段:CanvasExporter.cs pu…

lvs负载均衡实操模拟

目录 一、配置准备 二、NET模式 修改LVS端 开启路由 修改对内网卡 ens160 修改对外网卡 ens224 加载网卡配置文件 修改web1端 修改网卡信息 重启网络 检测 配置web2 检测 验证配置是否正常 启动nginx服务 验证以上配置 添加lvs规则 验证 三、DR模式 修改…

Spring Boot 是如何简化 IoC 的配置的?

首先Spring Boot 并没有发明新的 IoC 理论&#xff0c;它做的也不是替换掉 Spring IoC 容器。相反&#xff0c;Spring Boot 是 Spring IoC 思想的实践者和简化者。它通过**“约定优于配置”&#xff08;Convention over Configuration&#xff09;**的理念&#xff0c;将原本繁…

Go语言中的组合式接口设计模式

文章目录Go语言中的组合式接口设计模式背景和需求组合式接口设计Go语言中的组合式接口设计模式 背景和需求 在微服务架构和复杂业务系统中&#xff0c;我们经常需要调用多个外部服务或内部模块。传统的做法是将所有方法都放在一个大接口中&#xff0c;但这种设计会导致接口臃…

React - createPortal

什么是createPortal&#xff1f;注意这是一个API&#xff0c;不是组件&#xff0c;他的作用是&#xff1a;将一个组件渲染到DOM的任意位置&#xff0c;跟Vue的Teleport组件类似。用法 import { createPortal } from react-dom;const App () > {return createPortal(<div…

Cursor的使用

Cursor的使用 Ctrl L 打开历史对话记录 Tab智能助手 1.单行/多行补全 已有代码片段&#xff1a; //需求&#xff1a;写一个工具类计算数组平均值 public class ArrayUtils {//按tab会完成补全 }按tab键- Cursor 自动生成代码: //需求&#xff1a;写一个工具类计算数组平均值 p…

17.使用DenseNet网络进行Fashion-Mnist分类

17.1 DenseNet网络结构设计import torch from torch import nn from torchsummary import summary #卷积层 def conv_block(input_channels,num_channels):netnn.Sequential(nn.BatchNorm2d(input_channels),nn.ReLU(),nn.Conv2d(input_channels,num_channels,kernel_size3,pad…

网安系列【16】之Weblogic和jboss漏洞

文章目录一 Weblogic1.1 Weblogic相关漏洞1.2 Weblogic漏洞发现1.3 Weblogic漏洞利用二 Jboss2.1 Jboss漏洞2.2 Jboss识别与漏洞利用一 Weblogic WebLogic 是由 Oracle公司 开发的一款基于Java EE&#xff08;现称Jakarta EE&#xff09;的企业级应用服务器&#xff0c;主要用…

Unity URP + XR 自定义 Skybox 在真机变黑问题全解析与解决方案(支持 Pico、Quest 等一体机)

在使用 Unity 的 URP 渲染管线开发 XR 应用&#xff08;如 Pico Neo、Pico 4、Quest 2/3 等一体机&#xff09;时&#xff0c;很多开发者遇到一个奇怪的问题&#xff1a;打包后&#xff0c;Skybox&#xff08;天空盒&#xff09;在某些角度下突然变黑&#xff0c;只在转动头部后…

Cursor、飞算JavaAI、GitHub Copilot、Gemini CLI 等热门 AI 开发工具合集

Cursor&#xff1a;代码编写的智能伙伴​Cursor 是 Anysphere 公司推出的一款 AI 编程工具&#xff0c;它基于微软开源代码编辑器 VS Code 开发&#xff0c;将 AI 技术深度整合到开发人员的工作流程中。Cursor 的功能十分强大&#xff0c;不仅能够自动用纯英文编写代码&#xf…

如何安装历史版本或指定版本的 git

背景 有的时候&#xff0c;我们需要安装指定版本的git&#xff0c;或者希望旧一点的&#xff0c;毕竟我就遇到最新的2.50.1在win10安装后打开就一闪而过&#xff0c;而安装2.49.1就不会 下载 官网可能比较难找&#xff0c;但是这个github仓库&#xff1a;https://github.com/gi…

LaCo: Large Language Model Pruning via Layer Collapse

发表&#xff1a;EMNLP_FINDING_2024 机构&#xff1a;Shanghai Jiao Tong University 连接&#xff1a;LaCo: Large Language Model Pruning via Layer Collapse - ACL Anthology 代码&#xff1a;https://github.com/yangyifei729/LaCo Abstract 基于 Transformer 的大语…

服务器内核级故障排查

目录 **检查内核级故障(Oops/Panic)的具体操作步骤****1. 查看完整 `dmesg` 日志(含时间戳)****2. 过滤关键错误信息****3. 检查系统日志中的内核消息****4. 分析最近一次启动的日志****5. 检查是否有 `vmcore` 转储文件****常见内核错误示例及含义**补充说明:检查内核级故…

Flink学习笔记:整体架构

开一个新坑&#xff0c;系统性的学习下 Flink&#xff0c;计划从整体架构到核心概念再到调优方法&#xff0c;最后是相关源码的阅读。 今天就来学习 Flink 整体架构&#xff0c;我们先看官网的架构图图中包含三部分&#xff0c;分别是 Client、JobManager 和 TaskManager。其中…

【LeetCode 热题 100】105. 从前序与中序遍历序列构造二叉树——(解法二)O(n)

Problem: 105. 从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 【LeetCode 热题 100】105. 从前序与中序遍历序列构…