背景
最近在写一个即时聊天程序的DEMO,技术栈差不多是vue+nodejs+redis+rabbitmq,里面有用到rabbitmq来处理消息列队,
程序写好后,我用docker-compose做了个镜像,日后好给别人做DEMO。
docker-compose.yml大致如下
version: "3"
services:
redis:
image: redis:latest
ports:
- "6379:6379"
container_name: im-redis-compose
restart: always
command: redis-server --appendonly yes
rabbitmq:
image: rabbitmq:management
ports:
- "5672:5672"
- "15672:15672"
container_name: im-rabbitmq-compose
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
RABBITMQ_DEFAULT_VHOST: my_vhost
backend:
build: .
links:
- redis
- rabbitmq
container_name: im-server-compose
restart: on-failure
depends_on:
- rabbitmq
- redis
ports:
- "3000:3000"
起因
由于nodejs服务的启动会和redis和rabbitmq建立connection,有依赖关系。于是我使用docker-compose中的depends_on
属性来决定启动顺序,再用links
属性来做docker容器内访问的网络别名,一切看起来都很顺利。但是当我敲下docker-compose up
命令的时候却报错了。
分析
看了一下启动log,nodejs先报的错,启动的时候报了connect refused,连接rabbitmq的时候出错。
im-server-compose | Error: connect ECONNREFUSED 172.24.0.3:5672
im-server-compose | at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1136:16) {
im-server-compose | errno: -111,
im-server-compose | code: 'ECONNREFUSED',
im-server-compose | syscall: 'connect',
im-server-compose | address: '172.24.0.3',
im-server-compose | port: 5672
im-server-compose | }
im-server-compose | npm ERR! code ELIFECYCLE
im-server-compose | npm ERR! errno 1
接下来打印出了rabbitmq启动成功的消息
im-rabbitmq-compose | 2020-01-06 08:44:17.837 [info] <0.504.0> Management plugin: HTTP (non-TLS) listener started on port 15672
im-rabbitmq-compose | 2020-01-06 08:44:17.839 [info] <0.610.0> Statistics database started.
im-rabbitmq-compose | 2020-01-06 08:44:17.839 [info] <0.609.0> Starting worker pool 'management_worker_pool' with 3 processes in it
im-rabbitmq-compose | 2020-01-06 08:44:18.132 [info] <0.8.0> Server startup complete; 3 plugins started.
im-rabbitmq-compose | * rabbitmq_management
im-rabbitmq-compose | * rabbitmq_management_agent
im-rabbitmq-compose | * rabbitmq_web_dispatch
im-rabbitmq-compose | completed with 3 plugins.
咦!有点confused!明明设置了depends_on定了启动顺序,为何rabbitmq还没有启动完全,就去跑nodejs的服务了?
解决方案
google找了半天,还是没有找到比较好的解决方案,浪费了很多时间。最后还是去看了docker-compose文档关于depends_on的章节,才顺藤摸瓜找到解决方案
depends_on
does not wait for db and redis to be “ready” before starting web - only until they have been started. If you need to wait for a service to be ready, seeControlling startup order
for more on this problem and strategies for solving it.
depends_on
在启动web这个容器前,并不会等待db和redis这个两个容器进入ready状态,而只是等到它们被启动状态了。 如果你需要等待直到某个依赖的服务进入ready状态,需要看进一阅读控制启动顺序
一文
控制容器启动顺序
链接我贴在这里,具体内容可以自己去看https://docs.docker.com/compose/startup-order/
我大致说下他提到的解决思路
就在容器启动命令执行前,跑一个shell脚本,这个脚本会去访问依赖的服务的页面或者ping api来判断底层的服务有没有ready,随后再去启动真正的服务。里面有个现成的解决方案sh脚本叫wait-for, 只需要下载这份shell并且在dockerfile里加上
RUN apt-get -q update && apt-get -qy install netcat
docker-compose.yml里调用wait-for的command即可
version: "3"
services:
redis:
image: redis:latest
ports:
- "6379:6379"
container_name: im-redis-compose
restart: always
command: redis-server --appendonly yes
rabbitmq:
image: rabbitmq:management
ports:
- "5672:5672"
- "15672:15672"
container_name: im-rabbitmq-compose
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
RABBITMQ_DEFAULT_VHOST: my_vhost
backend:
build: .
links:
- redis
- rabbitmq
container_name: im-server-compose
restart: on-failure
depends_on:
- rabbitmq
- redis
ports:
- "3000:3000"
command: sh -c './wait-for.sh rabbitmq:15672 -- npm run start'
command: sh -c './wait-for.sh rabbitmq:15672 -- npm run start'这句意思,就是等rabbitmq:15672这个management管理页面可以访问了之后,在去执行真正的nodejs启动命令"npm run start"
最后
好久没有来sf写专栏了,希望对大家有帮助!