如何在 docker-compose 中执行多条命令?
command 字段用 sh -c "cmd1 && cmd2 && cmd3" 串多条命令,YAML 用 > 折叠多行书写;更规范的做法是把初始化逻辑放 entrypoint 脚本。本文给出写法与注意事项。
写法:在 command 里用 shell 串联——command: > sh -c "命令1 && 命令2 && 命令3"(> 是 YAML 折叠语法,多行会拼成一行)。&& 表示前一条成功才执行下一条;无论成败都执行用 ;。
基本写法
services:
app:
build: .
command: >
sh -c "python manage.py migrate &&
python manage.py collectstatic --noinput &&
exec python manage.py runserver 0.0.0.0:8000"
典型场景:启动前执行一次性命令再起服务。示例 runserver 仅供开发;多副本启动时迁移可能并发,应使用独立迁移任务或锁协调。command 不会自动启动 shell,且仍需核对镜像原有 ENTRYPOINT。
更规范的做法:entrypoint 脚本
命令多了之后 command 会越来越难读,推荐抽成脚本:
#!/bin/sh
set -e
python manage.py migrate
python manage.py collectstatic --noinput
exec gunicorn -w 4 app.wsgi:application
services:
app:
entrypoint: ["/app/entrypoint.sh"]
好处:set -e 会在许多命令失败时退出,但条件、管道等有例外,关键步骤应显式检查、exec 让主进程成为 PID 1(能收到停止信号)、脚本可独立测试、逻辑可版本管理。
注意事项
- 最后一条命令要以前台运行:主进程退出 = 容器退出。不要在末尾加
&或 daemonize。 &&与;的区别:迁移失败就不该起服务,用&&。- 信号处理:容器停止信号通常发给 PID 1,使用 exec 才能让最终服务替换 shell;前置命令执行期间也需考虑中断;若需要同时跑多个长期进程,该拆成多个服务(sidecar)或用 supervisord。
- 依赖顺序:迁移需要数据库先就绪,配
depends_on的condition: service_healthy+ 健康检查,或在脚本里做等待重试。
初始化失败时,先保存迁移命令的错误输出。需要跨重建保留记录的服务,可按观测云 容器日志采集指南配置采集与轮转,并用一次失败迁移验证日志是否带有服务名、容器标识和时间;不要等容器删除后再启用采集。
常见问题(FAQ)
Q:YAML 里 > 和 | 有什么区别?
A:> 折叠(换行变空格,适合拼成一条命令);| 保留换行。两种都可以配合显式 sh -c 使用,选择取决于是否需要保留换行,不能统一要求用 >。
Q:command 和 entrypoint 都写会怎样?
A:command 作为参数传给 entrypoint;是否执行这些参数取决于入口程序,入口脚本需用 exec "$@" 等方式显式执行,覆盖 entrypoint 还可能绕过镜像初始化。
Q:能在 command 里写 shell 变量和判断吗?
A:可以,走 sh -c 就是完整 shell 语法,容器内展开的变量要避免被 Compose 提前插值,例如写 $$HOSTNAME;复杂了就该进 entrypoint 脚本。