当多个容器需要协同:用 Docker Compose 一键拉起整个应用
前面几篇我们聊了 Docker 的基础、Dockerfile 的写法、pm2 在容器里的应用,可以说已经能熟练地把单个应用打包成镜像并跑起来了。但在实际开发中,一个完整的应用往往不止一个服务——前端可能需要一个 Nginx 容器来 serve 静态文件,后端可能需要一个 Node 容器跑 API,再加上数据库 MySQL 或 Redis,甚至还有消息队列。如果每次都要手动敲 docker run 启动这几个容器,还得操心网络怎么连通、数据卷怎么挂载、启动顺序怎么保证,那可就太折腾了。
这时候就该 Docker Compose 登场了。它就是一个“一键启动全家桶”的工具,让你用一个 YAML 文件描述整个应用的服务、网络、数据卷,然后一个命令就能把所有容器拉起来。今天我们就来聊聊 Docker Compose 怎么用,以及它如何让多容器环境的管理变得轻松愉快。
Docker Compose 是什么?
Docker Compose 是 Docker 官方推出的容器编排工具,专门用来定义和运行多个容器的应用。你只需要写一个 docker-compose.yml 文件,在里面声明有哪些服务(比如 web、db、redis),每个服务用哪个镜像、暴露哪些端口、挂载哪些卷、依赖哪些其他服务,然后执行 docker-compose up,Compose 就会按照依赖顺序启动所有容器,并自动创建一个默认的网络让它们可以互相通信。
它最适合用在开发环境和测试环境,也能用于单机的生产环境(虽然生产环境更推荐用 Kubernetes 做集群编排,但 Compose 在简单场景下完全够用)。
安装与准备
如果你已经安装了 Docker Desktop(Windows/Mac),Compose 是自带的,直接在终端输入 docker-compose --version 就能看到版本。Linux 用户可能需要单独安装,但也很简单,去官网下二进制文件就行。
我们假设现在要搭建一个简单的应用:一个 Node.js 写的 Web 服务,加上一个 Redis 来做缓存。两个服务各跑一个容器,Node 服务需要把代码挂载到容器里方便开发时热更新,Redis 需要把数据持久化到宿主机。
写一个 docker-compose.yml 文件
在项目根目录新建一个文件,名字必须是 docker-compose.yml(或 .yaml)。内容如下:
version: '3.8'
services:
web:
build: . # 使用当前目录的 Dockerfile 构建镜像
ports:
- "3000:3000" # 宿主机 3000 映射到容器 3000
volumes:
- .:/app # 挂载当前目录到容器 /app,实现代码热更新
- /app/node_modules # 匿名卷,避免覆盖容器内的 node_modules
environment:
- NODE_ENV=development
- REDIS_HOST=redis # 服务名就是 hostname,Compose 会自动做 DNS 解析
depends_on:
- redis # 确保 redis 先启动
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data # 使用命名卷持久化数据
volumes:
redis_data: # 声明一个命名卷解释一下关键部分:
- version:指定 Compose 文件格式的版本,一般用最新的
'3.8'或'3'。 services:定义要启动的服务。每个服务都是一个容器。
web:我们的 Node 应用。
build: .:告诉 Compose 用当前目录下的 Dockerfile 构建镜像,而不是直接拉取现成镜像。这样适合开发时频繁修改代码。ports:端口映射,和docker run -p一样。volumes:卷挂载。这里把当前目录挂载到容器的/app,这样本地修改代码容器内立即生效,不用重新构建。同时专门加了一个/app/node_modules的匿名卷,防止本地的 node_modules(可能没有或不同平台)覆盖掉容器里安装好的依赖。environment:环境变量,可以在 Node 里通过process.env.REDIS_HOST拿到 Redis 的地址。注意这里直接写了redis,因为 Compose 会为每个服务创建一个可解析的 hostname,服务名就是 hostname。depends_on:指定依赖关系,确保redis服务先启动。
redis:缓存服务。
image:直接使用官方 Redis 镜像。volumes:使用命名卷redis_data挂载到/data,Redis 的数据就会持久化到卷里,不会因为容器删除而丢失。
- volumes:在顶层声明命名卷,这样 Compose 会帮我们创建和管理这个卷。
启动和常用命令
在项目目录下执行:
docker-compose upCompose 就会开始构建 web 镜像(如果本地没有)、拉取 redis 镜像,然后启动两个容器,并把日志输出到当前终端。如果你想在后台运行,加个 -d 参数:
docker-compose up -d其他常用命令:
docker-compose down:停止并删除所有容器、网络,但不会删除卷(除非加-v)。docker-compose logs -f:查看所有服务的日志,-f表示持续跟踪。docker-compose exec web sh:在运行中的 web 容器里执行命令(比如进入容器调试)。docker-compose ps:列出当前项目相关的容器状态。docker-compose build:只构建镜像,不启动容器。
网络是怎么工作的?
当你执行 up 时,Compose 会自动创建一个默认的网络(名字通常是 项目名_default),并把所有服务都加入到这个网络里。这样服务之间就可以通过服务名直接通信,比如 Node 代码里连接 Redis 的 host 直接写 redis 就行。完全不用手动 --link 或者配置复杂的网络。
如果你想自定义网络,也可以在 networks 顶层定义,然后在每个服务的 networks 里指定,但默认网络已经足够满足大多数需求。
与 Docker 命令行的对比
如果不使用 Compose,你需要依次执行:
docker network create myapp
docker run -d --name redis --network myapp -v redis_data:/data redis:alpine
docker build -t myapp .
docker run -d --name web --network myapp -p 3000:3000 -v .:/app -v /app/node_modules -e REDIS_HOST=redis myapp而且停止时还要一个个 docker stop、docker rm,删除卷也要单独处理。用 Compose,只需要一个 docker-compose down。当项目变大、服务变多时,这种简化的优势会更明显。
开发环境与生产环境的配置差异
通常我们会为不同环境准备不同的 Compose 文件,或者使用 extends 或 profiles。一个常见的做法是:开发环境用 docker-compose.yml,生产环境用 docker-compose.prod.yml,启动时通过 -f 指定:
docker-compose -f docker-compose.prod.yml up -d生产环境的配置可能没有代码挂载,而是使用构建好的镜像,并加上重启策略等。
Compose 的局限
Docker Compose 非常强大,但它只能管理单台 Docker 主机上的容器。如果你需要在多台服务器上部署容器、实现自动伸缩、滚动更新,就需要用到更高级的编排工具,比如 Docker Swarm(也是 Docker 原生)或 Kubernetes。不过对于中小型项目、开发测试环境,Compose 已经足够好用。
小结
Docker Compose 是 Docker 生态里的一把利器,它把多容器的管理变得像单容器一样简单。通过一个 YAML 文件,你可以清晰地描述整个应用架构,然后用几个命令控制整个生命周期。结合之前学的 Dockerfile、数据卷、环境变量等知识,现在你完全可以构建出复杂的多服务应用了。

Comments | NOTHING