news 2026/10/13 12:39:11

Docker Compose 一键部署教程:从单体到微服务完整实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker Compose 一键部署教程:从单体到微服务完整实践

微服务 / 单体项目 Docker Compose 一键部署教程(完整版)

1. 这方案到底解决什么问题

先聊个场景。你手里有一个单体项目,或者一套微服务集群,本地跑得正欢,但要交付给同事联调、部署到测试服务器、或者换一台新机器,立刻就得折腾半天:装依赖、配数据库、改配置文件、起进程、对端口……每一步都暗藏惊喜。我见过不少人,项目本身没写几行代码,部署脚本倒是攒了一抽屉,每一套还都不太一样。

Docker Compose 就是来终结这种混乱的。

它做的事情用一句话说清楚:用一个 YAML 文件描述“你的应用由哪些容器组成、每个容器怎么跑、容器之间怎么通信”,然后用一条命令把整个应用拉起来。对于单体项目,一个 compose 文件就能把应用、数据库、缓存、反向代理全部编排好;对于微服务架构,十几个服务写进一个文件里,同样一条命令搞定批量启停。这个"一键",本质上是把环境配置、依赖关系、启动顺序、网络规则这些最容易出幺蛾子的东西,从"人肉操作"变成了"声明式描述"——写一次,到处复现。

这篇文章就是干这个用的。我按照自己在实际项目中反复验证过的路径,把 Compose 从安装、配置、编写、部署到问题排查,从头到尾拆开讲一遍。无论你是刚接触容器的小白,还是已经在生产环境摸爬滚打过的老手,里面总有几个"当时要是早知道就好了"的细节。

2. 先把最基础的概念和工具理顺

2.1 Docker 和 Compose 的关系,用一个生活类比

很多人刚接触时,会被 Docker 和 Docker Compose 这两个词搞混。其实没那么玄乎。

Docker 本身解决的,是"单个应用的打包和运行"。它把应用连同运行环境一起装进一个标准化的盒子里,这个盒子叫容器,装盒子的模板叫镜像。你可以把镜像理解成"安装包",容器就是"已经安装好并正在运行的程序"。

但真实项目从来不是一个盒子就完事的。典型场景:前端页面要有个 Web 服务器承载,后端接口要单独跑一个进程,数据要放数据库里,热点数据可能还需要 Redis 顶着。四个盒子,各管一摊。这时候 Compose 登场了——它专门负责编排多个容器,定义它们之间的关系、启动顺序、网络连接和存储挂载。

类比一下:Docker 是招进来的一个个员工,各有分工;Compose 是给他们画好的组织架构图和协作流程。没有 Compose,你得手动挨个启动、手动记 IP、手动连网络,工作量大且容易出错;有了 Compose,一个文件把组织关系写清楚,一条命令整个团队全部就位。

2.2 安装与版本选择,别在新版本上翻车

现在的 Docker 安装包,默认就把 Compose 插件一起带上了。在终端输入docker compose version,能输出版本号的话,说明已经内置,不需要单独安装。

有两种命令写法容易搞混:

  • 旧写法:docker-compose(带横杠),这是独立的 Python 脚本,很多旧教程还在用它,需要单独安装。
  • 新写法:docker compose(不带横杠),这是 Docker 官方推出的插件,使用 Go 语言编写,性能和兼容性更好。

我建议统一用新写法。如果你的机器上还留着旧的 docker-compose,也不用着急,两者不冲突,配置文件是通用的。

配置文件的语法版本也值得留意。Compose 文件头部常见的version: '3'字段,在新版本中已经不建议写了,官方推荐直接省略。文件格式上,业界常用的有两个主流版本:2.x 版,字段简洁,适合大多数项目;3.x 版,加入了一些部署相关的扩展字段,主要是为配合集群编排场景设计的。实际项目里,我一般用 2.x 语法写本地和测试环境,生产环境如果后面要往集群迁移,再考虑升级到 3.x 并加上资源限制等字段。

还有个容易忽视的点:Compose 版本和 Docker 引擎版本是有配套关系的。老引擎配新 Compose 文件语法,会报version not supported之类的错误。遇到这类提示,先查引擎版本,再决定是升级引擎还是降级文件语法,而不是盲目改来改去。

2.3 动手前的状态确认

