如何在Dockerfile中自动导入数据库?PHP容器化遇执行错误
问题描述
我有一个PHP开发的项目,想迁移到Docker容器中,希望在Dockerfile内实现数据库自动导入。手动进入容器执行docker exec -it my-laravel-app bash后,运行mysql -u user -p1993 -h db bsp < /var/www/database/bsp.sql能成功导入,但在Dockerfile中添加等待数据库就绪并导入的指令后,出现错误:
failed to solve: process "/bin/sh -c wait-for-it -t 30 ${DB_HOST}:${DB_PORT} if [ -f /var/www/database/bsp.sql ]; then mysql -u ${DB_USERNAME} -p${DB_PASSWORD} -h ${DB_HOST} ${DB_DATABASE} < /var/www/database/bsp.sql; fi" did not complete successfully: exit code: 2
以下是我的Dockerfile内容:
# Set the base image for subsequent instructions FROM php:8.2-fpm # Install dependencies RUN apt-get update && apt-get install -y \ build-essential \ libpng-dev \ libonig-dev \ libxml2-dev \ zip \ curl \ unzip \ git \ libzip-dev \ libfreetype6-dev \ libjpeg62-turbo-dev \ default-mysql-client \ wait-for-it \ libpng-dev && \ docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip # Clear cache RUN apt-get clean && rm -rf /var/lib/apt/lists/* # Install Composer COPY --from=composer:latest /usr/bin/composer /usr/bin/composer # Set working directory WORKDIR /var/www # Remove default server definition RUN rm -rf /var/www/html # Copy existing application directory contents COPY . /var/www # Copy existing application directory permissions COPY --chown=www-data:www-data . /var/www # Change current user to www USER www-data ENV DB_HOST=db ENV DB_PORT=3306 ENV DB_DATABASE=bsp ENV DB_USERNAME=user ENV DB_PASSWORD=1993 # Wait for the database to be ready and import SQL dump USER root RUN wait-for-it -t 30 ${DB_HOST}:${DB_PORT} \ if [ -f /var/www/database/bsp.sql ]; then \ mysql -u ${DB_USERNAME} -p${DB_PASSWORD} -h ${DB_HOST} ${DB_DATABASE} < /var/www/database/bsp.sql; \ fi # Expose port 9000 and start php-fpm server EXPOSE 9000 CMD ["php-fpm"]
错误原因
- RUN指令执行时机错误:RUN是镜像构建阶段执行的命令,此时数据库容器(
db服务)还未启动,wait-for-it无法连接到目标数据库,必然超时失败。 - 命令语法问题:
wait-for-it命令和后续的if语句之间缺少逻辑连接符(如&&),shell会把整个语句解析为错误的参数格式,导致执行失败。
解决方案
方案1:使用启动脚本替代RUN指令(推荐)
将数据库导入逻辑放到容器启动时执行的脚本中,因为CMD/ENTRYPOINT是容器启动阶段执行,此时数据库服务已正常运行(配合docker-compose的依赖配置)。
- 创建启动脚本
start.sh:
#!/bin/sh # 等待数据库服务就绪 wait-for-it -t 30 ${DB_HOST}:${DB_PORT} # 检查SQL文件并导入 if [ -f /var/www/database/bsp.sql ]; then mysql -u ${DB_USERNAME} -p${DB_PASSWORD} -h ${DB_HOST} ${DB_DATABASE} < /var/www/database/bsp.sql echo "数据库SQL文件导入完成" fi # 启动php-fpm服务 exec php-fpm
- 修改Dockerfile:
替换原有的RUN导入逻辑和CMD指令:
# ... 保留原有其他指令 ... # 复制启动脚本并赋予执行权限 COPY start.sh /usr/local/bin/start.sh RUN chmod +x /usr/local/bin/start.sh # 替换CMD为启动脚本 CMD ["start.sh"]
方案2:修正Dockerfile语法(仅解决语法问题,不推荐)
如果一定要尝试在构建阶段执行(不建议,因为构建阶段依赖外部服务不稳定),需要修正命令的语法,添加逻辑连接符:
# Wait for the database to be ready and import SQL dump USER root RUN wait-for-it -t 30 ${DB_HOST}:${DB_PORT} && \ if [ -f /var/www/database/bsp.sql ]; then \ mysql -u ${DB_USERNAME} -p${DB_PASSWORD} -h ${DB_HOST} ${DB_DATABASE} < /var/www/database/bsp.sql; \ fi
注意:此方法仍会因为构建阶段无数据库服务而失败,仅解决语法错误本身。
补充配置(docker-compose)
使用docker-compose时,需确保app容器依赖db容器启动:
services: db: image: mysql:8.0 # 数据库相关配置(环境变量、端口映射等) app: build: . depends_on: - db # 应用相关配置
内容的提问来源于stack exchange,提问作者florp

