Skip to content

Docker Compose:一条命令拉起前端 + 后端 + 数据库

更新: 8/8/2026 字数: 0 字 时长: 0 分钟

一、开篇:前端本地联调,到底难在哪?

作为有后端接口基础的前端开发者,你一定经历过这种“启动仪式”:

text
终端 1:cd frontend && npm run dev
终端 2:cd backend && npm run start
终端 3:本地装 MySQL / Postgres,改配置,建库建表
再回头:改后端的数据库连接串
最后:还要处理跨域、端口冲突、环境变量……

开三四个终端窗口,配一堆环境,稍不注意就报错。更糟的是,新同事入职、换台电脑、或者你三个月后回到这个项目,又要从头折腾一遍环境

联调的痛点其实就三个:

  • 服务多:前端、后端、数据库要一个个手动起;
  • 依赖装环境:本地要装数据库、装运行时,版本还容易不一致;
  • 难复现:“在我电脑上是好的”,换个人就崩。

Docker Compose 就是来解决这件事的:把前端、后端、数据库的启动方式写进一个配置文件,然后用一条命令全部拉起来。

一条命令拉起你的全栈环境

二、Compose 是什么?一句话理解

如果说 Dockerfile 是“如何打包一个服务”,那么 Docker Compose 就是“如何一次编排并启动多个服务”

你可以把它类比成前端的 package.json + npm scripts

一份声明式的配置文件,描述“我要哪些东西、怎么跑、谁依赖谁”,然后一条命令搞定。

区别是,package.json 管的是依赖包,Compose 管的是一整套运行中的服务

手动启动 vs Compose 一键启动

三、核心实操:含 3 个服务的 docker-compose.yml

假设我们有一个典型全栈项目,目录结构如下:

text
my-fullstack-app/
├── frontend/          # Vue / React 项目
│   └── Dockerfile
├── backend/           # Node.js / Express 接口服务
│   └── Dockerfile
└── docker-compose.yml # 编排文件(本文主角)

下面是完整可运行的 docker-compose.yml

yaml
# docker-compose.yml
services:
  # ---------- 服务一:前端 ----------
  web:
    build: ./frontend          # 用 frontend 目录下的 Dockerfile 构建
    ports:
      - "5173:5173"            # 宿主机端口:容器端口,浏览器访问 5173
    volumes:
      - ./frontend:/app        # 把本地代码挂进容器,实现热更新
      - /app/node_modules      # 匿名卷:避免本地覆盖容器内依赖
    environment:
      - VITE_API_BASE=http://localhost:8080  # 前端请求后端的地址
    depends_on:
      - api                    # 声明依赖:api 先启动

  # ---------- 服务二:后端 ----------
  api:
    build: ./backend
    ports:
      - "8080:8080"
    environment:
      # 注意:这里连数据库用的是服务名 db,不是 localhost
      - DB_HOST=db
      - DB_PORT=5432
      - DB_USER=appuser
      - DB_PASSWORD=apppass
      - DB_NAME=appdb
    depends_on:
      db:
        condition: service_healthy   # 等数据库健康后再启动

  # ---------- 服务三:数据库 ----------
  db:
    image: postgres:16-alpine   # 直接用官方镜像,无需本地安装
    environment:
      - POSTGRES_USER=appuser
      - POSTGRES_PASSWORD=apppass
      - POSTGRES_DB=appdb
    ports:
      - "5432:5432"             # 方便本地用数据库工具连接调试
    volumes:
      - db_data:/var/lib/postgresql/data  # 数据持久化,删容器也不丢
    healthcheck:                # 健康检查,供 api 的 depends_on 用
      test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
      interval: 5s
      timeout: 3s
      retries: 5

# 具名卷:由 Docker 管理,用于持久化数据库数据
volumes:
  db_data:

逐段解释(前端视角)

1. services 下三个服务:web / api / db

这就是你要的三样东西:前端、后端、数据库。每个服务名(webapidb)很重要——它同时也是容器之间互相访问的主机名

2. build vs image

  • webapibuild,表示用你项目里的 Dockerfile 现场构建(因为是你自己的代码);
  • dbimage: postgres:16-alpine,直接拉官方镜像,你不用在本机安装 Postgres

3. ports:端口映射

格式是 "宿主机端口:容器端口""5173:5173" 意味着你在浏览器访问 localhost:5173 就能打开前端。

4. environment:环境变量

这里有个关键点:后端连数据库时,DB_HOST=db,用的是服务名 db,而不是 localhost。因为在 Compose 里,各服务在同一个虚拟网络中,可以直接用服务名互相访问(下文详解)。

5. depends_on:启动顺序

保证数据库先起、后端再起。配合 healthcheck,还能做到“等数据库真正就绪”,避免后端启动太快连不上库。

启动命令:真正的“一条命令”

bash
# 在 docker-compose.yml 所在目录执行
docker compose up

想在后台运行(不占用终端):

bash
docker compose up -d

首次会构建镜像、拉取 Postgres、创建网络和卷,然后三个服务一起跑起来。停止并清理:

bash
docker compose down

四、服务之间怎么互相“看见”?网络与数据持久化

服务名即主机名,数据存进具名卷

1. 自动组网:服务名就是主机名

Compose 会自动为这一组服务创建一个虚拟网络。在这个网络里:

  • 后端连数据库:DB_HOST=db
  • 如果后端要调另一个服务,也直接用服务名。

这是新手最容易踩的坑:在容器里写 localhost 指的是容器自己,不是别的服务。所以容器间通信一定用服务名

