通过docker compose编排部署和更新容器
管理多个相互关联的 Docker 容器,一个一个地运行和配置会非常繁琐。Docker Compose 应运而生,它旨在解决这个问题,让多容器应用的部署和管理变得轻而易举。
自 Docker Desktop 3.0+ 和 Docker Engine 20.10+ 版本开始,docker-compose 命令已集成到 docker CLI 中,成为 docker compose(不带连字符)。建议使用新语法 docker compose。旧的 docker-compose 命令可能仍可工作,但不再是官方推荐的方式。
以下所有命令均假定在包含 docker-compose.yml 文件的目录下执行。
什么是 Docker Compose?
Docker Compose
是一个用于定义和运行多容器 Docker 应用的工具。可以把它想象成一个“导演”,你告诉它你的应用需要哪些“演员”(容器),它们怎么配合,它就会自动帮你把所有“演员”都安排好,一键启动或停止整个“剧组”——你的应用服务。它通过一个 .yaml
文件来配置你应用的服务。
Docker Compose 和 Docker Run 的区别
如果你把 docker run
理解为“手动启动一辆车”,那么 Docker Compose
就是“一键启动整个车队”。
docker run: 每次只能启动和配置一个单独的容器。如果你有一个由前端、后端、数据库组成的复杂应用,你需要手动敲写多个 docker run 命令,并分别处理它们之间的网络连接、卷挂载等问题。
Docker Compose
: 将整个应用的所有服务、网络和卷的配置都写入一个 docker-compose.yml
文件中。然后,你只需一个命令 (docker compose up
) 就能启动、停止、重启或管理整个多服务应用,大大简化了操作。
#创建一个数据卷
docker volume create my_mysql_data_single
#docker run -d \
--name my-mysql-app \ # 给容器一个好记的名字
-p 3306:3306 \ # 映射端口:宿主机3306端口 -> 容器3306端口
-e MYSQL_ROOT_PASSWORD=your_secure_password \ # 设置root用户密码,请务必更改!
-v my_mysql_data_single:/var/lib/mysql \ # 挂载数据卷,持久化数据库数据
--restart unless-stopped \ # 设置重启策略:除非手动停止,否则总是重启
mysql:8.0 # 使用 MySQL 8.0 官方镜像
# docker-compose.yaml
version: '3.8' # Docker Compose 文件格式版本
services:
mysql_db: # 定义一个名为 'mysql_db' 的服务
image: mysql:8.0 # 使用 MySQL 8.0 镜像
container_name: my-mysql-compose # 为容器指定名称
ports:
- "3306:3306" # 端口映射:宿主机3306 -> 容器3306
environment: # 环境变量配置
MYSQL_ROOT_PASSWORD: "your_secure_password" # root用户密码,请务必更改!
MYSQL_DATABASE: "my_database" # 初始化时创建的数据库
volumes:
- my_mysql_data_compose:/var/lib/mysql # 挂载数据卷,实现数据持久化
networks:
- my_app_network # 将此服务加入自定义网络
restart: unless-stopped # 重启策略:除非手动停止,否则总是重启
volumes:
my_mysql_data_compose: # 定义名为 'my_mysql_data_compose' 的具名数据卷
networks:
my_app_network: # 定义一个名为 'my_app_network' 的自定义网络
driver: bridge # 使用桥接网络驱动
Docker Compose 的优点
如果你曾尝试手动用 docker run
部署一个包含多个服务的应用(比如一个网站前端、一个后端 API 和一个数据库),你很快会发现这像一场“命令行参数地狱”。而 Docker Compose
正是为了将你从这个“地狱”中解救出来。
1.告别命令行参数地狱:简化配置与管理
痛点docker run
: 想象一下,你要启动一个由 Nginx (前端), Spring Boot (后端) 和 MySQL (数据库) 组成的应用。你需要为每个服务单独编写一个冗长 docker run 命令:指定镜像、映射端口、挂载卷、设置环境变量、连接网络、配置重启策略…… 这意味着至少三条复杂的命令,手动维护复杂且容易出错。
Compose 的解决方案: Docker Compose
将所有服务的配置(包括镜像、端口、卷、环境变量、网络、依赖关系等)都集中到一个名为 docker-compose.yml
的 YAML 文件中。这个文件清晰、结构化,就像是你的整个应用的“部署蓝图”。它可读性强,易于版本控制(比如用 Git 管理),也方便在团队间共享。你只需看一眼这个文件,就能明白整个应用的架构和配置。
2.一键启动与停止:告别手动操作
痛点docker run
: 要启动上述 Nginx+后端+MySQL,你需要按顺序敲入三条 docker run 命令,如果错了还得手动 docker stop,docker rm,再重新 run。停止一整个应用更是需要逐个 docker stop。
Compose 的解决方案: 文件写好后,你只需在终端输入两条简单的命令:
docker compose up -d
:即可一键启动 docker-compose.yml
中定义的所有服务(-d 表示后台运行)。Compose 会自动处理服务的启动顺序(如果定义了 depends_on)、网络连接等,就像一个经验丰富的“管家”,帮你把一切都打理得井井有条。
docker compose down
:即可一键停止并移除所有服务、网络和容器,非常方便。
对比举例: 你不需要记住每个服务的具体 docker run
命令,也不用担心启动顺序。Compose 成为了你的“启动/停止总开关”。
3.开发与生产环境的一致性:告别“在我这里能跑”
痛点docker run
: 团队成员各自手动 docker run
部署服务时,可能会因为镜像版本、环境变量、端口映射等差异,导致“代码在我机器上能跑,但在你机器上就不行”的难题。开发、测试、生产环境配置不一致,是常见的“地雷”。
Compose 的解决方案: docker-compose.yml
文件成为了团队协作的“统一标准”。所有开发者、测试人员乃至 CI/CD 流水线,都使用同一个 docker-compose.yml
文件来构建和运行环境。这意味着大家都在一个完全相同的环境中工作,大大减少了因环境差异导致的问题,提高了开发效率和部署的可靠性。
举例: 当新同事加入团队时,他们只需要 git clone 项目
,然后执行 docker compose up -d
,就能拥有一个与团队其他成员完全一致的开发环境,无需复杂的安装和配置步骤。
4.服务间的智能通信:告别手动网络配置
痛点docker run
: 当多个容器需要互相通信时(例如后端需要连接数据库),你需要手动创建 Docker 网络 docker network create
,并在 docker run
命令中使用 --network 参数将它们加入到同一个网络中。之后,你可能还需要通过容器的 IP 地址或 Docker 提供的 DNS 服务来确保通信。
Compose 的解决方案: Compose 默认会为 docker-compose.yml
中定义的所有服务创建一个独立的内部网络。最棒的是,在这个网络中,容器可以直接通过服务名称互相访问!例如,你的后端服务可以直接 ping db_server(这里的 db_server 就是你在 docker-compose.yml
中定义的数据库服务的名称),而无需关心其 IP 地址。这极大地简化了服务间的连接配置。
对比举例: 你的 Spring Boot 后端(服务名为 backend)想连接 MySQL 数据库(服务名为 mysql_db),你只需要在 Spring Boot 的配置文件中写 DB_HOST=mysql_db,Compose 就会自动解析 mysql_db 为数据库容器的内部 IP。而在 docker run
方式下,你可能需要复杂的网络配置或依赖于外部工具来解析。
5.快速迭代与更新:智能检测变化
痛点docker run
: 当你更新了某个服务的镜像版本,或者修改了某个服务的环境变量,你需要手动停止、移除旧容器,然后重新运行新的 docker run 命令。如果更新涉及多个服务,此过程会更加繁琐。
Compose 的解决方案: 当你修改了 docker-compose.yml
文件(例如更新了 image 版本、修改了 ports 或 environment),你只需再次执行 docker compose up -d
。Compose 会智能地检测哪些服务的配置发生了变化,并只针对性地停止、重建和启动那些需要更新的服务,而不会影响到其他未发生变化的服务。这使得应用的迭代和更新变得非常高效和顺畅。
举例: 你把后端镜像从 my-backend:v1.0 更新到 my-backend:v1.1。执行 docker compose up -d 后,Compose 只会重建和启动你的后端容器,而数据库和 Nginx 容器会保持原样继续运行。
Docker Compose 的模板
version: '3.8' # Docker Compose 文件格式版本。建议使用最新稳定版 (如 '3.8' 或更高),以支持更多新功能。
services:
# 定义一个服务,名为 'halo'。这是你的应用程序的核心组件。
halo:
image: halohub/halo:2.16.0 # 指定容器要使用的镜像。格式通常是 '镜像名:标签'。
# 这里使用 Halo 官方的 2.16.0 版本镜像。
container_name: halo-blog-server # 为容器指定一个易于识别的名称。如果不指定,Docker 会自动生成一个。
# 方便你使用 `docker logs` 或 `docker stop` 等命令。
ports:
# 端口映射:将宿主机的端口映射到容器内部的端口。
# 格式: "宿主机端口:容器端口"
# - "80:8090" # 示例:将宿主机的 80 端口映射到容器的 8090 端口 (Halo 默认端口),用于 HTTP 访问。
- "8090:8090" # 建议开发或测试时使用与容器内部相同的端口,便于理解和调试。
volumes:
# 数据卷挂载:实现数据持久化,确保容器被删除后数据不丢失。
# 1. 具名数据卷 (Named Volume):Docker 推荐方式,数据由 Docker 管理,生命周期独立于容器。
- halo_data:/root/.halo # 将名为 `halo_data` 的数据卷挂载到容器内的 `/root/.halo` 目录,
# Halo 的所有配置、数据、上传文件等都将存储在这里。
# 2. 绑定挂载 (Bind Mount):将宿主机上指定的文件或目录挂载到容器内。
# - ./config:/root/.halo/config # 示例:将宿主机当前目录下的 `config` 文件夹挂载到容器内的 `Halo` 配置目录。
# 适合开发时,直接修改宿主机文件即可同步到容器,无需重建镜像。
environment:
# 环境变量:定义容器启动时应用程序可以读取的环境变量。
# 可以直接在这里定义键值对,或从外部文件加载。
SERVER_PORT: "8090" # 明确指定 Halo 容器内部的服务端口。
HALO_EXTERNAL_URL: "http://your_domain.com:8090" # 非常重要!Halo 博客系统的外部访问地址,
# 用于生成正确的链接和 CDN 地址。请替换为你的实际域名或IP。
TZ: "Asia/Shanghai" # 设置容器的时区,确保日志和时间显示正确。
# 数据库配置示例 (如果使用外部数据库,H2 嵌入式数据库不需要这些)
# SPRING_DATASOURCE_URL: "jdbc:mysql://mysql_db:3306/halo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8"
# SPRING_DATASOURCE_USERNAME: "root"
# SPRING_DATASOURCE_PASSWORD: "your_mysql_password"
env_file:
# 从外部文件加载环境变量。文件中每行一个 KEY=VALUE。
# 适合存放敏感信息(如数据库密码)或在不同环境(dev/prod)使用不同配置。
# - ./.env.production # 示例:加载当前目录下的 '.env.production' 文件中的环境变量。
networks:
# 指定此服务将加入的网络。一个服务可以加入多个网络。
# 如果未指定,Compose 会创建一个默认的桥接网络并让所有服务加入其中。
- halo_blog_network # 将 'halo' 服务加入到名为 'halo_blog_network' 的自定义网络中。
restart: unless-stopped # 定义容器的重启策略:
# - "no": 不自动重启。
# - "on-failure": 仅在非零退出代码时(即程序异常退出)重启。
# - "always": 无论容器如何退出,总是重启(除非手动停止)。
# - "unless-stopped": 容器退出时重启,除非手动停止或 Docker 守护进程重启。
# 对于长期运行的服务,'unless-stopped' 或 'always' 是常用选择。
depends_on:
# 定义服务依赖关系。这会确保 'mysql_db' 服务在 'halo' 启动之前先启动。
# 注意:'depends_on' 只保证容器启动顺序,不保证其内部应用已经完全就绪(例如数据库端口监听)。
# - mysql_db # 示例:如果你的 Halo 依赖一个名为 'mysql_db' 的服务,则需要取消注释。
healthcheck:
# 健康检查:用于判断容器内的应用程序是否真正可用,而不仅仅是容器是否在运行。
# Docker Compose 可以利用健康检查来判断服务是否准备好,或在不健康时重启。
test: ["CMD-SHELL", "curl -f http://localhost:8090/actuator/health || exit 1"] # 示例:检查 Halo 的健康接口。
# `curl -f` 会在 HTTP 状态码表示错误时失败。
interval: 30s # 每 30 秒进行一次健康检查。
timeout: 10s # 健康检查命令的最大执行时间。
retries: 3 # 连续失败多少次后被标记为不健康。
start_period: 20s # 容器启动后,等待此时间再开始健康检查。在服务初始化耗时较长时有用。
# build:
# context: . # 如果需要从本地 Dockerfile 构建镜像,指定 Dockerfile 所在目录。
# dockerfile: Dockerfile # 指定 Dockerfile 的文件名(默认为 Dockerfile)。
# args: # 构建参数,在 Dockerfile 中通过 ARG 定义。
# BUILD_ENV: production
# logging:
# driver: "json-file" # 日志驱动,默认为 json-file。
# options:
# max-size: "10m" # 日志文件最大大小。
# max-file: "3" # 最多保留多少个日志文件。
# ulimits: # 为容器设置资源限制。
# nofile: # 最大文件打开数。
# soft: 65535
# hard: 65535
# sysctls: # 设置容器内部的内核参数。
# net.core.somaxconn: 2048
# mem_limit: 512m # 内存限制:容器最多可以使用 512MB 内存。
# cpus: 0.5 # CPU 限制:容器最多可以使用 0.5 个 CPU 核心的资源。
# 定义所有具名数据卷。这些数据卷的生命周期独立于容器,用于持久化数据。
volumes:
halo_data: # 定义名为 'halo_data' 的数据卷。
# 定义所有自定义网络。推荐使用自定义网络以隔离服务并提供更好的 DNS 解析。
networks:
halo_blog_network: # 定义名为 'halo_blog_network' 的自定义网络。
driver: bridge # 使用桥接网络驱动(默认且最常用)。
# internal: true # 【Internal 网络示例】如果取消注释,此网络将是“内部”网络。
# 内部网络只允许其内部的容器互相通信,无法从外部主机直接访问,
# 除非容器同时连接到其他外部可访问的网络。适用于纯后端服务。
# Halo 通常需要被外部访问,所以此处不常使用 internal。
# external: # 【External 网络示例】如果取消注释,表示将使用一个已经存在于 Docker 中的网络,而不是创建新网络。
# name: my_existing_shared_network # 指定要使用的外部网络的名称。
# (你需要先通过 `docker network create my_existing_shared_network` 手动创建该网络)
# 适用于多个 Compose 应用共享同一个网络。
# 如果使用 external,则 driver 配置不再需要。
Docker Compose的命令
生命周期管理命令
这些命令用于启动、停止、重启和管理 Compose 应用程序的生命周期。
1. docker compose up
作用: 构建(如果需要)、创建、启动、附属到(attach)Compose 项目中定义的所有服务。如果服务已经存在,它会尝试重新创建它们以反映
docker-compose.yml
文件的任何更改。语法:
docker compose up [OPTIONS] [SERVICE...]
常用选项:
-d
,--detach
: 在后台运行容器,不占用当前终端。强烈推荐用于生产或长期运行的服务。--build
: 在启动前强制重新构建镜像(即使镜像已经存在且没有 Dockerfile 更改)。--force-recreate
: 强制重新创建容器,即使服务配置没有变化。--no-build
: 不构建镜像,如果镜像不存在则会报错。--no-deps
: 不启动服务的链接依赖。--no-recreate
: 如果容器已经存在,则不重新创建。--remove-orphans
: 删除 Compose 文件中未定义的、但由上次up
命令创建的容器(即“孤儿”容器)。
注意事项:
初次运行时,它会拉取所需的镜像并构建自定义镜像。
默认情况下,
up
会将服务日志输出到终端。按Ctrl+C
会优雅地停止所有容器;如果使用-d
则不会将服务日志输出到终端。up
命令会默认创建一个网络,让所有服务在其中互相通信。
2. docker compose down
作用: 停止并移除(删除)由
up
命令创建的所有容器、网络以及可选的卷和镜像。语法:
docker compose down [OPTIONS]
常用选项:
-v
,--volumes
: 停止服务后,同时移除相关的具名数据卷(named volumes)。谨慎使用,这会删除所有持久化数据!-rmi all
: 停止服务后,同时移除 Compose 文件中使用的所有镜像(包括构建的和拉取的)。--timeout SECONDS
: 容器停止前的等待时间(默认10秒),超过时间会强制杀死容器。
注意事项:
这是清理整个 Compose 应用的最佳方式。
如果需要保留数据,切勿使用
-v
参数,或只删除特定的卷。
3. docker compose start
作用: 启动已经存在但处于停止状态的 Compose 项目中的服务。它不会创建新的容器。
语法:
docker compose start [SERVICE...]
注意事项: 只有当你之前用
docker compose stop
停止了服务,而不是用docker compose down
移除服务时,这个命令才有用。
4. docker compose stop
作用: 停止已经运行的 Compose 项目中的服务,但不会移除容器。
语法:
docker compose stop [OPTIONS] [SERVICE...]
常用选项:
-t
,--timeout SECONDS
: 定义容器停止前的等待时间。
注意事项: 停止后的容器可以用
docker compose start
快速启动,其数据也会保留。
5. docker compose restart
作用: 停止并重新启动 Compose 项目中的服务。
语法:
docker compose restart [OPTIONS] [SERVICE...]
常用选项:
-t
,--timeout SECONDS
: 定义容器停止前的等待时间。
注意事项: 当你修改了容器的环境变量,但不想重新创建容器时,
restart
是一个方便的命令。
6. docker compose rm
作用: 移除已停止的 Compose 项目中的服务容器。
语法:
docker compose rm [OPTIONS] [SERVICE...]
常用选项:
-f
,--force
: 强制移除,不进行确认提示。-s
,--stop
: 如果容器正在运行,先停止再移除。-v
: 同时移除与容器关联的匿名卷。
注意事项: 通常,
docker compose down
包含了stop
和rm
的功能,所以rm
单独使用较少。
构建与拉取镜像命令
这些命令用于管理 Compose 项目中使用的镜像。
1. docker compose build
作用: 构建(或重新构建)Compose 文件中定义的服务镜像。
语法:
docker compose build [OPTIONS] [SERVICE...]
常用选项:
--no-cache
: 构建时不使用缓存。--pull
: 在构建前总是尝试拉取更新的基础镜像。--progress string
: 设置构建输出的进度模式(auto
,plain
,tty
)。
注意事项: 如果 Dockerfile有更新或你想强制刷新缓存,使用这个命令。
docker compose up --build
包含了此功能。
2. docker compose pull
作用: 拉取 Compose 文件中引用的所有服务镜像的最新版本。
语法:
docker compose pull [OPTIONS] [SERVICE...]
常用选项:
--ignore-buildable
: 忽略需要构建的镜像,只拉取预构建的镜像。--no-parallel
: 串行拉取镜像,而不是并行。--include-deps
: 同时拉取所有依赖服务的镜像。
注意事项: 只拉取镜像,不会构建也不会启动服务。
3. docker compose push
作用: 推送 Compose 文件中定义的所有服务镜像到远程仓库。
语法:
docker compose push [SERVICE...]
注意事项: 容器的镜像必须已经被构建,并且你的 Docker Hub 或其他镜像仓库已登录。
信息与调试命令
这些命令用于获取服务状态、查看日志和执行调试操作。
1. docker compose ps
作用: 列出 Compose 项目中所有服务的容器状态。
语法:
docker compose ps [OPTIONS] [SERVICE...]
常用选项:
-a
,--all
: 显示所有(包括停止的)容器。--format string
: 格式化输出,例如json
或table
。-q
,--quiet
: 只显示容器 ID。
注意事项: 快速查看服务是否运行,状态是否健康。
2. docker compose logs
作用: 显示 Compose 项目中各个服务的日志输出。
语法:
docker compose logs [OPTIONS] [SERVICE...]
常用选项:
-f
,--follow
: 持续跟踪并显示新日志,类似于tail -f
。--no-log-prefix
: 不显示日志的时间戳和容器名称前缀。--since string
: 显示指定时间点之后的日志(例如10m
表示10分钟前,2023-01-01T00:00:00Z
)。--timestamps
: 显示日志的时间戳。--tail N
: 只显示日志的最后 N 行。
注意事项: 调试应用程序问题的关键工具。务必使用
-f
选项实时查看日志。
3. docker compose exec
作用: 在指定服务的运行中容器内执行命令。
语法:
docker compose exec [OPTIONS] SERVICE COMMAND [ARGS...]
常用选项:
-d
,--detach
: 在后台运行命令。-e
,--env KEY=VALUE
: 设置环境变量。-i
,--interactive
: 保持标准输入打开,即使不连接到 TTY。-t
,--tty
: 分配一个伪 TTY(通常与-i
结合使用,即-it
,用于交互式会话,如进入 Shell)。-u
,--user USER
: 指定执行命令的用户或 UID。
注意事项:
容器必须是运行中的状态。
docker compose exec -it my_service bash
是最常用的进入容器内进行调试的方式。
4. docker compose top
作用: 显示 Compose 项目中所有服务的容器进程。
语法:
docker compose top
注意事项: 类似于
docker top
,可以查看容器内部的进程列表。
5. docker compose config
作用: 校验 Compose 文件的语法,并打印出经过解析和扩展后的配置。
语法:
docker compose config [OPTIONS]
常用选项:
-q
,--quiet
: 只检查配置是否合法,不打印输出,通过退出码判断。--services
: 只列出服务名称。--volumes
: 只列出卷名称。
注意事项: 编写或修改 Compose 文件后,先用
config
进行校验是一个好习惯。
6. docker compose port
作用: 打印指定服务暴露给宿主机的端口。
语法:
docker compose port [OPTIONS] SERVICE PRIVATE_PORT
常用选项:
--index N
: 如果有多个容器实例,指定是第几个(从1开始)。
注意事项: 只适用于在
ports
段明确映射的端口。
7. docker compose production
作用: 列出 Compose 文件中使用的镜像的 SHA256 摘要。
语法:
docker compose images [OPTIONS] [SERVICE...]
常用选项:
-q
,--quiet
: 只显示镜像 ID。
注意事项: 对审计或确认镜像版本有用。
高级/不常用命令
1. docker compose run
作用: 在指定服务上运行一次性命令。它会创建一个新的容器(通常是临时的),执行命令,然后退出。
语法:
docker compose run [OPTIONS] SERVICE COMMAND [ARGS...]
常用选项:
--rm
: 命令完成后自动移除容器。强烈推荐!-e
,--env KEY=VALUE
: 设置运行命令时的环境变量。-p
,--publish PORT:HOST_PORT
: 临时发布端口。--entrypoint COMMAND
: 覆盖服务定义中的 ENTRYPOINT。-d
,--detach
: 分离模式,在后台运行命令。
注意事项:
与
exec
的关键区别:run
创建并启动一个新容器来执行命令,而exec
在已有运行中容器内执行命令。适用于数据库迁移、一次性脚本、测试等。
示例:
docker compose run --rm web python manage.py migrate
2. docker compose pause
/ docker compose unpause
作用:
pause
暂停 Compose 项目中的服务容器的所有进程;unpause
恢复被暂停的服务。语法:
docker compose pause [SERVICE...]
/docker compose unpause [SERVICE...]
注意事项: 不会释放资源,只是暂时冻结容器状态,不常用。
3. docker compose kill
作用: 通过发送 SIGKILL 信号(默认)强制停止服务容器。
语法:
docker compose kill [OPTIONS] [SERVICE...]
常用选项:
-s
,--signal SIGNAL
: 指定要发送的信号(例如SIGINT
,SIGTERM
)。
注意事项: 是一种非优雅的停止方式,通常优先使用
docker compose stop
。
4. docker compose cp
(复制文件)
作用: 在主机和容器之间复制文件/文件夹。
语法:
docker compose cp [OPTIONS] <source_path> <service>:<destination_path>
(从主机到容器)docker compose cp [OPTIONS] <service>:<source_path> <destination_path>
(从容器到主机)
注意事项:
需要指定服务的容器名称或 ID。
如果服务有多个实例,需要配合
--index
或明确容器 ID。例如:
docker compose cp ./local_file.txt my_service:/app/remote_file.txt
5. docker compose events
作用: 从各个服务容器实时获取 Docker 事件。
语法:
docker compose events [OPTIONS]
注意事项: 实时监听事件流,用于高级监控或调试。
- 感谢你赐予我前进的力量