Docker 遵循上下文外的符号链接

又一个 Docker 符号链接的问题。我有一堆文件要复制到我所有的 Docker 版本中。我的目标结构是:

parent_dir
- common_files
- file.txt
- dir1
- Dockerfile
- symlink -> ../common_files

在上面的例子中,我希望在 dir1内部构建 docker 时能够复制 file.txt。但是我不想维护 file.txt 的多个副本。 根据 这个链接,作为 Docker 0.10版本码头建设必须

按照容器根目录中的符号链接获取 ADD 构建指令。

但是当我使用 Dockerfile 中的这两行代码构建时,我得到了 没有这样的文件或目录:

ADD symlink /path/dirnameADD symlink/file.txt /path/file.txt

挂载选项不会为我解决(跨平台...)。 我尝试了 tar -czh . | docker build -t但没有成功。

有没有办法让 Docker 遵循符号链接并将 common _ files/file.txt 复制到构建的容器中?

114958 次浏览

这是不可能的,也不会实施。请看一下 关于 github 问题的讨论 # 1676:

我们不允许这样做,因为这是不可重复的。您的计算机上的符号链接与我的计算机上的不同,相同的 Dockerfile 将产生两种不同的结果。还有,对/etc/paasswd 的符号链接会导致问题,因为它会链接主机文件,而不是您的本地文件。

一种可能性是在父目录中运行构建,具有:

$ docker build [tags...] -f dir1/Dockerfile .

(或者相当于在子目录中)

$ docker build  [tags...] -f Dockerfile ..

Dockerfile 必须配置为使用适当的路径进行复制/添加。根据您的设置,您可能希望在父级中省略 .dockerignore 那些你不想被放进背景里的东西。

与使用 simlink 不同的是,可以通过仅仅将文件从 sites- 可用移动到 sites- 启用,而不是复制或制作 simlink 来从管理上解决问题

因此,如果站点配置已停止或出现问题,那么它将只出现在 site _ able 文件夹中的一个副本中; 如果应该使用站点配置,那么它将出现在 site _ enable 文件夹中的一个副本中

我知道它破坏了 Docker build 的可移植性,但是你可以使用硬链接代替符号:

ln /some/file ./hardlink

如果还有人有这个问题,我在 superuser.com 上找到了一个很好的解决方案:

Https://superuser.com/questions/842642/how-to-make-a-symlinked-folder-appear-as-a-normal-folder

它主要建议使用 tar 来取消对符号链接的引用,并将结果提供给 docker build:

$ tar -czh . | docker build -

我只需要在同样的背景下解决这个问题。我的解决方案是使用分层的 Docker 构建。换句话说:

parent_dir
- common_files
- Dockerfile
- file.txt


- dir1
- Dockerfile (FROM common_files:latest)

缺点是您必须记住在 dir1之前构建 common _ files。这样做的好处是,如果您有许多依赖图像,那么由于使用了一个公共图层,它们都会小一些。

我非常沮丧,于是我编写了一个小的 NodeJS 实用程序来帮助实现这个功能: 文件同步器

鉴于现有的目录结构:

parent_dir
- common_files
- file.txt
- my-app
- Dockerfile
- common_files -> symlink to ../common_files

基本用法:

cd parent_dir


// starts live-sync of files under "common_files" to "my-app/HardLinked/common_files"
npx file-syncer --from common_files --to my-app/HardLinked

然后在你的 Dockerfile:

[regular commands here...]


# have docker copy/overlay the HardLinked folder's contents (common_files) into my-app itself
COPY HardLinked /

问/答

  • 这比在 Docker 运行之前将 parent_dir/common_files复制到 parent_dir/my-app/common_files更好吗?

这将意味着放弃常规的符号链接,这将是一个损失,因为符号链接很有用,并且可以在大多数工具中正常工作。例如,这将意味着您无法从 in-my-app 副本中查看/编辑 common_files的源文件,这有一些缺点。(见下文)

  • 这比在 Docker 运行之前将 parent_dir/common-files复制到 parent_dir/my-app/common_files_Copy,然后让 Docker 在构建时将其复制到 parent_dir/my-app/common_files要好多少呢?

