【问题标题】:Docker and symlinksDocker 和符号链接
【发布时间】:2016-09-07 18:22:58
【问题描述】:

我有一个这样的回购设置:

/config
   config.json
/worker-a
   Dockerfile
   <symlink to config.json>
   /code
/worker-b
   Dockerfile
   <symlink to config.json>
   /code

但是,构建映像失败,因为 Docker 无法处理符号链接。我应该提到我的项目远比这复杂,所以重组目录不是一个好的选择。我该如何处理这种情况?

【问题讨论】:

    标签: docker symlink


    【解决方案1】:

    Dockerdoesn't support symlinking files outside the build context.

    以下是在容器中使用共享文件的一些不同方法:

    分享基础图片

    为包含共享配置/文件的基本 worker-config 映像创建一个 Dockerfile

    COPY config.json /config.json
    

    构建图像并将其标记为worker-config

    docker build -t worker-config:latest .
    

    为所有工作人员Dockerfiles 获取基本worker-config 图像

    FROM worker-config:latest
    

    构建脚本

    使用脚本将通用配置推送到每个工作容器。

    ./build worker-n

    #!/bin/sh
    set -uex 
    rundir=$(readlink -f "${0%/*}")
    container=$(shift)
    cd "$rundir/$container"
    cp ../config/config.json ./config-docker.json
    docker build "$@" .
    

    从 URL 构建

    从所有 worker-n 构建的公共 URL 中提取配置。

    ADD http://somehost/config.json /
    

    增加镜像构建上下文的范围

    通过从包含共享文件和特定容器文件的父目录构建,将符号链接目标文件包含在构建上下文中。

    cd ..
    docker build -f worker-a/Dockerfile .
    

    您在 Dockerfile 中引用的所有源路径也必须更改以匹配新的构建上下文:

    COPY workerathing /app
    

    变成

    COPY worker-a/workerathing /app
    

    如果您有一个大型构建上下文,则使用此方法可以使 所有 构建上下文变大,因为它们都是共享的。它会减慢构建速度,尤其是对于远程 Docker 构建服务器。请注意,仅引用了来自 the base of the build context.dockerignore 文件。

    从命名卷挂载配置目录

    像这样的卷只能用作目录,因此您不能像将文件从主机挂载到容器时那样指定文件。

    docker volume create --name=worker-cfg-vol
    docker run -v worker-cfg-vol:/config worker-config cp config.json /config
    
    docker run -v worker-cfg-vol:/config:/config worker-a
    

    从数据容器挂载配置目录

    同样,目录只是因为它与上面的基本相同。不过,这会自动将文件从目标目录复制到新创建的共享卷中。

    docker create --name wcc -v /config worker-config /bin/true
    docker run --volumes-from wcc worker-a
    

    从主机挂载配置文件

    docker run -v /app/config/config.json:/config.json worker-a
    

    【讨论】:

    • 虽然这是一个非常全面的答案,但看到它仍然很遗憾,因为 docker 应该为开发人员简化这些事情,以便他们能够完成他们真正的工作......
    • 对于我的用例,“增加图像构建上下文的范围”是最好的解决方案。不幸的是,这破坏了.dockerignore,这可能会导致巨大的容器映像包含来自扩大范围内其他目录(例如父目录)的无关代码。我一直在寻找几个小时,但似乎没有一个好的解决方案。 ?
    • @rinogo 很有趣,我以为它就像子目录中的 .gitignore。还有this workaroundthis proposal
    • @rinogo 现在您可以指定 dockerignore 的位置。检查详细信息here(已接受答案)。有人说他们在设置时遇到了问题,但我可以确认它对我有用。
    【解决方案2】:

    Node.js 特有的解决方案

    我也遇到了这个问题,想分享一下上面没有提到的另一种方法。我没有在我的Dockerfile 中使用npm link,而是使用了yalc

    1. 在您的容器中安装yalc,例如RUN npm i -g yalc
    2. 在 Docker 中构建您的库,然后运行 ​​yalc publish(如果您的共享库是私有的,请添加 --private 标志)。这将在本地“发布”您的库。
    3. 在运行npm install 之前,在通常使用npm link 的每个repo 中运行yalc add my-lib。它将在您的 Docker 容器中创建一个本地 .yalc 文件夹,在 node_modules 中创建一个在 Docker 内部工作的符号链接到此文件夹,并重写您的 package.json 以引用此文件夹,因此您可以安全地运行安装。
    4. 或者,如果您进行两阶段构建,请确保您还将 .yalc 文件夹复制到最终映像中。

    下面是一个示例Dockerfile,假设您有一个包含三个包的单声道存储库:模型、gui 和服务器,并且模型存储库必须共享并命名为my-models

    # You can access the container using:
    #   docker run -it my-name sh
    # To start it stand-alone:
    #   docker run -it -p 8888:3000 my-name
    
    FROM node:alpine AS builder
    # Install yalc globally (the apk add... line is only needed if your installation requires it)
    RUN apk add --no-cache --virtual .gyp python make g++ && \
      npm i -g yalc
    RUN mkdir /packages && \
      mkdir /packages/models && \
      mkdir /packages/gui && \
      mkdir /packages/server
    COPY ./packages/models /packages/models
    WORKDIR /packages/models
    RUN npm install && \
      npm run build && \
      yalc publish --private
    COPY ./packages/gui /packages/gui
    WORKDIR /packages/gui
    RUN yalc add my-models && \
      npm install && \
      npm run build
    COPY ./packages/server /packages/server
    WORKDIR /packages/server
    RUN yalc add my-models && \
      npm install && \
      npm run build
    
    FROM node:alpine
    RUN mkdir -p /app
    COPY --from=builder /packages/server/package.json /app/package.json
    COPY --from=builder /packages/server/dist /app/dist
    # Make sure you copy the yalc registry too.
    COPY --from=builder /packages/server/.yalc /app/.yalc
    COPY --from=builder /packages/server/node_modules /app/node_modules
    COPY --from=builder /packages/gui/dist /app/dist/public
    WORKDIR /app
    EXPOSE 3000
    CMD ["node", "./dist/index.js"]
    

    希望对您有所帮助...

    【讨论】:

    • yalc 看起来很有趣,但它真的比运行npm pack 更好吗?似乎步数相同。我不确定yalc 的锁定文件是做什么的。
    【解决方案3】:

    另一种解决方案是将所有软链接升级为硬链接。

    【讨论】:

    • 对于我们许多人来说,符号链接的意义在于它们可以指向单独的物理设备,例如 NFS。因此,对于符号链接最关键的用例(即,您的数据很大,因此您不想移动它),此建议是不可行的。
    • @carbocation 绝对正确。但是,在我的情况下,我只需要一个指向应该被烘焙到两个容器中的脚本的链接。因此,您的评论 +1,Benjamin Pastel 也 +1 :-)
    • @LaryxDecidua 不错!这是个好消息:-)
    • 这似乎是我们目前拥有的最佳解决方案。为了帮助简化这些硬链接的设置过程,我创建了一个小的 file-syncer 实用程序:stackoverflow.com/a/68765508
    • 绑定挂载是硬链接的另一种替代方案,但令人讨厌的是,它们需要在fstab 中或每次启动运行一次。在构建脚本中可能可以正常工作。 mount --bind /sourcefolder /dest/folder
    【解决方案4】:

    docker build CLI 命令将指定目录(通常为.)作为“构建上下文”发送到 Docker 引擎(守护进程)。不要将构建上下文指定为/worker-a,而是将构建上下文指定为根目录,并使用-f 参数在其中一个子目录中指定Dockerfile 的路径。

    docker build -f worker-a/Dockerfile .
    docker build -f worker-b/Dockerfile .
    

    您必须稍微修改您的 Dockerfile,将它们指向 ../config/config.json,但这很容易修复。

    还请查看此问题/答案,我认为它解决了您遇到的完全相同的问题。

    How to include files outside of Docker's build context?

    希望这会有所帮助!干杯

    【讨论】:

    • 不幸的是,从 repo 的根目录运行 docker 意味着我的构建变得更加繁琐,因为它们必须复制超过 250 兆的文件。我不能 .dockerignore 它们,因为其他 docker 构建需要它们中的一些。
    • 嗯,我理解这种担忧。尽管如此,仍然可能值得一试,具体取决于您的 Docker 引擎在哪里(例如本地、本地数据中心或云)。我能想到的另一个最佳选择是将config.json 复制到每个项目文件夹中。
    • 保持 7 个不同的配置文件同步似乎也很愚蠢。就像在构建发生之前编写将配置文件复制到目录中的构建脚本一样。对于 Docker 开发人员来说,这是一个非常愚蠢的限制。
    • 如果您在现有的 GitHub 线程中发布有关符号链接的帖子,您可能会获得更多关注。
    • 此外,如果没有将完整的目录结构复制到 repo 中,这个解决方案根本不起作用。你会得到指向不存在的文件的符号链接。
    猜你喜欢
    • 2021-11-07
    • 2015-10-08
    • 1970-01-01
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-23
    相关资源
    最近更新 更多