Docker COPY 命令未挂载目录

use*_*021 1 docker docker-compose

Host OS: Linux
Container OS: Linux
Run Code Online (Sandbox Code Playgroud)

我正在尝试学习如何使用 docker。我使用 docker-compose 并且成功构建了图像并运行了容器。

现在,如果我想在容器内挂载某个目录,文档表明我应该在 Dockerfile 内使用 COPY 命令。

COPY /path/to/my/addons/ /path/to/directory/inside/container
Run Code Online (Sandbox Code Playgroud)

遗憾的是,当我编写此容器时,COPY 命令被忽略,并且 /path/to/my/addons 中的内容无法进入容器。

我也尝试过使用ADD命令,但同样的问题。

Lio*_*ion 5

绝对路径

首先,不能使用绝对路径COPY。所有路径必须位于构建的上下文中,这意味着相对于Dockerfile. 如果你的主机上的文件夹结构是这样的

my-docker-directory
-- Dockerfile
-- docker-compose.yml
-- addons
Run Code Online (Sandbox Code Playgroud)

然后你就可以使用COPY addons /path/to/directory/inside/container. 对于所有后续解释,我假设您有一个addons相对于Dockerfile.

挂载目录

COPY不只是在运行时将文件夹安装到容器。它根本没有真正挂载目录。相反,addons被复制到图像/path/to/directory/inside/container内部。重要的是要理解,这个过程是单向发生的(主机>镜像)并且仅在构建镜像时发生。

COPY旨在将构建时所需的依赖项添加到映像中,例如编译为二进制文件的源代码。这就是为什么你不能使用绝对路径的原因。ADockerfile通常与源代码/配置文件一起放置在顶层区域。

图像的构建过程仅在第一次运行时发生,除非您使用docker-compose up --build. 但这似乎并不是你想要的。要在运行时从主机挂载目录,请volume在docker-compose文件中使用 a:

version: '3'
services:
  test:
    build: .
    volumes:
      - ./addons/:/path/to/directory/inside/container
Run Code Online (Sandbox Code Playgroud)

何时使用COPY以及何时使用?

认识到这一点很重要,COPY并将在构建时ADD将这些内容复制到映像中,并在volumes运行时从主机挂载它们(不将它们包含在映像中)。因此,您通常会将用户需要的一般内容(例如默认配置文件)复制到映像中。

卷需要包含来自主机的文件,例如自定义配置文件。或者作为数据库的数据目录的持久事物。如果没有卷,这些容器可以工作,但不会持久。因此,当容器重新启动时,所有内容都会丢失。

请注意,其中之一并不排除另一个。COPY镜像中某些应用程序的默认配置很好,用户可以使用卷覆盖它来修改它们。特别是在开发过程中,这可以使事情变得更容易,因为您不必为单个更改的配置文件重建整个映像*

* 尽管针对集成缓存机制优化 Dockerfile 是一个很好的做法。如果 aDockerfile写得好,重建小的配置更改通常不会花费太长时间。但这是这个范围之外的另一个话题。

更详细的解释和例子

基本COPY 设置Dockerfile

举个简单的例子,我们从 nginx 网络服务器镜像创建一个Dockerfile并将 html 复制到其中

FROM nginx:alpine
COPY my-html /usr/share/nginx/html
Run Code Online (Sandbox Code Playgroud)

让我们创建包含演示内容的文件夹

mkdir my-html
echo "Dockerfile content" > my-html/index.html
Run Code Online (Sandbox Code Playgroud)

并添加一个简约的docker-compose.yml

version: '3'
services:
  test:
    build: .
Run Code Online (Sandbox Code Playgroud)

如果我们第一次使用 运行它docker-compose up -d,则会构建图像并提供我们的测试页面:

root@server2:~/docker-so-example# docker-compose up -d
Creating network "docker-so-example_default" with the default driver
Creating docker-so-example_test_1 ... done

root@server2:~/docker-so-example# curl $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker-so-example_test_1)
Dockerfile content
Run Code Online (Sandbox Code Playgroud)

让我们操作我们的测试文件:

echo "NEW Modified content" > my-html/index.html
Run Code Online (Sandbox Code Playgroud)

如果我们再次请求服务器curl,我们会得到旧的响应:

root@server2:~/docker-so-example# curl $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker-so-example_test_1)
Dockerfile content
Run Code Online (Sandbox Code Playgroud)

要应用我们的内容,需要重建:

docker-compose down && docker-compose up -d --build
Run Code Online (Sandbox Code Playgroud)

现在我们可以看到我们的更改:

root@server2:~/docker-so-example# curl $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker-so-example_test_1)
NEW Modified content
Run Code Online (Sandbox Code Playgroud)

使用体积docker-compose

为了显示差异,我们通过修改文件来使用卷,docker-compose.yml如下所示:

version: '3'
services:
  test:
    build: .
    volumes:
      - ./my-html:/usr/share/nginx/html
Run Code Online (Sandbox Code Playgroud)

现在使用以下命令重新启动容器docker-compose down && docker-compose up -d并重试:

root@server2:~/docker-so-example# echo "Again changed content" > my-html/index.html
root@server2:~/docker-so-example# curl $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker-so-example_test_1)
NEW Modified content
root@server2:~/docker-so-example# echo "Some content" > my-html/index.html
root@server2:~/docker-so-example# curl $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker-so-example_test_1)
Some content
Run Code Online (Sandbox Code Playgroud)

请注意,我们没有重新构建图像,并且我们的修改会立即应用。使用卷时,文件不会包含在映像中。