写任何配置之前,先确认三个基本事实:

检查 Docker 引擎在运行、Compose 插件可用、当前目录有 compose 文件。这三点缺少任何一个,后面所有命令都会报错。

我在这上面吃过亏,新换一台服务器,配好了 compose 文件,执行docker compose up直接给我报拒绝连接。折腾半天才发现是 Docker 守护进程没拉起来。执行下面的命令能快速自检:

docker version docker compose version docker ps

第一条确认客户端和服务端版本,第二条确认 compose 插件,第三条确认守护进程正在运行且当前用户有权访问。三条都通过,再往下走。

3. 编排方案的整体设计思路

3.1 先想清楚你要编排什么

写 compose 文件之前,先画一张简单的"应用拓扑图"——不需要多专业,哪怕在纸上写几个框也行。框里是服务,框之间是依赖关系,箭头是数据流向。

拿一个典型单体应用举例:

  • 用户请求先到 Nginx(反向代理,静态资源 + 转发动态请求)
  • Nginx 把动态请求转发给后端服务(如 Java 或 Node)
  • 后端服务读写 MySQL,会话缓存用 Redis

这套拓扑里有四个容器。Compose 文件要做的,就是把四个容器放进同一个网络,让它们能用"服务名"互相找到对方,让数据落地到持久化卷里,让外部只能访问到 Nginx 这一个入口。

对于微服务项目,拓扑会复杂一些,但设计思路一致:网关为对外唯一入口,内部服务全部走内网通信,依赖关系通过服务名表达,而不是写死 IP。

3.2 目录结构怎么组织,一张图看懂

Compose 虽然只有一个主文件,但项目目录结构如果乱,后期维护就是噩梦。我的习惯是这样的:

project-root/ docker-compose.yml # 主编排文件 .env # 环境变量(不进代码仓库) .env.example # 环境变量模板(进代码仓库) services/ service-a/ Dockerfile conf/ service-b/ Dockerfile deploy/ deploy.sh # 一键部署脚本 backup.sh # 数据备份脚本

每个服务一个子目录,自己的 Dockerfile 和配置放自己家里,谁的东西谁管,互不越界。Compose 文件只做"组装",不存具体业务实现。

3.3 环境变量的管理,别把密码写进文件

很多人在 compose 文件里直接写死端口、密码、镜像版本号。短时间没啥问题,但一旦多人协作或者环境切换(开发/测试/生产),就等着难受吧——每次都得改文件,担心改错,还得防止密码泄露到代码仓库。

正确做法是引入.env文件。Compose 在解析文件时,会自动读取同目录下的.env文件,将其中的变量注入到配置里去做变量替换。例如:

services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}

.env文件里写上:

MYSQL_ROOT_PASSWORD=Th1sIs4Str0ngP@ss

这样密码只在.env里出现,.env放进.gitignore不进仓库。仓库里放一个.env.example作为模板,把变量名列出来,值留空或者写示例值,同事拿到后复制改名、填上自己的值即可。这套流程要当成铁律执行,尤其是多人协作的项目。

3.4 单体还是微服务,编排策略有什么不同

单体项目,所有服务基本是"同时刻生、同时刻死"的关系。Compose 文件的重点放在数据持久化和端口映射上,结构相对简单。

微服务项目,三个额外的点必须重点考虑:

  • 网络分区:内部业务服务放一个自定义网络里,数据库放另一个网络,网关跨网络转发。好处是网络隔离,防止业务服务意外连接到不该连的组件。
  • 启动顺序:注册中心必须先于业务服务启动,业务服务启动时才能顺利注册。
  • 日志和监控:服务数量多,日志不能每个容器单独看,得统一收集,或者至少统一配置日志轮转策略。

这些差异,会在后面两个实操章节里分别展开。

4. compose 文件核心配置逐项拆解

4.1 服务的三大件:镜像、构建、容器名

每个服务至少要写清楚"用什么镜像跑"。两种方式,各有适用场景:

services: app: image: nginx:1.25-alpine # 直接拉取现成镜像 container_name: web-app # 自定义容器名
services: app: build: ./app # 用当前目录的 Dockerfile 构建 container_name: web-app

