赞
踩
docker-compose可以方便组合多个 docker 容器服务, 但是, 当容器服务之间存在依赖关系时, docker-compose 并不能保证服务的启动顺序。docker-compose 中的 depends_on 配置是容器的启动顺序, 并不是容器中服务的启动顺序。那我们来看看如何 docker-compose 顺序启动微服务的问题
足够的容错和重试机制,比如从配置中心获取配置文件,服务消费者可以不断的重试直到连上为止,这里就用到了docker-compose 中的 restart配置,docker-compose.yml如下:
- version: "3"
- services:
- # 指定服务名称
- #服务注册与发现中心
- simonEureka:
- image: simon/eureka-server:2.0.1-SNAPSHOT
- hostname: simonEureka
- ports:
- - "8100:8100"
- #配置中心
- simonConfig:
- image: simon/config-server:2.0.1-SNAPSHOT
- hostname: simonConfig
- ports:
- - "8101:8101"
- depends_on:
- - simonEureka
- restart: always
- #路由网关
- apigateway:
- image: simon/apigateway:2.0.1-SNAPSHOT
- ports:
- - "8102:8102"
- depends_on:
- - simonEureka
- - simonConfig
- restart: always
- #监控平台
- admin:
- image: simon/admin:2.0.1-SNAPSHOT
- ports:
- - "8103:8103"
- depends_on:
- - simonEureka
- - simonConfig
- restart: always

docker-compose.yml进行拆分,分成两部分部署, 将要先启动的服务放在一个docker-compose中,后启动的服务放在第二个docker-compose中,启动两次,两者使用同一个网络,启动命令示例:
$ docker-compose -f docker-compose-commond.yml up
同步等待,使用shell脚本阻止当前服务启动,直到所需依赖的服务全部启动之后再启动当前服务。
下面我将详细的讲述第三种解决顺序启动问题的方案。部署的微服务清单如下:
服务名 | 端口 | 服务说明 | 依赖服务 | 启动优先级(优先级越高越先启动) |
eureka-service | 8100 | 服务注册与发现 | — | 1 |
config-server | 8101 | 配置中心 | eureka-server | 2 |
apigateway | 8102 | 网关服务 | eureka-server,config-server | 3 |
admin | 8103 | 监控服务 | eureka-server,config-server | 3 |
由于各微服务的镜像构建配置差不多,这里只列举配置中心的配置:
- <!-- Docker maven plugin -->
- <plugin>
- <groupId>com.spotify</groupId>
- <artifactId>docker-maven-plugin</artifactId>
- <version>1.0.0</version>
- <configuration>
- <imageName>simon/${project.artifactId}:${project.version}</imageName>
- <!--<dockerDirectory>src/main/docker</dockerDirectory>-->
- <forceTags>true</forceTags>
- <baseImage>java</baseImage>
- <!--安装镜像所需要的软件-->
- <runs>
- <!--同步 /etc/apt/sources.list 和 /etc/apt/sources.list.d 中列出的源的索引,这样才能获取到最新的软件包-->
- <run>["apt-get","update"]</run>
- <!--安装netcat-->
- <run>["apt-get","-y","install","netcat"]</run>
- </runs>
- <entryPoint>["java","-jar","/${project.build.finalName}.jar"]</entryPoint>
- <resources>
- <resource>
- <targetPath>/</targetPath>
- <directory>${project.build.directory}</directory>
- <include>${project.build.finalName}.jar</include>
- </resource>
- </resources>
- </configuration>
- </plugin>

runs标签表示在构建镜像的时候,会顺序执行标签run中的命令,因为后面顺序启动微服务需要镜像中包含netcat,所以在构建镜像的时候要进行安装。
下面一共提供两种脚本,但前提是镜像中必须如上一节安装netcat
- #!/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 "$@"

