用 Docker 与 Compose 拉起前后端项目的步骤

前言

本地跑一个「前端静态站点 + 后端 API」的项目时,常要在本机安装 Python、Node、Nginx 等环境,版本冲突和路径问题很占时间。
Docker 把运行环境打在镜像里,Docker Compose 用一份 YAML 把多个服务一次性定义好,适合团队内对齐「同一套启动方式」。
本文不取代官方文档的深度细节,而是按「项目根下放 backendfrontenddocker」这类常见布局,拆解 Dockerfile、compose、Nginx 三件事,并给出可照抄的启动顺序。
路径请以你本机实际目录为准,若结构与下文不同,只需替换服务名、contextdockerfile 与挂载路径即可。

环境

  1. 已安装 Docker Engine(或 Docker Desktop),终端可执行 docker-compose
  2. 需要构建前端静态产物时,在宿主机装好 Node/npm 或 Bun,与容器内 nginx 挂载目录配合。
  3. Linux 上使用 GPU 时,除镜像与驱动外,还需 NVIDIA Container Toolkit,并在 Compose 里按文档增加 deploy.resources(参考示例文件末尾注释)。

拆解配置

本节对应 docker 目录里与运行直接相关的四个文件:后端 Dockerfile、前端 Dockerfile、Compose、Nginx
它们共同完成「后端跑 API、前端用 nginx 提供页面并把 /api 转到后端」。

后端镜像

镜像基于 python slim,安装 uv 作为依赖与运行时管理工具,配置国内 PyPI、工作目录与健康依赖库(如 libgomp 等),并暴露 API 端口 8000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
FROM python:3.13-slim-bookworm

ARG APT_MIRROR_HOST=mirrors.tuna.tsinghua.edu.cn
ARG PIP_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple
ARG UV_INDEX_DEFAULT=https://pypi.tuna.tsinghua.edu.cn/simple

ENV PIP_INDEX_URL=${PIP_INDEX} \
UV_DEFAULT_INDEX=${UV_INDEX_DEFAULT} \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy

RUN set -eux; \
if [ -f /etc/apt/sources.list.d/debian.sources ]; then \
sed -i \
-e "s|deb.debian.org|${APT_MIRROR_HOST}|g" \
-e "s|security.debian.org|${APT_MIRROR_HOST}|g" \
/etc/apt/sources.list.d/debian.sources; \
elif [ -f /etc/apt/sources.list ]; then \
sed -i \
-e "s|deb.debian.org|${APT_MIRROR_HOST}|g" \
-e "s|security.debian.org|${APT_MIRROR_HOST}|g" \
/etc/apt/sources.list; \
fi; \
apt-get update; \
apt-get install -y --no-install-recommends \
libglib2.0-0 \
libgomp1; \
rm -rf /var/lib/apt/lists/*; \
pip install --no-cache-dir uv

WORKDIR /app
ENV PYTHONPATH=/app

EXPOSE 8000

Compose 里没有把「启动命令」写进 Dockerfile,而是在 Compose 的 command 里执行:先 uv sync --frozen --no-dev 装依赖,再 uv run python -m uvicorn app.main:app 启动 FastAPI/Uvicorn。
这样改启动参数时不一定重建镜像。

context: .. 表示构建上下文在多级目录上一级(仓库根),dockerfile 指向 docker/docker_backend.Dockerfile,与文件实际放置方式一致。
volumes 把宿主 backend 挂到容器 /app,开发时改代码可不重建后端镜像。

前端镜像

前端镜像很轻:基于 nginx Alpine,仅保留 Web 服务能力;真正的页面来自宿主机构建出的 frontend/dist,通过只读挂载到 nginx 默认站点目录。

1
2
3
4
# syntax=docker/dockerfile:1
FROM nginx:1.27-alpine

EXPOSE 80

宿主机需在项目 frontend 下先执行 npm run build(或项目文档中的等价命令),保证 dist 目录存在且有内容。
depends_on 只做启动顺序约束,不能保证后端 API 已就绪;若需在健康检查后再导流,可为 backend 增加 healthcheck 并在前端侧配合重试。

Nginx 反代 API

/api/ 的请求转发到 Compose 网络内的服务名 backend,端口与 Uvicorn 一致为 8000;SPA 路由用 try_files 落到 index.html

nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;

client_max_body_size 100m;

location /api/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
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;
proxy_read_timeout 600s;
}

location / {
try_files $uri $uri/ /index.html;
}
}

注意

这里 proxy_pass http://backend:8000; 是后端服务的内部端口,不是对外开放的端口。

Compose配置

Compose 可使用 name 为编排命名,生成的默认网络等项目级资源会带此前缀;下面示例中用 example-web-stack,你按项目改名即可。
每个需要 build 的服务都应配置 image,否则构建出的镜像只会得到随机 project_service 形式的临时名,不方便打标签发布。
GPU 相关说明见文末注释示意,需在真实环境中自行按 Compose 文档增补 deploy 段落。

以下为与前文片段衔接的完整 docker-compose.yml 骨架(含镜像名称与版本标签)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
name: paper-training-stack

services:
backend:
image: python3_13_uv:1.0.0
restart: unless-stopped
build:
context: ..
dockerfile: docker/docker_backend.Dockerfile
command:
- sh
- -c
- uv sync --frozen --no-dev && exec uv run python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
environment:
UV_INDEX: https://download.pytorch.org/whl/cpu
volumes:
- ../backend:/app

frontend:
image: nginx1_27:1.0.0
restart: unless-stopped
build:
context: ..
dockerfile: docker/docker_frontend.Dockerfile
ports:
- "10001:80"
volumes:
- ../frontend/dist:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- backend

# GPU:在 backend 服务下增加 deploy.resources(需 NVIDIA Container Toolkit),见 Compose 文档。

注意

这里 backend 不用对外开放,所以不用设置端口映射。

构建

这里拉 moby/buildkit 拉不下来,可临时关掉 BuildKit,改用旧版构建

1
2
cd docker
docker-compose build

运行

1
2
cd docker
docker-compose up -d

如果想强制重新构建镜像

1
docker-compose up -d --build