生产环境我倾向于构建与运行分离:CI 阶段构建好镜像、打好版本标签(比如app:20250101-v1.2.3),推到镜像仓库;compose 文件里用image直接指定版本号。这样能保证"构建的镜像和运行的镜像完全一致",回滚时只需要把镜像版本号调回去,而不是重新构建一遍。

容器名我建议主动设置,别让它自动生成随机名。理由很实际:排查问题的时候,docker logs web-app比docker logs project_app_xyz789舒服得多;脚本里要按名字精确操作容器也更方便。

4.2 端口映射:外部访问全靠它

容器内部有自己独立的网络命名空间,宿主机不能直接访问容器 IP。要让外部能访问到服务,必须把容器的端口映射到宿主机上。

services: nginx: ports: - "80:80" - "443:443"

格式是宿主机端口:容器端口。完整的写法还可以绑定指定 IP:

ports: - "127.0.0.1:8080:80"

这条规则的含义是:只允许本机通过 8080 端口访问容器的 80 端口,外部网络无法访问。如果只想给本机调试用,这招非常有用,可以避免端口被局域网暴露。

一个常见的误区:以为微服务每个服务都要暴露端口。大错特错。微服务架构里,服务间的调用都是走内网网络,通过服务名直接通信,只有对外提供入口的服务才需要映射端口。其他服务映射端口,等于把内网服务直接暴露给外部,是安全风险。

4.3 网络配置:服务名就是最好的 IP

Compose 默认会创建一个网络,所有没有显式指定网络的服务都会加入其中,同一网络内的服务可以用服务名互相解析访问。这是 Compose 最方便的特性之一——你不必关心容器的 IP 是多少,直接用服务名当主机名即可。

来看一个典型的内网通信配置:

services: backend: networks: - internal mysql: networks: - internal nginx: networks: - frontend - internal networks: frontend: internal:

这里 Nginx 同时挂了两个网络:frontend里的服务可以通过它访问外部,internal里它又能连通数据库和后端服务。而frontend网络里的其他服务,如果没挂internal,就无法直接访问数据库。这种分区设计,是实际项目中非常实用的网络隔离手法。

还有个知识点值得说透:服务名在 Docker 内置的 DNS 解析中就是主机名。比如后端服务连接数据库,数据库地址写mysql:3306即可,不要写localhost,因为每个容器的 localhost 都是它自己。我见过不少开发者在容器化初期踩这个坑,数据库怎么都连不上,后来发现代码里写的是 localhost。

4.4 卷挂载:数据不丢、代码热更新

容器本身是临时性的。删掉容器,容器内写的数据全部灰飞烟灭。所以凡是需要持久化的数据——数据库文件、上传的文件、日志——必须挂载到宿主机上,或者用 Docker 管理的命名卷。

services: mysql: volumes: - mysql-data:/var/lib/mysql # 命名卷,Docker 管理路径 - ./backups:/backups # 绑定挂载,宿主机相对路径 volumes: mysql-data:

绑定挂载使用宿主机路径,直观可见,适合放配置文件、代码目录、备份文件;命名卷由 Docker 管理,跨主机迁移时更省心,适合放数据库数据这种不太需要手动去翻的内容。

权限问题高发区在这里。容器内的进程是以容器内用户的身份运行的,比如 MySQL 容器内用的是mysql用户(UID 是 999)。如果宿主机上的挂载目录权限不对,容器写入时会报Permission denied。处理办法:给挂载目录设置宽松一点的权限,或者干脆在镜像里用user:字段指定容器进程以宿主机用户身份运行,后者更精确但需要考虑镜像内的文件权限。

4.5 启动顺序:depends_on 并不万能

微服务或者多服务环境下,服务间的启动顺序直接影响可用性。Compose 提供了depends_on字段:

services: backend: depends_on: - mysql - redis

它会保证backend容器在mysql和redis容器启动之后才启动。注意,是"容器启动",不是"服务就绪"。MySQL 容器起来了,但数据库可能还在初始化,这时后端去连,一样是连接被拒。

要想真正"等就绪",需要配合健康检查(healthcheck):

services: mysql: healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"] interval: 10s timeout: 5s retries: 3 backend: depends_on: mysql: condition: service_healthy

这样 Compose 会等 MySQL 通过健康检查,再启动 backend。生产环境强烈建议这样配置,本地开发依赖少,可以偷懒;生产环境服务多、依赖链条长,不做好健康检查,启动即崩溃的连环事故很容易出现。

