【Web全栈进阶】Docker Compose:一键起全栈
早报站要真正“可交付”。现在起一套全栈要5条命令、关注启动顺序、操心环境变量——本篇之后一条docker compose upPG Redis 后端 前端全部就位。从“我机器上能跑”到“任何机器一条命令能跑”这是部署的起点也是开篇词“能上线”四件事的最后一坎。本篇产出后端/前端Dockerfile docker-compose.yml健康检查、依赖顺序、卷持久化 常用编排命令。含代码约130行。 太长不看版给想快速上手的你项目信息一句话说明本篇目标用Docker Compose一键拉起全栈代码行数~130行Dockerfile compose依赖Docker Desktop / Docker Engine Compose v2核心功能四服务编排 健康检查 依赖顺序 卷持久化跑起来的命令docker compose up -d --build核心知识点服务名即主机名、depends_on healthcheck、volume做完你能得到任何机器一条命令能跑的全栈应用一、概念课Compose在解决什么手工起全栈的痛点现在的你正在经历起PostgreSQL → 起Redis → 装依赖 → 跑迁移 → 起uvicorn → 起npm dev——6条命令顺序不能错环境变量散在三个终端里换个机器全部重来。Compose的做法把“有哪些服务、谁依赖谁、环境变量是什么、数据存哪里”写进一个compose.yaml声明式配置一条命令按依赖顺序全部拉起。这就是**服务编排orchestration**的入门——所有容器编排工具K8s是重型版解决的都是同一件事。 四个术语一次认清术语含义类比image镜像只读模板含运行时与依赖光盘container容器镜像的运行实例用光盘开的机service服务compose里定义的容器配置一个机组volume卷容器外挂的持久化目录外接硬盘 关于version字段重要version:字段已废弃。Compose v2不再需要它写了反而会收到警告the attribute version is obsolete, it will be ignored。新写法直接以services:开头本篇的compose.yaml就是正确姿势。二、第0步两个Dockerfile Dockerfile.backendFROM python:3.13-slim WORKDIR /app # pip 走国内镜像源加速非国内网络可删除这行 ENV PIP_INDEX_URLhttps://pypi.tuna.tsinghua.edu.cn/simple # 依赖先行单独一层代码改动不用重装依赖 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 后端运行所需的目录逐个拷见 .dockerignore 排除大目录 COPY alembic.ini ./ COPY app ./app COPY core ./core COPY collector ./collector COPY migrations ./migrations COPY main.py . EXPOSE 8000 CMD [uvicorn, app.main:app, --host, 0.0.0.0, --port, 8000] frontend/Dockerfile开发模式生产Nginx版后续篇章FROM node:24-alpine WORKDIR /app COPY package*.json . RUN npm install COPY . . CMD [npm, run, dev, --, --host, 0.0.0.0] 三个细节#细节说明①WORKDIR固定工作目录②依赖先COPY再装利用Docker层缓存——改代码不用重装依赖③--host 0.0.0.0让容器外可访问默认只听localhost镜像版本选择python:3.13-slim是当前稳定Python线node:24-alpine是Active LTSNode 26将在2026年10月28日进入Active LTS届时可升级。三、第1步compose.yaml——核心编排文件compose.yaml项目根注意没有version:字段services:db:image:postgres:18-alpineenvironment:POSTGRES_USER:postgresPOSTGRES_PASSWORD:postgresPOSTGRES_DB:dailyvolumes:-pgdata:/var/lib/postgresql# 数据放卷里容器删了数据还在healthcheck:# 健康检查pg_isready才算就绪test:[CMD-SHELL,pg_isready -U postgres -d daily]interval:5stimeout:5sretries:10start_period:10s# 给PG启动留缓冲避免初期误判redis:image:redis:8-alpinehealthcheck:test:[CMD,redis-cli,ping]interval:5stimeout:3sretries:5backend:build:./backendenvironment:DATABASE_URL:postgresqlpsycopg://postgres:postgresdb:5432/dailyREDIS_URL:redis://redis:6379/0# 主机名就是服务名SECRET_KEY:${SECRET_KEY:-dev-secret}DEEPSEEK_API_KEY:${DEEPSEEK_API_KEY}depends_on:db:condition:service_healthy# 等db健康再启动redis:condition:service_healthy# 等redis健康再启动ports:[8000:8000]volumes:-uploads:/app/uploads# 上传的封面图持久化frontend:build:./frontendports:[5173:5173]volumes:pgdata: 四个关键设计全是新手必踩的坑#设计为什么重要①服务名即主机名容器网络里backend访问db用db不是localhost——同一网络内的服务通过名字互相访问②depends_on healthcheck双保险只写depends_on不够它只管启动顺序不管“就绪”——db的健康检查通过后backend才启动否则后端一启动就报“连接被拒”③volume持久化pgdata卷挂在PG数据目录——docker compose down后数据还在down -v才会删第八节⑤④环境变量注入DATABASE_URL从本地开发的localhost换成db——同一份配置宿主机视角和容器视角的“地址”不一样第八节③start_period的作用给PG启动留缓冲时间在这段时间内健康检查失败不计数避免PG还没起来就被判定为“不健康”。四、第2步迁移与种子数据自动化容器启动后要跑Alembic迁移再灌种子数据。backend服务的command覆盖backend:...command:sh -c alembic upgrade head python seed.py uvicorn app.main:app --host 0.0.0.0 --port 8000启动顺序变成建表 → 种子 → 起服务。Alembic的env.py从settings读DATABASE_URL容器里注入的环境变量自动生效——迁移配置复用零改动。Compose v5.3的新选择如果你用的是较新的Composev5.3.02026年7月发布可以用**pre_start生命周期**替代“sh -c串联”——它原生支持“主容器启动前先跑一个短命容器”比用depends_on service_completed_successfully更干净。入门阶段先用sh -c串联足够知道有这个方向即可。五、第3步一键起 常用命令dockercompose up-d--build# 构建并后台启动-d不占终端dockercomposeps# 看四个服务的状态dockercompose logs-fbackend# 跟后端日志dockercompose down# 停止保留数据卷dockercompose down-v# 停止并删除数据卷⚠️ 数据没了 浏览器访问端地址说明前端http://127.0.0.1:5173页面正常加载后端APIhttp://127.0.0.1:8000/api前端调用的地址⚠️注意5173页面的API请求依然是127.0.0.1:8000——因为浏览器在宿主机上它看到的“地址”是宿主机视角只有容器内互相访问时才用服务名。两个视角别混第七节小结。六、验收清单1. 一条命令docker compose up-d--build→ 四个服务全部Running2.dockercompose ps显示db / redis / backend / frontend都在且db/redis为healthy3. 浏览器打开http://127.0.0.1:5173 → 首页正常加载、登录、收藏全通4.dockercompose down → 再docker compose up -d不带--build→ 数据还在pgdata卷生效5. 把本地已装的PG/Redis停掉全栈依然工作全部在容器里6. 换台“干净机器”或删除所有旧容器重跑第1步 → 依然全绿7. 全程无报错后提交Gitgitadd.gitcommit-mDocker Compose一键起全栈PGRedis后端前端七、常见报错这6个Compose上手的标配重点①service backend depends on db but db has no healthcheck 原因depends_on里写了condition: service_healthy但db服务没配healthcheck或格式写错。✅ 解法给db补上healthcheck第3节写法test用pg_isready最稳。② backend一启动就报could not connect to server: Connection refused 原因两个可能——①DATABASE_URL里写的是localhost容器内没有PG②depends_on没配conditionbackend比db先起。✅ 解法地址用服务名dbdepends_on加condition: service_healthy。③port is already allocated5432已被占用 原因宿主机本地也装了PostgreSQL端口冲突。✅ 解法映射改宿主侧端口5433:5432容器内不变——宿主端口冲突改冒号左边服务名不用动。④ 前端页面能开但所有API请求失败 原因前端代码里API地址写死了localhost且端口没映射 / 或vite proxy指向了容器内才存在的地址。✅ 解法浏览器视角用127.0.0.1:8000映射过即可容器视角才用服务名——两个视角的地址表写代码时贴在显示器上。⑤docker compose down -v之后数据全没了 原因-v会删除声明过的卷——PG数据、上传的图片全没。✅ 解法把down -v当“格式化硬盘”看待确认要重建才用日常一律down不带-v。⑥ 改了代码但容器里还是旧版本 原因镜像没重新构建up -d不重建或后端没挂载代码卷改了不生效。✅ 解法代码变更 →docker compose up -d --build重新构建镜像或开发期用volume挂载源码bind mount。八、课后练习#练习难度提示1补全健康检查给backend也加healthcheck⭐⭐curl -f localhost:8000/docs2.env化敏感配置把SECRET_KEY等接入.env文件⭐⭐compose自动读取且.env进gitignore3Makefile化命令写一个Makefile封装make up/make down/make logs⭐⭐⭐团队里“连docker命令都不用记”4选做bind mount开发把backend源码挂载进容器- ./backend:/app⭐⭐⭐体验“改代码即热更新”的开发模式 配套代码两个Dockerfile compose.yaml 种子脚本已上传Gitpython_daily/【gitee仓库地址】