EDA365电子论坛网
标题:
docker compose 服务启动顺序控制
[打印本页]
作者:
baqiao
时间:
2020-8-10 15:11
标题:
docker compose 服务启动顺序控制
概要
& ^1 f$ Y; {6 Q! x0 `
docker-compose 可以方便组合多个 docker 容器服务, 但是, 当容器服务之间存在依赖关系时, docker-compose 并不能保证服务的启动顺序.
% H' K6 w+ Z: p" n! `$ ~
# f9 p1 X- W! }, ~3 U7 f/ w
docker-compose 中的 depends_on 配置是容器的启动顺序, 并不是容器中服务的启动顺序.
7 f) I% B% r1 n; {6 W1 }& }
+ O; ~/ H1 N U
问题重现
5 h7 y7 r5 ^/ W1 M0 O
首先, 我们构造一个示例, 来演示 docker-compose 带来的问题. docker-compose.yml 文件如下:
$ C7 g! d7 h* r& H2 }- {
' d4 X; D7 t7 X& e; h
version: '2'
$ I/ j/ [9 q# r7 R# D- y
services:
: r! d7 w I5 \8 o _+ ^5 ^
web:
: |$ E% M7 X, j W/ e6 Z* i
image: ubuntu:14.04
* t" p3 X/ ]+ ^$ ?5 `
depends_on:
# i& v, }8 H4 r6 F- p
- web
9 N3 u( i' B, G7 M' }
command: nc -z database 3306
( w# Z, _* F" S1 T
5 D/ D9 D# W, ^1 j
database:
( g: f8 [5 b# n3 [9 ?
image: ubuntu:14.04
7 e( K" b6 _% q0 x* t2 \
command: >
/ M1 j" ?6 B. @
/bin/bash -c '
& H% Q% M3 E8 `# L" j2 s
sleep 5;
; o$ U6 @6 H/ p
echo "sleep over";
+ h+ I# E X' g
nc -lk 0.0.0.0 3306;
* { k7 ~( I" _) C) P* R" ]
'
/ w$ m* `: L* G \& _8 d2 n' W
启动后, 可以发现, 确实是先启动 database, 后启动 web, 但是 database 中的服务是在大约 5 秒后才完成的, 所以导致 web 的启动失败.
7 n" u0 o# n) f$ w' h7 n* D
* E) i2 m% q3 Q& |& ~* ^7 p
$ docker-compose up
2 ?8 l& {/ Z; E
Creating tmp_database_1 ... done
0 `* s( \! \# J$ N/ ]5 G8 w! b
Creating tmp_database_1 ...
, N! F, B0 P5 y# t7 {+ p2 G. l$ ~
Creating tmp_web_1 ... done
- W) k% w+ J; _2 d
Attaching to tmp_database_1, tmp_web_1
0 a) @9 K5 q0 Q7 \8 N
tmp_web_1 exited with code 1
/ J$ D+ m3 E9 K: A, }
database_1 | sleep over
V0 B$ V1 Y% M# t T$ v9 `" s
问题解决方式 1.0
2 O0 J! ?% u& y8 @
修改 web 的启动脚本, 等待 database 的端口通了之后再启动服务
W9 Z& \# I5 j/ }
$ F2 Y; b" ]4 Q9 H' r6 y
version: '2'
; Y3 m! q' j2 N1 U N9 @" Q C
services:
; I! c) i( l' K: \1 p% _* l
web:
1 E: z7 Z- p4 N% O* ]
image: ubuntu:14.04
: @ [/ l7 q; N
depends_on:
6 Y! G( |% j0 Q2 ?8 P
- database
, G: I! K8 u; z/ P9 A
command: >
7 @6 }: E r# [
/bin/bash -c '
, K# `& e5 O9 E! w& T5 e2 G' e
while ! nc -z database 3306;
% h3 Q! n! g2 B `" A" o
do
% Q2 U4 J8 o) d" r
echo "wait for database";
9 i. g7 W! o; S4 r* K5 R" H4 z
sleep 1;
% S- C4 e" @1 _) y( ?
done;
- r( D/ b1 Z; ]) _) N- d! M2 M3 ]
' s; G% e. c5 W1 t: B( q
echo "database is ready!";
. d/ z3 ]8 ~7 a. {& T
echo "start web service here";
* ?$ {+ f# [& e5 p3 ~& M
'
& r$ A# K4 g9 |( e8 U
: v9 T" Q v3 {0 R2 i3 d
database:
6 Z% D9 A' i% S% J/ {+ f" V
image: ubuntu:14.04
/ v" N6 E$ @% e v" z5 U: H& J& A4 B9 k
command: >
5 M- q+ U7 E! z
/bin/bash -c '
9 ]- r- h, I- `5 d* b; ]0 y
sleep 5;
, `% u& _$ A! l0 c! ~% j
echo "sleep over";
1 x5 B$ v, Q W0 u6 t* _
nc -lk 0.0.0.0 3306;
. f) G8 Z% Z; H! F
'
6 Q Z( t4 [& z; [
再次启动,
: C4 o" Z f6 d3 O& y+ k( y& N
( Y2 O8 ]) t2 ^0 n0 n
$ docker-compose up
4 Q8 g- t b# j9 O( k
Creating tmp_database_1 ... done
* ?3 y8 M) Z6 N h8 Z
Creating tmp_database_1 ...
8 J8 U) p; ]3 `* T. S( d
Creating tmp_web_1 ... done
; s7 R8 \( X9 l0 v% [
Attaching to tmp_database_1, tmp_web_1
3 v9 O( B3 j6 o4 S
web_1 | wait for database
: b( }* l) H! C) q( I
web_1 | wait for database
, x/ F& t/ O, l8 v
web_1 | wait for database
, L( N! T( T- K6 C' h" R. A+ Q
web_1 | wait for database
M; y* q/ N, i
web_1 | wait for database
. A" N: m! m& d% P
database_1 | sleep over
( z2 I( m8 s/ k' A
web_1 | database is ready!
7 R$ e/ ^* w/ r; f) j3 b3 u
web_1 | start web service here
) t4 } g, L3 P X3 k) g: a! S. x
tmp_web_1 exited with code 0
% V# F+ h/ B0 `, H( f
web 会在 database 启动完成, 端口通了之后才启动.
% l2 H/ t; q' k4 [& n
6 C, J6 L' d) K0 V6 b \
问题解决方式 2.0
. U" B* W4 t! u( h! ^
上面的解决方式虽然能够解决问题, 但是在 yaml 中直接插入脚本不好维护, 也容易出错. 如果有多个依赖, 或者多层依赖的时候, 复杂度会直线上升.
/ w) I% E2 T% i8 K4 F
' E6 g9 x7 b6 O* ^% H2 B/ n
所以, 要封装一个 entrypoint.sh 脚本, 可以接受启动命令, 以及需要等待的服务和端口. 脚本内容如下:
7 m; x) j; U) q( N
$ C. ^+ ~, J7 O& p- k# P: w
#!/bin/bash
# C S9 i) X# `' f/ \9 }2 T
#set -x
3 {4 {! {$ T" }, L; Q
#******************************************************************************
0 e- I$ p5 \: S) h& `: o
#
@File
: entrypoint.sh
' \% M3 G9 S& v8 h8 B
#
@author
: wangyubin
$ ]+ g6 [& W9 e- S/ f B
# @date : 2018-08- 1 10:18:43
$ o+ z$ g% V0 j# K& g2 Z
#
, w9 X2 N" k! p( s4 G1 p
# @brief : entry point for manage service start order
: t8 J; D! Y3 X1 q7 k# i3 P! N$ v
# history : init
8 x/ N2 F9 N& \& O p6 I
#******************************************************************************
( A' x! y: q+ S3 U+ C- G, M
0 y" n& F( t: @. b$ `
: ${SLEEP_SECOND:=2}
! x3 Y4 y X, V$ D# V
* {& D3 `: m6 f( V1 ^( a% _9 t9 o7 q- L
wait_for() {
' {" ?3 ?9 P- D
echo Waiting for $1 to listen on $2...
8 Y* x; e/ @6 e$ A0 g1 ?) g
while ! nc -z $1 $2; do echo waiting...; sleep $SLEEP_SECOND; done
% t5 V1 L1 W f9 ~) Q5 i2 [6 k
}
8 M2 e; H4 q c5 l
: ~3 I1 ?% {1 E0 v+ l$ t
declare DEPENDS
/ R! x/ k6 P) @
declare CMD
: r9 o1 Y* w: L* \
c4 D# ?) I" u/ |' B* {
while getopts "d:c:" arg
% }1 O9 f+ ~; m
do
3 b2 U% e$ q" _* u
case $arg in
1 k$ r- l+ R) R: M
d)
' ~: u7 o1 F. ?* C9 u
DEPENDS=$OPTARG
) ?" W3 h9 _6 D a
;;
2 Q- J& U$ I3 Q' t
c)
2 U u; y1 h7 n" `' S- x% d
CMD=$OPTARG
. {& ?6 |- U" _3 X9 w2 p
;;
2 s) `6 c" o, u$ s! _1 y
?)
/ Q& D" P8 {' W% K6 Z; z( k( l6 g
echo "unkonw argument"
6 \, M$ m% {. K% K6 q
exit 1
0 W# L9 z) Y" B, z% j% L+ u
;;
) q8 u& I5 N# S! Z0 Z5 J
esac
4 C0 Q" o7 q' @% t0 b+ n
done
) U. Y1 H8 T7 m
: y3 W. d& N/ {5 s5 e' S" ?
for var in ${DEPENDS//,/ }
8 e% s+ T3 [: v; n$ d
do
2 S2 O2 i' ]- }4 C3 w2 \
host=${var%:*}
6 x }$ @. z9 u# D
port=${var#*:}
h4 c0 ~+ Q% C
wait_for $host $port
' ]- W0 \/ Y/ d, ^) u
done
* S& i/ u% I9 v1 l7 }8 B$ y+ O
' n) o: T5 x8 m; l! S; _0 t3 {1 s
eval $CMD
/ l1 ?% }& M+ j% Y$ {
这个脚本有 2 个参数, -d 需要等待的服务和端口, -c 等待的服务和端口启动之后, 自己的启动命令
; M! K; l& V9 U' @& ~
/ W% c* I! o4 i
修改 docker-compose.yml, 使用 entrypoint.sh 脚本来控制启动顺序.
4 W, x$ W) s# v" z7 R( R
. \+ ?3 q* O7 J: p: e5 X
version: '2'
( e. ]2 k: c+ R# a: w+ E
services:
" e/ @0 f+ o* d+ E
web:
. w$ X7 S& O/ m' e1 j! R
image: ubuntu:14.04
$ q- Q S2 C! r" R4 b2 D
depends_on:
/ D H2 u* @0 v) L! v. c5 W: ]* b. B
- database
# L$ J5 X1 L& M* V" X2 f) Z
volumes:
* ?7 i8 V0 e9 b
- "./entrypoint.sh:/entrypoint.sh"
9 U' V* r, j2 f! m4 {3 y5 @. j
entrypoint: /entrypoint.sh -d database:3306 -c 'echo "start web service here"';
8 _0 g$ ?% t- i1 M5 A2 [
( r$ |' @& g' U1 O
database:
& V( a" ?& [5 t) l( k( V3 a) J
image: ubuntu:14.04
8 v' n( ]: h/ N: X8 o
command: >
* u; e5 n8 Q0 J" F" f5 p8 a# v7 k
/bin/bash -c '
- s2 ?1 }; v# I; X) _
sleep 5;
: }# ]2 E! r, c, j& I
echo "sleep over";
3 C' c% e9 ~( j6 j& R2 N+ \
nc -lk 0.0.0.0 3306;
, X% Z% u( S8 J `
'
8 p+ C5 d/ G5 q+ A' h6 ]: U8 C
实际使用中, 也可以将 entrypoint.sh 打包到发布的镜像之中, 不用通过 volumes 配置来加载 entrypoint.sh 脚本.
9 J0 z) Y" K4 c
' H7 c! Y) l9 c6 } c9 p- J6 {: W) o& }
测试结果如下:
8 A' y3 K+ N# C3 {; ?- n
& u5 X2 y* i9 E- S* H- q) X
$ docker-compose up
( ?5 J% \$ ]1 r0 M
Starting tmp_database_1 ... done
! t% l+ o0 I* D7 E
Starting tmp_web_1 ... done
' q5 W5 }1 F7 B! U" \/ e: {2 `5 v
Attaching to tmp_database_1, tmp_web_1
1 p* D" k: P ?0 b9 f4 p+ `9 u
web_1 | Waiting for database to listen on 3306...
T7 f: T$ i8 d* v* f- h" Q
web_1 | waiting...
, V T3 @3 I5 E8 ?' M+ a
web_1 | waiting...
% {' d! b& V1 A
web_1 | waiting...
6 r6 ?0 Q& S3 P) i/ O
database_1 | sleep over
1 V, O; S- R/ y3 |
web_1 | start web service here
! h" I# e# ~* ]* T
tmp_web_1 exited with code 0
/ \* @7 l% z& B+ m! c
补充
+ S7 g, Q& c2 e# x& y1 U
依赖多个服务和端口
7 ^5 S* Q) m6 y/ H$ P7 _
使用上面的 entrypoint.sh 脚本, 也可以依赖多个服务和端口, -d 参数后面的多个服务和端口用逗号(,)隔开.
0 q5 B! d) H/ L1 }
+ e5 K7 s/ q9 S
version: '2'
! f/ v W; C. N+ `, x9 {
services:
: I& _8 F, Y3 K! U: O
web:
% Z: o( \4 J2 ?+ ^8 d
image: ubuntu:14.04
2 f4 D) P6 l5 Y* V
depends_on:
F& K9 r( D3 Y( a! g
- mysql
( s3 w( J" x1 s7 _3 |6 d
- postgresql
6 b" ^ {! z5 r
volumes:
1 G: ?, |# k6 T# r O6 h& L, U
- "./entrypoint.sh:/entrypoint.sh"
. Z1 ^$ W) Y# {& m. X
entrypoint: /entrypoint.sh -d mysql:3306,postgresql:5432 -c 'echo "start web service here"';
1 C- W2 l5 e9 a! C4 \/ o
B( ]/ \6 A, w! |
mysql:
: a n: z, } w, m. h
image: ubuntu:14.04
4 e# v. R/ _8 N* K
command: >
% _5 w/ v) b1 g6 |& A# H2 v+ \
/bin/bash -c '
+ Z4 \" |6 z) s4 B' M/ s" I
sleep 4;
- H+ \6 k1 P8 c1 K9 Z) C* f
echo "sleep over";
; E. ^7 ~" G! M+ m' ^% F ?
nc -lk 0.0.0.0 3306;
' x. O3 V; S! v; ^5 D3 J. A
'
# u0 I( r' q6 S0 }/ v+ i
postgresql:
6 A+ L0 o- w0 k! d9 H# R
image: ubuntu:14.04
5 ~: R. j' W5 O3 K; d& e
command: >
7 _! r. Q' W" Q) |# O
/bin/bash -c '
8 K8 j0 i2 x. j4 x. g+ V6 t
sleep 8;
9 T' M( P$ U% `; H6 v. |
echo "sleep over";
) P1 ?' ]) T- ] e5 D! X/ ~
nc -lk 0.0.0.0 5432;
0 N8 M, b/ p' g& w T
'
: U3 p& o3 i; C$ X
执行的效果可以自行尝试.
/ R) p/ E0 P* _
5 N- M+ W. }! W- k: L
尝试间隔的配置
# j9 e) a+ X& i5 L% M
每次尝试连接的等待时间可以通过 环境变量 SLEEP_SECOND 来配置, 默认 2 秒 下面的配置等待时间设置为 4 秒, 就会每隔 4 秒才去尝试 mysql 服务时候可连接.
+ s1 ^" C- O9 ]# h4 p/ O3 v! ?
7 D6 p$ \3 E8 {
version: '2'
3 m; x% @; |. F$ b5 H* H8 [
services:
W: U) V& ?- p+ d2 Y
web:
7 y% E3 ]. {7 `0 D0 U% [+ ]2 E
image: ubuntu:14.04
# c. I$ ? W' X3 Z, g
environment:
1 \3 a% H5 s3 q& D; @
SLEEP_SECOND: 4
) s; B% l, x3 }$ m; C3 f, ^
depends_on:
9 ]$ ~3 S$ c6 M% I7 s+ L
- mysql
7 u' \' h& s% S' B2 ^# Z
volumes:
6 a2 R' ?' h! N
- "./entrypoint.sh:/entrypoint.sh"
! e ]. B; A& J, y
entrypoint: /entrypoint.sh -d mysql:3306 'echo "start web service here"';
1 \7 l- j( A# h x2 P3 h7 I% P+ L
6 [1 b0 C- Q# c5 E
mysql:
3 J" O1 j$ M2 q p8 w
image: ubuntu:14.04
, ~( S0 X* V" T5 B6 @
command: >
. o0 {1 q/ N3 T3 ]+ k
/bin/bash -c '
' \* S8 j. c" f; `" \
sleep 4;
# z/ Y1 q6 X5 Z G
echo "sleep over";
/ `# s( A/ T/ k' a+ z) b
nc -lk 0.0.0.0 3306;
作者:
regngfpcb
时间:
2020-8-10 18:05
docker compose 服务启动顺序控制
欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/)
Powered by Discuz! X3.2