首页 > 解决方案 > 使用纯粹的 Dockerfile 创建带有恢复的备份数据库的 SQL Server docker 映像

问题描述

以下 docker 文件使用从备份 (rmsdev.bak) 恢复的数据库创建自定义 SQL 服务器映像。

FROM mcr.microsoft.com/mssql/server:2019-latest
ENV MSSQL_PID=Developer
ENV SA_PASSWORD=Password1?
ENV ACCEPT_EULA=Y

USER mssql

COPY rmsdev.bak /var/opt/mssql/backup/

# Launch SQL Server, confirm startup is complete, restore the database, then terminate SQL Server.
RUN ( /opt/mssql/bin/sqlservr & ) | grep -q "Service Broker manager has started" \
    && /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q 'RESTORE DATABASE rmsdev FROM DISK = "/var/opt/mssql/backup/rmsdev.bak" WITH MOVE "rmsdev" to "/var/opt/mssql/data/rmsdev.mdf", MOVE "rmsdev_Log" to "/var/opt/mssql/data/rmsdev_log.ldf", NOUNLOAD, STATS = 5' \
    && pkill sqlservr

CMD ["/opt/mssql/bin/sqlservr"]

问题是,一旦恢复完成,就不再需要备份文件,我想将其从映像中删除。

不幸的是,由于 docker 图像的形成方式(层),我不能像我想的那样简单地“rm”文件。

多阶段 Dockerfile 在这种情况下并不像在构建场景中那样容易应用。另一种方法是运行容器,恢复备份,然后提交一个新图像,但我想做的是只使用 docker build 和正确的 Dockerfile。

有人知道方法吗?

标签: sql-serverdockerdockerfile

解决方案


如果您知道数据目录在映像中的位置,并且映像没有将该目录声明为VOLUME,那么您可以为此使用多阶段构建。如您所见,第一阶段将设置数据目录。第二阶段将从第一阶段复制填充的数据目录,但不复制备份文件。这个技巧可能取决于运行相同构建的底层软件的两个阶段。

对于 SQL Server,Docker Hub 页面GitHub存储库都很难找到,而且令人惊讶的是,它们都没有谈到数据存储问题(正如 @HansKillian 在评论中指出的那样,您几乎总是希望以某种形式存储数据库数据体积)。GitHub 存储库确实包含一个围绕Kubernetes StatefulSet构建的Helm 图表,从中我们可以发现数据目录将安装在./var/opt/mssql

所以我可能会像这样编写一个多阶段构建:

# Put common setup steps in an initial stage
FROM mcr.microsoft.com/mssql/server:2019-latest AS setup
ENV MSSQL_PID=Developer
ENV SA_PASSWORD=Password1?  # (weak password, easily extracted with `docker inspect`)
ENV ACCEPT_EULA=Y           # (legally probably the end user needs to accept this not the image builder)

# Have a stage specifically to populate the data directory
FROM setup AS data
# (copy-and-pasted from the question)
USER mssql
COPY rmsdev.bak /  # not under /var/opt/mssql
RUN ( /opt/mssql/bin/sqlservr & ) | grep -q "Service Broker manager has started" \
    && /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P $SA_PASSWORD -Q 'RESTORE DATABASE rmsdev FROM DISK = "/rmsdev.bak" WITH MOVE "rmsdev" to "/var/opt/mssql/data/rmsdev.mdf", MOVE "rmsdev_Log" to "/var/opt/mssql/data/rmsdev_log.ldf", NOUNLOAD, STATS = 5' \
    && pkill sqlservr

# Final stage that actually will actually be run.
FROM setup
# Copy the prepopulated data tree, but not the backup file
COPY --from=data /var/opt/mssql /var/opt/mssql
# Use the default USER, CMD, etc. from the base SQL Server image

标准的 Docker Hub 开源数据库镜像喜欢mysql并且postgres通常VOLUME在其 Dockerfile 中为数据库数据声明一个,这会强制将数据存储在一个卷中。这意味着重要的是您不能像这样在图像中设置数据;您必须在外部填充数据,然后将数据树复制到 Docker 映像系统之外。


推荐阅读