Git的Dockerfile策略

Hem*_*ela 23 git git-clone docker dockerfile

使用Dockerfile将私有Git存储库克隆到Docker容器的最佳策略是什么?优点缺点?

我知道我可以在Dockerfile上添加命令,以便将我的私有存储库克隆到docker容器中.但我想知道人们在这个案例中采用了哪些不同的方法.

它没有包含在Dockerfile最佳实践指南中.

Hem*_*ela 14

我将分享到目前为止我发现的内容.

将Git源代码添加到Docker构建中有不同的策略.其中许多方法与Docker的缓存机制有不同的交互方式,可能或多或少地适合您的项目以及您打算如何使用Docker.

运行git clone

如果您像我一样,当您在Dockerfile中看到可用的命令时,首先想到的是这种方法.这样做的问题在于,它可以通过Docker的构建缓存机制以几种不直观的方式进行交互.例如,如果您对git存储库进行更新,然后重新运行具有RUN git clone命令的docker build,则可能会或可能不会获取新的提交,具体取决于前面的Dockerfile命令是否已失效缓存.

解决这个问题的一种方法是使用docker build --no-cache,但是如果在克隆之前有任何时间密集的命令,那么它们也必须再次运行.

另一个问题是,您(或者您已将Dockerfile分发给的人)可能会在上游git存储库更新后意外地返回到破坏的构建.

在使用RUN git clone的同时采用双鸟一石的方法是将其放在具有特定修订结帐的一行1上,例如:

RUN git clone https://github.com/example/example.git && cd example && git checkout 0123abcdef
Run Code Online (Sandbox Code Playgroud)

然后更新要在Dockerfile中签出的修订版将使该行的缓存无效并导致克隆/签出运行.

这种方法的一个可能的缺点是你必须在容器中安装git.

运行curl或添加标记/提交tarball URL

这样可以避免必须在容器环境中安装git,并且可以明白缓存何时中断(例如,如果标记/修订版是URL的一部分,那么URL更改将破坏缓存).请注意,如果使用Dockerfile ADD命令从远程URL进行复制,则每次运行构建时都会下载该文件,并且HTTP Last-Modified标头也将用于使缓存无效.

您可以在golang Dockerfile中看到这种方法.

Dockerfile存储库中的Git子模块

如果你将Dockerfile和Docker构建在与源代码不同的存储库中,或者你的Docker构建需要多个源存储库,那么在这个存储库中使用git子模块(或git子树)可能是一种有效的方法来将你的源代码存储库放到你的构建中上下文.这可以避免一些关于Docker缓存和上游更新的问题,因为您在子模块/子树规范中锁定了上游修订版.更新它们会在更改构建上下文时破坏Docker缓存.

请注意,这只会将文件放入Docker构建上下文中,您仍需要在Dockerfile中使用ADD命令将这些路径复制到容器中预期的位置.

你可以在这里看到这种方法

Dockerfile里面有git仓库

在这里,您只需将Dockerfile与您要构建/测试/部署的代码放在同一个git存储库中,因此它会自动作为构建上下文的一部分发送,因此您可以添加ADD./ project将上下文复制到容器中.这样做的好处是,您可以测试更改,而无需提交/推送它们以使它们进入测试docker构建; 缺点是每次修改工作目录中的任何文件时,它都会使ADD命令的缓存失效.发送大型源/数据目录的构建上下文也很耗时.因此,如果您使用此方法,您可能还想明智地使用.dockerignore文件,包括忽略.gitignore中的所有内容以及可能的.git目录本身.

卷映射

如果您正在使用Docker设置要在主机上的各种源代理中共享的开发/测试环境,则将主机目录作为数据卷安装可能是一种可行的策略.这使您能够指定要在docker运行时包含哪些目录,并避免担心docker构建缓存,但这些都不会在Dockerfile或容器映像的其他用户之间共享.

-

参考文献:

  • 这个问题具体是关于克隆私人仓库的。您的建议不适用于私人回购。 (2认同)

Von*_*onC 7

您通常有两种方法:

  • 引用保险库,您可以在其中获取必要的秘密数据,以访问您需要放入图像的内容(此处,您的ssh密钥可访问您的私人仓库)

更新2018:请参阅" 如何确保您的容器机密安全 ",其中包括:

  • 使用卷装入在运行时将密钥传递给容器
  • 有一个轮换秘密的计划
  • 确保您的秘密已加密

  • 或挤压技术(不推荐,见评论)

对于第二种方法,请参阅"将Git拖入Docker镜像而不留下SSH密钥 "

  • 将私钥添加到Dockerfile
  • 将其添加到ssh-agent
  • 运行需要SSH认证的命令
  • 删除私钥

Dockerfile:

ADD ~/.ssh/mykey /tmp/  
RUN ssh-agent /tmp  
# RUN bundle install or similar command
RUN rm /tmp/mykey  
Run Code Online (Sandbox Code Playgroud)

我们现在构建图像:

$ docker build -t original .
Run Code Online (Sandbox Code Playgroud)

  • 添加私钥可能会在Dockerfile上添加一层,这意味着它们存储在此处,并且即使足够聪明的人也可以检索,即使此后“删除” (4认同)
  • @alexvicegrab好点。2年后,我编辑了答案,以使第一种方法更可取。 (2认同)

