尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python全栈实战:Docker Compose + Nginx 部署 FastAPI、Vue3 与 MySQL

Python全栈实战:Docker Compose + Nginx 部署 FastAPI、Vue3 与 MySQL Python全栈实战Docker Compose Nginx 部署 FastAPI、Vue3 与 MySQL完成一个全栈项目的开发只是第一步将它真正部署到生产环境并稳定运行才是更大的挑战。本文将带你从零开始使用Docker Compose和Nginx将FastAPI后端、Vue3前端和MySQL数据库部署为一个完整的生产级应用。这不仅是一个教程更是一套可复用的部署模板。一、最终架构概览浏览器 │ │ 80端口 ▼ Nginx反向代理 静态资源服务 │ ├── /api/* ──────► FastAPI:8000后端API ├── /static/* ───► Nginx直接返回静态文件 └── /* ──────────► Vue3前端静态页面 │ ▼ MySQL:3306数据库 │ ▼ Docker Volume数据持久化这套架构的核心设计思想Nginx作为统一入口处理静态资源、反向代理和负载均衡FastAPI只负责业务逻辑和API不处理静态文件Vue3编译为纯静态文件由Nginx直接服务MySQL通过Docker Volume持久化数据确保容器重启不丢失二、项目结构project/ ├── backend/ │ ├── app/ │ │ ├── __init__.py │ │ ├── main.py # FastAPI入口 │ │ ├── models.py # SQLAlchemy模型 │ │ ├── schemas.py # Pydantic模型 │ │ ├── crud.py # 数据库操作 │ │ ├── database.py # 数据库连接 │ │ └── routers/ # API路由 │ │ ├── __init__.py │ │ ├── auth.py # 认证路由 │ │ └── users.py # 用户路由 │ ├── requirements.txt │ ├── Dockerfile │ └── alembic/ # 数据库迁移 ├── frontend/ │ ├── src/ │ ├── package.json │ ├── vite.config.ts │ ├── Dockerfile │ └── nginx.conf # 前端Nginx配置 ├── nginx/ │ └── nginx.conf # 主Nginx配置 ├── docker-compose.yml └── .env三、FastAPI后端实现3.1 数据库连接# backend/app/database.pyfromsqlalchemyimportcreate_enginefromsqlalchemy.ext.declarativeimportdeclarative_basefromsqlalchemy.ormimportsessionmakerimportos DATABASE_URLos.getenv(DATABASE_URL,mysqlpymysql://user:passworddb:3306/app_db)enginecreate_engine(DATABASE_URL,pool_size20,max_overflow40,pool_pre_pingTrue,# 自动检测连接是否有效pool_recycle3600,# 每小时回收连接)SessionLocalsessionmaker(autocommitFalse,autoflushFalse,bindengine)Basedeclarative_base()defget_db():dbSessionLocal()try:yielddbfinally:db.close()3.2 用户模型# backend/app/models.pyfromsqlalchemyimportColumn,Integer,String,Boolean,DateTimefromsqlalchemy.sqlimportfuncfrom.databaseimportBaseclassUser(Base):__tablename__usersidColumn(Integer,primary_keyTrue,indexTrue)usernameColumn(String(50),uniqueTrue,indexTrue,nullableFalse)emailColumn(String(100),uniqueTrue,indexTrue,nullableFalse)hashed_passwordColumn(String(255),nullableFalse)is_activeColumn(Boolean,defaultTrue)is_superuserColumn(Boolean,defaultFalse)created_atColumn(DateTime(timezoneTrue),server_defaultfunc.now())updated_atColumn(DateTime(timezoneTrue),onupdatefunc.now())3.3 JWT认证# backend/app/routers/auth.pyfromdatetimeimportdatetime,timedeltafromfastapiimportAPIRouter,Depends,HTTPException,statusfromfastapi.securityimportOAuth2PasswordBearer,OAuth2PasswordRequestFormfromjoseimportJWTError,jwtfrompasslib.contextimportCryptContextfrompydanticimportBaseModel routerAPIRouter(prefix/auth,tags[认证])# 配置SECRET_KEYos.getenv(SECRET_KEY,your-secret-key-change-in-production)ALGORITHMHS256ACCESS_TOKEN_EXPIRE_MINUTES30pwd_contextCryptContext(schemes[bcrypt],deprecatedauto)oauth2_schemeOAuth2PasswordBearer(tokenUrl/api/auth/login)classToken(BaseModel):access_token:strtoken_type:strclassTokenData(BaseModel):username:str|NoneNonedefcreate_access_token(data:dict,expires_delta:timedelta|NoneNone):to_encodedata.copy()expiredatetime.utcnow()(expires_deltaortimedelta(minutes15))to_encode.update({exp:expire})returnjwt.encode(to_encode,SECRET_KEY,algorithmALGORITHM)router.post(/login,response_modelToken)asyncdeflogin(form_data:OAuth2PasswordRequestFormDepends(),db:SessionDepends(get_db)):userdb.query(User).filter(User.usernameform_data.username).first()ifnotuserornotpwd_context.verify(form_data.password,user.hashed_password):raiseHTTPException(status_codestatus.HTTP_401_UNAUTHORIZED,detail用户名或密码错误,headers{WWW-Authenticate:Bearer},)access_tokencreate_access_token(data{sub:user.username},expires_deltatimedelta(minutesACCESS_TOKEN_EXPIRE_MINUTES))return{access_token:access_token,token_type:bearer}asyncdefget_current_user(token:strDepends(oauth2_scheme),db:SessionDepends(get_db)):credentials_exceptionHTTPException(status_codestatus.HTTP_401_UNAUTHORIZED,detail无法验证凭据,headers{WWW-Authenticate:Bearer},)try:payloadjwt.decode(token,SECRET_KEY,algorithms[ALGORITHM])username:strpayload.get(sub)ifusernameisNone:raisecredentials_exceptionexceptJWTError:raisecredentials_exception userdb.query(User).filter(User.usernameusername).first()ifuserisNone:raisecredentials_exceptionreturnuser3.4 FastAPI应用入口# backend/app/main.pyfromfastapiimportFastAPIfromfastapi.middleware.corsimportCORSMiddlewarefrom.routersimportauth,users appFastAPI(title全栈应用API,version1.0.0,docs_url/api/docs,redoc_url/api/redoc,)# CORS配置app.add_middleware(CORSMiddleware,allow_origins[*],# 生产环境应限制具体域名allow_credentialsTrue,allow_methods[*],allow_headers[*],)# 注册路由app.include_router(auth.router,prefix/api)app.include_router(users.router,prefix/api)app.get(/api/health)asyncdefhealth_check():return{status:healthy,version:1.0.0}3.5 后端Dockerfile# backend/Dockerfile FROM python:3.12-slim WORKDIR /app # 安装系统依赖 RUN apt-get update apt-get install -y --no-install-recommends \ gcc \ default-libmysqlclient-dev \ rm -rf /var/lib/apt/lists/* # 安装Python依赖 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY . . # 创建非root用户 RUN useradd -m -u 1000 appuser chown -R appuser:appuser /app USER appuser # 使用uvicorn运行 CMD [uvicorn, app.main:app, --host, 0.0.0.0, --port, 8000, --workers, 4]四、Vue3前端4.1 Vite配置// frontend/vite.config.tsimport{defineConfig}fromviteimportvuefromvitejs/plugin-vueimport{resolve}frompathexportdefaultdefineConfig({plugins:[vue()],resolve:{alias:{:resolve(__dirname,src),},},server:{port:5173,proxy:{/api:{target:http://localhost:8000,changeOrigin:true,},},},build:{outDir:dist,assetsDir:assets,sourcemap:false,rollupOptions:{output:{manualChunks:{vue-vendor:[vue,vue-router,pinia],ui-vendor:[element-plus],},},},},})4.2 前端Dockerfile多阶段构建# frontend/Dockerfile # 阶段1构建 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction COPY . . RUN npm run build # 阶段2生产运行 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;]4.3 前端Nginx配置# frontend/nginx.conf server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; # Vue Router history模式 location / { try_files $uri $uri/ /index.html; } # 静态资源缓存 location /assets { expires 1y; add_header Cache-Control public, immutable; } # API代理 location /api/ { proxy_pass http://backend:8000/api/; 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; } }五、Docker Compose编排# docker-compose.ymlversion:3.8services:# MySQL数据库db:image:mysql:8.0container_name:app_dbrestart:alwaysenvironment:MYSQL_ROOT_PASSWORD:${MYSQL_ROOT_PASSWORD}MYSQL_DATABASE:${MYSQL_DATABASE}MYSQL_USER:${MYSQL_USER}MYSQL_PASSWORD:${MYSQL_PASSWORD}ports:-3306:3306volumes:-mysql_data:/var/lib/mysql-./db/init.sql:/docker-entrypoint-initdb.d/init.sqlhealthcheck:test:[CMD,mysqladmin,ping,-h,localhost]interval:10stimeout:5sretries:5networks:-app_network# FastAPI后端backend:build:context:./backenddockerfile:Dockerfilecontainer_name:app_backendrestart:alwaysenvironment:DATABASE_URL:mysqlpymysql://${MYSQL_USER}:${MYSQL_PASSWORD}db:3306/${MYSQL_DATABASE}SECRET_KEY:${SECRET_KEY}depends_on:db:condition:service_healthynetworks:-app_network# Vue3前端 Nginxfrontend:build:context:./frontenddockerfile:Dockerfilecontainer_name:app_frontendrestart:alwaysports:-80:80depends_on:-backendnetworks:-app_networkvolumes:mysql_data:driver:localnetworks:app_network:driver:bridge六、部署与运维6.1 启动服务# 构建并启动所有服务docker-composeup-d--build# 查看运行状态docker-composeps# 查看日志docker-composelogs-fbackend# 执行数据库迁移docker-composeexecbackend alembic upgradehead6.2 健康检查与监控# 健康检查端点curlhttp://localhost/api/health# 监控容器资源使用dockerstats# 查看数据库连接docker-composeexecdb mysql-uuser-p-eSHOW PROCESSLIST6.3 备份策略# 备份MySQL数据docker-composeexecdb mysqldump-uuser-papp_dbbackup_$(date%Y%m%d).sql# 备份Docker Volumedockerrun--rm-vapp_mysql_data:/data-v$(pwd):/backup alpinetarczf /backup/mysql_backup.tar.gz-C/data.6.4 生产环境优化清单启用HTTPS使用Let’s Encrypt Certbot自动获取SSL证书限流保护在Nginx中配置rate limiting日志轮转配置Docker日志驱动为json-file并设置max-size资源限制在docker-compose.yml中设置CPU和内存限制秘密管理使用Docker Secrets或外部密钥管理服务监控告警集成Prometheus Grafana七、常见问题排查问题1后端无法连接数据库# 检查数据库是否就绪docker-composeexecbackend python-c import pymysql conn pymysql.connect(hostdb, useruser, passwordpassword, databaseapp_db) print(连接成功) conn.close() 问题2前端API请求404检查Nginx的proxy_pass配置确保URL路径正确映射。特别注意proxy_pass末尾的斜杠。问题3容器内存泄漏使用docker stats监控内存使用在docker-compose.yml中设置mem_limit和mem_reservation。八、总结通过Docker Compose Nginx的部署方案我们实现了一套完整的生产级全栈应用部署架构。这套方案的核心优势在于环境一致性Docker确保开发、测试、生产环境完全一致一键部署docker-compose up -d即可启动整个应用栈服务隔离每个服务在独立容器中运行互不干扰水平扩展通过docker-compose up -d --scale backend3轻松扩展后端实例数据持久化Docker Volume确保数据在容器重启后不丢失这套方案可以直接作为新项目的部署模板只需替换业务代码和配置即可快速上线。
返回列表