当前位置: 首页 > news >正文

Docker Compose 深度剖析:一文打尽所有配置信息

你以为docker-compose up -d只是启动几个容器?大错特错。真正读懂docker-compose.yml的每一层含义,你才能在生产环境里游刃有余——网络到底怎么隔离、依赖为什么有时失效、卷挂载有哪些坑、环境变量如何优雅替换。这篇文章以一个 Java Spring Boot 应用(JAR 包) + PostgreSQL 数据库的真实场景为蓝本,逐行解析docker-compose.yml的所有核心字段,让你从“会抄”变成“会写”。

一、Compose 是什么?为什么值得深度剖析?

Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。用一个 YAML 文件配置所有服务、网络、卷,然后一行命令就能启动整个应用栈。但大多数人对它的理解停留在“照着模板改”,一旦遇到:

  • 容器启动顺序错乱导致应用连不上数据库

  • 环境变量没生效

  • 数据卷权限问题

  • 生产环境和开发环境配置混乱

就束手无策。本文从一个Java Jar 应用 + 远程 PostgreSQL 数据库的场景出发,把docker-compose.yml的每一个角落都翻出来讲透。

二、场景案例:一个典型的微服务雏形

假设我们有一个Spring Boot 应用,打包成app.jar,它需要连接一个PostgreSQL 数据库(通过 Docker 运行,也可以理解为“拉取远程 PG 镜像”)。我们的目标是:

  1. 用 Dockerfile 构建 Java 应用的镜像。

  2. docker-compose.yml编排 Java 容器和 PostgreSQL 容器。

  3. 确保 Java 容器能通过服务名postgres访问数据库。

  4. 持久化 PostgreSQL 的数据,避免重启丢失。

三、从零构建 Java 应用镜像(Dockerfile)

在项目根目录创建Dockerfile

dockerfile

# 基础镜像:使用 OpenJDK 17 精简版 FROM openjdk:17-jdk-slim # 维护者信息 LABEL maintainer="yourname@example.com" LABEL description="Spring Boot App for Compose Demo" # 设置工作目录 WORKDIR /app # 将宿主机的 JAR 包复制到镜像内(构建时复制) COPY target/app.jar /app/app.jar # 创建非 root 用户运行(安全实践) RUN addgroup --system javauser && \ adduser --system --no-create-home --ingroup javauser javauser && \ chown -R javauser:javauser /app USER javauser # 暴露应用端口(根据你的 Spring Boot 配置,比如 8080) EXPOSE 8080 # 健康检查(可选,用于 Compose 的 depends_on 条件) HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/actuator/health || exit 1 # 启动命令 ENTRYPOINT ["java", "-jar", "/app/app.jar"]

关键点解释

  • COPY target/app.jar假设你在构建镜像前已经通过 Maven/Gradle 打包好 JAR 到target/目录。

  • 创建非 root 用户是安全基线,防止容器内进程提权。

  • HEALTHCHECK让 Compose 能检测应用是否真正就绪(而不仅仅是容器启动)。

四、docker-compose.yml 完整解剖(带逐行注释)

创建docker-compose.yml

yaml