4.6 资源限制和日志轮转,生产环境必备

本地开发不用太在意资源限制,但生产环境没有限制,一个服务内存泄漏能把整台机器拖垮。Compose 支持为每个服务设置资源上限:

services: app: deploy: resources: limits: cpus: '1.0' memory: 512M

注意:deploy.resources在 Docker Compose 的 2.x 和 3.x 中,默认只有启用集群部署模式时才会生效,本地用docker compose命令时,这个限制有时不生效。要强制本地也生效,可以在命令前设置COMPOSE_BAKE=true环境变量,或者使用docker compose --compatibility参数(这个参数在较新的版本中已经不太推荐了)。

日志配置同样是必选项,不配置的话,容器日志无限增长,磁盘被跑满只是时间问题:

services: app: logging: driver: json-file options: max-size: "50m" max-file: "5"

单文件 50 兆,最多保留 5 份,超过就滚动清理。这套配置等于给日志上了保险,不会因为日志撑爆磁盘而引发服务宕机。

5. 单体项目一键部署实操:完整案例

5.1 案例背景

拿一个最常见的技术栈组合:Nginx + 后端服务 + MySQL + Redis。假设后端是一个简单的 Node.js 应用,Redis 管缓存,MySQL 管持久化。

5.2 完整 compose 文件

services: nginx: image: nginx:1.25-alpine container_name: app-nginx ports: - "80:80" volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./app/public:/usr/share/nginx/html:ro networks: - external-net - backend-net depends_on: - backend restart: unless-stopped backend: image: node:18-alpine container_name: app-backend working_dir: /app volumes: - ./app:/app - /app/node_modules command: sh -c "npm install && npm run start" environment: NODE_ENV: production DB_HOST: mysql DB_PORT: 3306 DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} REDIS_HOST: redis networks: - backend-net depends_on: mysql: condition: service_healthy redis: condition: service_started restart: unless-stopped mysql: image: mysql:8.0 container_name: app-mysql environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${DB_USER} MYSQL_PASSWORD: ${DB_PASSWORD} volumes: - mysql-data:/var/lib/mysql - ./backups:/backups networks: - backend-net healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-p${MYSQL_ROOT_PASSWORD}"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped redis: image: redis:7-alpine container_name: app-redis command: ["redis-server", "--appendonly", "yes"] volumes: - redis-data:/data networks: - backend-net restart: unless-stopped volumes: mysql-data: redis-data: networks: external-net: backend-net:

5.3 这份文件的设计思路解读

几个配置点背后的考量说一说:

  • Nginx 挂了两个网络:外部网络让用户能通过 80 端口访问,后端网络让它能转发请求给 backend,同时 backend 和 MySQL、Redis 在同一个后端网络里互相通信,但都被外部网络隔离。
  • backend 匿名卷:/app/node_modules用匿名卷,是为了避免把宿主机 node_modules 目录覆盖进容器。如果不加这个,宿主机安装的依赖可能和容器内的 Node 版本不兼容。
  • mysql 启动失败不影响其他服务已知:depends_on配合条件控制,只有 MySQL 健康后才启动 backend,这是防止后端启动时疯狂报错的关键。
  • .env文件提供敏感信息:数据库账号密码全部走变量注入,不写死在配置里。

5.4 命令行操作:完整的生命周期

在docker-compose.yml所在目录执行:

# 拉取镜像并后台启动所有服务 docker compose up -d # 查看服务状态 docker compose ps # 实时查看所有服务的日志(加上 -f 跟踪输出) docker compose logs -f # 只重启 backend 这个服务 docker compose restart backend # 停止所有服务但保留数据卷和网络 docker compose down # 停止所有服务并删除容器、网络(保留数据卷) docker compose down --volumes

down不会自动删除命名卷(mysql-data、redis-data),数据还在。要清掉数据卷才用--volumes参数。这个参数慎用,一旦执行,数据库数据彻底没了。

这个流程跑一遍,"一键部署"的概念就立住了。所谓一键,实际是:服务已经定义好了,up -d一条命令,所有容器按依赖关系依次启动,该等就等,该健康检查就等待通过。在这套流程里,"跑起来"已经从技术问题变成了基础设施问题。