2. 数据持久化:具名卷(volume)

数据库数据默认存在容器里,容器一删数据就没了。通过:

yaml
volumes:
  - db_data:/var/lib/postgresql/data

把数据存进 Docker 管理的具名卷 db_data,即使 docker compose down 删掉容器,数据依然保留。只有执行 docker compose down -v 才会连卷一起删。

五、前端常用拓展知识点

Compose 前端实用知识点

1. 卷挂载实现热更新(前端开发必备)

前端开发最怕“改一行代码要重新构建镜像”。用绑定挂载把本地代码映射进容器:

yaml
volumes:
  - ./frontend:/app        # 本地代码 → 容器,改代码容器内实时同步
  - /app/node_modules      # 匿名卷,保护容器内已装好的依赖不被覆盖

这样你在本地编辑器改代码,容器里的 dev server 会自动热更新。

注意第二行- /app/node_modules 是为了防止本地目录(可能没装依赖或平台不兼容)覆盖掉容器里的 node_modules。这是前端用 Compose 的高频坑点。

2. 跨域处理:两种主流思路

本地联调最烦的就是 CORS 跨域。有两种常见解法:

思路 A:后端开发环境放开 CORS

js
// backend 仅在开发环境
app.use(cors({ origin: 'http://localhost:5173' }));

思路 B:前端 dev server 配代理(推荐,更贴近生产)

以 Vite 为例:

js
// vite.config.js
export default {
  server: {
    host: '0.0.0.0',   // 容器里必须监听 0.0.0.0,否则外部访问不到
    proxy: {
      '/api': {
        target: 'http://api:8080',  // 用服务名 api
        changeOrigin: true
      }
    }
  }
}

前端请求 /api/xxx,由 dev server 转发到后端,浏览器视角没有跨域,也更接近生产环境的网关代理模式。

前端专属坑:容器里 dev server 一定要监听 0.0.0.0,只监听 localhost 会导致宿主机浏览器打不开。

3. 常用命令速查

bash
# 启动(前台,看日志)
docker compose up

# 后台启动
docker compose up -d

# 重新构建并启动(改了 Dockerfile / 依赖后用)
docker compose up -d --build

# 查看所有服务日志
docker compose logs -f

# 只看某个服务日志
docker compose logs -f api

# 查看运行状态
docker compose ps

# 停止并删除容器、网络(保留数据卷)
docker compose down

# 连数据卷一起删(慎用,数据会丢)
docker compose down -v

# 进入某个容器调试
docker compose exec api sh

4. 用 .env 管理配置

把敏感信息和易变配置抽到 .env 文件,Compose 会自动读取:

text
# .env
DB_USER=appuser
DB_PASSWORD=apppass
DB_NAME=appdb

在 yml 里引用:

yaml
environment:
  - POSTGRES_USER=${DB_USER}
  - POSTGRES_PASSWORD=${DB_PASSWORD}
  - POSTGRES_DB=${DB_NAME}

记得把 .env 加进 .gitignore,别把密码提交上去。

5. 数据库初始化脚本

想让数据库启动时自动建表、灌初始数据,可以挂载初始化脚本:

yaml
db:
  image: postgres:16-alpine
  volumes:
    - db_data:/var/lib/postgresql/data
    - ./init.sql:/docker-entrypoint-initdb.d/init.sql  # 首次启动自动执行

init.sql 会在数据库首次初始化时执行,非常适合本地联调准备测试数据。

六、常见坑与上手建议

常见坑

  1. 容器内用 localhost 连别的服务

    • 错误:DB_HOST=localhost
    • 正确:DB_HOST=db(用服务名)
  2. dev server 只监听 localhost

    • 容器里必须 host: 0.0.0.0,否则宿主机浏览器访问不了。
  3. 本地 node_modules 覆盖容器依赖

    • 记得加匿名卷 - /app/node_modules
  4. 后端启动比数据库快,连不上库

    • depends_on + healthcheck + condition: service_healthy
  5. down -v 误删数据

    • 日常停服务用 docker compose down,别随手加 -v
  6. 端口冲突

    • 本地已占用 5432/8080 时,改宿主机端口即可,如 "15432:5432"

上手建议

  • 先跑通再优化:先用最简配置把三个服务连起来,再逐步加 healthcheck、代理、初始化脚本。
  • 善用 logs -f:出问题第一时间看日志,定位是哪个服务、哪一步失败。
  • 前端优先用代理方案proxy 转发比放开 CORS 更贴近生产,减少“本地能跑上线跨域”的问题。
  • 把 compose 文件纳入版本管理:这是团队“环境即代码”的核心资产,新人 git clone + docker compose up 就能开工。

七、总结

Docker Compose 对前端开发者的价值,可以浓缩成一句话:

把“开好几个终端、装一堆环境”的联调仪式,变成一个文件 + 一条命令。

核心要点回顾:

text
1. 一个 docker-compose.yml 描述前端、后端、数据库三个服务
2. docker compose up 一键拉起,down 一键清理
3. 服务间通信用「服务名」,不是 localhost
4. 数据库数据用具名卷持久化,不怕删容器
5. 前端热更新靠卷挂载,跨域优先用 dev server 代理
6. 用 depends_on + healthcheck 控制启动顺序

掌握它之后,无论是本地全栈联调、新人环境搭建,还是给项目写一份“开箱即用”的运行说明,你都能做到克隆代码 → 一条命令 → 环境起来。这正是现代前端工程化里“环境即代码”的重要一环。