查看使用示例输入一下命令:
./wait-for.sh --help
示例:
- $ ./wait-for.sh www.baidu.com:80 -- echo "baidu is up"
-
- baidu is up
- #docker compose编排微服务脚本version: "3"services:
- # 指定服务名称
- simonEureka:
- image: simon/eureka-server:2.0.1-SNAPSHOT
- hostname: simonEureka
- ports:
- - "8100:8100"
- simonConfig:
- image: simon/config-server:2.0.1-SNAPSHOT
- hostname: simonConfig
- ports:
- - "8101:8101"
- volumes:
- - "./wait-for.sh:/wait-for.sh"
- entrypoint: "sh /wait-for.sh ibaseEureka:8100 -- java -jar /config-server.jar"
实际使用中, 可以将 wait-for.sh 打包到发布的镜像之中, 不用通过 volumes 配置来加载wait-for.sh脚本;
entrypoint配置会覆盖maven docker插件entrypoint标签的配置而执行,这里就是控制服务启动顺序的关键配置。
- #!/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
-
- 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
- #避免执行完命令之后退出容器
- tail -f /dev/null

这个脚本有 2 个参数,:
-d: 需要等待的服务和端口,例如:simonEureka:8080,simonEureka:8080,simonConfig:8081;
-c: 等待的服务和端口启动之后, 自己的启动命令,例如:java -jar eureka.jar
- #docker compose编排微服务脚本version: "3"services:
- # 指定服务名称
- simonEureka:
- image: simon/eureka-server:2.0.1-SNAPSHOT
- hostname: simonEureka
- ports:
- - "8100:8100"
- simonConfig:
- image: simon/config-server:2.0.1-SNAPSHOT
- hostname: simonConfig
- ports:
- - "8101:8101"
- depends_on:
- - simonEureka
- volumes:
- - "./entrypoint.sh:/entrypoint.sh"
- environment:
- SLEEP_SECOND: 4
- tty: true
- entrypoint: /entrypoint.sh -d simonEureka:8100 -c 'java -jar /config-server.jar';
- apigateway:
- image: simon/apigateway:2.0.1-SNAPSHOT
- ports:
- - "8102:8102"
- depends_on:
- - simonEureka
- - simonConfig
- volumes:
- - "./entrypoint.sh:/entrypoint.sh"
- environment:
- SLEEP_SECOND: 4
- tty: true
- entrypoint: /entrypoint.sh -d simonEureka:8100,simonConfig:8101 -c 'java -jar /apigateway.jar';
- admin:
- image: simon/admin:2.0.1-SNAPSHOT
- ports:
- - "8103:8103"
- depends_on:
- - simonEureka
- - simonConfig
- volumes:
- - "./entrypoint.sh:/entrypoint.sh"
- environment:
- SLEEP_SECOND: 4
- tty: true
- entrypoint: /entrypoint.sh -d simonEureka:8100,simonConfig:8101 -c 'java -jar /admin.jar';

实际使用中, 可以将 entrypoint.sh 打包到发布的镜像之中, 不用通过 volumes 配置来加载entrypoint.sh脚本;
entrypoint配置会覆盖maven docker插件entrypoint标签的配置而执行,这里就是控制服务启动顺序的关键配置。
其它服务都在等待simonEureka服务启动,这样就实现了服务的顺序启动,最终所有服务全部启动,如下是注册服务信息:
- $ docker-compose up
-
- [root@kingbal simon2.0]# docker-compose up
- Starting simon20_simonEureka_1 ... done
- Starting simon20_simonConfig_1 ... done
- Starting simon20_admin_1 ... done
- Starting simon20_apigateway_1 ... done
- Attaching to simon20_simonEureka_1, simon20_simonConfig_1, simon20_admin_1, simon20_apigateway_1
- simonConfig_1 | Waiting for simonEureka to listen on 8100...
- simonConfig_1 | waiting...
- admin_1 | Waiting for simonEureka to listen on 8100...
- admin_1 | waiting...
- apigateway_1 | Waiting for simonEureka to listen on 8100...
- apigateway_1 | waiting...
- ......
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。