6. 微服务架构编排实战:从零到能跑

6.1 微服务编排的关键差异

微服务相比单体的明显差异,是服务数量多、依赖链条长、状态分散在多个独立进程中。比如一套典型的微服务骨架:

  • API 网关(入口)
  • 注册中心(服务发现)
  • 配置中心(集中配置管理)
  • 三个业务服务(各自独立数据库,或者共享中间件)
  • Kafka/RabbitMQ 消息队列
  • Redis、MySQL、Prometheus 等基础设施

这些组件如果各写各的启动命令、各连各的网络,光起服务就能把人绕晕。Compose 恰恰是管理这种复杂拓扑的利器:一个文件,整条链路。

6.2 拆解一份微服务编排文件

services: gateway: image: gateway:1.2.0 container_name: ms-gateway ports: - "8080:8080" depends_on: - service-a - service-b environment: SPRING_CONFIG_IMPORT: "configserver:http://config-server:8888" networks: - ms-external - ms-internal healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] interval: 15s timeout: 5s retries: 3 config-server: image: config-server:1.2.0 container_name: ms-config-server volumes: - ./config:/config ports: - "8888:8888" networks: - ms-internal registry: image: registry:2.3.1 container_name: ms-registry ports: - "8761:8761" networks: - ms-internal service-a: image: service-a:1.2.0 container_name: ms-service-a depends_on: registry: condition: service_started config-server: condition: service_started environment: EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: "http://registry:8761/eureka" SPRING_CONFIG_IMPORT: "configserver:http://config-server:8888" networks: - ms-internal restart: unless-stopped service-b: image: service-b:1.2.0 container_name: ms-service-b depends_on: - registry - config-server environment: EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: "http://registry:8761/eureka" networks: - ms-internal restart: unless-stopped postgres: image: postgres:15-alpine container_name: ms-postgres environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - pg-data:/var/lib/postgresql/data networks: - ms-internal networks: ms-external: ms-internal: volumes: pg-data:

6.3 这份文件背后的核心思想

服务发现与地址解析。所有内部服务统一在ms-internal网络中,服务之间互访直接用服务名。比如service-a要访问service-b,地址就是http://service-b:端口,而不需要知道它被分配了哪个 IP。注册中心(Eureka/Nacos/Consul)也一样,服务注册地址写成http://registry:8761,容器内解析到的就是注册中心容器的 IP,天然适合动态环境。

依赖与启动边界。config-server和registry是基础设施,必须最先就绪。业务服务依赖它们,通过depends_on+ 条件控制顺序。微服务的健康检查尤其重要——服务多,任何一个起不来或者没注册上,调用链就断。我给每个服务都配了actuator/health(Java 生态普遍支持)或者类似健康端点,通过healthcheck定期探测,把"服务启动了"从"容器起来了"细化成"服务真的能处理请求了"。

端口映射最小化。整个文件里,对外映射端口的只有网关和注册中心。业务服务的端口只暴露在内部网络上,外部无法直接访问。这在微服务安全里是基础要求,也是很多人刚开始写时最容易犯的错——把每个服务都映射到宿主机端口上,表面上方便调试,实际上把内部服务全暴露了,一旦服务有未授权访问的漏洞,后果很严重。

配置与代码分离。config-server把配置文件挂载到宿主机./config目录,不同环境覆盖文件内容即可,不需要重新构建镜像。应用代码里的账号密码、数据库地址,尽量通过环境变量或配置中心下发,这样镜像本身可以做到非常干净,回滚、扩容都方便。

6.4 微服务环境下的常用命令

微服务的容器数量多,单独操作某一两个服务是高频操作:

# 构建并启动所有服务 docker compose up -d --build # 只重新构建并启动 service-a docker compose up -d --build service-a # 查看特定服务的日志 docker compose logs -f service-a # 一键停止整个微服务组 docker compose down

容器之间共享同一个 compose 项目时,它们的生命周期是统一管起来的,不会出现"启动了一批、忘了另一批"的情况。

7. 一键部署脚本:把命令升级成"真·一键"

7.1 最简单的部署脚本长什么样

