docker-compose 镜像启动顺序调整

788 阅读4分钟

使用 HEALTHCHECK 自定义方法实现

假设我们有一个Web应用,需要依赖于一个MySQL数据库。我们需要在MySQL数据库启动并进入ready状态后,才能启动Web应用。

首先,我们需要创建一个Dockerfile来定义MySQL数据库容器:

FROM mysql:5.7

# 设置环境变量
ENV MYSQL_DATABASE=mydb \
    MYSQL_ROOT_PASSWORD=123456

# 添加健康检查脚本
COPY healthcheck.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/healthcheck.sh
HEALTHCHECK --interval=5s --timeout=3s \
  CMD /usr/local/bin/healthcheck.sh || exit 1

上面的Dockerfile定义了一个MySQL数据库容器,设置了数据库名称和root用户密码,并添加了一个健康检查脚本healthcheck.sh。该脚本会检查MySQL数据库是否已经启动并准备好接收请求,如果是则返回0,否则返回1。

健康检查脚本healthcheck.sh的内容如下:

#!/bin/bash

if mysql -hlocalhost -uroot -p123456 -e "SELECT 1"; then
  exit 0
else
  exit 1
fi

该脚本会使用mysql命令连接到MySQL数据库,并执行一个简单的SQL语句,如果连接成功则返回0,否则返回1。

接下来,我们需要创建一个Dockerfile来定义Web应用容器:

FROM node:10

# 设置工作目录
WORKDIR /app

# 安装依赖
COPY package*.json ./
RUN npm install

# 添加应用代码
COPY . .

# 添加健康检查脚本
COPY healthcheck.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/healthcheck.sh
HEALTHCHECK --interval=5s --timeout=3s \
  CMD /usr/local/bin/healthcheck.sh || exit 1

# 启动应用
CMD ["npm", "start"]

上面的Dockerfile定义了一个Node.js应用容器,安装了应用依赖,并添加了一个健康检查脚本healthcheck.sh。该脚本会检查应用是否已经启动并准备好接收请求,如果是则返回0,否则返回1。

最后,我们可以使用docker-compose来启动这两个容器,定义如下的docker-compose.yml文件:

version: "3"

services:
  db:
    build:
      context: .
      dockerfile: Dockerfile.db
    ports:
      - "3306:3306"

  app:
    build:
      context: .
      dockerfile: Dockerfile.app
    ports:
      - "3000:3000"
    depends_on:
      db:
        condition: service_healthy

上面的docker-compose.yml文件定义了两个服务dbapp,分别对应MySQL数据库容器和Web应用容器。db服务的Dockerfile文件名为Dockerfile.dbapp服务的Dockerfile文件名为Dockerfile.app

app服务中,我们使用了depends_on参数来指定依赖关系。condition: service_healthy表示只有当db服务进入健康状态后,才会启动app服务。

现在,我们可以使用docker-compose up命令启动这两个容器:

$ docker-compose up

Docker会先启动db服务,等待MySQL数据库进入ready状态后,再启动app服务。这样,我们就实现了在MySQL数据库启动并进入ready状态后,才启动Web应用的需求。

下面是使用wait-for的方式实现

它需要在数据库启动之后才能启动。我们可以使用wait-for来等待数据库启动完成后再启动Web应用程序。

首先,我们需要在Dockerfile中添加wait-for脚本:

FROM node:12

# 安装wait-for脚本
ADD https://github.com/eficode/wait-for/raw/master/wait-for /usr/local/bin/wait-for
RUN chmod +x /usr/local/bin/wait-for

# 拷贝应用程序代码
WORKDIR /app
COPY . .

# 安装依赖
RUN npm install

# 暴露端口
EXPOSE 3000

# 启动应用程序
CMD ["wait-for", "db:5432", "--", "npm", "start"]

在Dockerfile中,我们下载了wait-for脚本,并将其添加到容器中的/usr/local/bin目录。然后,我们拷贝应用程序代码到工作目录/app中,并安装依赖。最后,我们暴露端口并使用wait-for脚本等待数据库启动完成后再启动应用程序。

接下来,在docker-compose.yml中定义服务:

version: '3'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
  db:
    image: postgres

在docker-compose.yml中,我们定义了两个服务:web和db。web服务依赖于db服务。这意味着在启动web服务之前,docker-compose会先启动db服务。

最后,在应用程序中,我们可以使用以下命令来连接数据库:

const { Pool } = require('pg');

const pool = new Pool({
  host: 'db',
  port: 5432,
  database: 'mydatabase',
  user: 'myuser',
  password: 'mypassword',
});

pool.query('SELECT NOW()', (err, res) => {
  console.log(err, res);
  pool.end();
});

在应用程序中,我们使用了pg库来连接数据库。我们将host设置为db,这是docker-compose.yml文件中定义的db服务的名称。这样,当我们使用wait-for脚本等待数据库启动完成后,我们就可以成功连接到数据库了。