如何在Dockerfile中执行db-migrate up?迁移失败问题排查与解决
为啥Dockerfile里的RUN迁移命令会失败?
Dockerfile里的RUN命令是在镜像构建阶段执行的,这时候你docker-compose里的mysqldb服务还没启动,数据库连都连不上,db-migrate up自然会卡住失败。而docker exec是等容器启动后执行的,这时候数据库已经正常跑起来了,所以命令能成功。
两种解决办法
办法一:给docker-compose加个专门的迁移服务
新增一个只负责执行数据库迁移的服务,等数据库就绪后跑迁移,跑完自动退出。修改后的docker-compose.yml如下:
version: '3.8' services: mysqldb: image: mysql:5.7 restart: unless-stopped env_file: ./.env environment: - MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD - MYSQL_DATABASE=$MYSQLDB_DATABASE ports: - $MYSQLDB_LOCAL_PORT:$MYSQLDB_DOCKER_PORT volumes: - db:/var/lib/mysql # 新增迁移服务 migrate: build: ./auth depends_on: - mysqldb env_file: ./.env environment: - DB_HOST=mysqldb - DB_USER=$MYSQLDB_USER - DB_PASSWORD=$MYSQLDB_ROOT_PASSWORD - DB_NAME=$MYSQLDB_DATABASE - DB_PORT=$MYSQLDB_DOCKER_PORT # 先等数据库就绪,再执行迁移命令 command: > sh -c " until nc -z mysqldb $MYSQLDB_DOCKER_PORT; do sleep 1; done; db-migrate up && prisma db pull && prisma generate " # 构建时安装netcat,用来检测数据库是否就绪 build: context: ./auth dockerfile: Dockerfile args: - INSTALL_NETCAT=true auth: depends_on: - migrate # 等迁移完成后再启动应用 - mysqldb build: ./auth restart: unless-stopped env_file: ./.env ports: - $NODE_LOCAL_PORT:$NODE_DOCKER_PORT environment: - DB_HOST=mysqldb - DB_USER=$MYSQLDB_USER - DB_PASSWORD=$MYSQLDB_ROOT_PASSWORD - DB_NAME=$MYSQLDB_DATABASE - DB_PORT=$MYSQLDB_DOCKER_PORT stdin_open: true tty: true volumes: db:
同时修改你的Dockerfile,移除构建阶段的迁移命令,添加可选的netcat安装:
FROM node:16.15.0-alpine ARG INSTALL_NETCAT=false WORKDIR /app COPY package*.json ./ COPY prisma ./prisma/ COPY .env ./ COPY . . RUN npm install RUN npm install -g db-migrate RUN npm install -g prisma # 仅迁移服务构建时安装netcat RUN if [ "$INSTALL_NETCAT" = "true" ]; then apk add --no-cache netcat-openbsd; fi # 构建阶段仅生成prisma客户端,迁移命令移到迁移服务中 RUN prisma generate EXPOSE 3000 CMD ["npm", "run", "dev"]
说明:
until nc -z ...是等待数据库完全启动就绪,避免数据库还在初始化就执行迁移导致失败。auth服务依赖migrate,确保迁移完成后再启动应用,不会出现应用先跑起来但数据库表未创建的问题。
办法二:容器启动时执行迁移(无需新增服务)
不想新增服务的话,直接修改Dockerfile的启动命令,让容器启动时先跑迁移,再启动应用:
修改Dockerfile的最后几行:
# 移除构建阶段的迁移命令 # RUN db-migrate up # RUN prisma db pull RUN prisma generate # 安装netcat用于检测数据库状态 RUN apk add --no-cache netcat-openbsd EXPOSE 3000 # 启动逻辑:等数据库就绪→执行迁移→启动应用 CMD sh -c " until nc -z $DB_HOST $DB_PORT; do sleep 1; done; db-migrate up && prisma db pull && npm run dev "
这种方式的优缺点:
- 优点:无需修改docker-compose,逻辑简单直接。
- 缺点:每次容器重启都会执行一次迁移,但
db-migrate和prisma的迁移命令都是幂等的,重复执行不会破坏数据库。
关键提醒
镜像构建阶段无法访问其他Docker服务,所以任何需要连接数据库、调用外部服务的操作,都不能放在RUN命令里,必须放到容器启动阶段执行。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