# 1. 版本声明:决定支持的 Compose 特性,3.8 是目前主流 version: '3.8' # 2. 定义所有服务(容器) services: # ---------- PostgreSQL 服务 ---------- postgres: # 拉取远程镜像(相当于从 Docker Hub 拉取 postgres:15-alpine) image: postgres:15-alpine # 容器名称(方便命令行操作) container_name: my-postgres # 重启策略:除非手动停止,否则总是重启 restart: unless-stopped # 环境变量:设置数据库初始配置 environment: POSTGRES_DB: myappdb # 自动创建的数据库名 POSTGRES_USER: myuser POSTGRES_PASSWORD: secretpass PGDATA: /var/lib/postgresql/data/pgdata # 数据目录(可自定义) # 卷挂载:持久化数据库文件 volumes: - postgres_data:/var/lib/postgresql/data # 可选:初始化脚本挂载(首次启动自动执行) - ./init-scripts:/docker-entrypoint-initdb.d # 端口映射:宿主机端口:容器端口(可选,不映射也可供其他容器访问) ports: - "5432:5432" # 网络:自定义网络,便于服务发现 networks: - app-network # 健康检查:确认数据库真正可用 healthcheck: test: ["CMD-SHELL", "pg_isready -U myuser -d myappdb"] interval: 10s timeout: 5s retries: 5 start_period: 10s # ---------- Java Spring Boot 应用 ---------- java-app: # 构建上下文:当前目录(即 Dockerfile 所在位置) build: . # 也可以直接用 image: my-java-app:latest,但用 build 更灵活 container_name: my-java-app restart: unless-stopped # 依赖声明:等待 postgres 服务“启动”(不是就绪) depends_on: - postgres # 但 depends_on 默认只等容器状态为 running,不关心健康。 # 使用 condition 可以等待健康检查通过(Compose 3.8+ 实验特性,需启用) # 实际生产更推荐用 wait-for-it 脚本,此处演示 condition 语法: depends_on: postgres: condition: service_healthy # 环境变量:传递给 Spring Boot 应用 environment: SPRING_PROFILES_ACTIVE: prod # 关键:数据库连接 URL 使用服务名 postgres(因为同网络) SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/myappdb SPRING_DATASOURCE_USERNAME: myuser SPRING_DATASOURCE_PASSWORD: secretpass # JVM 内存限制(容器内生效) JAVA_OPTS: "-Xmx256m -Xms128m" # 端口映射:将应用暴露到宿主机 ports: - "8080:8080" # 卷挂载:挂载日志或配置文件(可选) volumes: - ./logs:/app/logs # 如果不想每次重新构建镜像就更新 jar,可以挂载 jar(开发时有用) # - ./target/app.jar:/app/app.jar # 网络:与 postgres 同一个网络 networks: - app-network # 资源限制(防止容器耗尽宿主机资源) deploy: resources: limits: cpus: '0.5' memory: 512M reservations: memory: 256M # 3. 定义网络(方便服务间使用服务名通信) networks: app-network: driver: bridge # 如果希望使用外部已有网络,取消下面的注释 # external: true # name: my-custom-network # 4. 定义卷(持久化数据) volumes: postgres_data: # 可以指定卷驱动和选项,默认 local driver: local

五、深度拆解:每个字段为什么这么写?

5.1version—— 选对版本很重要

  • '3.8'是目前广泛支持的稳定版,支持depends_onconditiondeploy资源限制等。

  • 如果你用旧版2.x,则没有deploy,且depends_on不支持健康等待。

  • 建议:新项目一律3.8或更高(3.93.10差别不大)。

5.2services—— 心脏地带

imagevsbuild
  • image: postgres:15-alpine直接从仓库拉取。

  • build: .会从当前目录的 Dockerfile 构建镜像。如果同时指定image,则构建后还会打上该镜像名。

container_name—— 固定容器名
  • 不指定则自动生成项目名_服务名_序号。固定名称方便docker exec -it my-java-app bash

restart: unless-stopped
  • no:不自动重启

  • always:总是重启(包括守护进程重启)

  • on-failure:仅非正常退出时重启

  • unless-stopped:除了手动停止外都重启 —— 生产推荐

environment—— 注入环境变量

两种写法:

yaml

environment: - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/myappdb - JAVA_OPTS=-Xmx256m

或者字典格式。注意变量值不要加引号除非包含特殊字符。

连接数据库的关键jdbc:postgresql://postgres:5432/myappdb—— 其中postgres是服务名,因为两个容器在同一个自定义网络app-network中,Docker 内部 DNS 会解析服务名为容器 IP。

depends_on—— 启动顺序的误解与真相

很多人以为depends_on会等待数据库完全就绪再启动 Java 应用。错!默认情况下,它只等待依赖容器的状态变为running(即进程启动),不代表数据库可以接受连接。

解决方案:

  • 条件等待(Compose 3.8+ 需开启COMPOSE_EXPERIMENTAL=1):

    yaml

    depends_on: postgres: condition: service_healthy

    这需要 postgres 服务定义了healthcheck

  • 更通用的方法:在应用启动脚本中轮询数据库(如使用wait-for-it.sh)。示例:

    dockerfile

    COPY wait-for-it.sh /wait-for-it.sh CMD ["/wait-for-it.sh", "postgres:5432", "--", "java", "-jar", "app.jar"]
