为何Python脚本在Docker构建阶段未生成SQLite数据库文件?
Docker构建阶段无法生成SQLite数据库文件的问题分析与解决
问题场景
尝试在Docker构建阶段执行db_setup.py脚本生成SQLite数据库,执行docker compose build后无报错,但宿主机/data/streaming目录下未出现预期的数据库文件。手动进入容器终端执行该脚本,却能正常生成文件。
项目目录结构
/conf | /dev | | /py | | db_setup.py | | Dockerfile | requirements.txt /data | /streaming | # 期望在此处创建SQLite .db文件 .env docker-compose.yml
相关文件内容
.env 文件
DB_ABS_PATH=/home/fs/data/streaming DB_FILE_NAME=server-archive.db
docker-compose.yml 文件
version: '3.7' services: app: build: context: . dockerfile: conf/dev/py/Dockerfile container_name: fs-py env_file: - .env volumes: - .:/home/fs ports: - ${SERVER_PORT_IN}:${SERVER_PORT_OUT}
Dockerfile 文件
FROM python:3.9.16 WORKDIR /home/fs COPY . . RUN pip install -r conf/requirements.txt RUN python conf/dev/py/db_setup.py # 疑问针对此行 ENTRYPOINT [ "tail", "-f", "/dev/null" ]
db_setup.py 文件
import sqlite3 from dotenv import load_dotenv from pathlib import Path import os load_dotenv(Path(".env")) try: sqliteConnection = sqlite3.connect(Path(os.environ.get("DB_ABS_PATH"), os.environ.get("DB_FILE_NAME"))) cursor = sqliteConnection.cursor() cursor.execute("""CREATE TABLE files( id INTEGER PRIMARY KEY, original_name VARCHAR(200) NOT NULL, actual_name VARCHAR(200) NOT NULL, created_at TIMESTAMP NOT NULL, deleted_at TIMESTAMP NULL ); """) cursor.close() except sqlite3.Error as err: print("Error while connecting to sqlite", err) finally: if sqliteConnection: sqliteConnection.close()
问题原因
- 构建阶段无宿主机挂载:Docker构建过程中,
docker-compose.yml中定义的volumes: - .:/home/fs并未生效,挂载是容器启动阶段才会执行的操作。因此构建阶段执行db_setup.py生成的数据库文件,是保存在镜像内部的/home/fs/data/streaming路径下,而非宿主机的对应目录。 - 容器启动时挂载覆盖镜像文件:当容器启动后,宿主机的
.目录会挂载到容器内的/home/fs,这会覆盖镜像中该路径下的所有内容,包括构建阶段生成的数据库文件,所以你在宿主机看不到这个文件。
解决方法
方法1:将数据库初始化移到容器启动阶段(推荐)
把初始化脚本从构建阶段移到容器启动时执行,此时挂载已经生效,文件会直接生成在宿主机挂载的目录中:
- 在项目根目录创建启动脚本
start.sh:#!/bin/bash # 执行数据库初始化 python conf/dev/py/db_setup.py # 保持容器运行 tail -f /dev/null - 修改Dockerfile,移除构建阶段的
RUN python conf/dev/py/db_setup.py,并添加启动脚本相关配置:FROM python:3.9.16 WORKDIR /home/fs COPY . . RUN pip install -r conf/requirements.txt # 给启动脚本添加执行权限 RUN chmod +x start.sh ENTRYPOINT ["./start.sh"] - 重新构建并启动容器:
docker compose build docker compose up -d
方法2:手动复制镜像内的数据库文件(不推荐)
如果必须在构建阶段生成文件,可以通过临时容器将镜像内的文件复制到宿主机:
- 构建镜像后创建临时容器:
docker create --name temp-fs fs-py - 复制数据库文件到宿主机目标目录:
docker cp temp-fs:/home/fs/data/streaming/server-archive.db ./data/streaming/ - 删除临时容器:
此方法需要手动操作,且镜像会残留数据库文件,不符合Docker无状态镜像的设计原则,仅作为临时方案。docker rm temp-fs
方法3:调整挂载策略(按需选择)
若不想修改初始化时机,可以调整挂载路径,避免覆盖镜像内的data/streaming目录:
在docker-compose.yml中调整挂载顺序,先单独挂载data/streaming再挂载根目录:
volumes: - ./data/streaming:/home/fs/data/streaming - .:/home/fs
但这种方式下,宿主机/data/streaming的内容会优先覆盖镜像内的对应目录,构建阶段生成的文件依然会被覆盖,仅当你不需要宿主机目录覆盖容器目录时适用,实用性较低。
内容的提问来源于stack exchange,提问作者Andrew Sharifikia
相关产品推荐
相关产品推荐

