Docker Compose 中执行多条命令的方法
技术背景
在使用 Docker Compose 部署应用时,有时需要在一个服务中执行多条命令,例如在启动 Django 应用时,可能需要先执行数据库迁移命令,再启动开发服务器。然而,Docker Compose 默认只能指定一个 command
,因此需要找到一种方法来执行多条命令。
实现步骤
方法一:使用 bash -c
可以使用 bash -c
来执行多条命令,示例如下:
1 2 3 4 5 6 7 8 9
| version: '3' services: web: build: . command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000" volumes: - .:/code ports: - "8000:8000"
|
也可以写成多行形式:
1 2 3 4 5 6 7 8 9 10 11
| version: '3' services: web: build: . command: > bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000" volumes: - .:/code ports: - "8000:8000"
|
方法二:使用 sh -c
对于大多数基于 Unix 的镜像,sh
比 bash
更常用,示例如下:
1 2 3 4 5 6 7 8 9
| version: '3' services: app: build: context: . command: > sh -c "python manage.py wait_for_db && python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
|
方法三:使用单独的临时容器
可以将预启动的操作(如数据库迁移)放在一个单独的临时容器中执行,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| version: '2' services: db: image: postgres web: image: app command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" links: - db depends_on: - migration migration: build: . image: app command: python manage.py migrate volumes: - .:/code links: - db depends_on: - db
|
方法四:使用数组形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| version: '3.1' services: web: build: . command: - /bin/bash - -c - | python manage.py migrate python manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" links: - db
|
方法五:使用 entrypoint
可以创建一个启动脚本,将其作为 entrypoint
执行,示例如下:
1 2 3 4
|
python manage.py migrate exec "$@"
|
1 2 3 4 5 6 7 8 9 10 11 12
| version: '3' services: web: build: . entrypoint: /docker-entrypoint.sh command: python manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - "8000:8000" links: - db
|
核心代码
以下是使用 bash -c
执行多条命令的完整 docker-compose.yml
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| version: '3' services: db: image: postgres web: build: . command: bash -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000" volumes: - .:/code ports: - "8000:8000" links: - db
|
最佳实践
- 选择合适的方法:根据镜像的基础系统和具体需求选择合适的方法。如果镜像中没有安装
bash
,可以使用 sh -c
;如果需要将预启动操作和主服务分离,可以使用单独的临时容器。 - 使用脚本:将复杂的命令逻辑封装在脚本中,提高代码的可读性和可维护性。
- 处理依赖关系:使用
depends_on
确保服务的启动顺序正确,对于需要等待数据库等服务就绪的情况,可以使用 wait-for-it.sh
等工具。
常见问题
镜像中没有安装 bash
如果镜像中没有安装 bash
,可以使用 sh -c
代替,例如:
1 2 3 4 5
| version: '3' services: app: build: . command: sh -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"
|
depends_on
不能保证服务就绪
depends_on
只能保证服务按顺序启动,但不能保证服务已经就绪。可以使用 wait-for-it.sh
等工具来解决这个问题,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12
| version: '3' services: db: image: postgres web: build: . command: > bash -c "./wait-for-it.sh db:5432 -- python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0.0:8000" depends_on: - db
|
变量替换问题
docker-compose
在运行命令之前会尝试解析变量,如果需要 bash
处理变量,需要对美元符号进行转义,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| version: '3.1' services: web: build: . command: - /bin/bash - -c - | var=$$(echo 'foo') echo $$var # prints foo volumes: - .:/code ports: - "8000:8000" links: - db
|