BMi*_*tch 5

我能想到的有几种策略:

选项 A:Dockerfile 中的单个阶段:

ADD ssh-private-key /root/.ssh/id_rsa
RUN git clone git@host:repo/path.git
Run Code Online (Sandbox Code Playgroud)

这有几个明显的缺点:

  • 您的私钥在 docker 镜像中。
  • 该步骤将从之前的构建中缓存到以后的构建中,即使您的 repo 更改,除非您在较早的步骤中破坏了缓存。那是因为这RUN条线没有改变。

选项 B:Dockerfile 中的多阶段:

FROM base-image as clone
ADD ssh-private-key /root/.ssh/id_rsa
RUN git clone git@host:repo/path.git
RUN rm -rf /path/.git

FROM base-image as build
COPY --from=clone /path /path
...
Run Code Online (Sandbox Code Playgroud)

通过使用多阶段,只要您从不将“克隆”阶段层推送到任何地方,您的 ssh 凭证现在只在构建主机上。这稍微好一点,但仍然存在缓存问题(请参阅最后的提示)。通过添加rm步骤,后者COPY --from将不再复制这些文件。由于构建映像或更高版本应该是您交付的全部内容,因此在克隆阶段的层上效率低下就不那么重要了。

选项 C:从您的 CI 服务器:

通常,Dockerfile 位于代码存储库中,人们倾向于在运行构建之前先克隆它(尽管可以通过使用 git 存储库作为构建上下文来跳过它)。因此,您经常会看到 CI 服务器执行克隆和更新,而不是 Dockerfile 本身。生成的 Dockerfile 就是:

COPY path /path
Run Code Online (Sandbox Code Playgroud)

这有几个优点:

  • 凭据永远不会添加到 docker 镜像层。
  • 更新 repo 不会从头开始重新运行克隆,以前的克隆已经存在,您可以运行 agit pull代替,这要快得多。
  • 将文件复制到映像中可以包含.git在 内部.dockerignore以排除所有 git 内部结构。因此,您只需将 repo 的最终状态添加到您的 docker 镜像中,从而生成一个小得多的镜像。

诚然,这个选项对您的问题说“不要那样做”,但这也是我从面临这一挑战的人那里看到的最受欢迎的选项,这是有充分理由的。

选项 D:使用 BuildKit:

BuildKit 有几个可能有用的实验性功能。这些需要较新版本的 Docker,可能不是每个构建主机上都有,并且注入选项的语法不向后兼容。主要的两个选项是秘密或 ssh 凭据注入和缓存目录。这两者都可以将文件或目录注入到构建步骤中,而不会保存到生成的图像层中。这是可能的样子(这是未经测试的):

# syntax=docker/dockerfile:experimental
FROM base-image
ARG CACHE_BUST
RUN --mount=type=cache,target=/git-cache,id=git-cache,sharing=locked \
    --mount=type=secret,id=ssh,target=/root/.ssh/id_rsa \
    if [ ! -d /git-cache/path/.git ]; then \
      git clone git@host:repo/path.git /git-cache/path; \
    else \
      (cd /git-cache/path && git pull --force); \
    fi; \
    tar -cC /git-cache/path --exclude .git . | tar -xC /path
Run Code Online (Sandbox Code Playgroud)

然后构建看起来像:

DOCKER_BUILDKIT=1 docker build \
  --secret id=ssh,src=$HOME/.ssh/id_rsa \
  --build-arg "CACHE_BUST=$(date +%s)" \
  -t img:tag \
  .
Run Code Online (Sandbox Code Playgroud)

这是相当复杂的,但有一些优点:

  • 缓存目录保存了上次构建的 git repo,为每个构建保存一个大的克隆,只提取更改。
  • tar 命令基本上是一个.git从最终图像中排除目录的副本,使您的图像更小。由于缓存目录未保存到生成的图像层中,因此需要此副本。
  • ssh 凭证作为一个秘密注入,看起来类似于该特定RUN步骤的单个文件只读卷安装,并且该秘密的内容没有保存到生成的图像层。

要阅读有关 BuildKit 的实验性功能的更多信息,请参阅:https : //github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md

提示:缓存破坏特定行:

要在特定行上破坏 docker 构建缓存,您可以在要重新运行的 RUN 行之前注入一个构建参数,该参数在每个构建中都会发生变化。在 BuildKit 示例中,有:

ARG CACHE_BUST
Run Code Online (Sandbox Code Playgroud)

RUN我不想缓存的行之前,构建包括:

--build-arg "CACHE_BUST=$(date +%s)"
Run Code Online (Sandbox Code Playgroud)

为每个构建注入一个唯一的变量。这确保构建始终运行该步骤,即使命令未更改。build arg 作为环境变量注入RUNso docker 然后看到此命令已更改并且无法从缓存中重用。

理想情况下,您将克隆特定的标记或提交 ID,这允许您缓存使用与先前构建相同的 git clone 的构建。但是,如果您是克隆大师,则需要这种缓存破坏技术。