找回密码
 注册
关于网站域名变更的通知
查看: 273|回复: 1
打印 上一主题 下一主题

docker compose 服务启动顺序控制

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-8-10 15:11 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
概要! ]- o( j! I& Q6 ?, h
docker-compose 可以方便组合多个 docker 容器服务, 但是, 当容器服务之间存在依赖关系时, docker-compose 并不能保证服务的启动顺序.
& Z0 V/ a: u: U% q0 m8 J- ?9 i' K/ i! b' _+ ~9 c( m
docker-compose 中的 depends_on 配置是容器的启动顺序, 并不是容器中服务的启动顺序.
, W+ t* b( S% R, T9 y$ F9 i2 K& n: Q# s7 N; v% A5 n, }6 |
问题重现
( C) X& s/ r5 R! z# `首先, 我们构造一个示例, 来演示 docker-compose 带来的问题. docker-compose.yml 文件如下:" k) _, u  p+ g8 z
' ^4 J4 K# e5 d2 a
version: '2'( B0 i* n6 O0 A: _: c( C$ }
services:
+ U/ e$ w* J; M( \4 ~: B  web:
: v& w6 ~5 K- I+ F) e    image: ubuntu:14.04" f) P4 J8 L+ T& }! C9 c# v
    depends_on:; {2 d3 q; A- o
      - web- q" q7 h2 O0 Q9 O8 v/ U* ?
    command: nc -z database 3306
/ R2 k: u. O0 W2 v5 u/ K% }, Z; ~, a8 l3 U7 S
  database:8 C6 }) x5 g! R# g
    image: ubuntu:14.04; `9 s: C  s: u
    command: >$ P* B* f/ h# P5 I, b6 E
      /bin/bash -c '
8 ~0 M# K; E. `+ r# y0 W# m      sleep 5;" \$ Y" [. p+ G
      echo "sleep over";
4 X; {! h' G# P. |, g' T) m      nc -lk 0.0.0.0 3306;5 }9 V3 N4 x$ M- q, H/ u
      '6 s5 ?+ u/ K3 `& t3 ]- h1 Q: d% z
启动后, 可以发现, 确实是先启动 database, 后启动 web, 但是 database 中的服务是在大约 5 秒后才完成的, 所以导致 web 的启动失败.
- X9 G7 ~* j- L2 H" c, `4 l! T
( [" A9 y. L( m( {! O$ docker-compose up
6 I" H  m) S" y# E( @$ e/ dCreating tmp_database_1 ... done
" y" ^8 F3 I8 G! m, E% K8 gCreating tmp_database_1 ...
' Y0 B5 p* f% ~; |: XCreating tmp_web_1      ... done
1 k" G3 r! p! {Attaching to tmp_database_1, tmp_web_1
8 V) L/ t, [" W7 g9 S! ltmp_web_1 exited with code 1( {  m2 l6 L6 m  g
database_1  | sleep over6 _. ?' N( L6 I' S7 l0 w+ P; C
问题解决方式 1.0$ @  G6 l$ m. R4 w( `! O
修改 web 的启动脚本, 等待 database 的端口通了之后再启动服务) J% r- t4 h' b- Y
  `9 W+ {. p% A# N& i
version: '2'
9 n/ H. ?9 j  B8 Z' L5 _services:9 e5 L* H3 e( p! z, V
  web:( H3 }* `0 j3 G2 X1 A/ \
    image: ubuntu:14.04( o: u3 v% |% V, T% h$ U5 A
    depends_on:
8 W. E* f& Q" R8 C- Z/ R      - database
4 H1 J( u7 X3 G' O, \! O2 f    command: >
. W" W8 M9 ^9 Q) R1 C      /bin/bash -c '
: z  r4 I: f! a- I      while ! nc -z database 3306;0 p" i$ G" M0 w( \  p* `5 q
      do
7 k& R5 O$ c" w$ p        echo "wait for database";$ i/ _) O, G  i
        sleep 1;& w. P/ H. P/ k3 B6 i) t
      done;" f+ v" q- i: B6 j9 _) h
) f/ o- N+ L9 q9 h6 u% M
      echo "database is ready!";0 d+ P0 h9 t) @" _6 f3 Q
      echo "start web service here";
3 k& r$ S7 R$ \. B8 V      '* w" \3 s8 V+ A1 H* f/ I9 w

7 Z4 Q- o: Q# T$ C( [3 f8 X  database:7 {; d; v+ R1 _0 r/ v+ N( y9 B
    image: ubuntu:14.04
, E' z. F- g+ h8 J5 E% b4 f1 z    command: >+ f% ?( p  e0 x
      /bin/bash -c '
4 k" Q- D* ?$ N! o; i$ Y" H; K      sleep 5;
9 a$ V2 X/ U/ x# ~) J      echo "sleep over";
' X7 N% Q; Z" W) O2 y- l0 Y' f      nc -lk 0.0.0.0 3306;
+ `/ G; a. S) q0 _) p8 H      '
* k; [9 q" {4 h* w% u再次启动,. d2 c- e7 t3 Q2 {

5 y# x3 z2 u" {" {; J3 U! M$ docker-compose up
7 L; x. e6 G) _5 HCreating tmp_database_1 ... done5 Z9 a$ g& P' W8 o% P. p
Creating tmp_database_1 ...
2 ~& j- P. }! N, RCreating tmp_web_1      ... done
* S" _; f  J$ Y# Y) i% |! r( GAttaching to tmp_database_1, tmp_web_1
/ u8 f  I- Y( `4 Eweb_1       | wait for database
/ y$ D! V1 T' l2 ~web_1       | wait for database
3 c2 ~) g: ]7 c# N2 W+ i/ E+ t, Vweb_1       | wait for database
7 {# _: G1 f1 B/ a& i% Zweb_1       | wait for database) Q/ L% @3 d6 C& ]
web_1       | wait for database
( \% d7 z2 _4 F, _database_1  | sleep over
1 x$ T9 [0 p8 W0 aweb_1       | database is ready!% X$ x4 w' K! K0 m4 j  a
web_1       | start web service here
. T0 U! H% a# W, k+ W" I1 I. Ktmp_web_1 exited with code 0
2 o1 ^6 t. e/ d. h. s% u4 a/ Wweb 会在 database 启动完成, 端口通了之后才启动.
6 ^- S* ]( \" v: P/ Y6 L: K& P# s0 ~  M6 f! b5 i1 F  D5 t$ A( I
问题解决方式 2.0( j; l1 x: r; M; `( C( v; F( z
上面的解决方式虽然能够解决问题, 但是在 yaml 中直接插入脚本不好维护, 也容易出错. 如果有多个依赖, 或者多层依赖的时候, 复杂度会直线上升.
) g: Y( }2 }- E* W7 G( _
- P2 s- B5 d6 d% {' O$ D/ l; W; R所以, 要封装一个 entrypoint.sh 脚本, 可以接受启动命令, 以及需要等待的服务和端口. 脚本内容如下:
$ ~; n3 ~$ H! Z; O9 @
. n" t- J0 X. E, B#!/bin/bash; B7 j* K! i: i
#set -x* W& Y4 X( K3 _
#******************************************************************************
9 z. [. x1 f/ _/ q* |# @File    : entrypoint.sh
" }3 _+ Y7 `9 z8 n# @author  : wangyubin8 t' [6 O1 N' b
# @date    : 2018-08- 1 10:18:43. [1 |2 e  ~7 P) b0 k. `3 R
#
) T9 b* n( c) @' E3 X# @brief   : entry point for manage service start order
! p( L" `' V. H# history  : init
9 t3 [- O4 a6 ?$ j#******************************************************************************
5 ~: [, h  ]. G7 l, K9 O7 D" q& p- E/ c" P- `" b: [5 L
: ${SLEEP_SECOND:=2}; J6 c; P3 ~% e% U' x1 f
  O' ]4 {5 ]+ \3 v1 Q$ Z9 t; F
wait_for() {
7 j$ l  g: b) \    echo Waiting for $1 to listen on $2...
+ s' g' `) q* y- M; W    while ! nc -z $1 $2; do echo waiting...; sleep $SLEEP_SECOND; done
6 M3 [! a4 B( s. o7 N}
  e: x5 F5 `  ]# D1 G8 l; }6 o; |4 l; j
declare DEPENDS
* [& x% V/ d' q2 mdeclare CMD5 p' G7 H7 D& o' G2 [

! T4 A* Y. F- s* \7 w% Pwhile getopts "d:c:" arg
  ]- X; u& S( a" u4 q+ hdo
$ e5 K. j. b# ~# W* S% z    case $arg in
# G$ S( J8 Y* m4 S        d)
0 b& x! i  b6 W: T7 K            DEPENDS=$OPTARG
* M4 H0 W5 c1 |  ]$ u3 j            ;;
: b0 v0 |6 }6 `3 E        c)/ _' y& \4 `& r# ~( x( j
            CMD=$OPTARG
6 S/ V7 M$ ?* K7 z( T3 p            ;;
  E' n7 T# M$ n0 S7 V, U2 A        ?)5 i. j1 b2 a. I9 [3 o: f! ?
            echo "unkonw argument") A. y; `) i# A3 i: o* @
            exit 1! M, h" Z: Q  {# k
            ;;( C) N1 D/ e$ [3 n
    esac
! s4 z, K: ?( _done" t$ y  n! |# Y& \0 x& s
- y$ f1 Y8 M5 T; Z8 B6 d; {, E  p' O
for var in ${DEPENDS//,/ }
* r' Y! n, f' Y3 ~do1 `( I6 i5 ]$ |: H0 W4 @1 v/ a7 z0 d" g7 Q
    host=${var%:*}, l% H- n* v9 ]: }( p
    port=${var#*:}/ E0 m2 n& w& `  O
    wait_for $host $port+ u$ v3 P  b, }: B
done
9 @6 i  M* d/ I7 U) N8 W2 u1 t& [* O+ f) p( ?" l6 ^" z
eval $CMD7 h7 _# k+ O3 s1 N
这个脚本有 2 个参数, -d 需要等待的服务和端口, -c 等待的服务和端口启动之后, 自己的启动命令
! S4 d6 c' M6 p% c  z/ L  ?  g
) g2 g' b+ D' n! O修改 docker-compose.yml, 使用 entrypoint.sh 脚本来控制启动顺序./ B2 {- p" o4 p8 Z: f
6 u# |) ]. p7 U
version: '2'2 n+ T  X% I( ~
services:3 o' Q, Z9 Y2 E4 p: ~9 P+ _
  web:
1 ~* |# O9 M4 E    image: ubuntu:14.049 `5 \. N- z6 h3 H( |+ }! ?! r; _
    depends_on:
5 t7 a2 k5 \6 j  L0 Z9 X3 B# D      - database
: v8 T# K7 c; P. M# N( @) \    volumes:% ?6 B5 ^, Z# T- z0 O& D, y# N
      - "./entrypoint.sh:/entrypoint.sh"/ l  A8 _1 z# H8 y  Z8 |
    entrypoint: /entrypoint.sh -d database:3306 -c 'echo "start web service here"';8 o/ E4 k: t& J3 c. D
+ u2 S$ B: a* @% ?
  database:
( y6 R1 t# U; \) O& i9 J! q    image: ubuntu:14.04
2 r& c' A  g) G    command: >
  Z- r& W8 U$ R0 j; T      /bin/bash -c '8 C( I8 T, Q4 \8 g
      sleep 5;
% G5 {: c" m5 S6 ~9 [      echo "sleep over";
, t& Y' X  O/ ^, T# s      nc -lk 0.0.0.0 3306;0 e' o5 g, m) u
      '
; b* X6 P2 O* m+ Y实际使用中, 也可以将 entrypoint.sh 打包到发布的镜像之中, 不用通过 volumes 配置来加载 entrypoint.sh 脚本.9 h; I. l: ~( P; i
0 `' I3 E' Z& c# p3 k- X5 u4 k
测试结果如下:& N- d/ j( F; [

8 _$ h+ n. X$ h# n. M$ docker-compose up3 r. ~- `) |, `+ w' ^
Starting tmp_database_1 ... done0 |0 J# u7 z' |: Y! s; V
Starting tmp_web_1 ... done
* j. @2 z& t1 D- X8 a6 BAttaching to tmp_database_1, tmp_web_12 @4 N0 i# t. n7 v% g
web_1       | Waiting for database to listen on 3306...9 B# `3 [- E: r2 t* H1 F
web_1       | waiting...% U0 s9 W0 r, z: G0 A5 B
web_1       | waiting...: ~; A' c! q4 O+ i
web_1       | waiting...
& J( G: `) w( Q  T3 A- Y  g0 edatabase_1  | sleep over
" X% {) V8 t  {. c) Q3 @5 Nweb_1       | start web service here4 E1 A, Q' T1 v  i2 ~* N. p
tmp_web_1 exited with code 0, P+ D" d  H+ l4 q
补充6 }. F+ s8 C$ \0 ~+ K
依赖多个服务和端口/ @0 V+ U& F6 E' b
使用上面的 entrypoint.sh 脚本, 也可以依赖多个服务和端口, -d 参数后面的多个服务和端口用逗号(,)隔开.
* C" L4 h1 L' K5 a3 C) L' G( n+ f0 o. ?: i( C: V
version: '2', W- K  r  s$ J. K- Y3 G' b
services:
* [' w4 P3 x3 j$ D' m. v( N: s  web:
% x; H% f+ `& c! D6 b6 c: l    image: ubuntu:14.04
9 |6 x" b5 w" `    depends_on:
, i# O  [  a7 n% W      - mysql
4 u! c* U9 }% d8 R8 Q! Q7 U      - postgresql( m' T. T7 `1 }" w; J
    volumes:
- u- ^7 y. {9 ?% D; q6 x      - "./entrypoint.sh:/entrypoint.sh"
: c" d" s/ K! \1 n    entrypoint: /entrypoint.sh -d mysql:3306,postgresql:5432 -c 'echo "start web service here"';& c* k' Z- [7 Q; x$ [4 |
6 R3 {1 n% a9 i0 t6 P9 y$ }
  mysql:
5 B$ R. @* h& S    image: ubuntu:14.04, c0 S. p" B7 \& J: U
    command: >. y) c8 I" G3 x
      /bin/bash -c '+ r) Z; k, s/ ~8 D& U3 h( c
      sleep 4;3 ~+ t1 k- L4 {
      echo "sleep over";3 ~* t* ?' A  \+ z. H
      nc -lk 0.0.0.0 3306;, F/ \; j# p1 y& Z7 m
      ') J# v2 `" J( n* {
  postgresql:
+ @, p! ~. f* d& `  a/ y; d. E    image: ubuntu:14.04
5 f% `) X5 @) w+ U    command: >( Q* I) f8 [1 T  K' w: v; E& ^* g
      /bin/bash -c '
3 ]+ Z7 g6 u* Q* k      sleep 8;
0 q. h* X9 U6 I/ C5 _% r  e      echo "sleep over";. z7 g# g( V  S4 l" }. `/ f9 M1 O8 m
      nc -lk 0.0.0.0 5432;, h  R/ M! Z3 ^7 d! {
      '
' D. C* X8 K+ Z+ z. r执行的效果可以自行尝试.9 H1 u% W& `7 }+ _4 W( s+ F) c

: I- @$ t& `9 J' H: p# P$ Y尝试间隔的配置7 |3 p+ W# Z! P% H( }
每次尝试连接的等待时间可以通过 环境变量 SLEEP_SECOND 来配置, 默认 2 秒 下面的配置等待时间设置为 4 秒, 就会每隔 4 秒才去尝试 mysql 服务时候可连接.
4 \* d5 _$ c. p0 `. e$ Z; `5 b5 h# u: `& J
version: '2'; T( R+ l0 A' U% e9 l
services:
9 u2 H, @% S# M% b# ?  web:
, g, g. b) v" m: H0 M+ D& {    image: ubuntu:14.04) c( Z2 q# s/ h% x1 Y# e3 |
    environment:( N5 u: ]) {* Z9 b! p( ?
      SLEEP_SECOND: 4
  m0 u0 Y# B* a# i) S* w    depends_on:$ y( S# H  W! c, T( F
      - mysql6 F5 K$ ]4 m. S8 j& S" f
    volumes:
7 ]5 `! @  I5 o8 W      - "./entrypoint.sh:/entrypoint.sh"# G5 n# L3 q- U7 K. {6 g
    entrypoint: /entrypoint.sh -d mysql:3306 'echo "start web service here"';! k' F0 f8 H* b' [/ W, ?
$ \: s7 z4 @* V# d
  mysql:
2 J: n# K' n5 v) G/ y    image: ubuntu:14.04! `6 Q* @% S% |  v; x3 w
    command: >1 u( z: q/ ]0 P# E9 o- I+ N
      /bin/bash -c '
4 g; _0 m6 @, i1 C# U      sleep 4;
& x) Q$ j3 q9 [; ?  d1 M! N      echo "sleep over";
% L6 f( o( _6 ^      nc -lk 0.0.0.0 3306;

该用户从未签到

2#
发表于 2020-8-10 18:05 | 只看该作者
docker compose 服务启动顺序控制
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-9-13 21:00 , Processed in 0.140625 second(s), 24 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表