FastAPI Vue3 类型项目 Docker 部署完整教程以 “AI掘金头条” 新闻资讯项目为例涵盖从零到一的 Docker Compose 部署全流程包括常见踩坑与解决方案。快速开始老手直接看这里如果对 Docker 熟悉直接复制下面文件即可部署。前提服务器已装 Docker Docker Compose项目目录按下方结构放好。# 1. 确保项目根目录有这些文件# docker-compose.yml、database.sql# toutiao_backend/Dockerfile、toutiao_backend/requirements.txt# xwzx-news/Dockerfile、xwzx-news/nginx.conf、xwzx-news/.env.production# 2. 启动cd/opt/fastapidockercompose up-d--build# 3. 验证curlhttp://192.168.194.10/api/news/categories三个最关键的注意点不搞清楚必踩坑api.js中baseURL用??不用||否则空字符串会走 fallbackrequirements.txt必须包含cryptography否则 MySQL 8.0 连不上MySQL command 必须加--skip-character-set-client-handshake否则中文乱码下面是从原理到实践的完整讲解。目录一、项目架构概览二、准备工作让代码适配 Docker三、编写 Dockerfile四、编写 docker-compose.yml五、部署到服务器六、验证与测试七、常见踩坑与解决一、项目架构概览项目根目录/ ├── docker-compose.yml # 服务编排 ├── database.sql # 数据库初始化脚本 ├── toutiao_backend/ # FastAPI 后端 │ ├── Dockerfile │ ├── requirements.txt │ ├── main.py │ ├── config/ │ │ ├── db_config.py # MySQL 配置 │ │ └── cache_conf.py # Redis 配置 │ ├── models/ # ORM 模型 │ ├── routers/ # API 路由 │ ├── crud/ # 数据库操作 │ ├── schemas/ # Pydantic 模型 │ └── utils/ # 工具函数 └── xwzx-news/ # Vue3 前端 ├── Dockerfile ├── nginx.conf # Nginx 反向代理配置 ├── .env.production # 生产环境变量 ├── vite.config.js └── src/ └── config/ └── api.js # API 地址配置容器拓扑结构浏览器 ──→ Nginx:80 (frontend) │ ├── / → 静态文件 (Vue SPA) └── /api/* → 反向代理 → FastAPI:8000 (backend) │ ├── MySQL:3306 └── Redis:6379Docker 网络通信原理理解这个才能排查连接问题Docker Compose 会为所有服务创建一个默认网络每个容器自动获得以服务名为域名的内部 DNS。例如# 在后端容器中可以直接通过服务名访问其他容器pingmysql# 解析到 MySQL 容器 IP172.18.0.xpingredis# 解析到 Redis 容器 IP这就是为什么配置文件里写DB_HOSTmysql、REDIS_HOSTredis而不是 IP 地址。通信场景地址写法说明前端 → 后端Nginx 代理http://backend:8000容器内部通信后端 → MySQLDB_HOSTmysql环境变量注入后端 → RedisREDIS_HOSTredis环境变量注入浏览器 → 前端http://192.168.194.10:80外部访问走端口映射容器内访问宿主机host.docker.internal特殊域名Docker 20.10二、准备工作让代码适配 Docker源码中数据库和 Redis 连接地址是硬编码的localhost容器化后需要通过环境变量动态注入。2.1 修改数据库配置文件toutiao_backend/config/db_config.py修改前硬编码连接字符串ASYNC_DATABASE_URLmysqlaiomysql://root:123456localhost:3306/news_app?charsetutf8mb4修改后importosfromsqlalchemy.ext.asyncioimportasync_sessionmaker,AsyncSession,create_async_engine# 从环境变量读取本地开发用默认值 localhostDB_HOSTos.getenv(DB_HOST,localhost)DB_PORTos.getenv(DB_PORT,3306)DB_USERos.getenv(DB_USER,root)DB_PASSWORDos.getenv(DB_PASSWORD,123456)DB_NAMEos.getenv(DB_NAME,news_app)ASYNC_DATABASE_URLfmysqlaiomysql://{DB_USER}:{DB_PASSWORD}{DB_HOST}:{DB_PORT}/{DB_NAME}?charsetutf8mb4# 后续代码保持不变 ...关键原则os.getenv(KEY, 默认值)确保本地开发直接python main.py也能跑Docker 部署时通过环境变量覆盖。2.2 修改 Redis 配置文件toutiao_backend/config/cache_conf.pyimportjsonimportosfromtypingimportAnyimportredis.asyncioasredis REDIS_HOSTos.getenv(REDIS_HOST,localhost)REDIS_PORTint(os.getenv(REDIS_PORT,6379))REDIS_DBint(os.getenv(REDIS_DB,0))# 后续代码保持不变 ...2.3 修改前端 API 地址配置文件xwzx-news/src/config/api.js核心改动||换成??||会把空字符串判为 false 导致走误 fallback// 开发环境: http://127.0.0.1:8000 (直连后端)// Docker部署: 空字符串 (Nginx反向代理 /api/ → backend)exportconstapiConfig{baseURL:import.meta.env.VITE_API_BASE_URL??http://127.0.0.1:8000,}为什么用??而不是||值 || fallback ?? fallback(空字符串)fallback← 错误← 正确undefinedfallbackfallbacknullfallbackfallbackhttp://...http://...http://...Docker 构建时需要把VITE_API_BASE_URL设为空字符串让请求走同源 Nginx 代理空字符串是 falsy 值所以必须用??。2.4 添加 Vite 开发代理可选文件xwzx-news/vite.config.jsimport{defineConfig}fromviteimportvuefromvitejs/plugin-vueexportdefaultdefineConfig({plugins:[vue()],server:{proxy:{/api:{target:http://127.0.0.1:8000,changeOrigin:true,},},},})加了这个之后本地npm run dev也能用相对路径/api/xxx不需要直连 8000 端口避免跨域问题。2.5 添加 .dockerignore加快构建速度Docker 构建时会把整个上下文目录发给 Docker daemon。不加.dockerignore会把.venv、node_modules、__pycache__等大文件也传过去严重影响构建速度。文件toutiao_backend/.dockerignore__pycache__ *.pyc .venv .env .git .gitignore *.md test_* .idea cache文件xwzx-news/.dockerignorenode_modules dist .git .gitignore *.md .env .env.local注意.env.production不能被忽略构建时需要它。三、编写 Dockerfile3.1 后端 Dockerfile文件toutiao_backend/DockerfileFROM python:3.12-slim WORKDIR /app # 安装 gccbcrypt 编译需要 RUN apt-get update apt-get install -y --no-install-recommends \ gcc \ rm -rf /var/lib/apt/lists/* # 先复制依赖文件利用 Docker 缓存层代码改动时不用重新 pip install COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 再复制应用代码 COPY . . EXPOSE 8000 CMD [uvicorn, main:app, --host, 0.0.0.0, --port, 8000]要点说明python:3.12-slim— 体积小够用gcc—bcrypt库编译需要否则安装会报错先COPY requirements.txt再COPY . .— Docker 分层缓存优化改代码不用重新装依赖--host 0.0.0.0— 必须否则容器外无法访问3.2 后端依赖文件文件toutiao_backend/requirements.txtfastapi0.115.0 uvicorn[standard]0.30.0 sqlalchemy[asyncio]2.0.0 aiomysql0.2.0 redis5.0.0 passlib[bcrypt]1.7.4 bcrypt3.2.2 pymysql1.1.0 cryptography41.0.0 python-multipart0.0.9重要cryptography必须加MySQL 8.0 默认使用caching_sha2_password认证没有这个包会报RuntimeError: cryptography package is required。3.3 前端 Dockerfile文件xwzx-news/Dockerfile# 阶段一Node 构建 FROM node:22-alpine AS builder WORKDIR /app COPY package.json ./ RUN npm install COPY . . # VITE_API_BASE_URL 为空字符串 前端请求走同源 Nginx 代理 ARG VITE_API_BASE_URL RUN VITE_API_BASE_URL${VITE_API_BASE_URL} npm run build # 阶段二Nginx 运行 FROM nginx:alpine COPY --frombuilder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD [nginx, -g, daemon off;]多阶段构建的好处阶段一Node 环境只用于编译阶段二Nginx 环境只包含编译产物最终镜像体积很小~20MB不含node_modules3.4 Nginx 配置文件xwzx-news/nginx.confserver { listen 80; server_name localhost; # Vue SPA 静态文件 location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; # history 模式路由支持 } # API 反向代理到后端容器 location /api/ { proxy_pass http://backend:8000; # backend docker-compose 服务名 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_set_header X-Forwarded-Proto $scheme; } }关键点try_files $uri $uri/ /index.html— Vue Router history 模式必须否则刷新 404proxy_pass http://backend:8000—backend是 docker-compose 中的服务名Docker 内部 DNS 自动解析不暴露 8000 端口给外部 — 所有请求走 803.5 前端生产环境变量文件xwzx-news/.env.productionVITE_API_BASE_URLVite 在vite build时自动加载.env.production把VITE_API_BASE_URL设为空字符串。四、编写 docker-compose.yml文件项目根目录docker-compose.ymlservices:# MySQL mysql:image:mysql:8.0container_name:news_mysqlrestart:alwaysenvironment:MYSQL_ROOT_PASSWORD:123456MYSQL_DATABASE:news_appMYSQL_CHARSET:utf8mb4MYSQL_COLLATION:utf8mb4_unicode_ciports:-3306:3306volumes:-mysql_data:/var/lib/mysql# 数据持久化-./database.sql:/docker-entrypoint-initdb.d/init.sql# 自动建表command:---character-set-serverutf8mb4---collation-serverutf8mb4_unicode_ci---init-connectSET NAMES utf8mb4# 每个连接也强制 utf8mb4---skip-character-set-client-handshake# 跳过客户端字符集协商healthcheck:test:[CMD,mysqladmin,ping,-h,localhost,-u,root,-p123456]interval:10stimeout:5sretries:10# Redis redis:image:redis:7-alpinecontainer_name:news_redisrestart:alwaysports:-6379:6379healthcheck:test:[CMD,redis-cli,ping]interval:10stimeout:5sretries:5# FastAPI 后端 backend:build:./toutiao_backendcontainer_name:news_backendrestart:alwaysports:-8000:8000depends_on:mysql:condition:service_healthy# 等 MySQL 健康检查通过才启动redis:condition:service_healthyenvironment:-DB_HOSTmysql# 容器名 主机名-DB_PORT3306-DB_USERroot-DB_PASSWORD123456-DB_NAMEnews_app-REDIS_HOSTredis-REDIS_PORT6379# Vue 前端 frontend:build:context:./xwzx-newsargs:VITE_API_BASE_URL:# 空字符串 走 Nginx 代理container_name:news_frontendrestart:alwaysports:-80:80depends_on:-backendvolumes:mysql_data:配置解读配置项作用restart: always容器崩溃自动重启开机自启depends_on condition: service_healthy等待依赖服务就绪避免启动顺序问题volumes: mysql_data数据卷持久化容器删除数据不丢./database.sql:/docker-entrypoint-initdb.d/init.sqlMySQL 首次启动自动执行建表--skip-character-set-client-handshake跳过客户端字符集协商强制 utf8mb4为什么需要 skip-character-set-client-handshakeMySQL 8.0 容器即使设置了character-set-serverutf8mb4客户端连接时仍可能协商成latin1导致中文乱码显示为央行等乱码。加上这个参数彻底锁定字符集。五、部署到服务器5.1 服务器环境要求操作系统CentOS 7 / Red Hat / Ubuntu已安装 Docker 和 Docker Compose# Red Hat / CentOS 安装 Dockersudodnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.reposudodnfinstall-ydocker-ce docker-ce-cli containerd.io docker-compose-plugin# 启动sudosystemctl startdockersudosystemctlenabledocker5.2 传输项目文件将整个项目目录上传到服务器排除node_modules、.venv、__pycache__# 本地先清理rm-rfxwzx-news/node_modules toutiao_backend/.venv toutiao_backend/__pycache__# 上传到服务器scp-r./fastapi root192.168.194.10:/opt/5.3 一键启动cd/opt/fastapidockercompose up-d--build首次启动 MySQL 会自动执行database.sql建表约 1-2 分钟。5.4 常用管理命令# 查看运行状态dockercomposeps# 查看日志dockercompose logs-f# 所有服务dockercompose logs-fbackend# 指定服务# 重启dockercompose restart# 停止dockercompose stop# 停止并删除容器保留数据dockercompose down# 停止并删除容器 数据卷⚠ 数据库数据会丢失dockercompose down-v# 重新构建并启动dockercompose up-d--build# 只重建单个服务dockercompose build --no-cache frontenddockercompose up-dfrontend5.5 更新代码后如何重新部署日常开发中改代码 → 重新部署是最频繁的操作。不同改动的更新方式不同只改了前端代码页面、样式、API 调用cd/opt/fastapidockercompose build --no-cache frontenddockercompose up-dfrontend只改了后端代码路由、CRUD、逻辑cd/opt/fastapidockercompose build --no-cache backenddockercompose up-dbackend改了 requirements.txt 或 Dockerfile依赖变更cd/opt/fastapidockercompose build --no-cache backenddockercompose up-dbackend改了 database.sql数据库结构变更cd/opt/fastapidockercompose down-v# 删数据卷重建dockercompose up-d--build改了 docker-compose.yml服务配置变更cd/opt/fastapidockercompose up-d--build# Docker 会自己判断哪些需要重建小技巧如果不是确定需要--no-cache先试docker compose up -d --build它只重建有变化的层快很多。六、验证与测试6.1 浏览器访问http://192.168.194.10 → 前端页面 http://192.168.194.10/api/ → 后端接口通过 Nginx 代理 http://192.168.194.10:8000/ → 后端接口直连6.2 命令行接口测试# 测试 APIcurlhttp://192.168.194.10/api/news/categories# 测试注册curl-XPOST http://192.168.194.10/api/user/register\-HContent-Type: application/json\-d{username:test,password:123456}# 测试 查看后端日志排查问题用curl-v-XPOST http://192.168.194.10/api/user/register\-HContent-Type: application/json\-d{username:test,password:123456};\dockerlogs news_backend--tail206.3 验证数据库# 进入 MySQL 容器dockerexec-itnews_mysql mysql-uroot-p123456news_app# 查看表SHOW TABLES;# 验证中文字符集SHOW VARIABLES LIKEcharacter%;七、常见踩坑与解决坑 1cryptography package is requiredRuntimeError: cryptography package is required for sha256_password or caching_sha2_password auth methods原因MySQL 8.0 默认认证插件caching_sha2_password需要cryptography包。解决requirements.txt加入cryptography41.0.0。坑 2前端请求127.0.0.1:8000连接拒绝Failed to load resource: net::ERR_CONNECTION_REFUSED http://127.0.0.1:8000/api/user/register原因前端构建时VITE_API_BASE_URL没生效走了 fallback 值。排查方法# 检查构建产物中是否还有 127.0.0.1dockerexecnews_frontendsh-cgrep -r 127.0.0.1:8000 /usr/share/nginx/html/根因api.js中用了||运算符baseURL:import.meta.env.VITE_API_BASE_URL||http://127.0.0.1:8000// ^^ 空字符串是 falsy会走 fallback解决改用??空值合并运算符baseURL:import.meta.env.VITE_API_BASE_URL??http://127.0.0.1:8000// ^^ 只有 null/undefined 才走 fallback坑 3中文乱码央行宣å¸原因MySQL 容器字符集协商问题客户端连接降级为latin1。排查方法dockerexecnews_mysql mysql-uroot-p123456-eSHOW VARIABLES LIKE character%;解决docker-compose.ymlMySQL command 加两行command:---character-set-serverutf8mb4---collation-serverutf8mb4_unicode_ci---init-connectSET NAMES utf8mb4# 新增---skip-character-set-client-handshake# 新增然后重建⚠ 会清空数据dockercompose down-vdockercompose up-d--build坑 4Docker 构建缓存导致修改不生效现象改了代码但构建后还是旧逻辑。解决# 强制无缓存重建dockercompose build --no-cache frontenddockercompose up-dfrontend坑 5bcrypt 版本兼容性问题Python 3.12 bcrypt 最新版可能编译失败或 API 不兼容。解决锁定版本passlib[bcrypt]1.7.4 bcrypt3.2.2坑 6Vue Router 刷新 404原因Nginx 没有配置try_files非根路径刷新时找不到文件。解决nginx.conf中location / { try_files $uri $uri/ /index.html; # 这句必须有 }附录文件清单与路径文件路径说明docker-compose.yml项目根目录服务编排Dockerfile后端toutiao_backend/DockerfileFastAPI 镜像requirements.txttoutiao_backend/requirements.txtPython 依赖db_config.pytoutiao_backend/config/db_config.py需改环境变量cache_conf.pytoutiao_backend/config/cache_conf.py需改环境变量Dockerfile前端xwzx-news/Dockerfile多阶段构建nginx.confxwzx-news/nginx.confNginx 代理.env.productionxwzx-news/.env.productionVite 生产环境变量api.jsxwzx-news/src/config/api.js||改??vite.config.jsxwzx-news/vite.config.js开发代理配置.dockerignore后端toutiao_backend/.dockerignore排除 .venv 等大文件.dockerignore前端xwzx-news/.dockerignore排除 node_modulesdatabase.sql项目根目录数据库建表脚本附录 B移植到自己的项目这份配置不是绑定这个新闻项目的。如果你的项目也是FastAPI 后端 Vue3 前端 MySQL Redis改动清单如下需要改的文件改什么数据库密码docker-compose.ymlMYSQL_ROOT_PASSWORD、DB_PASSWORD、healthcheck 中的密码数据库连接db_config.py如果表结构不同改DB_NAME后端端口Dockerfile、docker-compose.yml、nginx.conf如果后端不是 8000API 路由前缀nginx.conf、vite.config.js如果 API 前缀不是/api/项目路径docker-compose.ymlbuild: ./你的后端目录、build: ./你的前端目录前端依赖xwzx-news/Dockerfile如果有额外依赖需要安装数据库初始化database.sql换成你自己的建表 SQL非 MySQL改requirements.txt中的数据库驱动如asyncpg替代aiomysql修改db_config.py中的连接字符串格式改docker-compose.yml中数据库镜像。非 Redis把相关配置删掉即可这个项目对 Redis 是弱依赖缓存。以上就是从源码到 Docker 部署的完整流程。核心思路环境变量注入配置 → Dockerfile 定义镜像 → docker-compose 编排服务 → 一键部署。遇到问题先看日志大部分问题都是环境变量或字符集配置引起的。