一篇文章带你从0构架一个真实的AI应用实战(通过Docker部署FastAPI应用程序) 📅 发布时间:2026/9/3 16:28:04 👁 浏览次数: 欢迎来到PyTorch深度学习实战的世界博客主页卿云阁欢迎关注点赞收藏⭐️留言首发时间2026年8月31日✉️希望可以和大家一起完成进阶之路作者水平很有限如果发现错误请留言轰炸哦万分感谢Docker 的简单认识把应用程序和它所需要的运行环境一起打包从而保证程序在不同机器上具有一致的运行环境。Dockerfile描述应用应该如何构建。Image镜像打包完成后的应用运行模板。Container容器镜像真正运行起来后的实例。一个具体的项目例子时间序列预测任务deploy_t1h 文件夹用于存放训练完成的预测模型predictor.py负责定义模型加载与预测逻辑app.py则负责将模型预测功能封装为 FastAPI 服务接口使外部程序能够通过 HTTP 请求调用模型并获取预测结果。Dockerfile描述应用应该如何构建。# 基础镜像: Python 3.10 精简版 (比完整版小 600MB) FROM python:3.10-slim # 设置工作目录 (容器内的路径) WORKDIR /app # 先复制依赖文件 (利用 Docker 缓存层, 后续改代码不用重装依赖) COPY requirements.txt . # 安装依赖 # -i 用清华镜像源 (国内构建快 10 倍) RUN pip install --no-cache-dir -r requirements.txt \ -i https://pypi.tuna.tsinghua.edu.cn/simple # 复制代码和模型文件 COPY app.py predictor.py ./ COPY deploy_t1h ./deploy_t1h # 声明端口 (仅做说明, 实际靠 docker run 的 -p 参数映射) EXPOSE 8000 # 启动命令 CMD [uvicorn, app:app, --host, 0.0.0.0, --port, 8000]WORKDIR /app 定义容器内部的工作目录。 后续复制进来的代码、模型全部都放在/app下面。aeration-api:v2 └── /app ├── app.py ← V2 ├── predictor.py ← V2 └── old.py aeration-api:v3 └── /app ├── app.py ← V3 ├── predictor.py ← V3 └── utils.py ← V3新增CMD [uvicorn, app:app, --host, 0.0.0.0, --port, 8000]uvicorn app:app启动 FastAPI 服务。第一个app文件名app.py第二个appapp.py里面实例化的 FastAPI 对象app FastAPI()运行 app.py 文件使用文件里面名字叫 app 的 FastAPI 实例启动 web 服务。构建 Docker 镜像docker build -t aeration-api:v1 .REPOSITORYTAGIMAGE IDCREATEDSIZE镜像仓库镜像版本标签镜像真正的身份证镜像构建生成时间镜像磁盘占用大小aeration‑apiv1abc123def4562 minutes ago1.2GB启动容器docker run -d \ --name aeration \ -p 8000:8000 \ --restartalways \ aeration-api:v1参数含义-d后台运行detach--name aeration给容器起个名字方便后续操作-p 8000:8000把容器内的 8000 端口映射到服务器的 8000 端口--restartalways重要服务器重启或容器崩溃后自动重启aeration-api:v1用哪个镜像启动CONTAINER ID IMAGE COMMAND ... PORTS NAMES abc123... aeration-api:v1 uvicorn... ... 0.0.0.0:8000-8000/tcp aeration模型/代码更新流程① 上传 V3 最新项目文件 ↓ ② 构建 V3 新镜像 aeration-api:v3 ↓ ③ 使用 V3 镜像创建测试容器 例如8001:8000 ↓ ④ 测试 V3 容器 FastAPI接口 模型预测 ↓ ⑤ 测试正常 ↓ ⑥ 停止旧正式容器 V28000 ↓ ⑦ 删除旧正式容器 ↓ ⑧ 使用 V3 镜像创建新的正式容器 8000:8000 ↓ ⑨ 再次测试正式服务 ↓ ⑩ 确认 V3 稳定运行 ↓ ⑪ 删除旧 V2 镜像# 1. 进入上传后的 V3 项目目录 cd ~/aeration # 2. 构建 V3 镜像 docker build -t aeration-api:v3 . # 3. 创建 V3 测试容器 docker run -d \ --name aeration-v3-test \ -p 8001:8000 \ aeration-api:v3 # 4. 测试 V3 docker logs aeration-v3-test --tail 50 curl http://localhost:8001/ # 5. V3 测试正常后删除测试容器 docker stop aeration-v3-test docker rm aeration-v3-test # 6. 停止并删除 V2 正式容器 docker stop aeration docker rm aeration # 7. 使用 V3 镜像创建新的正式容器 docker run -d \ --name aeration \ -p 8000:8000 \ --restartalways \ aeration-api:v3 # 8. 再次验证正式服务 docker logs aeration --tail 50 curl http://localhost:8000/ # 9. V3 稳定运行一段时间后再删除 V2 镜像 docker rmi aeration-api:v2真实的项目例子在项目目录新增 3 个文件requirements.txt记录项目运行所需要的 Python 第三方依赖方便 Docker 在构建镜像时统一安装运行环境。Dockerfile定义 Docker 镜像的构建过程包括基础环境、依赖安装以及程序启动方式。.dockerignore指定构建镜像时不需要发送或复制到 Docker 中的文件和目录从而减小构建上下文并提高构建效率。把文件传到服务器在服务器上安装 Docker更新包索引apt update装必要的工具apt install -y ca-certificates curl gnupg lsb-release用阿里云的 Docker 源# 创建 keyring 目录 install -m 0755 -d /etc/apt/keyrings # 下载阿里云的 Docker GPG key curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg # 设置权限 chmod ar /etc/apt/keyrings/docker.gpg # 添加阿里云的 Docker 软件源 echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(. /etc/os-release echo $VERSION_CODENAME) stable | tee /etc/apt/sources.list.d/docker.list /dev/null再更新一次加载新源apt update装 Dockerapt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这一步会下载几百 MB等几分钟。安装成功了这是 Docker 安装完成后的正常输出。启动并验证systemctl start docker systemctl enable docker docker --version验证能运行容器docker run hello-world构建 Docker 镜像cd /root/sv30_app docker build -t sv30-api:v1.0 .构建成功后验证docker images sv30-api检查项目结果当前运行容器无镜像名称与标签sv30-api:v1.0镜像 ID3be5d957cd21磁盘占用648MB内容大小156MB镜像状态已构建成功尚未启动容器创建 .env 文件cd /root/sv30_app nano .env进入编辑器后只写一行DASHSCOPE_API_KEYsk-xxxxxxxxxxxxxxxx保存并退出按 Ctrl O 按回车 按 Ctrl X限制权限并检查chmod 600 /root/sv30_app/.env检查权限ls -l /root/sv30_app/.env-rw------- 1 root root ... /root/sv30_app/.env只检查变量名称不显示 Keycut -d -f1 /root/sv30_app/.env启动容器docker run -d \ --name sv30 \ --restart unless-stopped \ --env-file /root/sv30_app/.env \ -p 8000:8000 \ sv30-api:v1.0项目内容容器 ID2f5920875ad5使用镜像sv30-api:v1.0容器名称sv30运行状态正在运行健康检查正常创建时间约 22 秒前容器端口8000主机端口8000IPv4 监听0.0.0.0:8000IPv6 监听[::]:8000端口映射宿主机8000→ 容器8000本机健康检查地址http://127.0.0.1:8000/health公网健康检查地址http://8.222.205.39:8000/health如果后面指向使用私有的访问docker run -d \ --name sv30 \ --restart unless-stopped \ --env-file /root/sv30_app/.env \ -p 私网IP:8000:8000 \ sv30-api:v1.0验证容器在跑docker psPython端调用import requests url data { MLSS: 3000, pH: 7.1, DO: 2.0, Temp: 20.0, } with open(rD:\vscode-water\app\1.png, rb) as sv30, \ open(rD:\vscode-water\app\2.png, rb) as sv5, \ open(rD:\vscode-water\app\3.png, rb) as surface: files { 1_image: 1, 2_image: 25, aeration_image: surface, } response requests.post(url, datadata, filesfiles, timeout600) print(response.status_code) print(response.json())Java端调用import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Scanner; public class Main { // multipart/form-data 请求的分隔符。 // 请求头和请求体必须使用完全相同的分隔符。 private static final String BOUNDARY ----SV30Boundary; public static void main(String[] args) throws Exception { // FastAPI 的完整接口地址。 String apiUrl http://; // 创建 HTTP 连接。 HttpURLConnection connection (HttpURLConnection) new URL(apiUrl).openConnection(); // 设置请求方法为 POST。 connection.setRequestMethod(POST); // 允许向服务器发送请求体。 connection.setDoOutput(true); // 建立网络连接的最长等待时间10 秒。 connection.setConnectTimeout(10_000); // 等待 FastAPI 返回结果的最长时间10 分钟。 // 接口内部需要进行图像识别和大模型调用因此超时时间不能太短。 connection.setReadTimeout(600_000); // 指定请求类型为 multipart/form-data。 // 这种请求格式可以同时上传普通参数和图片文件。 connection.setRequestProperty( Content-Type, multipart/form-data; boundary BOUNDARY ); // 获取请求输出流并开始写入传感器数据和图片。 // try 代码块结束后输出流会自动关闭。 try (OutputStream output connection.getOutputStream()) { // 添加四个传感器参数。 // 参数名称必须与 FastAPI 接口定义完全一致包括大小写。 addPart(output, MLSS, 3000); addPart(output, pH, 7.1); addPart(output, DO, 2.0); addPart(output, Temp, 20.0); // addPart( output, Paths.get(D:/vscode-water/) ); // 添加5分钟污泥沉降图片。 // addPart( output, Paths.get(D:/vscode-water/) ); // 添加好氧池表面图片。 // aeration_image 是可选字段。 addPart( output, aeration_image, Paths.get(D:/vscode-water/) ); // 写入 multipart 请求体的结束标记。 // 如果缺少该结束标记FastAPI 可能无法正确解析请求。 output.write( (-- BOUNDARY --\r\n).getBytes(UTF-8) ); } // 获取服务器返回的 HTTP 状态码。 // 200 通常表示请求成功。 int statusCode connection.getResponseCode(); // 状态码小于400时读取正常响应 // 状态码大于等于400时读取错误响应。 InputStream responseStream statusCode 400 ? connection.getInputStream() : connection.getErrorStream(); // 使用 UTF-8 读取 FastAPI 返回的完整 JSON 内容。 Scanner scanner new Scanner(responseStream, UTF-8) .useDelimiter(\\A); String responseBody scanner.hasNext() ? scanner.next() : ; // 输出 HTTP 状态码。 System.out.println(HTTP状态码 statusCode); // 输出 FastAPI 返回的 JSON 分析结果或错误信息。 System.out.println(响应内容 responseBody); // 关闭读取流和网络连接。 scanner.close(); connection.disconnect(); } /** * 向 multipart/form-data 请求体中添加一个参数。 * * value 支持两种类型 * 1. 普通字符串用于 MLSS、pH、DO、Temp * 2. Path 文件路径用于上传图片。 * * param output HTTP 请求输出流 * param name FastAPI 接口字段名称 * param value 字符串参数值或图片文件路径 */ private static void addPart( OutputStream output, String name, Object value ) throws IOException { // 每个参数开始前都需要写入 multipart 分隔符。 output.write( (-- BOUNDARY \r\n).getBytes(UTF-8) ); // 如果 value 是 Path按照文件格式写入。 if (value instanceof Path) { Path file (Path) value; // 检查文件是否存在避免发送请求后才发现路径错误。 if (!Files.exists(file)) { throw new IOException(图片不存在 file); } // 写入文件字段说明包括字段名称和文件名称。 output.write( ( Content-Disposition: form-data; name\ name \; filename\ file.getFileName() \\r\n Content-Type: application/octet-stream\r\n \r\n ).getBytes(UTF-8) ); // 将图片的二进制内容复制到 HTTP 请求输出流。 Files.copy(file, output); // 文件内容结束后换行为下一个参数做准备。 output.write(\r\n.getBytes(UTF-8)); } else { // 普通字符串参数的字段说明。 output.write( ( Content-Disposition: form-data; name\ name \\r\n \r\n value \r\n ).getBytes(UTF-8) ); } } }其它问题假设后面更新程序但是api不变java端是否还要重启还是说不用进行其它的操作当 FastAPI 服务仅更新内部算法、模型或业务逻辑而服务地址、端口、API 路径、请求参数和返回格式保持不变时Java 调用端通常无需修改或重启只需保证新的 FastAPI 容器重新在原地址和端口提供相同接口即可。在实际部署中FastAPI 的访问地址取决于 Docker 端口绑定方式以及 Java 服务与 FastAPI 服务的位置关系。如果使用 -p 127.0.0.1:8000:8000则 FastAPI 只绑定在服务器本机此时接口地址为http://127.0.0.1:8000/predict只能由同一台服务器上的 Java 程序访问如果 Java 与 FastAPI 部署在不同服务器但两台服务器处于同一内网/VPC 中则应绑定 FastAPI 服务器的私有 IP例如-p 172.17.2.136:8000:8000Java 再通过 http://172.17.2.136:8000/predict 进行内网调用如果两台服务器无法通过内网通信才需要使用公网 IP 对外提供接口。总体来说生产环境优先采用“同机使用 127.0.0.1、跨机使用私有 IP”的方式尽量避免直接将 FastAPI 的 8000 端口暴露到公网。