14

背景

最近在写一个即时聊天程序的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的章节,才顺藤摸瓜找到解决方案
image.png

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, see Controlling 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写专栏了,希望对大家有帮助!


熊丸子
5.6k 声望293 粉丝

现在sf的文章质量堪忧~~~