首页 / 资讯中心 / 文章详情

Flask实战:从零搭建大模型推理服务并部署到云端

Flask实战:从零搭建大模型推理服务并部署到云端 ★ FEATURED ARTICLE
1. 为什么选Flask承载大模型推理服务1.1 云端部署的本质需求做模型部署这件事很多人第一步就卡在“不知道用哪个框架”。看到网上那些vllm、Ollama、Triton之类的名词新人很容易发懵。但如果你只是把一个训练好的大模型哪怕是7B、13B这种量级封装成HTTP接口供业务系统调用那么Flask其实是一个非常务实的起点。部署的本质是什么是把模型从开发环境“搬”到生产环境并且对外暴露一个稳定的接口。这个接口要满足三件事第一能接收外部请求第二能加载模型并执行推理第三能把结果以结构化的方式返回给调用方。Flask作为轻量级Web框架天然适合干这个——它不限制你怎么写推理逻辑也不包办你的并发模型而是提供最基础的HTTP路由、请求解析、响应封装能力剩下的全部由你掌控。很多所谓“大模型部署方案”之所以看起来复杂是因为它们把推理引擎、加速框架、服务化框架揉在了一起。但对一个需要快速落地、验证想法的项目来说用Flask直接加载模型并暴露接口往往三天内就能跑通远比一开始就上全套分布式方案靠谱。1.2 Flask在模型推理中的角色定位你要清楚一点Flask不是推理引擎它是服务化层。真正干活的是你加载进来的模型比如Transformers库里的LLMFlask负责的是“把HTTP请求翻译成模型输入把模型输出翻译成HTTP响应”。这个角色定位决定了你的项目架构模型单独实例化Flask只持有模型引用。在服务启动时加载一次模型后续所有请求共用一个模型实例避免每个请求都重新加载权重。这也是Flask类轻量框架部署模型的通用姿势——把耗时操作放到启动阶段把业务接口写成薄薄的一层。相比之下FastAPI、Flask、Django都可以做这件事但Flask胜在简单直接、生态成熟、资料一堆。你搜“flask部署模型”能翻出大量历史案例踩坑成本极低。而且它对我们这种熟悉Python的人来说几乎零学习成本。1.3 与vllm/Ollama等方案的边界划分我经常看到有人问有vllm不就行了为什么还要用Flask这其实是不理解工具的分工。vllm、Ollama这些工具解决的是“如何把模型跑得更快、更省显存”的问题它们自带推理引擎有的还自带API服务。但问题在于第一它们往往要求特定的模型格式比如vllm要装特定依赖第二它们的默认接口风格不一定契合你的业务第三你没法灵活地在请求前后加逻辑比如用户鉴权、参数校验、多模型路由。Flask在这种场景下更像一个“胶水层”。你可以让vllm跑一个本地推理进程然后Flask作为业务层去调用它也可以直接让Flask加载一个量化后的模型文件自己管理推理循环。对于“简单案例”级别的项目后者更常见也更直观。所以这里你完全可以把Flask看作一个灵活的接口壳子模型在壳子里跑对外表现统一。2. 模型与环境的准备2.1 如何挑选适合Flask部署的模型不是所有大模型都适合Flask直接部署。模型越大加载越慢、显存占用越高、单次推理耗时越长这对一个简单的Flask服务是灾难。我建议从两个维度选择一是参数量二是量化方式。以常见的7B模型为例FP16精度下权重大约14GB如果服务器只有一块16GB显存的卡勉强能塞进去但很紧张。换用4bit量化之后权重能压到4GB左右普通消费级显卡甚至纯CPU服务器都能跑只是速度慢一些。热词里反复出现的“本地部署大模型让个人电脑智能化”就是从量化模型开始的。部署时的模型格式也很关键。HuggingFace原版权重PyTorch格式兼容性最好Transformers库直接加载GGUF格式则更适合Ollama、llama.cpp这类工具。用Flask部署时我推荐优先选HuggingFace原版权重或者经过量化的HuggingFace权重如GPTQ、AWQ因为Transformers库的加载接口最统一代码也最好写。2.2 本地环境与云端环境的差异本地跑通只是第一步“云端部署”才是这个标题的核心命题。你在笔记本上能跑通的Flask服务搬到云服务器以后往往暴露出新问题环境依赖不一致、显存和内存额度不同、防火墙限制、部署方式原始比如直接python app.py。云端环境有两点要特别留意第一服务器大概率没有图形界面你要保证Flask服务能脱离控制台运行第二模型文件通常很大上传下载要规划好存盘位置不能放在用户目录里随手一丢。从实操角度我建议你用Linux云服务器Ubuntu 22.04或CentOS 7都行配合systemd或Docker来管理服务生命周期。如果你只是临时验证nohup也不是不行但长期跑一定要上进程守护工具。2.3 依赖安装与目录规划一个标准的大模型Flask部署项目依赖其实非常少flask、transformers、torch、accelerate外加一点点tokenizers。安装命令也简单pip install flask transformers torch accelerate如果你服务器显存不够可以装CPU版的torch。千万注意CPU版torch和GPU版torch不能同时存在装之前清干净。目录规划上我的习惯是这样的/opt/model-api/ ├── app.py # Flask主程序 ├── models/ # 存模型权重 ├── requirements.txt # 依赖清单 └── logs/ # 日志输出把模型文件单独放在models目录有两点好处一是模型文件动辄几个GB和代码分离以后更新代码不用重新拷贝模型二是日志单独存放排查问题时不用在终端里翻历史输出。3. 核心代码实现Flask调用模型3.1 模型加载与全局缓存Flask部署模型中最重要的一个代码设计原则是模型只加载一次全局复用。很多新手犯的低级错误是在每次请求里都实例化一次模型结果服务一启动第一个请求就直接把服务器内存撑爆了。正确做法是模块级别的加载也就是在Python文件被导入的时候加载模型。示例代码如下from flask import Flask, request, jsonify from transformers import AutoModelForCausalLM, AutoTokenizer import torch app Flask(__name__) MODEL_PATH ./models/qwen2-7b-instruct-4bit tokenizer AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( MODEL_PATH, device_mapauto, torch_dtypetorch.float16, trust_remote_codeTrue ) model.eval()这段代码的关键点有三个。第一trust_remote_codeTrue如果不是所有模型都需要但很多国产模型要求这个参数第二device_mapauto让PyTorch自动把模型分配到可用设备上有GPU就用GPU没有就CPU第三model.eval()以后模型关闭了训练模式梯度计算关闭内存占用和推理速度都会明显改善。加载时间这个概念要提前做好预期。7B模型从磁盘加载到显存通常需要30秒到2分钟。这段时间里Flask其实还没启动所以服务日志里会出现一段时间的“静默期”这是正常的。3.2 模型推理安全参数设置很多人部署模型时直接套用HuggingFace官方的generate函数但生产环境里绝不能这么干。你要设置几个关键参数来控制推理行为否则真实用户请求会让模型输出不可控的内容。以文本生成接口为例我常用下面的配置generation_config { max_length: 512, # 最大生成长度 temperature: 0.7, # 采样温度 top_p: 0.85, # 核采样 repetition_penalty: 1.05, # 重复惩罚 do_sample: True }max_length决定了返回文本的上限防止用户提问后模型无限输出。temperature越低回答越保守越高回答越随机。top_p配合temperature使用控制候选词的概率累积范围。repetition_penalty很实用能抑制模型反复说同一句话的毛病这也是大模型常见的退化问题。如果你是做纯代码或数学问答建议把temperature调到0.2甚至0因为这类问题有标准答案随机性越低越准确。如果是做创意写作temperature可以设到0.8以上。这些参数我会明确写进接口文档方便调用方根据场景调整。3.3 接口设计与请求解析有了模型之后Flask要做的就一件事定义路由解析请求调用模型返回结果。以最简单的文本对话接口为例完整代码如下app.route(/chat, methods[POST]) def chat(): data request.get_json() prompt data.get(prompt) if not prompt: return jsonify({error: prompt is required}), 400 messages [ {role: system, content: 你是一个乐于助人的AI助手。}, {role: user, content: prompt} ] input_ids tokenizer.apply_chat_template( messages, tokenizeTrue, add_generation_promptTrue, return_tensorspt ).to(model.device) with torch.no_grad(): outputs model.generate( input_ids, **generation_config ) response_ids outputs[0][input_ids.shape[1]:] response tokenizer.decode(response_ids, skip_special_tokensTrue) return jsonify({ response: response, status: 200 })这里面有几个细节值得展开。第一request.get_json()是Flask解析JSON请求体最标准的办法。如果调用方传的不是合法JSON这里会抛异常所以最好包一层try/except。第二apply_chat_template是把用户原始输入包装成带system、user角色的对话格式。这个做法比简单拼字符串要科学得多因为模型在预训练时见过的模板和这个是一致的直接拼接会让模型困惑输出质量明显下降。第三input_ids.shape[1:]是从生成的token序列里截掉输入部分只保留新生成的内容。这个写法是为了避免把用户自己的输入原样回显到返回值里很多人初次写这一步会漏掉导致接口返回的内容里重复了用户输入。第四torch.no_grad()是推理模式的核心。在模型推理时PyTorch默认还是会跟踪计算图浪费内存和算力。手动加上这个上下文管理器以后计算图不保留显存和内存占用都会下降推理也会更快。3.4 启动入口与多线程问题Flask自带的服务能跑起来但它开发气息太重了。默认的Flask开发服务器是单进程多线程模型遇到稍微高一点的并发就会变得很脆弱。这里先给出最简单的启动方式后面讲到云端部署时再升级成gunicornif __name__ __main__: app.run(host0.0.0.0, port8080, debugFalse)host0.0.0.0表示监听所有网络接口不是只监听本机回环地址。这点特别容易踩坑——如果写成127.0.0.1你从云服务器外部怎么都访问不到8080端口还总以为是防火墙问题。port8080是自定义端口实际云服务器上你可能要选8000、8080或者9000这类高位端口。不要在80端口直接跑因为80端口通常要留给Nginx做反向代理。debugFalse也是硬性要求。打开debug模式时Flask会启用调试器允许外部在Web页面上执行任意Python代码这其实是极大的安全隐患。线上服务绝对不能用debug模式。还有一个容易被忽略的问题Flask开发服务器默认是线程池模式多线程同时调用同一个PyTorch模型会涉及线程安全问题。好在PyTorch的模型推理本身在推理时是线程安全的因为不涉及梯度更新但你要保证在请求处理函数里不要手动修改模型的任何可训练状态。简单说只要你不做model.train()或者model.load_state_dict()这类操作多线程调用同一个模型是没问题的。4. 部署到云端从本地到生产4.1 云服务器选型与基础配置Flask部署大模型的云端服务器配置核心其实不是CPU而是内存和显存。如果你用CPU推理内存至少要有16GB否则加载7B模型时内存会直接打满系统开始疯狂换页服务响应慢到无法接受。如果用GPU推理显存建议16GB起步对应常见的就是NVIDIA T4、A10、A30这类卡。选型上我给三条建议预算有限、做技术验证选带单卡T4的服务器显存16GB加载4bit量化7B模型没问题。对响应速度有要求、做小规模生产选A10或者L40S显存大且FP16算力好。纯CPU做推理选8核以上的实例内存压到32GB以上把模型加载到内存里跑慢但能稳定服务几个并发。操作系统方面Ubuntu 22.04 Server版是首选。原因不是它比CentOS高级而是PyTorch、Transformers这些库在Ubuntu上预编译包最全安装时会少很多“从源码编译”这种让人头疼的事。另外云服务器的安全组和防火墙一定要提前放行对应的端口。很多阿里云、腾讯云的服务器默认安全组只开放22端口SSH你要额外放行8080或者Nginx用的80端口。不然你服务跑起来了外部就是访问不通排查到半夜才发现是安全组问题。4.2 使用Gunicorn代替Flask开发服务器Flask自带的app.run只适合本地开发调试云端生产环境必须换更健壮的WSGI服务器。我常用的方案是Gunicorn。Gunicorn是一个Python写的WSGI HTTP服务器它可以用多个worker进程来接收请求。每个worker进程中加载一份模型副本这样就能利用多核CPU来处理并发请求——虽然代价是多份模型会占用N倍内存但并发能力和稳定性都比Flask自带的单进程强得多。安装和启动命令如下pip install gunicorn gunicorn -w 2 -b 0.0.0.0:8080 --timeout 120 app:app解释一下参数-w 2表示启动2个worker进程。每个worker独立加载一份模型所以如果你的模型加载要占8GB内存2个worker就要16GB。这个参数要根据服务器内存和并发需求来调整不是越大越好。-b 0.0.0.0:8080指定监听地址和端口。--timeout 120表示请求超时时间120秒。这个很关键因为大模型推理很慢尤其在高负载时一个请求可能要跑几十秒。默认的超时时间只有30秒很容易误杀正在推理的请求。如果你用单卡GPUGunicorn有一个经典配置问题多个worker都会尝试往同一张GPU上加载模型显存会变成2倍或N倍占用。这种情况我建议要么只开1个worker要么用gunicorn的worker_class设置为gevent或者threads模式避免多进程争抢显存。实际项目中如果在GPU服务器上部署我更倾向于用单worker 多线程模式gunicorn -w 1 --threads 8 -b 0.0.0.0:8080 --timeout 120 app:app这样只有一份模型加载在显存里8个线程共享同一份权重并发能力足够应对中小规模的调用量而且显存占用不会爆炸。4.3 Nginx反向代理与外部访问Gunicorn虽然能跑服务了但还需要一层Nginx来做反向代理。这倒不是Gunicorn不够好而是生产环境有几个刚需静态资源处理、负载均衡、安全防护、统一入口。很多团队直接把8080端口暴露给外部访问时带端口号。这不难用但从规范和安全角度考虑都不理想。Nginx监听80端口把请求转发到Gunicorn监听的8080端口外部用户看到的只有标准的80端口也更方便后续扩展域名和HTTPS证书。一个最小可用的Nginx配置长这样server { listen 80; server_name your_domain.com; client_max_body_size 20M; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 300s; } }这里重点说三个参数第一client_max_body_size 20M这是限制请求体大小。一般文本请求很小但如果你后续要支持上传图片给多模态模型这个必须调大。第二proxy_read_timeout 300s这是Nginx等待上游Gunicorn返回数据的超时时间。大模型推理动辄几十秒如果沿用默认的60秒很容易报504 Gateway Timeout。我把这里设成300秒给足推理时间。第三proxy_set_header很重要。Gunicorn默认只能看到它自己监听的连接信息经过Nginx转发以后如果不手动设置X-Real-IP和X-Forwarded-For你的服务端日志里记录的IP全是127.0.0.1排查问题时就分不清请求来自哪里了。配置完Nginx记得执行nginx -t检查语法然后nginx -s reload重载服务。4.4 模型文件上传与存储把本地训练好的模型上传到云服务器是一项容易被低估的体力活。模型动辄几个GB甚至几十GB你不可能用scp一条条传。这里分享我用的两种方式。第一种直接从HuggingFace拉取到服务器上。如果服务器的网络能访问HuggingFace那么直接在服务器上用Transformers加载时写上模型的repo id就行它会自动下载到~/.cache/huggingface目录。这个办法最省事但拉取大模型可能耗时较长且对服务器网络带宽要求高。第二种本地先下载再打包压缩传到服务器。比如用scp或者rsyncrsync -avzP ./models/qwen2-7b-instruct-4bit/ userserver:/opt/model-api/models/qwen2-7b-instruct-4bit/参数-P表示显示进度并支持断点续传。模型文件动辄几十GB传到一半断线是常事rsync能续传这一点非常关键。上传完模型文件后务必验证文件完整性。最靠谱的办法是运行一次load脚本看能否成功加载模型并执行一次简单的generate。不要指望上传完直接就跑文件截断或者解压损坏的问题时有发生。对于GGUF这类文件格式用Flask部署时不建议直接load最好先转成Transformers支持的格式或者直接通过llama-cpp-python库接入。但那些都属于扩展玩法了这个案例里先把HuggingFace权重跑通就够用了。5. 常见问题与排查技巧实录5.1 内存不足导致进程被系统Kill现象服务跑着跑着进程突然消失查看日志发现Killed字样。原因大模型加载时内存占用会有一个暴涨期特别是在使用device_mapauto的时候PyTorch会把各层分散加载中间状态的临时内存可能比模型本身还大。排查与解决思路# 查看服务器内存和交换分区情况 free -h # 查看进程的内存占用变化 top -o %MEM如果内存明显不够有两个立竿见影的办法一是换成更小的量化模型3B、1.5B等二是给操作系统增加swap分区。注意swap是权宜之计速度远不如内存但有比没有强至少不会直接OOM。5.2 并发请求时响应速度断崖式下跌现象单个请求响应2秒并发10个请求时所有请求都变成50秒以上。原因大模型推理是计算密集型操作多个线程同时抢占同一张GPU时推理队列会变得很长。这和MySQL的行锁其实是一个道理——多个事务同时改一行后面的都得排队。常用的解决办法是限流和排队。Flask本身不提供队列能力你的选择有两个使用gunicorn的线程数控制并发上限把线程数限制在能接受的范围。自己写一个简单的请求队列将并发请求串行化处理超过队列长度的请求直接返回429 Too Many Requests。我个人倾向于后者因为模型推理串行是常态与其让请求都堆在GPU上争抢显存然后互相拖慢不如明确告诉调用方“稍后再试”。5.3 模型加载非常慢卡在Downloading状态现象每次启动Flask服务都要等几分钟日志一直显示Downloading或Loading状态。原因如果模型目录里缺少某个文件比如tokenizer.model或者配置文件Transformers在启动时会尝试在线下载缺失组件。如果服务器访问HuggingFace不稳定就会一直卡在下载阶段。解决思路分两步第一在本地把模型完整下载好包括config.json、tokenizer.json、tokenizer_config.json、model.safetensors等全部文件一次性rsync到服务器第二把HUGGING_FACE_HUB_OFFLINE环境变量设为1强制离线加载模式export TRANSFORMERS_OFFLINE1这样Transformers就不会尝试在线下载任何缺失文件如果真缺文件就直接报错问题能立刻暴露而不是傻等超时。5.4 跨域请求与接口调试现象前端页面调接口时浏览器报CORS错误。原因如果你的Flask服务要提供给前端JS调用而前端运行在另一个域名或端口上浏览器默认禁止这种跨域访问。解决也简单使用Flask-CORS扩展pip install flask-cors代码里加一行from flask_cors import CORS CORS(app)CORS(app)默认允许所有域名跨域如果你的接口有鉴权需求要配置成指定域名。这个细节对前后端联调阶段特别重要很多后端同学自己用Postman测接口一切正常一接前端就报错往往就是这个原因。调试阶段还有一个小技巧用curl发请求比用Postman更直白也更容易复现问题。比如测试上面的接口curl -X POST http://server-ip:8080/chat \ -H Content-Type: application/json \ -d {prompt: 你好请介绍一下自己}返回的JSON错误或者异常curl终端里会直接打印出来一目了然。6. 服务化部署的几点个人体会搭过几次这种从零开始的模型部署之后我越来越觉得Flask部署大模型这个事技术难点反而不在Web服务本身而在模型推理与工程架构的边界划分上。你花十分钟写一个Flask接口但可能要花一整天去解决模型加载失败、内存爆炸、并发掉速的问题。我个人在实操中的一个体会是在做这种部署方案时先把接口定义和参数格式确定下来再去折腾模型加载顺序不要反。接口先行有个好处——你可以先用一个假模型比如直接从函数里返回固定字符串把整条链路跑通再换真模型。这样能把网络、端口、跨域、Nginx这些外围问题先筛掉最后剩下的问题几乎全部集中在模型本身上。还有一个值得养成的习惯每次修改代码前先备份一份能运行的版本。大模型部署项目里“改了一行代码导致整个服务起不来”的情况太常见了尤其涉及device_map、量化参数、模型路径这类配置。有备份兜底至少不会把自己锁死在故障状态。另外最后再说一个小技巧把推理日志和访问日志分开。Flask自带的请求日志会打印每一条请求混着推理过程中的warning提示一起输出排查问题时根本分不清主次。我用的是logging模块把模型推理相关的日志写到logs/model.log访问日志写到logs/access.log两不相干调试效率提升非常明显。如果你手头还没有云端服务器在本地虚拟机上完全可以把这套流程先演练一遍验证好了再搬上云。真正决定部署成败的往往不是某个高深的技术点而是对环境的掌控程度——你知道模型文件在哪、依赖是什么、服务怎么启动、日志去哪里看问题出现时就能稳得住。这也是我写这个案例最想传递的东西。
阅读完成 · 觉得有帮助?
咨询建站