1. 为什么FastAPI项目要折腾Docker
做FastAPI开发的朋友迟早会碰到这样一个场景:本地跑得好好的接口,部署到服务器就各种报错。我遇到过最离谱的一次,本地Python 3.10跑得欢,服务器装的是3.8,语法兼容没问题,但是某个依赖库的版本对不上,接口能启动,一调就抛异常。排了一晚上,最后发现是pydantic版本差异导致的数据校验行为不同。
这就是典型的"在我机器上好好的"问题。FastAPI本身是一个很轻量的Web框架,但它背后依赖的uvicorn、pydantic、SQLAlchemy这些库,对Python版本和系统环境格外敏感。Docker做的事情,就是把你的整个运行环境——包括Python版本、系统依赖、第三方库、配置文件——全部打包成一个标准化的容器,不管放到哪台机器上,跑起来的结果完全一致。
这一篇是《FastAPI零基础入门与进阶实战》的第26篇,主题很明确:Docker安装。但我不打算只丢给你一段"去官网下载安装包"的废话。咱们从底层把Docker这玩意儿讲明白,再把Windows、macOS、Linux三大平台的安装一步步走通,最后用一个FastAPI项目把容器跑起来。看完这一篇,你不仅能装好Docker,还能立刻上手用它部署FastAPI项目,顺带把MySQL、Redis这些开发依赖也一并容器化。
先说清楚这篇适合谁看:你是FastAPI初学者,刚写完几个接口,想在本地把环境规范起来;或者你是个全栈开发者,想用FastAPI+Vue3做前后端分离项目,需要统一团队的开发环境;又或者你纯粹是对Docker好奇,想搞明白容器到底是什么。这三种情况的读者,这篇都能让你有收获。有基础的老手可以直接跳到第3节看安装实操,新手建议从头看完,Docker的几个核心概念不搞清楚,后面遇到问题你会连报错都看不懂。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 装Docker之前,先把这几个概念嚼碎了
2.1 镜像、容器、仓库,三者到底是什么关系
Docker有三个基础概念你得先刻在脑子里:镜像(Image)、容器(Container)、仓库(Repository)。我用一个最容易理解的类比来解释。
镜像就是一个模板,相当于你电脑上装的一个"纯净版Windows系统"的Ghost镜像文件。它不可变、可复用,里面包含了你应用需要的所有东西:操作系统基础层、Python解释器、代码、依赖库、配置文件。你可以把它理解成一个"打包好的运行环境快照"。
容器是镜像的运行实例。同一个镜像可以启动多个容器,就像同一张系统安装盘可以装到多台电脑上。容器之间相互隔离,每个容器有自己的文件系统、网络、进程空间。你在容器里改任何东西,都不会影响其他容器,也不会污染宿主机。
仓库是存放镜像的地方。Docker官方的镜像仓库叫Docker Hub,你可以从上面拉取别人做好的镜像,也可以把自己的镜像推上去分享。实际开发中最常用的几个镜像,比如python、mysql、redis、nginx,Docker Hub全都有官方维护的版本。
这三个概念的关系一句话总结:从仓库拉取镜像,用镜像启动容器。
2.2 Docker引擎、Docker Desktop、命令行工具的职责划分
很多新手被Docker的安装搞晕,就是因为分不清这几个东西。
Docker引擎(Docker Engine)是真正干活的组件,它负责管理镜像、启动容器、分配网络资源。它运行在你的操作系统后台,像一个守护进程一样等待你下发指令。
Docker Desktop是可视化管理工具,同时也负责帮你把Docker引擎跑起来。Windows和macOS上,Docker不能直接运行,因为Docker引擎底层依赖Linux内核的特性,所以Docker Desktop会在你的系统里建立一个轻量级的Linux虚拟机(Windows上用的是WSL2或者Hyper-V,macOS上用的是Apple Virtualization框架),Docker引擎就跑在这个虚拟机里。这就是为什么Windows和macOS安装Docker需要开启虚拟化支持,也是热搜词里那个"virtualization support not detected"报错的根源——你的CPU虚拟化功能没开,虚拟机建不起来,Docker引擎自然没法启动。
终端里敲的docker命令则是客户端工具,它通过API和Docker引擎通信。你在命令行里输入的每个指令,都会翻译成引擎能理解的请求发送过去,引擎执行完再把结果返回给你。
搞清楚这三层关系,后面遇到任何Docker相关的报错,你都能快速定位问题出在哪一层。
3. 三大平台Docker安装完整实操
3.1 Windows安装:WSL2还是Hyper-V,怎么选
Windows装Docker Desktop,官网安装包下载下来一路下一步就行,但有两个关键选项在安装前就要想清楚。
Docker Desktop在Windows上依赖WSL2或者Hyper-V来运行Linux虚拟机。WSL2是微软官方的Windows子系统Linux,相比Hyper-V更轻量、启动更快、内存占用更小,而且能和文件系统无缝互通。个人开发强烈推荐用WSL2。Hyper-V是微软的虚拟机平台,功能更全但开销更大,适合需要同时管理多台虚拟机的场景。
判断你的电脑支持哪种方式,打开PowerShell执行:
powershell复制systeminfo | findstr "Hyper-V"
看到"Hyper-V要求: 已检测到虚拟机监控程序。将不显示Hyper-V所需的功能。"这一行,说明虚拟化已经开启。如果显示"Hyper-V要求: 固件中已启用虚拟化",但后面跟的是"是"或者"否",需要进BIOS确认一下。
BIOS里开启虚拟化,不同主板品牌叫法不一样。Intel平台叫"Intel Virtualization Technology"或者"VT-x",AMD平台叫"SVM Mode"。开机按Del或F2进BIOS,找到这个选项改成Enabled,保存重启。
安装WSL2的完整命令是,用管理员权限打开PowerShell:
powershell复制# 安装WSL2并设置默认版本
wsl --install
wsl --set-default-version 2
装完后,再安装Docker Desktop。安装包里有个选项会让你勾选"Use WSL 2 based engine",一定要勾上。装好后打开Docker Desktop,在Settings -> General里确认"Use the WSL 2 based engine"处于开启状态,然后在Settings -> Resources -> WSL Integration里把你要用的发行版(比如Ubuntu)的开关打开。
注意:装完Docker Desktop后,如果双击图标没反应,或者一直卡在"Docker Desktop starting...",90%的可能是WSL2没装好或者虚拟化没开。先跑一下
wsl --status看看WSL2是否正常,再用wsl --update更新到最新版。
3.2 macOS安装:Intel芯片和Apple Silicon要区分开
macOS装Docker Desktop相对省心,但有一个坑必须提醒你:一定要去Docker官网下载对应你芯片架构的版本。
Apple Silicon(M1/M2/M3)的Mac,下载文件名里带"Apple Silicon"字样的安装包;Intel芯片的Mac,下载文件名里带"Intel Chip"字样的。下载错了装不上,或者装上了跑起来性能极差。
下载完把Docker.app拖到Applications文件夹,双击启动。首次启动会要求你授权,输入系统密码即可。Docker Desktop会自己处理虚拟化的事情,不需要你手动开什么功能。
验证是否装好,打开终端执行:
bash复制docker --version
docker compose version
如果能正常输出版本号,说明安装成功。Docker Desktop菜单栏的鲸鱼图标变绿,说明引擎已经启动,可以开始拉镜像了。
macOS上有一个经常被忽略的配置:Docker Desktop的资源限制。默认设置下,Docker会占用你Mac的大量内存和CPU。打开Docker Desktop的Settings -> Resources,把内存调到4GB左右就够开发用了,CPU分配2-4核。设太高反而会影响你本机的流畅度,尤其是用Docker跑FastAPI项目的同时还在跑PyCharm和浏览器的时候。
3.3 Linux安装:Ubuntu/CentOS服务器部署的必备技能
服务器上装Docker,主要是为了部署FastAPI项目或者搭建MySQL、Redis这类中间件。Linux装Docker有两条路:用官方脚本一键安装,或者手动配置软件源安装。
用官方脚本安装最简单:
bash复制curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
这个脚本会自动检测你的Linux发行版,配置软件源,安装Docker引擎。装完后执行:
bash复制sudo systemctl enable docker
sudo systemctl start docker
每次敲docker命令都要加sudo很烦,把当前用户加入docker用户组,重新登录后就可以直接敲docker了:
bash复制sudo usermod -aG docker $USER
注意:把用户加入docker组有安全风险,相当于给了这个用户root级别的权限。单机开发环境无所谓,生产环境不建议这么干。
手动安装的话,Ubuntu用apt,CentOS用yum,核心步骤是先添加Docker官方GPG密钥和软件源,再安装docker-ce。道理和装其他软件一样,只不过软件源多了一些步骤。个人建议第一次装别折腾手动安装,直接用官方脚本最稳,成功率最高。
国内服务器如果拉镜像慢,需要配置镜像加速器。编辑/etc/docker/daemon.json文件,填入镜像源地址,重启Docker生效:
json复制{
"registry-mirrors": ["https://docker.m.daocloud.io"]
}
bash复制sudo systemctl daemon-reload
sudo systemctl restart docker
4. 安装后的验证与基础配置
4.1 验证Docker是否装好的三条命令
装好Docker之后,很多人直接开始拉镜像,结果跑不起来一头雾水。我建议你先花一分钟,按顺序执行下面三条命令,确认环境是健康的。
bash复制docker version
这条命令会输出Client和Server两个部分的版本信息。如果你看到Server那一栏有"ERROR"或者"permission denied",说明Docker引擎没启动,或者当前用户没有权限访问引擎。正常情况应该能看到两栏完整的版本信息。
bash复制docker info
这条命令输出的是Docker引擎的详细信息,包括容器数量、镜像数量、存储驱动、系统类型等。重点看最后有没有"ERROR"字样。
bash复制docker run hello-world
这条命令是Docker官方的"hello world"测试。它会自动从Docker Hub拉取一个名叫hello-world的测试镜像,然后启动一个容器,容器运行完就退出。如果屏幕上打印出"Hello from Docker!",说明你的Docker引擎完整跑通了拉取镜像、创建容器、运行容器的整个链路。
4.2 针对FastAPI开发的镜像源配置建议
装好Docker后,第一件事就是配置镜像加速器。这一步的直接收益就是拉取python、mysql这些镜像的速度从每分钟几百KB变成每秒几十MB。
Windows和macOS在Docker Desktop的Settings -> Docker Engine里修改,Linux直接编辑/etc/docker/daemon.json。推荐配置的镜像源:
json复制{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}
配置完点了Apply & Restart,再用docker info查看Registry Mirrors一栏是否生效。
还有一个对FastAPI开发很实用的技巧:区分开发环境和生产环境的依赖。写Dockerfile的时候,我们通常会让开发环境的镜像装完整依赖,包括pytest、httpx这些测试库;生产环境的镜像只装运行依赖。这就需要把requirements.txt拆成两个文件,或者在requirements.txt里用注释区分。后面第5节我会展示完整写法。
5. 用Docker跑起一个FastAPI项目
5.1 Dockerfile的核心写法与每行指令的用意
环境装好了,概念也理清了,现在开始实战。假设你有一个最简单的FastAPI项目,目录结构是这样的:
code复制fastapi-project/
├── app/
│ ├── __init__.py
│ └── main.py
├── requirements.txt
└── Dockerfile
main.py内容:
python复制from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI(title="My FastAPI App")
@app.get("/")
async def root():
return JSONResponse({"message": "Hello from Docker"})
@app.get("/health")
async def health():
return JSONResponse({"status": "healthy"})
Dockerfile内容:
dockerfile复制FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
逐行拆解一下。
FROM python:3.11-slim是基础镜像。python:3.11-slim是基于Debian的精简版Python镜像,比完整版python:3.11体积小很多,从几百MB压到一百多MB。为什么用slim版?因为FastAPI项目在容器里只需要Python运行时和依赖库,不需要编译器、构建工具这些"笨重"的东西。如果依赖里有需要编译的包(比如某些C扩展),再用完整版镜像,否则slim版完全够用。
WORKDIR /app设置工作目录,后面所有命令都会在这个目录下执行。这个路径没有硬性规定,看你习惯,/app是社区最常用的约定。
COPY requirements.txt .先把依赖文件复制进去。这里有个很重要的优化:先复制依赖文件、安装依赖,再复制项目代码,是为了利用Docker的层缓存机制。Docker构建镜像是一层一层叠加的,每一行指令都会生成一个缓存层。如果先复制整个项目再安装依赖,那么你每次修改代码,依赖安装这步都要重新执行。先装依赖,只要requirements.txt不变,后续构建都会直接命中缓存,构建速度快很多。
RUN pip install --no-cache-dir -r requirements.txt安装Python依赖。--no-cache-dir参数告诉pip不要缓存下载的安装包,省掉那部分镜像体积。
COPY . .把项目代码复制进镜像。
EXPOSE 8000声明容器对外监听的端口。注意这只是一个声明,不代表真的会把端口暴露出去。真正要访问容器里的服务,还得靠后面说的端口映射。
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]是容器启动时执行的命令。uvicorn是FastAPI的ASGI服务器,--host 0.0.0.0表示监听所有网络接口,这样容器外才能访问到。如果你只写--host 127.0.0.1,那容器外部永远连不上,这是很多新手踩的坑。
构建并运行:
bash复制docker build -t fastapi-demo:latest .
docker run -d --name fastapi-demo -p 8000:8000 fastapi-demo:latest
浏览器访问http://localhost:8000/health,看到{"status":"healthy"}说明跑通了。
5.2 多容器编排:FastAPI + MySQL + Redis的docker-compose写法
实际开发中FastAPI项目很少单打独斗,一般都要挂数据库和缓存。热搜词里就有"docker安装mysql8.0并使用"和"docker安装redis主从",说明这是大家普遍的需求。
手动用docker run一个个启动MySQL、Redis、FastAPI太痛苦了,容器间的网络通信配置也更复杂。docker-compose就是来干这个的,用一个YAML文件统一描述所有服务,一条命令全部启停。
在项目根目录新建docker-compose.yml:
yaml复制version: "3.8"
services:
db:
image: mysql:8.0
container_name: fastapi-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: root123456
MYSQL_DATABASE: fastapi_demo
MYSQL_USER: fastapi_user
MYSQL_PASSWORD: fastapi_pass123
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:7-alpine
container_name: fastapi-redis
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
app:
build: .
container_name: fastapi-app
restart: always
ports:
- "8000:8000"
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
environment:
DATABASE_URL: mysql+pymysql://fastapi_user:fastapi_pass123@db:3306/fastapi_demo
REDIS_URL: redis://redis:6379/0
volumes:
mysql_data:
redis_data:
这里有几个细节要说透。
MySQL和Redis的data用volumes做了持久化。容器删除后,数据还存在宿主机上,下次重新创建容器数据不丢。不挂volumes的话,容器一删数据全没了,生产环境这是灾难。
environment里配置的数据库连接串,主机名写的是db和redis,不是localhost。这是docker-compose的一个核心特性:同一个compose文件里的所有服务会自动加入同一个网络,服务之间通过服务名互相访问。你的FastAPI代码里连接数据库,主机名就写db,连接Redis就写redis。这个连接方式跟你本机的localhost完全隔离,所以本地开发时如果你的FastAPI同时要连本机MySQL又要连容器MySQL,端口冲突问题要注意。最简单的方式是开发时数据库全走容器,本机只装客户端工具连接。
健康检查机制很关键。depends_on里db加了condition: service_healthy,意思是app服务会在db容器健康检查通过后才启动。为什么需要这个?因为MySQL容器启动到真正能接受连接是有延迟的,如果app容器先启动,它去连数据库就会报"Connection refused"。MySQL容器自己倒是没挂,但app起不来,服务之间就形成了启动顺序依赖。健康检查就是为了解决这个问题。redis的healthcheck我没写,用service_started就行,Redis启动速度极快,几乎不存在连不上的问题。
启动命令:
bash复制docker-compose up -d
停止并删除所有容器:
bash复制docker-compose down
注意,down不会删除volumes挂载的数据。真想连数据一起删干净加-v参数:
bash复制docker-compose down -v
提醒:本机如果装了MySQL或者Redis并占用了3306/6379端口,启动compose会报端口占用。要么停掉本机服务,要么改掉compose里的端口映射,比如把MySQL映射到
"3307:3306"。实际开发中很多人习惯让容器的MySQL跑3306端口,因为代码里写3307总感觉不舒服。个人建议是本地开发就用容器MySQL,彻底停掉本机的MySQL服务,省心很多。
5.3 用Docker加速FastAPI本地开发:挂载代码目录实现热更新
Docker部署FastAPI有一个痛点:每次改代码都要重新构建镜像,开发体验很差。解决办法是使用bind mount,把宿主机上的代码目录直接挂载到容器里,代码一改容器里的进程就能感知到。
开发用的docker-compose.dev.yml:
yaml复制version: "3.8"
services:
app:
build: .
container_name: fastapi-app-dev
ports:
- "8000:8000"
volumes:
- .:/app
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
environment:
DATABASE_URL: mysql+pymysql://fastapi_user:fastapi_pass123@db:3306/fastapi_demo
REDIS_URL: redis://redis:6379/0
db:
image: mysql:8.0
container_name: fastapi-mysql-dev
restart: always
environment:
MYSQL_ROOT_PASSWORD: root123456
MYSQL_DATABASE: fastapi_demo
MYSQL_USER: fastapi_user
MYSQL_PASSWORD: fastapi_pass123
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:7-alpine
container_name: fastapi-redis-dev
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
volumes:
mysql_data:
redis_data:
关键差异在volumes配置里的.:/app。宿主机当前目录(也就是你的项目代码目录)挂载到容器的/app目录,容器里的文件就是你宿主机上的文件,改动实时同步。配合uvicorn的--reload参数,保存代码后FastAPI会自动重启,开发效率直接起飞。
这样跑起来的福利是,宿主机上你不用装Python、不用装依赖,甚至连虚拟环境都不用建。PyCharm只需要保留编辑器功能,运行调试全部交给Docker。对于团队协作,新人克隆代码后一条docker-compose up -d就能把整套环境拉起来,不用再经历痛苦的"环境配置一夜"。
6. 高频报错排查实录
6.1 Docker Desktop启动失败全家桶
热搜词里"virtualization support not detected docker desktop failed to start because v"这个报错,是最常见也最容易解决的。
这个报错的完整提示通常是:"Docker Desktop failed to start because virtualization support is not detected. Please ensure virtualization is enabled in BIOS."意思是虚拟化支持没检测到。解决方法就三步:
- 重启电脑,进BIOS,找到Intel VT-x或AMD SVM选项,设置为Enabled。
- 如果BIOS里已经开启了,检查Windows功能里Hyper-V和"虚拟机平台"是否启用。控制面板 -> 程序 -> 启用或关闭Windows功能,勾选"Hyper-V"和"虚拟机平台",重启。
- 在"启用或关闭Windows功能"里,如果WSL相关的选项没勾上,也会导致Docker Desktop无法启动。确保"适用于Linux的Windows子系统"这一项被勾上。
还有一个经常被忽略的坑:Windows的"内核隔离"和"基于虚拟化的安全"功能会占用虚拟化能力,导致Docker Desktop起不来。Windows安全中心 -> 设备安全性 -> 内核隔离,把"内存完整性"关掉试试。
Docker Desktop卡在"Docker is starting"界面不动了,多半是WSL2的问题。用管理员权限打开PowerShell执行:
powershell复制wsl --shutdown
wsl --update
然后重启Docker Desktop。如果还是不行,把WSL发行版重新注册一下:
powershell复制wsl --unregister docker-desktop
注意这个操作会把Docker Desktop的Linux虚拟机数据清掉,已存在的容器都会消失,但镜像和volumes会保留。
6.2 容器运行时的常见报错
报错:docker: Error response from daemon: driver failed programming external connectivity on endpoint
这个报错说是"驱动程序无法编程外部连接",本质是端口冲突。有另一个进程占用了你要映射的端口,或者之前有一个同名的容器占用了这个端口。排查命令:docker ps -a看看有没有同名的旧容器,删掉即可;再netstat -ano | findstr 8000(Windows)或者lsof -i:8000(macOS/Linux)看看端口被谁占着。
报错:ModuleNotFoundError: No module named 'fastapi'
容器启动后报找不到fastapi模块,说明依赖没装好。检查三步:requirements.txt里有没有fastapi和uvicorn;Dockerfile里的COPY requirements.txt .和RUN pip install这两行是否在COPY . .之前执行;镜像里装的Python版本和你项目代码的Python版本是否兼容。FastAPI要求Python 3.8以上,如果你的基础镜像还是python:3.6,装不上fastapi或者说装上了也有兼容问题。
报错:dial tcp 127.0.0.1:3306: connect: connection refused
FastAPI容器连接数据库失败。先说结论:容器内部不能用localhost连数据库。在docker-compose环境里,fastapi代码里连接MySQL的主机名要写db,连接Redis的主机名要写redis。如果你是在容器里手动跑代码,比如通过docker exec -it fastapi-app bash进到容器里想调试,那也表示localhost指向的是容器自己,连不到宿主机上的服务。宿主机和容器之间的网络是隔离的。
报错:The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8)
Apple Silicon的Mac上拉取了一个只提供amd64架构的镜像,会触发这个报错。处理方式是在docker-compose.yml或docker run里指定platform参数:
yaml复制services:
app:
build: .
platform: linux/amd64
但这个只是权宜之计,amd64架构的镜像在arm64机器上通过模拟运行,性能会打折扣,而且某些依赖可能直接跑不起来。最好的方案是找这个镜像有没有提供arm64版本,或者自己构建一个。
6.3 镜像拉取失败和构建慢的排查思路
镜像拉取一直超时,大概率是网络问题。配置镜像加速器是第一步(第4.2节有写),配置完还是不行,可以试试临时换一个镜像源。Docker Hub本身在国内的连通性不稳定,有时候换个源就能解决。
镜像构建慢,要从两个方向排查。一是基础镜像太大。python:3.11完整版接近1GB,slim版只有150MB左右,alpine版更小。如果项目依赖没有编译需求,优先用slim。二是依赖安装重复执行。前面说过,先COPY requirements再装依赖,利用层缓存。如果你发现自己每次构建都重新装一遍依赖,检查一下Dockerfile里COPY的路径是不是写得太宽了,比如COPY . /app把代码整个复制进去了,那只要改了任何一个文件,后面的缓存全部失效。
实际项目里还有个经验:requirements.txt里的大版本号要锁死。如果某个依赖的版本写的是fastapi>=0.68,<1.0这种范围,今天构建镜像装的是0.68,一个月后再构建可能就装了0.99,接口行为可能就变了。建议把requirements.txt固定到精确版本号:fastapi==0.104.1。
7. 从开发环境到生产部署的平滑切换
Docker装好、FastAPI项目能跑起来,这只是开始。真正有挑战的是把开发环境的容器配置平滑迁移到生产环境。
开发环境和生产环境的区别主要有三点:代码变更方式、依赖安装策略、进程管理方式。
开发环境用bind mount实现代码热更新,生产环境应该把代码打进镜像里,不挂载宿主机目录。生产环境的Dockerfile构建完,镜像里的代码就是不可变的,部署时用这个镜像启动容器,代码是固定的,不会因为宿主机上代码被改动而跟着变。
开发环境用uvicorn --reload,生产环境要用多worker方式跑。uvicorn支持--workers参数,这个参数在机制上其实是通过multiprocessing启动多个进程来提升并发处理能力。实际使用的命令是:
bash复制uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
Worker数量一般建议等于CPU核心数乘以2加1。不过注意,如果你的应用内部用了全局状态或者内存缓存,多worker模式会有问题——每个worker进程是独立的,一个worker里塞进缓存的数据,另一个worker读不到。有这种需求就得引入Redis做共享缓存。
生产环境还建议加一层反向代理。虽然FastAPI的uvicorn本身能直接对外服务,但实际部署时前面一般要挂Nginx,负责静态文件、TLS证书、负载均衡。docker-compose生产配置里加一个nginx服务:
yaml复制 nginx:
image: nginx:alpine
container_name: fastapi-nginx
restart: always
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
nginx.conf里把请求转发到app容器:
nginx复制upstream fastapi_app {
server app:8000;
}
server {
listen 80;
server_name _;
location / {
proxy_pass http://fastapi_app;
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;
}
}
这样一来,你对外暴露的入口就是Nginx的80端口,FastAPI服务的8000端口只在Docker内部网络里开放,宿主机都不用暴露8000端口。
注意:
proxy_set_header头信息必须配全,特别是X-Forwarded-For和X-Forwarded-Proto。FastAPI在需要获取客户端真实IP或者处理HTTPS跳转逻辑时,依赖这两个头。不配置的话,即使服务能跑,日志和鉴权逻辑都可能拿到错误的客户端信息。
8. 写在最后的几点实操心得
装Docker这件事,看起来是"下载安装包、双击、下一步"这么简单,但实际上踩坑的人不少。我自己在Windows和服务器上装Docker的次数没有二十次也有十五次了,有几点体会想分享给各位。
第一,Windows平台装Docker,优先确保WSL2链路完整。很多报错表面上看是Docker Desktop的问题,根子全在WSL2没配置好。先用wsl --status确认WSL2已经启用,再装Docker Desktop,能省掉80%的麻烦。
第二,镜像加速器一定要配。国内网络环境下,不配加速器,拉取一个python镜像都可能要十分钟,配上之后十几秒就搞定。这个投入产出比太高了,别偷懒。
第三,开发环境一定要用bind mount加--reload。没有热更新,Docker开发等同于自虐。但生产环境不要用,这个切换要熟练。
第四,那些热门搜索里提到的"python使用uv包管理器创建虚拟环境与fastapi",和Docker并不矛盾。本地写代码时用uv或者venv管理虚拟环境很快;部署时用Docker打包环境,两条路可以结合使用。很多人的工作流是:本地用uv快速建虚拟环境跑开发,提交代码后用Docker做标准化部署。
第五,学会看Docker日志。容器起不来,第一件事不是瞎猜,而是docker logs 容器名看日志输出。FastAPI的报错信息已经写得很明确了,95%的问题看一眼日志就能定位。
这一篇的内容到这里就完整了。从Docker的核心概念讲到三大平台的安装实操,再从FastAPI项目的Dockerfile讲到docker-compose多容器编排,最后附上高频报错排查思路。跟着操作一遍,你就能把本地的FastAPI开发环境全部迁到Docker里。下一篇文章的实操,我们可以基于这套容器环境,把FastAPI和Vue3的前后端分离项目完整跑起来。容器环境永远是统一的,你的代码才能做到"到处跑"。