这样做有两个好处:

  1. file-syncer不“复制”常规意义上的文件。相反,它从源文件夹的文件创建 硬链接。这意味着如果编辑 parent_dir/my-app/HardLinked/common_files下的文件,parent_dir/common_files下的文件会立即更新,反之亦然,因为它们引用相同的文件/inode。(这对于调试和跨项目编辑很有帮助(特别是如果你正在同步的文件夹是符号链接的节点-模块,你正在积极编辑) ,并确保你的文件版本始终与源文件同步/相同)
  2. 因为 file-syncer只更新硬链接文件中被更改的文件,所以像 倾斜Skaffold这样的文件监视工具可以检测到最小文件集的更改,这意味着比基本的“文件更改时复制整个文件夹”工具更快的实时更新推送时间。
  • 这怎么会比常规的文件同步工具(如 Syncthing)更好呢?

其中一些工具可能是可用的,但大多数都存在这样或那样的问题。最常见的一个问题是,这个工具要么不能生成现有文件的硬链接,要么不能为已经硬链接的文件“推送更新”(因为硬链接的文件不会自动通知文件观察者他们的更改,如果编辑的和观察的路径不同)。另一个原因是,这些同步工具中的许多都不是为即时响应而设计的,并且/或者没有运行标志,这使得它们很容易在受限制的构建工具中使用。(例如,对于 Tilt,file-syncer--async标志使它能够在项目的 Tiltfile中的 local(...)调用中使用)

使用一个小的包装脚本将所需的目录复制到 Dockerfile 的位置;

  • build.sh;

.

#!/bin/bash
[ -e bin ] && rm -rf bin
cp -r ../../bin .
docker build -t "sometag" .

如果你在吃麦克,记得 brew install gnu-tar 使用 gtar 而不是 tar 看起来两者之间有些不同。

Gtar 至少为我工作过。

通常我将构建指令隔离到子文件夹,因此应用程序和逻辑级别位于更高的位置:

.
├── app
│   ├── package.json
│   ├── modules
│   └── logic
├── deploy
│   ├── back
│   │   ├── nginx
│   │   │   └── Chart.yaml
│   │   ├── Containerfile
│   │   ├── skaffold.yaml
│   │   └── .applift -> ../../app
│   ├── front
│   │   ├── Containerfile
│   │   ├── skaffold.yaml
│   │   └── .applift -> ../../app
│   └── skaffold.yaml
└── .......

对于这些符号链接,我使用名称“ . applift” . applif- > . ./. ./app

现在通过 真实路径跟踪 symlink,而不需要关心路径深度

dir/deploy/front$ docker build -f Containerfile --tag="build" `realpath .applift`

或者带上乐队

dir/deploy$ docker_build () { docker build -f "$1"/Containerfile --tag="$2" `realpath "$1/.applift"`; }
dir/deploy$ docker_build ./back "front_builder"

所以

COPY app/logic/ ./app/

集装箱文件将工作

是的,在这种情况下,您将失去其他层的上下文。但是一般来说,build-directory 中没有任何其他上下文文件

允许以 docker 接受的方式“链接”目录的一个工具是 docker 本身。

可以运行一个临时的 docker 容器,将所有必要的文件/目录装入适当的路径,并从这样的容器中构建映像。例如:

docker run -it \
--rm \
-v /var/run/docker.sock:/var/run/docker.sock \
--mount "type=bind,source=$ImageRoot/Dockerfile,destination=/Image/Dockerfile,readonly" \
--mount "type=bind,source=$ImageRoot/.dockerignore,destination=/Image/.dockerignore,readonly" \
--mount "type=bind,source=$ReposRoot/project1,destination=/Image/project1,readonly" \
--mount "type=bind,source=$ReposRoot/project2,destination=/Image/project2,readonly" \
--env DOCKER_BUILDKIT=1 \
docker:latest \
docker build "/Image" --tag "my_tag"

在上面的例子中,我假设设置了变量 $ImageRoot$ReposRoot