容器化 Docker 部署
本教程共 50 篇 · 第 49 篇 · 更新于 2026-08-12 · 约 7 分钟阅读
本节目标:学会写一个 Dockerfile 把 FastAPI 应用打包成容器镜像,用 docker build/run 跑起来,并理解镜像分层缓存与多进程 workers 的设置。
当接口要上线,最省心的办法是把它打包成一个「容器镜像」。容器把你的代码和依赖一起打包,跑在哪台机器行为都一致,不会出现「我本地能跑」的尴尬。Docker 是最常用的容器工具。
49-1 容器与镜像是什么
容器(主要讲 Linux 容器)是一种轻量打包方式:把应用、依赖、文件全部隔离地装在一起,共享宿主机的 Linux 内核,所以比整台虚拟机轻得多、启动快。
「镜像」是静态的打包文件(类似装好的系统盘),「容器」是镜像跑起来的实例(对应正在运行的进程)。没有进程在跑,容器就停了。一个容器通常只跑一个主进程。
用 Docker 你能方便地把多个容器(比如一个数据库、一个 Python 应用、一个前端)用内部网络连起来。Docker Hub 上还有官方 Python、PostgreSQL、Redis 等现成镜像。
49-2 准备应用代码
假设项目结构如下:
.
├── app
│ ├── __init__.py
│ └── main.py
├── requirements.txt
└── Dockerfile
app/main.py 内容:
# app/main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
requirements.txt 写依赖:
fastapi[standard]
这里要写 fastapi[standard] 而不是裸 fastapi:fastapi run 命令来自 fastapi-cli,它属于 standard 额外组件;只装裸 fastapi 时容器里没有 fastapi 命令,CMD ["fastapi", "run", ...] 会报 executable file not found。如果你改用 uvicorn app.main:app 启动,则可以只装需要的最小依赖。
Note用
uv管理项目的,可导出为 requirements 格式:uv export --format requirements-txt --no-dev --no-emit-project --output-file requirements.txt。Dockerfile 里仍用pip install安装这份导出文件。
49-3 编写 Dockerfile
在项目根目录建一个 Dockerfile(无扩展名):
FROM python:3.12-slim
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["fastapi", "run", "app/main.py", "--port", "80"]
逐行看:
FROM python:3.12-slim:以官方精简版 Python 3.12 为基础镜像,体积小、够用WORKDIR /code:设定工作目录,后面命令都在这目录执行- 先只复制
requirements.txt,再pip install:依赖文件不常变,先装依赖能充分利用缓存 COPY ./app /code/app:最后才复制业务代码,因为代码改得最频繁CMD:容器启动命令,用fastapi run以生产模式跑在 80 端口
WarningCMD 一定要用「exec 形式」的列表写法:
CMD ["fastapi", "run", ...]。不要写成CMD fastapi run ...(shell 形式)。exec 形式能让 FastAPI 优雅关闭、触发 lifespan 事件;shell 形式会拖慢容器停止。
49-4 单文件应用也行
若你的 FastAPI 就是单个 main.py、没有 app/ 目录,结构改成:
.
├── Dockerfile
├── main.py
└── requirements.txt
Dockerfile 对应调整路径:
FROM python:3.12-slim
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./main.py /code/
CMD ["fastapi", "run", "main.py", "--port", "80"]
fastapi run 能自动识别单文件并加载其中的 app 对象。
49-5 利用 Docker 缓存省时间
Docker 是分层构建的,每一步产生一层,并用缓存。关键技巧是:先把「很少变」的依赖文件复制并安装,把「经常变」的代码放最后。
因为 requirements.txt 不怎么变,Docker 能复用它那一层的缓存,连带复用 pip install 的缓存——装依赖常要几分钟,用缓存只要几秒。代码每天改,放在靠近末尾,几乎每次都重新复制,但不影响前面依赖层的缓存。
开发时你要反复 build,这个顺序能省下大量等待。
49-6 构建镜像
在含 Dockerfile 的目录执行:
docker build -t myimage .
-t myimage 给镜像起名叫 myimage,结尾的 . 表示用当前目录作为构建上下文。看到 Successfully built 就成功了。
49-7 运行容器
docker run -d --name mycontainer -p 80:80 myimage
-d后台运行--name mycontainer给容器起名-p 80:80把宿主机的 80 端口映射到容器内的 80 端口
之后访问 http://127.0.0.1/ 或 http://127.0.0.1/items/5?q=hi,能看到接口返回。/docs 也能正常打开。
Tip端口映射格式是「宿主机端口:容器端口」。容器里应用跑在 80,想从本机 8000 访问就写
-p 8000:80。
49-8 多进程 workers
容器里想开多进程,和本地一样加 --workers:
CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"]
不过要注意场景:如果你用 Kubernetes 这类集群做容器编排,通常「每个容器只跑一个 Uvicorn 进程」,由集群层面复制出多个容器来扩容。这种情况下就不要开 --workers,让集群去管复制。单台机器用 Docker Compose 部署时,才适合在容器内用 --workers 起多进程。
49-9 已弃用的官方镜像
网上可能见到老镜像 tiangolo/uvicorn-gunicorn-fastapi。它已被官方弃用,不要再用。原因是如今 Uvicorn 和 fastapi 命令自带 --workers 管理并重启 worker,没必要再叠一层 Gunicorn。直接从官方 Python 镜像手写 Dockerfile 即可,代码量差不多。
49-10 部署镜像的方式
镜像建好后,可以:
- 用 Docker Compose 在单台服务器跑
- 用 Kubernetes 集群跑
- 用 Docker Swarm、Nomad 等
- 交给云服务商(它接收你的镜像并部署)
容器方式天然帮你照顾好「开机自启」「崩溃重启」这些概念——Docker 自身的 --restart 策略或编排平台就能搞定。
还有一件必须提前想好的事:容器的文件系统是临时的,容器一删,里面写的文件就全没了。所以上传的图片、SQLite 数据库文件这类需要长期保存的数据,绝不能只存在容器内部,要用 -v 把宿主机目录挂载进去,或者干脆换成外部的数据库服务。配置也遵循同样的思路——密钥和连接串通过环境变量注入,而不是打进镜像,这样同一个镜像才能不改一行地在测试和生产环境之间搬动。
49-11 小结
用官方 python:3.12-slim 做基础,先装依赖再拷代码以利用缓存,CMD 用 exec 形式 fastapi run ... --port 80。docker build -t 名 . 构建,docker run -p 宿主:容器 运行。多进程用 --workers,但集群环境一般每容器单进程、靠集群复制。
49-12 一个最小可直接跑的清单
回顾整个流程,部署一个 FastAPI 镜像只需几步:
- 准备
app/main.py与requirements.txt - 写
Dockerfile,基于python:3.12-slim,CMD 用fastapi run app/main.py --port 80 docker build -t myimage .构建镜像docker run -d -p 80:80 myimage起容器- 访问
http://你的机器IP/验证接口
之后要加 HTTPS 和域名,就在容器前加 Nginx/Traefik(见下一章)。容器本身已经帮你解决了开机自启和崩溃重启——只要给 Docker 配好 --restart unless-stopped 或交给编排平台即可。