本地开发环境一旦超过一个服务,就很容易从“启动应用”变成“到处找端口、连数据库、翻日志”。这时候 Docker Compose 的价值就出来了:它不是单纯帮你少敲几条 docker run,而是把应用、数据库、缓存、反向代理这些依赖放进同一份可重复执行的环境说明里。
如果只是临时启动一个 Redis,用单条 docker run 就够了。但如果是一个 Java 服务依赖 MySQL、Redis、Nginx,或者本地要跑 Kafka、Zookeeper 这种多容器组合,用 Compose 会清晰很多。
Compose 适合解决什么问题
Compose 最适合这几类场景:
- 应用服务加 MySQL、Redis、Nginx。
- Kafka 加 Zookeeper。
- 本地开发环境。
- 简单测试环境。
它的核心好处是把多个容器放在同一个编排文件里。容器之间可以直接用服务名访问,比如应用容器访问 MySQL 时可以写 mysql:3306,不需要先查容器 IP。
这点很重要。很多本地环境问题,其实不是服务没启动,而是连接地址写错了:宿主机访问容器用 localhost:映射端口,容器访问另一个容器则优先用 Compose 服务名。
常用命令先记住这几个
日常使用不需要背太多命令,先记住下面几条就够:
# 后台启动
docker compose up -d
# 查看容器状态
docker compose ps
# 查看日志
docker compose logs -f
# 停止并删除容器
docker compose down
# 重新构建并启动
docker compose up -d --build
旧版本命令是 docker-compose,新版本更推荐 docker compose。如果你在网上看到两种写法,不一定是谁写错了,多半是版本差异。
Java 服务加 MySQL 的基本结构
一个常见的本地环境是 Java 服务加 MySQL。大体结构可以这样理解:
version: "3"
services:
mysql:
image: mysql/mysql-server:5.7
environment:
MYSQL_DATABASE: demo
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: "%"
TZ: Asia/Shanghai
ports:
- "3306:3306"
volumes:
- ./db/migration:/docker-entrypoint-initdb.d
- ./mysql/data:/var/lib/mysql
restart: always
server:
build: .
working_dir: /app
environment:
TZ: Asia/Shanghai
volumes:
- ./:/app
- ~/.m2:/root/.m2
ports:
- "8080:8080"
command: mvn spring-boot:run -Dspring-boot.run.profiles=docker
depends_on:
- mysql
这份配置里,真正要理解的不是每个字段,而是几个关键点。
服务名就是容器之间的域名
在同一个 Compose 项目里,mysql 这个服务名可以当成内部域名使用。也就是说,server 容器里访问数据库时,连接地址通常写:
jdbc:mysql://mysql:3306/demo
而不是写:
jdbc:mysql://localhost:3306/demo
localhost 在容器里指的是容器自己,不是宿主机,也不是 MySQL 容器。这个概念没分清时,经常会出现“端口明明映射了,应用还是连不上数据库”的问题。
数据卷决定数据会不会丢
MySQL、Redis 这类服务要特别注意数据目录。比如:
volumes:
- ./mysql/data:/var/lib/mysql
左边是宿主机目录,右边是容器内目录。这样做的好处是容器删了,数据还能留在项目目录里。否则你每次重建容器,都可能像换了一台新机器一样,数据库内容跟着没了。
Redis 也类似:
volumes:
- ./redis/data:/data
- ./redis/redis.conf:/etc/redis/redis.conf
本地开发建议把数据和配置映射出来。这样配置改动能追踪,数据也更容易备份和清理。
初始化脚本只在空数据目录执行
MySQL 官方镜像有个常用目录:
/docker-entrypoint-initdb.d
你可以把初始化 SQL 或 shell 脚本挂进去,例如:
volumes:
- ./db/migration:/docker-entrypoint-initdb.d
但这里有个容易踩的点:初始化脚本通常只会在 /var/lib/mysql 为空时执行。也就是说,如果数据目录已经存在,哪怕你后面新增了 SQL 文件,也不会自动重新跑。
所以排查“初始化 SQL 怎么没执行”时,先看数据目录是不是旧的。开发环境可以删除本地数据目录重来,但生产或重要测试数据不要这么干。
depends_on 只保证启动顺序
depends_on 很容易被误解。它能保证 server 在 mysql 之后启动,但不保证 MySQL 已经完全 ready。
MySQL 容器启动后,还要经历初始化、建库、加载权限等过程。这个时间里应用如果立刻连接,仍然可能失败。
更稳的做法是:
- 应用侧连接池和启动流程支持重试。
- 本地排查时先看
docker compose logs -f mysql。 - 必要时加健康检查,等依赖 ready 后再启动应用。
不要把 depends_on 当成“依赖服务已经可用”的保证。
Kafka 和 Zookeeper 要注意 advertised 地址
Kafka 这类服务比 MySQL 更容易踩网络地址问题。历史上常见配置会出现:
environment:
KAFKA_ADVERTISED_HOST_NAME: 192.168.5.139
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
这里的 KAFKA_ADVERTISED_HOST_NAME 要能被客户端访问到。如果客户端在宿主机、容器内、局域网其他机器,写法可能都不一样。
所以 Kafka 连不上时,不要只看端口是否暴露,也要看它对外宣告的地址是不是客户端能访问的地址。
本地排查顺序
Compose 环境启动失败时,可以按这个顺序查:
docker compose ps看容器是否都在运行。docker compose logs -f 服务名看启动日志。- 确认端口映射有没有冲突。
- 容器之间访问用服务名,宿主机访问用映射端口。
- 确认数据卷路径是否正确。
- 确认初始化脚本是否因为旧数据目录而没有执行。
- 外网镜像或 Maven 依赖下载慢时,先解决代理和镜像源。
排查时不要急着改一堆配置。先确认是哪一层不通:容器没启动、端口没暴露、服务名写错、数据库没 ready,还是初始化数据不对。
小结
Docker Compose 搭本地开发环境,最重要的不是把 YAML 写得多复杂,而是理解几个稳定规则:
- 多容器环境用 Compose 比一堆
docker run更容易复现。 - 容器之间优先用服务名访问。
- 数据目录要用 volume 映射出来。
- MySQL 初始化脚本只在空数据目录时自动执行。
depends_on只管启动顺序,不保证服务已可连接。
把这几件事想清楚,本地 Java、MySQL、Redis、Nginx、Kafka 这类组合环境就会少很多玄学问题。