光会敲docker compose up -d还不够,完整的"一键部署"还需要处理构建、迁移数据、备份、回滚这些流程。我通常会在项目里放一个deploy.sh,把常用操作封装成带参命令。基础版:

#!/bin/bash set -e ACTION=${1:-up} case "$ACTION" in up) echo ">>> 开始部署..." docker compose pull docker compose up -d --remove-orphans echo ">>> 部署完成" ;; build) docker compose build --no-cache ;; restart) docker compose restart ;; down) docker compose down ;; logs) docker compose logs -f ;; *) echo "用法: $0 {up|build|restart|down|logs}" ;; esac

执行./deploy.sh up就是一条命令完成环境检查、镜像更新、服务启动的完整闭环。脚本里那个--remove-orphans值得说一下:它会删除 compose 文件里已不存在的容器,防止旧容器残留占用端口或网络资源。

7.2 升级与回滚的心法

生产环境"一键部署"最怕升级失败。我的方案是镜像版本号 + 脚本回滚。每次构建镜像打上部署日期或版本号,比如app:20250115-v2.1.0,compose 文件里改成新版本号,执行./deploy.sh up即可完成升级。如果新版本出问题,将版本号改回上一个,再次执行./deploy.sh up,旧版本就回来了。

回滚时有个小坑提示:数据库结构可能有变更。代码回滚了,数据库通常不跟着回滚,可能出现代码和库结构不匹配。所以升级前务必备份数据库,回滚前也要评估库结构兼容性,这是经验之谈。

7.3 数据备份脚本的思路

部署脚本之外,备份脚本是另一个刚需。最直接的方案是容器内执行数据库的导出命令:

docker compose exec mysql sh -c "mysqldump -u${DB_USER} -p${DB_PASSWORD} ${MYSQL_DATABASE}" > backups/db_$(date +%Y%m%d_%H%M%S).sql

把导出文件放到宿主机挂载的./backups目录,再配合 cron 定时执行,数据安全就有了基本保障。微服务项目如果数据库分片多,就给每个库写一条导出任务,再用一个汇总脚本打包上传到对象存储,算是冷备份的最低成本方案。

8. 高频问题排查与避坑手册

8.1 容器起不来,先看哪三板斧

容器报错的排查路径是有规律可循的。我的习惯是,先用docker compose ps看状态,再用docker compose logs <服务名>看日志,最后用docker inspect看配置细节。三步基本能锁定九成问题。

比较典型的状况:

  • 容器退出码是 0,说明进程正常退出,多半是命令写错,服务启动后自己结束了。
  • 退出码是 1 或 2,说明程序异常,看日志里最后的堆栈或报错信息。
  • 退出码是 137 或 139,代表被系统杀掉,往往和内存不足或权限有关。
  • 容器状态一直显示 Restarting,说明启动失败后触发 restart 策略,在无限重启循环。这时候千万别干等,看日志找真实原因。

8.2 按常见症状整理的速查表

症状可能原因排查/解决办法
端口无法访问端口映射冲突、防火墙拦截docker compose ps检查映射;ss -tlnp看端口占用;检查宿主机防火墙规则
容器间无法通信服务不在同一网络、服务名打错docker network inspect <网络名>确认容器归属网络;在容器内ping 服务名测试 DNS 解析
数据库连不上地址写成了 localhost、SQL 初始化超时确认写的是服务名;depends_on加condition: service_healthy
容器内时间不对容器用的 UTC 时区挂载/etc/localtime:/etc/localtime:ro或设置TZ=Asia/Shanghai环境变量
挂载目录没有权限容器内用户 UID 与宿主机目录不匹配调整宿主机目录权限chmod -R 777,或者用user:指定容器内用户
日志导致磁盘满了没配日志轮转配置logging驱动的max-size和max-file参数
服务起来后马上又退出启动命令错误、依赖缺失、健康检查失败看docker compose logs;确认健康检查命令和探活端口正确
HTTPS 证书失效挂载的是旧证书或路径错误检查挂载路径和容器内的证书路径是否一致,重新拷贝证书

8.3 几个容易忽视的经验细节

down和stop的区别。down会把容器和网络也删掉,下次up会重建,若配置有误反而能暴露问题;stop只是暂停容器,网络保留,数据不变,适合临时维护。不要两个滥用,尤其是多个项目共用宿主机时,down一个项目不会影响别的项目的容器,但如果网络名冲突,坑就大了。

