我有可与 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
...
如果您想水平扩展应用程序,链接命令或使用入口点并不是最佳选择。
然后所有副本将同时进行迁移。它可能不会造成真正的问题,但在我看来它仍然不完美。
相反,这应该单独处理,在实际需要时作为一次性命令。例如,在 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
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。
我解决了这个问题,可能是用愚蠢的方式,但它有效。所以,我所做的只是将其添加到我的 API 容器中:
restart: on-failure
command: bash -c "npm run knex && npm run start"
现在,它只是重新启动容器,直到连接到数据库并执行所有迁移。