在使用docker-compose组织docker时,我们可能需要控制docker的启动顺序,保证container的正常运行。

启动顺序的依赖可以分为两种类型:

  1. containerA依赖containerB启动,只要containerB启动,即可启动containerA
  2. containerA依赖containerB启动,但containerA依赖containerB中某些耗时操作(初始化数据、建立一定数量的tcp连接),在这些操作完成后,containerA才能启动,否则无法正常运行

对于情况1,可以使用熟悉的方案,即depens_on处理,当containerB created后,才会触发containerA的创建。

对于情况2,处理方式较为复杂。

docker-compose format v2

在docker-compose v2中,可以通过监听container的healthcheck状态进行,示例如下:

假设我们有应用myapp依赖mysql启动并初始化数据(注意,mysql容器启动后数据初始化并未完成,需要通过访问数据库内容进行确认),那么我们在mysql service中加入:

healthcheck:
      test: ["CMD-SHELL", 'mysql --user=user --password=pwd --database=mydb --execute "select * from mytable;"']
      interval: 10s
      timeout: 5s
      retries: 4
      start_period: 10s

其中test部分包含了用于执行的shell指令,当shell指令的执行结果code(并非具体指令返回的数据,而是shell执行是否成功)为0时,意味着数据库的表已经存在(此处的验证语句可自行替换为所需的逻辑),healthcheck部分将把mysql容器的状态置为healthy。

此时意味着我们可以启动myapp容器了,我们在myapp service中加入:

depends_on:
      mysql:
        condition: service_healthy

意味着当mysql service处于healthy状态后才会开始启动myapp service。

该方法在format v2.4中测试成功,参考链接如下:

Compose file version 2 reference | Docker Documentation

docker-compose format v3

在docker-compose v3中,由于depends_on condition已经被废弃,因此我们需要采取其他方法。

官方提示:

There are several things to be aware of when using depends_on:

depends_on does not wait for db and redis to be “ready” before starting web - only until they have been started. If you need to wait for a service to be ready, see Controlling startup order for more on this problem and strategies for solving it.
The depends_on option is ignored when deploying a stack in swarm mode with a version 3 Compose file.

官方的建议是采用wait-for-it.sh方法,在检测到外部依赖ready后再尝试启动。

wait-for-it是一个简单的shell脚本,能够尝试连接port、curl等操作,可以控制超时时间和外部依赖ready后的执行动作,参考:GitHub - vishnubob/wait-for-it: Pure bash script to test and wait on the availability of a TCP host and port

 我们需要在docker-compose.yaml中加入:

command: ["./wait-for-it.sh", "db:3306", "--", "python", "myapp.py"]

在db端口能够连接后执行myapp的启动脚本。

此处的问题在于,mysql port能够连接并不意味着数据初始化完成,依赖端口是否能够连接进行判断不正确,而尝试查询mysql内容需要支持mysql协议,提高了复杂度。

比较上述两种方法,v2版本的定义更加灵活,能够实现细粒度的控制,如果有此类需求,选择docker-compose format v2更合适。