macOS 和 Windows 的 Docker Desktop,默认有文件共享边界设置。挂载宿主机目录进容器,先确认路径在共享列表里,否则目录挂载进去是空的。这个问题我在跨平台部署时踩过几次。

大项目别把全部服务塞进一个文件。超过十几个服务时,docker compose up会明显变慢,日志也不易排查。可以拆多个 compose 文件,比如docker-compose.infra.yml管中间件、docker-compose.app.yml管业务服务,启动时用多个-f参数合并执行:

docker compose -f docker-compose.infra.yml -f docker-compose.app.yml up -d

还可以用extends字段复用公共配置,或者用 YAML 合并锚点减少重复代码。这些进阶技巧,等基础跑顺了再上不迟。

9. 后续还能怎么扩展

Docker Compose 练熟了,你会在某个节点发现它的边界。服务规模超过三四十个,滚动更新、灰度发布、跨主机集群这些词就开始频繁出现。那时候再往上走,Kubernetes 会是绕不开的一站,而 Compose 学习的那些模型——声明式配置、健康检查、服务发现、网络隔离——正好是理解 K8s 的基础。

我个人的体会是:Compose 的价值不只是"能一键部署",而是逼你把部署这件事标准化了。环境差异、依赖顺序、数据持久化、日志处理,这些问题以前靠人肉记忆或者零散脚本,Compose 让它们变成了一份可版本化、可评审、可追溯的配置。即使哪天项目迁移到了集群编排工具,这份 YAML 背后的设计思路,依然处处适用。

最后一次提醒,所有配置修改之前,先看一眼.env是否已经准备好,备份是否做过了——这两件事做顺手了,部署过程里一半以上的惊吓都能提前挡掉。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 12:38:11

Python列表操作避坑指南:从append到insert的索引全解

很多人接触Python的第一站就是列表。而像“创建空列表names&#xff0c;添加old_driver、rain、jack、shanshan、peiqi、black_girl&#xff0c;再在black_girl前面插入一个元素”这种题目&#xff0c;几乎每个初学者都做过。看似简单&#xff0c;但我在实际交流中发现&#xf…

作者头像 李华
网站建设 2026/10/13 12:34:03

SolidWorks Routing中文显示完全指南:从语言包到系统设置

简介&#xff1a;在工程软件本地化过程中&#xff0c;界面语言与实际功能模块的语言支持往往并非同步实现。以SolidWorks为例&#xff0c;主程序已切换为中文&#xff0c;但Routing管线布置插件仍可能显示英文&#xff0c;这背后涉及软件架构中的多层语言资源机制。其原理在于S…

作者头像 李华
网站建设 2026/10/13 12:33:04

端侧Agent落地、多模态上下文与开源实战:AI日报详解技术选型与踩坑

2026年10月7日&#xff0c;AI圈的节奏还是快得让人喘不过气。今天这份日报&#xff0c;我照例从早上刷到傍晚&#xff0c;粗略过了一遍社群、技术社区和几个项目仓库的动态&#xff0c;大概两百多条消息。先给个整体感受&#xff1a;今天没有那种炸裂级别的大新闻&#xff0c;不…

作者头像 李华
网站建设 2026/10/13 12:32:33

在线期末考试主观题答题框架与时间分配实战指南

1. 项目背景与整体设计思路1.1 这个项目到底在做什么“2020南京邮电大学Mooc—在线期末考试主观卷”这个标题&#xff0c;核心指向的是一套在线课程期末考试中主观题部分的完整应对方案。它不是一个单纯的题库&#xff0c;也不是一份简单的复习提纲&#xff0c;而是一套围绕“在…

作者头像 李华
网站建设 2026/10/13 12:31:36

Vuforia Engine 9.4.6 Unity集成:从RAR包导入到AR识别参数配置与踩坑指南

简介&#xff1a;Vuforia Engine 9.4.6 离线缓存包是面向 Unity 开发者的增强现实&#xff08;AR&#xff09;开发插件&#xff0c;由 PTC 公司提供&#xff0c;主要解决无互联网环境下 Vuforia 组件安装与集成的难题。该包适用于图像识别、目标追踪、3D 模型渲染、云目标等主流…

作者头像 李华