ports—— 端口映射
  • "8080:8080"宿主机端口:容器端口。

  • 如果只写"8080",则宿主机随机端口映射。

  • 多端口可以列表形式。

volumes—— 数据持久化与共享
  • 命名卷(如postgres_data):由 Docker 管理,路径在/var/lib/docker/volumes/,适合数据库等需要持久化的场景。

  • 绑定挂载(如./logs:/app/logs):直接挂载宿主机相对路径,适合开发热加载或输出日志。

  • 注意权限:容器内进程的 UID 可能不是宿主机的用户,导致无法写入。可以用:Z:z处理 SELinux,或者指定user参数。

networks—— 容器通信的桥梁
  • 自定义网络app-network,让两个容器通过服务名互相访问。

  • 如果不定义网络,Compose 会默认创建一个default网络,服务名也可以互通,但自定义网络可以更精细控制(如设置 IPAM、限制外部访问)。

  • 如果不需要对外暴露数据库端口,可以不写ports,只让 Java 容器内部访问,提高安全性。

deploy—— 资源限制(仅用于 swarm 模式?)
  • docker-compose up模式下,deploy字段被忽略。它只在docker stack deploy(Swarm 模式)下生效。

  • 如果要在单机 Compose 中限制资源,使用:

    yaml

    cpus: '0.5' mem_limit: 512m mem_reservation: 256m

    注意mem_limit是旧版写法,新版建议用deploy.resources但仅在 swarm 有效。坑点:很多人搞混。单机模式使用cpusmem_limit需要配合--compatibility或使用docker-compose 1.29+--compatibility标志。最佳实践:生产环境用 Swarm 或 K8s,单机开发暂不限制。

5.3networksvolumes的顶层定义

  • 网络:app-network可以被多个服务引用。driver: bridge是默认单机桥接网络。

  • 卷:postgres_data如果不指定driver,默认local。可以通过driver_opts配置 NFS 等。

六、常用命令与调试技巧

bash

# 构建并启动所有服务(-d 后台运行) docker-compose up -d # 只构建不启动 docker-compose build # 只拉取镜像(postgres 等) docker-compose pull # 查看日志(-f 实时跟踪) docker-compose logs -f java-app # 进入容器调试 docker-compose exec java-app sh # 在 Java 容器内测试数据库连通性 docker-compose exec java-app curl http://postgres:5432 # 不应直接访问,仅测试网络 # 停止并删除所有容器、网络(但保留卷) docker-compose down # 停止并删除卷(数据丢失) docker-compose down -v # 重启单个服务 docker-compose restart java-app # 查看服务状态 docker-compose ps

七、生产环境必加的配置项

7.1 环境变量文件(.env

把敏感信息移到.env文件,避免写死在 YAML 中。

.env内容:

text

DB_PASSWORD=secretpass DB_USER=myuser

docker-compose.yml中引用:

yaml

environment: POSTGRES_PASSWORD: ${DB_PASSWORD} SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}

7.2 日志驱动(避免磁盘爆炸)

yaml

logging: driver: "json-file" options: max-size: "10m" max-file: "3"

7.3 时区设置

yaml

environment: TZ: Asia/Shanghai volumes: - /etc/localtime:/etc/localtime:ro

7.4 健康检查的 start_period

  • start_period: 10s给予数据库启动的宽限期,期间健康检查失败不计入重试。

八、排坑指南(血泪经验)

坑1:Java 应用启动比数据库快导致连接失败
解决:使用wait-for-it.sh或 Spring Boot 的数据库重连机制(spring.datasource.hikari.connection-timeout设置足够长)。

坑2:卷权限导致 PostgreSQL 无法写入
现象:容器反复重启,日志显示permission denied
解决:可以指定user: "999:999"(postgres 官方镜像的 UID),或者让宿主机目录权限为 999。

坑3:网络不通,Java 应用报UnknownHostException: postgres
检查:两个服务是否在同一个网络?是否拼写错误?用docker-compose exec java-app ping postgres测试。

坑4:端口冲突
宿主机 5432 或 8080 已被占用。修改ports左侧的宿主机端口,如"5433:5432"

坑5:depends_on不生效,应用仍早于数据库启动
根本原因:depends_on不等健康。必须配合healthcheck+condition: service_healthy或启动脚本轮询。

