ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Docker Compose 核心命令全解析:从入门到实战应用编排

Docker Compose 核心命令全解析:从入门到实战应用编排 1. 从“docker run”到“docker-compose up”为什么我们需要编排工具如果你和我一样是从单打独斗的docker run命令开始接触 Docker 的那么第一次看到docker-compose.yml文件时可能会觉得有点“杀鸡用牛刀”。一个简单的docker run -d -p 80:80 --name my-nginx nginx就能跑起来一个 Nginx 容器为什么还要费劲去写一个 YAML 文件呢这个问题的答案就藏在“应用”和“服务”的区别里。docker run操作的是单个容器它是一个孤立的进程单元。而现代的应用尤其是微服务架构下的应用很少是单一容器就能搞定的。一个典型的 Web 应用栈可能包括一个 Web 应用容器、一个数据库容器、一个缓存容器可能还有一个消息队列容器。这些容器之间需要网络互通、数据共享、启动顺序协调。想象一下每次部署或开发调试时你都需要手动敲四条甚至更多的docker run命令并且要确保它们之间的网络连接、卷挂载、环境变量都正确无误。这不仅是效率低下更是极易出错的。Docker Compose 的出现就是为了解决这个“多容器应用定义与运行”的问题。它允许你用一个声明式的 YAML 文件docker-compose.yml来描述整个应用栈Stack然后通过一组简单的命令来统一管理这个栈的生命周期。从docker-compose up一键启动所有服务到docker-compose down清理所有资源它把琐碎的操作封装成了简洁的工程化流程。所以当你从“玩转单个容器”进阶到“驾驭容器化应用”时Docker Compose 是你必须熟练掌握的工具。它不是什么高深莫测的编排系统如 Kubernetes而是面向开发、测试和单机部署场景的、最接地气的容器编排入门利器。本文将带你彻底吃透 Docker Compose 的核心命令并通过一个从简单到复杂的实战示例让你不仅能看懂命令更能理解其背后的设计逻辑和最佳实践。2. 核心命令全景解读不止于启动与停止很多人对 Docker Compose 的命令认知停留在up和down这就像只学会了汽车的油门和刹车远未发挥其全部效能。实际上Docker Compose 的命令体系围绕应用栈的整个生命周期构建涵盖了构建、启动、查看、交互、停止和清理。我们按功能模块来逐一拆解。2.1 构建与启动up命令的多种面孔docker-compose up是核心中的核心但它远不止一个简单的启动按钮。基础启动docker-compose up这是最常用的命令。它基于当前目录下的docker-compose.yml文件执行以下操作构建镜像如果docker-compose.yml中服务的build字段指定了构建上下文如build: .且镜像尚未构建或代码有更新Compose 会先执行docker build。创建网络为整个 Compose 项目创建一个独立的默认网络通常以项目目录名加_default后缀命名确保所有服务在同一个网络内可以通过服务名直接通信。创建并启动容器为每个服务创建并启动容器。容器命名规则为{项目名}_{服务名}_{序号}。默认情况下up命令会占用当前终端并实时打印所有容器的日志输出stdout和stderr。这对于开发调试阶段观察日志非常方便。后台启动docker-compose up -d-d或--detach参数让服务在后台运行。这是生产环境或长期运行服务时的标准用法。命令执行后立即返回你可以继续使用当前终端。强制重建docker-compose up --build这个参数强制 Compose 在启动前重新构建镜像即使镜像已存在。当你修改了 Dockerfile 或构建上下文中的代码后必须使用此参数或先执行docker-compose build来确保变更生效。一个常见的误区是只修改了代码然后直接up发现容器行为没变就是因为没有重新构建镜像。重新创建容器docker-compose up --force-recreate此参数强制停止并重新创建所有容器即使其配置和镜像没有改变。这在某些特定场景下有用比如你需要容器以全新的状态运行例如容器内生成的临时文件影响了运行或者你修改了某些无法通过热更新生效的容器运行时参数如restart策略。但要注意这会导致容器数据的丢失如果数据未挂载到卷中。仅启动指定服务docker-compose up service1 service2你可以在up命令后接一个或多个服务名Compose 将只启动这些服务及其依赖的服务。这在调试多服务应用中的特定部分时非常有用。例如你的应用有web、api、db、redis四个服务你只想测试web和api可以运行docker-compose up web apiCompose 会自动分析依赖关系确保db和redis如果被依赖也会被启动。2.2 状态查看与日志洞察容器内部启动服务只是第一步如何观察其运行状态和日志至关重要。查看运行状态docker-compose ps这个命令列出当前 Compose 项目中所有服务的容器状态类似于docker ps但只显示与本项目相关的容器。输出信息包括容器名称、所使用的命令、状态Up、Exit、端口映射情况。这是快速确认所有服务是否按预期运行的首选命令。实时追踪日志docker-compose logslogs命令用于查看容器的日志输出。docker-compose logs查看所有服务的日志从启动到当前时刻。docker-compose logs -f-f或--follow参数可以实时跟踪Follow日志输出类似于tail -f。这在调试问题时非常有用。docker-compose logs service_name只查看特定服务的日志。docker-compose logs --tail100只显示最后100行日志避免被历史日志淹没。注意logs命令显示的是容器内进程输出到stdout和stderr的信息。确保你的应用将日志打印到标准输出而不是写入容器内的文件这样才能被 Docker 捕获并通过logs命令查看。这是容器化应用日志处理的最佳实践。查看服务依赖与配置docker-compose config这个命令非常实用但常被忽略。它会解析并输出你当前的docker-compose.yml文件内容同时会合并你可能使用的.env文件或-f指定的其他配置文件中的环境变量。这可以用来验证配置检查 Compose 最终解析出的配置是否正确特别是当使用了环境变量或多个配置文件时。调试变量确认环境变量是否被正确替换。生成标准格式输出一个标准的、变量已被替换的 YAML便于分享或作为其他工具的输入。2.3 交互与执行进入容器世界有时我们需要进入容器内部进行操作或执行一次性命令。进入容器docker-compose exec这个命令用于在正在运行的容器中执行命令。最常用的就是启动一个交互式 Shell。docker-compose exec service_name sh(或bash): 进入指定服务的容器内部。这是调试、查看文件、手动执行操作的直接方式。 它与docker exec类似但无需输入冗长的容器ID或名称直接使用服务名即可。执行一次性命令docker-compose runrun命令用于启动一个新的容器来执行一次性任务任务完成后容器会自动停止。这与up启动长期运行的服务有本质区别。典型场景包括数据库迁移docker-compose run --rm web python manage.py migrate运行测试docker-compose run --rm web pytest执行管理脚本docker-compose run --rm backend ./scripts/seed_data.sh关键参数--rm强烈建议在run命令后加上--rm。这表示命令执行完毕后自动删除该临时容器避免产生大量停止状态的、无用的容器占用磁盘空间。execvsrun的核心区别exec是在已有的、运行中的服务容器内执行命令而run是为执行命令而专门创建一个新的、独立的容器。如果你需要修改一个持久化服务的状态如重启进程用exec如果你需要运行一个独立任务如数据备份、测试用run。2.4 停止、暂停与清理优雅地关闭应用如何停止服务不同命令有不同的语义和后果。停止容器docker-compose stop此命令会停止正在运行的容器但不会删除它们。容器文件系统、网络配置、卷都保持不变。你可以通过docker-compose start重新启动它们。这适用于你暂时不需要服务但想保留其完整状态以备快速恢复的场景。暂停容器docker-compose pause/unpausepause会挂起Freeze容器内所有进程使用 cgroups freezer。容器状态变为Paused它不再占用 CPU 周期但内存中的状态被保留。unpause则恢复运行。这在需要短暂“冻结”应用状态进行调试或检查时有用但日常使用频率不高。停止并清理docker-compose down这是与up相对应的清理命令。它的作用是停止up启动的所有容器。删除这些已停止的容器。删除Compose 为该项目创建的网络默认网络。这是开发完成后或需要彻底重置应用状态时的标准操作。它让环境回到up之前的状态除了持久化卷和镜像。down的进阶参数docker-compose down -v极其重要。这个-v参数会在执行down的常规操作基础上同时删除 Compose 文件中定义的匿名卷在volumes部分声明但未命名的卷。如果你在开发中使用数据库数据存储在匿名卷中那么down -v会清除所有数据请谨慎使用。通常对于命名的数据卷db-data:down -v也不会删除这是为了保护生产或重要数据。docker-compose down --rmi all删除所有与本 Compose 项目相关的镜像。这可以用于彻底清理释放磁盘空间。停止与删除容器docker-compose rm这个命令用于删除已停止的容器。通常在你运行了很多次docker-compose run未加--rm或某些容器异常退出后用于清理。它可以配合-f强制删除或-s删除时同时停止容器如果还在运行。但在大多数工作流中直接使用docker-compose down更为常见和彻底。3. 实战示例构建一个完整的Python Flask Redis访问计数器理论说再多不如动手做一遍。我们通过一个经典的“网页访问计数器”示例将上述命令串联起来。这个应用包含两个服务一个用 Python Flask 写的 Web 应用和一个 Redis 缓存用于存储计数。3.1 项目结构与核心文件解析首先创建项目目录并初始化文件mkdir flask-redis-counter cd flask-redis-counter1.app.py(Flask 应用)from flask import Flask import redis import os app Flask(__name__) # 从环境变量获取Redis主机名在Compose中服务名redis就是主机名 redis_host os.environ.get(REDIS_HOST, localhost) redis_port int(os.environ.get(REDIS_PORT, 6379)) # 连接Redis cache redis.Redis(hostredis_host, portredis_port, decode_responsesTrue) app.route(/) def hello(): # 每次访问计数器加1 count cache.incr(hits) return fHello Docker Compose! 本页面已被访问 {count} 次。\n if __name__ __main__: # 监听所有网络接口端口从环境变量获取 app.run(host0.0.0.0, portos.environ.get(FLASK_PORT, 5000))这个应用逻辑很简单根路径/每次被访问Redis 中的hits键值就增加 1并返回当前计数。2.requirements.txt(Python依赖)Flask2.3.3 redis4.6.03.Dockerfile(构建Flask应用镜像)# 使用官方Python轻量级镜像 FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 复制依赖文件并安装 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY . . # 定义环境变量可在docker-compose.yml中被覆盖 ENV FLASK_PORT5000 ENV REDIS_HOSTredis ENV REDIS_PORT6379 # 暴露端口 EXPOSE 5000 # 启动命令 CMD [python, app.py]4.docker-compose.yml(核心编排文件)version: 3.8 # 指定Compose文件格式版本 services: # Web应用服务 web: build: . # 使用当前目录的Dockerfile构建镜像 ports: - 8000:5000 # 将宿主机的8000端口映射到容器的5000端口 environment: - REDIS_HOSTredis # 通过服务名连接Redis - FLASK_PORT5000 - REDIS_PORT6379 depends_on: - redis # 声明依赖确保redis服务先启动 volumes: - ./app.py:/app/app.py # 挂载代码实现开发时热重载 # 设置健康检查确保应用完全启动 healthcheck: test: [CMD, curl, -f, http://localhost:5000] interval: 30s timeout: 10s retries: 3 start_period: 40s # Redis缓存服务 redis: image: redis:7-alpine # 使用官方Redis镜像 ports: - 6379:6379 # 暴露Redis端口方便宿主机连接测试生产环境通常不暴露 volumes: - redis-data:/data # 使用命名卷持久化Redis数据 command: redis-server --appendonly yes # 启用AOF持久化 # 定义命名卷数据独立于容器生命周期 volumes: redis-data:这个 Compose 文件定义了两个服务 (web和redis) 和一个命名卷 (redis-data)。它清晰地描述了服务间的依赖、网络、端口映射和数据持久化策略。3.2 分步操作与命令实战现在让我们用一系列 Compose 命令来操作这个应用。步骤1构建并启动所有服务后台模式docker-compose up -d执行后终端会输出构建和启动过程。最后使用docker-compose ps查看状态应该看到两个容器的状态都是Up。步骤2验证应用运行打开浏览器访问http://localhost:8000。每次刷新页面计数器都应该增加。你也可以用curl命令测试curl http://localhost:8000步骤3查看实时日志如果你想看 Flask 应用的访问日志可以docker-compose logs -f web你会看到类似GET / HTTP/1.1 200的访问日志。按CtrlC退出日志跟踪。步骤4进入Redis容器查看数据docker-compose exec redis redis-cli进入 Redis 命令行后执行127.0.0.1:6379 GET hits你会看到当前的计数值。输入quit退出。步骤5运行一次性命令例如检查Python环境docker-compose run --rm web python --version这个命令会启动一个临时的web服务容器执行python --version然后停止并删除容器。输出应为Python 3.11.x。步骤6修改代码并热重载编辑app.py文件修改返回的信息例如return fHello Docker Compose World! 本页面已被访问 {count} 次。\n由于我们在docker-compose.yml中为web服务配置了卷挂载- ./app.py:/app/app.py宿主机文件的更改会立即同步到容器内。但 Flask 默认不监听文件变化我们需要让 Flask 重启。有几种方式重启服务docker-compose restart web更优雅的方式在开发时修改 Dockerfile 的启动命令或使用支持热重载的服务器。例如可以将CMD改为CMD [flask, run, --host0.0.0.0, --port5000, --reload]但需要先设置FLASK_APP环境变量。更常见的做法是在docker-compose.yml的web服务环境变量中添加FLASK_ENVdevelopment并在代码中根据环境启用调试模式生产环境切勿这样做。为了演示命令我们使用重启docker-compose restart web再次访问页面就能看到更新后的消息。步骤7停止服务docker-compose stop运行docker-compose ps会看到容器状态变为Exited。但容器和卷都还在。步骤8彻底清理环境docker-compose down运行后容器和默认网络被删除。但命名卷redis-data被保留。运行docker volume ls可以看到它。这是为了保护数据。 如果你想彻底清理包括删除这个命名卷数据会丢失需要手动删除卷docker-compose down -v # 这会删除匿名卷但我们的redis-data是命名卷通常不会被删 docker volume rm flask-redis-counter_redis-data # 手动删除命名卷4. 高级场景与深度配置解析掌握了基础命令和示例后我们来看一些更复杂的场景和配置这些能让你在真实项目中游刃有余。4.1 多环境配置开发、测试与生产一个常见的需求是区分开发、测试和生产环境。Docker Compose 通过多种方式支持1. 使用多个 Compose 文件这是官方推荐的方式。你有一个基础的docker-compose.yml然后通过-f参数指定覆盖文件。docker-compose.yml: 基础配置包含服务的通用定义。docker-compose.override.yml:默认自动加载的文件用于开发环境配置如挂载源代码卷、暴露调试端口。docker-compose.prod.yml: 生产环境配置如移除卷挂载、设置资源限制、使用特定镜像标签。开发时直接运行docker-compose up它会自动合并docker-compose.yml和docker-compose.override.yml。生产部署运行docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d使用基础文件和生产覆盖文件。示例docker-compose.override.yml:version: 3.8 services: web: volumes: - ./:/app # 开发时挂载整个目录实现代码实时同步 - /app/__pycache__ # 可选的避免将缓存文件同步到宿主机 environment: - FLASK_ENVdevelopment - DEBUG1 ports: - 5678:5678 # 可能用于调试器端口示例docker-compose.prod.yml:version: 3.8 services: web: image: myregistry/flask-app:latest # 生产使用构建好的特定镜像 build: . # 可以移除或指向生产Dockerfile volumes: [] # 生产环境通常不挂载源代码卷 environment: - FLASK_ENVproduction deploy: # 可以使用deploy配置资源限制在swarm模式下更有效 resources: limits: cpus: 1 memory: 512M2. 使用环境变量文件.envCompose 会自动读取项目目录下的.env文件你可以在其中定义环境变量并在docker-compose.yml中使用${VARIABLE_NAME}语法引用。这非常适合存储敏感信息如密码或环境差异配置如不同环境的数据库地址。.env 文件示例:COMPOSE_PROJECT_NAMEmyapp_dev DB_PASSWORDsecret123 REDIS_URLredis://redis:6379在docker-compose.yml中引用:services: db: image: postgres environment: POSTGRES_PASSWORD: ${DB_PASSWORD}3. 配置合并的优先级当使用多个文件和.env时配置的合并遵循一定规则。理解docker-compose config命令的价值在这里凸显。运行它可以看到最终生效的完整配置是调试多环境配置问题的利器。4.2 健康检查与依赖管理确保服务稳定启动在之前的示例中我们使用了depends_on。但depends_on仅仅控制容器的启动顺序并不保证服务已经准备好接受请求。例如web服务依赖redisCompose 会先启动redis容器然后立即启动web容器。此时redis进程可能还在初始化中导致web连接失败。解决方案是healthcheckdepends_on的条件模式。services: redis: image: redis:alpine healthcheck: # 为Redis定义健康检查 test: [CMD, redis-cli, ping] interval: 5s timeout: 3s retries: 3 start_period: 10s web: build: . depends_on: redis: condition: service_healthy # 等待redis服务健康状态为healthy这样web服务会一直等待直到redis的健康检查通过即redis-cli ping返回成功后才启动。这大大增加了多服务应用的启动可靠性。4.3 网络与端口策略安全与通信自定义网络默认的网络是桥接网络服务间可通过服务名通信。你也可以定义更复杂的网络拓扑。networks: frontend: driver: bridge backend: driver: bridge internal: true # 内部网络不允许外部连接 services: web: networks: - frontend api: networks: - frontend - backend db: networks: - backend这样web可以访问apiapi可以访问db但web无法直接访问db增加了安全性。端口策略开发环境通常将端口映射到宿主机8000:5000方便访问和调试。生产环境在 Compose 内部服务间通过服务名和内部端口通信即可。通常不建议将数据库、缓存等后端服务的端口映射到宿主机以减少暴露面。只有前端服务如web的端口需要映射出去。4.4 资源限制与重启策略提升健壮性在docker-compose.yml中你可以为每个服务设置资源限制和重启策略这对于防止单个容器耗尽主机资源或确保服务异常退出后自动恢复至关重要。services: web: build: . deploy: # 注意在单机Compose下deploy部分的一些选项如resources可能被忽略最好使用resources顶级键 resources: limits: cpus: 0.5 memory: 512M reservations: cpus: 0.1 memory: 256M # 单机Compose下更通用的资源限制写法Compose文件版本2.x # mem_limit: 512M # mem_reservation: 256M # cpus: 0.5 restart: unless-stopped # 重启策略重启策略 (restart):no默认不自动重启。always总是重启无论退出状态码是什么。on-failure仅在非正常退出非0状态码时重启。unless-stopped总是重启除非用户显式执行docker stop或docker-compose stop停止了容器。unless-stopped是生产环境常见的选择它能在服务器意外重启如系统崩溃、断电恢复后自动拉起服务同时又尊重了管理员的手动停止操作。5. 常见问题排查与效能提升技巧在实际使用中你肯定会遇到各种问题。这里分享一些高频问题的排查思路和提升效率的技巧。5.1 容器启动失败如何快速定位问题查看日志第一时间使用docker-compose logs [service_name]。如果服务启动即退出日志可能一闪而过用docker-compose logs --tail50 [service_name]查看最后几行输出通常错误信息就在这里。检查依赖服务如果日志显示连接被拒绝如Connection refused to redis:6379检查依赖服务如redis是否真的在运行且健康。使用docker-compose ps确认状态并用docker-compose exec redis redis-cli ping测试连通性。进入容器排查如果服务能启动但行为异常可以docker-compose exec [service_name] sh进入容器手动检查配置文件、环境变量、运行目录等。例如检查环境变量printenv检查文件ls -la /app。验证Compose配置运行docker-compose config确保最终的配置和你预期的一致特别是环境变量替换和文件合并的结果。检查镜像构建如果是自定义镜像确保 Dockerfile 构建无误。可以单独运行docker-compose build [service_name]并观察构建输出或者docker images查看镜像是否生成。5.2 性能与资源优化利用构建缓存编写 Dockerfile 时将不经常变动的层如安装系统依赖放在前面经常变动的层如复制应用代码放在后面。在docker-compose build时合理使用--no-cache参数平时开发不用需要彻底重建时才用。使用.dockerignore文件在项目根目录创建.dockerignore忽略不需要复制到镜像中的文件如.git,__pycache__,node_modules,.env,*.log。这能显著减少构建上下文大小提升构建速度。谨慎使用卷挂载开发时挂载源代码卷 (- ./:/app) 很方便但可能会带来性能问题特别是在 macOS 和 Windows 的 Docker Desktop 上。如果遇到文件同步慢的问题可以考虑只挂载必要的文件/目录。使用:delegated或:cached挂载选项具体取决于主机系统来优化性能。对于前端项目在容器内安装node_modules而不是从宿主机挂载可以避免兼容性问题。管理磁盘空间定期清理无用的镜像、容器和卷。# 删除所有已停止的容器、未被任何容器使用的网络、构建缓存 docker system prune -f # 谨慎操作删除所有未被使用的镜像、卷、网络 # docker system prune -a --volumes5.3 命令别名与脚本化提升日常效率频繁输入完整的docker-compose命令很繁琐。我习惯在项目根目录创建一个简单的Makefile或 Shell 脚本 (compose.sh) 来封装常用命令。示例Makefile:.PHONY: up down build logs ps exec-bash restart clean up: docker-compose up -d down: docker-compose down build: docker-compose build --no-cache logs: docker-compose logs -f ps: docker-compose ps exec-bash: docker-compose exec $(service) bash restart: docker-compose restart $(service) clean: docker-compose down -v docker system prune -f使用方式make up,make logs,make exec-bash serviceweb。或者在 Shell 配置文件 (~/.bashrc或~/.zshrc) 中设置别名alias dcupdocker-compose up -d alias dcddocker-compose down alias dcldocker-compose logs -f alias dcpdocker-compose ps alias dcedocker-compose exec alias dcrdocker-compose run --rm这些小技巧能让你在开发和运维中的效率提升一个档次。从一条简单的docker-compose up到复杂的多环境配置、健康检查与资源管理Docker Compose 的命令远不止是启动和停止的开关。它是一套完整的、用于定义和运行容器化应用工作流的语言。理解每个命令背后的意图结合docker-compose.yml的声明式配置你就能将本地开发、CI/CD 流水线和单机部署的体验变得一致且高效。记住最好的学习方式就是动手。找一个你的现有项目尝试用 Docker Compose 把它“组装”起来过程中遇到的所有坑都会成为你熟练掌握这个工具的宝贵经验。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进