在容器中挂载目录并与主机共享

27
我原以为我理解了文档,但可能并非如此。我认为 -v /HOST/PATH:/CONTAINER/PATH 标志是双向的。如果容器中有文件或目录,则它们会在主机上镜像,这样即使移除Docker容器,我们也能保留目录和文件。
在官方MySQL Docker镜像中,这个功能是可行的。可以将 /var/lib/mysql 绑定到主机上,并在重新启动和替换容器时保留数据。
我编写了一个sphinxsearch-2.2.9的Docker文件,只是为了练习和学习理解,以下是它:
FROM debian

ENV SPHINX_VERSION=2.2.9-release

RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -yqq\
    build-essential\
    wget\
    curl\
    mysql-client\
    libmysql++-dev\
    libmysqlclient15-dev\
    checkinstall

RUN wget http://sphinxsearch.com/files/sphinx-${SPHINX_VERSION}.tar.gz && tar xzvf sphinx-${SPHINX_VERSION}.tar.gz && rm sphinx-${SPHINX_VERSION}.tar.gz

RUN cd sphinx-${SPHINX_VERSION} && ./configure --prefix=/usr/local/sphinx

EXPOSE 9306 9312

RUN cd sphinx-${SPHINX_VERSION} && make

RUN cd sphinx-${SPHINX_VERSION} && make install

RUN rm -rf sphinx-${SPHINX_VERSION}

VOLUME /usr/local/sphinx/etc
VOLUME /usr/local/sphinx/var

非常简单易懂,容易掌握。我将sphinx生成的/etc和/var目录分配给VOLUME命令,并认为这将允许我执行如下操作:-v ~/dev/sphinx/etc:/usr/local/sphinx/etc -v ~/dev/sphinx/var:/usr/local/sphinx/var,但实际上不是这样的,它会覆盖容器内的目录并使其为空白。当我删除-v标志并创建容器后,目录中有预期的文件并且不会被覆盖。
以下是我在所在目录中运行的命令以创建docker文件:docker build -t sphinxsearch . 一旦我创建了这个,我会执行以下步骤来创建一个基于该映像的容器:docker run -it --hostname some-sphinx --name some-sphinx --volume ~/dev/docker/some-sphinx/etc:/usr/local/sphinx/etc -d sphinxsearch 我真的很感谢任何帮助和见解,以便使这项工作。我查看了MySQL图像,并没有看到他们对使目录可绑定做出任何神奇的事情,他们使用了VOLUME。
提前感谢你的帮助。

你知道在Dockerfile中,"VOLUME /usr/local/sphinx/etc"和"docker run -v /usr/local/sphinx/etc:/usr/local/sphinx/etc"不是一回事吗?前者绕过了分层文件系统,而后者将其映射到主机上你选择的路径。通常两者会一起使用。 - hookenz
是的,我知道那个。我想从容器中访问目录,以便我可以在主机上拥有数据。这是我能想到的唯一方法。我在Docker论坛上发布了帖子,但没有得到任何帮助或建议,这是通过尝试和查看无数示例得出的结果。 - Hatem Jaber
3个回答

16

经过无数小时的研究,我决定使用以下Dockerfile来扩展我的镜像:

FROM sphinxsearch

VOLUME /usr/local/sphinx/etc
VOLUME /usr/local/sphinx/var

RUN mkdir -p /sphinx && cd /sphinx && cp -avr /usr/local/sphinx/etc . && cp -avr /usr/local/sphinx/var .

ADD docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]

通过扩展它,使我在测试时不必从头开始构建整个镜像,只需构建相关的部分。

我创建了一个ENTRYPOINT来执行一个bash脚本,将文件复制回所需的目标以使sphinx正常运行,下面是该代码:

#!/bin/sh
set -e

target=/usr/local/sphinx/etc

# check if directory exists
if [ -d "$target" ]; then
    # check if we have files
    if find "$target" -mindepth 1 -print -quit | grep -q .; then
        # no files don't do anything
        # we may use this if condition for something else later
        echo not empty, don\'t do anything...
    else
        # we don't have any files, let's copy the
        # files from etc and var to the right locations
        cp -avr /sphinx/etc/* /usr/local/sphinx/etc && cp -avr /sphinx/var/* /usr/local/sphinx/var
    fi
else
    # directory doesn't exist, we will have to do something here
    echo need to creates the directory...
fi

exec "$@"

有权访问主机上的 /etc 和 /var 目录允许我在重新启动等过程中调整文件并保留它们在主机上的副本... 我也将数据保存在主机上,这样就可以在重启后恢复数据。
我知道关于数据容器与存储在主机上的争议话题,目前我更倾向于将数据存储在主机上,但以后会尝试其他方法。如果有人有任何建议、意见等来改进我的方法或找到更好的方法,请分享。
感谢 @h3nrik 的建议和帮助!

6
挂载容器目录到主机违反了Docker的概念,这会破坏进程/资源封装原则。相反地,将主机文件夹挂载到容器中是可以的。但我更建议使用卷容器来代替。请参考volume containers

MySQL 如何使用挂载 /var/lib/mysql 目录的方式将数据持久化到主机上?我想实现相同的功能。 - Hatem Jaber
也许“mounting”这个词不太合适,那么从容器共享目录到主机怎么样?例如配置目录? - Hatem Jaber
1
只需使用以下命令为/var/lib/mysql创建一个卷容器:docker run -it --name mysqldata -v /var/lib/mysql mysql /bin/true。然后通过以下方式从您的mysql服务容器链接到它:docker run -d --name mysqlserver --volumes-from mysqldata mysql - Henrik Sachse
2
它的行为类似于Linux挂载。假设您有一个带有现有文件的目录。然后,您将一个空的磁盘驱动器挂载到该目录中。结果是,存在于该目录中的文件被挂载“隐藏”,该目录看起来是空的。类似的情况也会发生在绑定的Docker卷中。因此,请勿将卷挂载到/etc或类似位置。这将隐藏所有先前存在的配置文件。相反,当您想要仅向/etc目录提供一小部分文件时,请尽可能使用所描述的单个文件挂载。 - Henrik Sachse
4
如果这与概念不符,那么如何在Docker中开发应用程序?如果我在每次更改代码后构建Docker镜像,那么速度会很慢。如果我在Docker外运行应用程序,则会削弱Docker的优势。 - Gherman
显示剩余2条评论

0

因为mysql在映射之后才进行初始化,所以在映射之前,在/var/lib/mysql中没有任何数据。

因此,如果您在启动容器之前有数据,则-v操作将覆盖您的数据。

请参阅entrypoint.sh。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接