如何从 Dockerfile 或 docker-compose 运行 knex 迁移

How to run knex migrations from Dockerfile or docker-compose

我有与 API 和 MySQL 数据库一起使用的 Dockerfile,它应该进行迁移:

FROM node

WORKDIR /api

COPY . .

RUN npm install

EXPOSE 3001

VOLUME [ "/api/node_modules" ]

CMD [ "npm", "start" ]

此外,还有一个 docker-compose 文件,我将数据库作为服务:

  db:
    image: mysql
    container_name: database
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_PASSWORD: password
      MYSQL_DATABASE: testdb

问题是,我不知道如何 运行 迁移。我应该从 docker-compose 文件还是 Dockerfile 来完成?

我试图在 Dockerfile 中做类似的事情,但它似乎不起作用:

...
CMD [ "knex", "migrate:latest" ]
...

或:

...
RUN knex migrate:latest
...

我解决了这个问题,可能是以愚蠢的方式,但它有效。所以,我所做的只是将其添加到我的 API 容器中:

restart: on-failure
command: bash -c "npm run knex && npm run start"

现在,它只是重新启动容器,直到连接到数据库并执行所有迁移。

如果您想水平扩展应用程序,则链接命令或使用入口点不是最佳选择。

然后所有副本将同时进行迁移。它可能不会造成真正的问题,但它仍然不是完美的 IMO。

相反,这应该单独处理,在实际需要时作为一次性命令。例如,在 Kubernetes 中 运行 一个专门的迁移作业与你的应用程序发布一起会很好,如果数据库架构实际上已经改变。

使用 compose,没有作业,但您可以实现类似的行为。

services:
  migration:
    image: busybox
    command: sh -c 'echo "running migration..."; sleep 20; echo "migration completed"'
  app:
    image: busybox
    command: echo "app started"
    depends_on:
      migration:
        condition: service_completed_successfully
    deploy:
      replicas: 3

现在您只需对迁移进行一次排行,所有 3 个应用程序副本在启动之前等待迁移完成。

$ docker compose up
Attaching to app_1, app_2, app_3, migration_1
migration_1  | running migration...
migration_1  | migration completed
migration_1 exited with code 0
app_2        | app started
app_3        | app started
app_1        | app started

在您的情况下,您将使用从 Dockerfile 构建的同一映像用于迁移和应用程序服务。在您使用 knex migrate 的迁移服务和您使用 npm run start.

的应用服务中

如果您需要迁移甚至等待数据库,depends_on 可能还不够,除非您构建健康检查以反映数据库是否真的准备好接受连接。如果你有健康检查,那么你可以使用条件 service_healthy.

例如,你可以做这样的事情。

services:
  db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: "root"
      MYSQL_DATABASE: "wordpress"
      MYSQL_USER: "wordpressuser"
      MYSQL_PASSWORD: "wordpresspassword"
    healthcheck:
      test: mysqladmin -u root --password=$$MYSQL_ROOT_PASSWORD ping
      interval: 30s
      timeout: 10s
      retries: 10

  migration:
    image: busybox
    command: sh -c 'echo "running migration..."; sleep 20; echo "migration completed"'
    depends_on:
      db:
        condition: service_healthy

  app:
    image: busybox
    command: echo "app started"
    depends_on:
      migration:
        condition: service_completed_successfully
    deploy:
      replicas: 3

您可以通过容器检查来检查日志的健康状况。

$ docker inspect sample_db_1 --format \
  '{{range .State.Health.Log}}{{.End}} | Exit Code: {{.ExitCode}} | {{.Output}}{{end}}'
2022-01-30 12:53:43.749365 +0000 UTC | Exit Code: 0 | mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqld is alive

如果您不想使用健康检查,您也可以使用第三方解决方案,例如 https://github.com/Eficode/wait-for