docker-compose编排的服务启动顺序问题

在上一篇中,我们展示了一个基本由docker-composer的编排案例,然而那个案例却有个隐藏的问题:

虽然,我们在myapp服务中指明了依赖于redis服务 ,也就是所docker-compose能够确保容器的启动顺序是先启动redis再启动nodejs,然而容器的启动顺序并不是服务的启动顺序。

假想一下,如果我们的后端服务不是redis,而是一个启动耗时较长的后端应用,compose启动后端容器后,立即就去启动nodejs容器。很可能的结果是nodejs容器启动完成了,但是后端的容器还未到达就绪状态。这时,我们需要一种机制,判断后端容器的就绪状态(如端口是否能连通),只有当后端容器可用后,我们再启动前端容器。

 

优化方案一: 使用wait-for-it.sh 脚本,延迟启动容器

vi wait-for-it.sh  ,内容如下:

#!/bin/sh
 
TIMEOUT=15
QUIET=0
 
echoerr() {
  if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
}
 
usage() {
  exitcode="$1"
  cat << USAGE >&2
Usage:
  $cmdname host:port [-t timeout] [-- command args]
  -q | --quiet                        Do not output any status messages
  -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout
  -- COMMAND ARGS                     Execute command with args after the test finishes
USAGE
  exit "$exitcode"
}
 
wait_for() {
  for i in `seq $TIMEOUT` ; do
    nc -z "$HOST" "$PORT" > /dev/null 2>&1
 
    result=$?
    if [ $result -eq 0 ] ; then
      if [ $# -gt 0 ] ; then
        exec "$@"
      fi
      exit 0
    fi
    sleep 1
  done
  echo "Operation timed out" >&2
  exit 1
}
 
while [ $# -gt 0 ]
do
  case "$1" in
    *:* )
    HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
    PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
    shift 1
    ;;
    -q | --quiet)
    QUIET=1
    shift 1
    ;;
    -t)
    TIMEOUT="$2"
    if [ "$TIMEOUT" = "" ]; then break; fi
    shift 2
    ;;
    --timeout=*)
    TIMEOUT="${1#*=}"
    shift 1
    ;;
    --)
    shift
    break
    ;;
    --help)
    usage 0
    ;;
    *)
    echoerr "Unknown argument: $1"
    usage 1
    ;;
  esac
done
 
if [ "$HOST" = "" -o "$PORT" = "" ]; then
  echoerr "Error: you need to provide a host and port to test."
  usage 2
fi
 
wait_for "$@"

修改docker-compose.yml文件,

version: '3'
services:
  myapp:
    build:
     context: .
     dockerfile: myapp.dockerfile
    ports:
      - "3000:3000"
    volumes:
      - "./wait-for-it.sh:/wait-for-it.sh"
    #links:
    #  - redis 
    depends_on:
      - redis
    entrypoint: sh /wait-for-it.sh redis:6379 -t 5  --
    command: node /myapp/app.js
  redis:
    image: "redis:4-alpine"

wait-for-it.sh脚本的使用,检测依赖的[主机]:[端口]是否可用 ,  -t 指定检测的超时时间,默认15秒。  "--" 后跟的是检测通过后所执行的命令。上例中,会将command作为参数传递给entrypoint指令。 

 

二、优化方案二:使用entrypoint.sh脚本,延迟启动容器

vi entrypoint.sh ,内容如下:

#!/bin/bash
#set -x
#******************************************************************************
# @file    : entrypoint.sh
# @author  : simon
# @date    : 2018-08-28 15:18:43
#
# @brief   : entry point for manage service start order
# history  : init
#******************************************************************************
 
: ${SLEEP_SECOND:=2}
 
wait_for() {
    echo Waiting for $1 to listen on $2...
    while ! nc -z $1 $2; do echo waiting...; sleep $SLEEP_SECOND; done
}
 
#declare DEPENDS
#declare CMD
 
DEPENDS=''
CMD=''
while getopts "d:c:" arg
do
    case $arg in
        d)
            DEPENDS=$OPTARG
            ;;
        c)
            CMD=$OPTARG
            ;;
        ?)
            echo "unkonw argument"
            exit 1
            ;;
    esac
done
 
for var in ${DEPENDS//,/}
do
    host=${var%:*}
    port=${var#*:}
    wait_for $host $port
done
 
eval $CMD

修改docker-compose.yml文件,

version: '3'
services:
  myapp:
    build:
     context: .
     dockerfile: myapp.dockerfile
    ports:
      - "3000:3000"
    volumes:
      - "./entrypoint.sh:/entrypoint.sh"
    #links:
    #  - redis 
    depends_on:
      - redis
    #entrypoint:  
    command: sh /entrypoint.sh  -d 'redis:6379' -c 'node /myapp/app.js'
  redis:
    image: "redis:4-alpine"

注意:

sh /entrypoint.sh  -d 'redis:6379 192.168.11.1:80' -c 'node /myapp/app.js'   这一行既可以放到entrypoint指令中,也可以放到command指令中。 

entrypoint.sh脚本的使用:   -d 引号中跟的是[主机]:[端口] ,有多个以空格隔开,代表要检测的服务。当所检测的服务均可用(端口可连通),则会执行 -c 引号中的命令。使用这个脚本可以同时检测多个依赖项。

你可能感兴趣的:(docker)