分析和思路

我们在docker运维的过程中经常会遇到一个问题,我们启动一个docker-compose.yml文件时,虽然我们使用depends_on属性用依赖关系定义了两个容器的启动顺序,但这个顺序仅仅是docker创建启动的顺序,而并不是在上一个容器完全启动成功的情况下启动下一个容器。
比如一个docker-compose.yml脚本中有mysql和依赖此mysql的一个java程序a容器,虽然a容器在mysql启动之后启动,但还是存在a容器启动的时候mysql容器还在启动中,还无法提供可用的数据库连接,这就导致一个问题,a容器此时启动异常或者失败,虽然部分有做异常情况快速失败停止程序触发docker重启机制,但也会导致a容器多次重启。
甚至在部分服务器资源有限的情况下,多个容器同时启动,但因为数据库等容器还未启动导致其他服务多次重启互相抢占系统资源,导致数据库容器也无法正常启动,陷入死循环,甚至严重的导致docker服务直接无响应。

为了实现上诉a容器在mysql容器完全启动可以提供数据库连接的情况下再启动,就需要在a容器中加入一个逻辑来检查等待mysql的3306端口已经打开,然后执行a容器的启动程序语句。

在这里用到了github上的一个开源项目wait-for-it,可以看到其中有一个shell文件wait-for-it.sh,这个自行去github项目中下载,这里以下面的docker-compose.yml为例:

version: '3'
services:
  mysql:
    restart: always
    image: mysql:5.6
    ports:
      - "3306:3306"
    env_file:
      - env/mysql.env
  app:
    image: app:latest
    restart: always
    env_file:
      - env/mysql.env
    ports:
      - 8054:8054
    depends_on:
      - mysql
    volumes:
      - ./wait-for-it.sh:/wait-for-it.sh
    command: ["/wait-for-it.sh", "-t", "0", "mysql:3306", "--", "java", "-jar", "/app.jar"]

这里将wait-for-it.sh挂载到app容器内,然后设置执行语句command: ["/wait-for-it.sh", "-t", "0", "mysql:3306", "--", "java", "-jar", "/app.jar"],其中mysql:3306代表检查mysql容器的3306端口是否可用,-t 0代表设置超时为0代表禁用超时功能,即wait-for-it脚本一直等mysql服务可用之后再执行后续命令,java -jar app.jar就是app容器原本的java程序启动命令。

实战操作

有两个容器:AListAList_OCR_Server容器,前者需要依托后者的验证码接口,故需要先启动AList_OCR_Server容器,等待该容器9898端口生效后,再启动AList容器,创建docker-compose.yml,如下:

version: '3'
services:
    AList_OCR_Server:
        container_name: AList_OCR_Server
        restart: always
        ports:
            - 9898:9898
        image: xhofe/ddddocr_server:main

    AList:
        container_name: AList
        restart: always
        volumes:
            - /etc/alist:/opt/alist/data
            - ./wait-for-it.sh:/opt/alist/wait-for-it.sh  # 将本地的wait-for-it.sh挂载到容器中
        ports:
            - 5244:5244
        environment:
            - PUID=0
            - PGID=0
            - UMASK=022
        depends_on:
            - AList_OCR_Server  # 等待AList_OCR_Server容器启动(但不会检查端口是否生效)
        image: xhofe/alist:latest
        command:  # 覆盖容器启动后默认执行的命令
            [
                '/opt/alist/wait-for-it.sh',  # 执行wait-for-it.sh脚本
                '-t',  # -t 0 代表禁用超时
                '0',
                'AList_OCR_Server:9898',  # 等待AList_OCR_Server:9898生效
                '--',
                '/entrypoint.sh'  # AList原执行命令
            ]

docker-compose 可以指定配置文件

docker-compose -f <FileName.yml> up -d