九、扩展:连接远程已有的 PostgreSQL(非容器)

如果你的 PostgreSQL 运行在外部服务器(不是本机容器),则不需要在docker-compose.yml中定义postgres服务。只需修改 Java 应用的SPRING_DATASOURCE_URL指向远程 IP。但为了网络连通性,需要注意:

  • Java 容器需要能访问远程数据库的 IP 和端口。

  • 可以使用extra_hosts或宿主机网络模式network_mode: host

示例:

yaml

java-app: build: . network_mode: host # 直接使用宿主机网络栈,可通过 localhost 访问宿主机上的服务 environment: SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/myappdb

network_mode: host会失去网络隔离,一般不建议。

十、总结:一份合格 Compose 文件的自我修养

一份高质量的docker-compose.yml应当满足:

  1. 明确版本,使用3.8及以上。

  2. 所有服务都有健康检查(至少关键依赖)。

  3. 敏感信息通过.env注入

  4. 数据持久化使用命名卷

  5. 自定义网络,确保服务发现。

  6. 日志限制,防止磁盘占满。

  7. 启动顺序保障(健康等待或脚本)。

  8. 资源限制(生产 Swarm 模式)。

掌握了这些,你就从“docker-compose 用户”升级为“Compose 配置架构师”。下一步,可以研究docker-compose.override.yml实现开发/生产环境分离,或者向 Kubernetes 迁移。

如果这篇文章帮你彻底啃下了docker-compose.yml的硬骨头,欢迎点赞、收藏、转发。评论区留下你遇到的 Compose 怪问题,我帮你排!

附录:完整的项目文件树

text

. ├── docker-compose.yml ├── .env ├── Dockerfile ├── target/ │ └── app.jar ├── logs/ │ └── (运行时生成) └── init-scripts/ └── init.sql
http://www.zskr.cn/news/1496349.html

相关文章:

  • 告别手忙脚乱:如何用League-Toolkit让英雄联盟游戏体验更丝滑
  • 基于Spring Boot的智能停车导航与管理系统设计与实现
  • MPV播放器终极配置指南:从零构建专业级媒体播放体验
  • 2026年主流AI招聘工具深度对比:哪款真正能帮你省下80%筛选时间
  • AlistHelper:告别命令行,用图形界面轻松管理Alist文件服务
  • Autolabel自动标注工具终极指南:5分钟让AI帮你搞定数据标注难题
  • Smart-SIM工程案例—船舶筏架力学性能快速预测
  • 芯片制造:Bandgap(带隙基准源)电路中重要的模块和功能
  • 自动装盘机倒瓶检测系统的传感器选型与信号处理
  • PCDN服务SLA设计:承诺多少节点在线才合理?
  • Playwright MCP Docker 部署:mcr 镜像、浏览器工具和权限配置
  • 鸿蒙App开发--心愿池的动画特效:投币动画与进度条
  • 拒绝“胶水架构”:大模型时代,如何用统一任务基座破解 AI 研发的技术债?
  • 打造你的专属音乐库:LXMusic音源配置实战指南
  • 亚马逊270天库存生死线!超龄库存清仓全攻略
  • 终极指南:5分钟在Mac上实现Android手机USB网络共享
  • 计算机毕业设计之智能家居安全体系设计及实现
  • 手动创建Gazebo环境与自适应控制PX4
  • Windows 10 Android子系统移植实战指南:架构解析与深度部署方案
  • AI驱动的自动化——金融、物流与医疗行业的转型
  • 前端八股文面经大全:美团前端暑期实习一面(2026-06-08)·面经深度解析
  • equals 和 hashCode 方法:使用它们的原因和位置,以及它们的工作原理
  • 小程序开发入门:从零掌握基础代码结构
  • 华硕笔记本终极性能调校指南:5分钟掌握G-Helper完整配置
  • 逆水寒手游装备系统主要ui界面拆解
  • 【软件发布】光明正大的看小说而不被发现的摸鱼软件:伪输入法(FakeType)
  • PPDS生产计划排产-报工
  • 异地收单不用愁,H5 支付链接一键收款。
  • 计算机毕业设计之智能仓库管理系统开发与设计
  • GD32F470平台RT-thread-nano移植lwip-2.1.2(网卡CH182H2)