Git的Dockerfile策略

icomxhvb  于 2023-05-05  发布在  Git
关注(0)|答案(4)|浏览(230)

使用Dockerfile将私有Git仓库克隆到Docker容器中的最佳策略是什么?利/弊?
我知道我可以在Dockerfile上添加命令,以便将我的私有存储库克隆到Docker容器中。但我想知道人们在这个案子上使用了哪些不同的方法。
Dockerfile最佳实践指南中没有介绍它。

qvtsj1bj

qvtsj1bj1#

来自Ryan Baumann的博客文章“Git strategies for Docker”
将Git源代码导入Docker构建有不同的策略。其中许多都有不同的方式与Docker的缓存机制进行交互,并且可能或多或少适合您的项目以及您打算如何使用Docker。

运行git clone

如果你像我一样,这是当你看到Dockerfile中可用的命令时首先想到的方法。这样做的问题是,它可以以几种不直观的方式与Docker的构建缓存机制进行交互。例如,如果你对git仓库进行了更新,然后重新运行带有RUN git clone命令的docker build,你可能会也可能不会得到新的提交,这取决于前面的Dockerfile命令是否该高速缓存无效。
解决这个问题的一个方法是使用docker build --no-cache,但是如果克隆之前有任何时间密集的命令,它们也必须再次运行。
另一个问题是,当上游的git仓库更新时,您(或您将Dockerfile分发给的人)可能会意外地返回到一个损坏的构建。
在使用RUN git clone的情况下,一个一石二鸟的方法是将它放在一行1中,并带有特定的修订 checkout ,例如:

RUN git clone https://github.com/example/example.git && cd example && git checkout 0123abcdef

然后更新Dockerfile中要 checkout 的修订版本将使该行该高速缓存无效,并导致克隆/ checkout 运行。
通常这种方法的一个可能的缺点是你必须在容器中安装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命令将这些路径复制到容器中您期望的位置。
您可以在here中看到这种方法

git仓库内的Dockerfile

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

卷Map

如果您使用Docker来设置一个开发/测试环境,并希望在主机上的各种源代码库中共享,那么将主机目录挂载为数据卷可能是一个可行的策略。这使您能够指定在Docker运行时要包含哪些目录,并避免对Docker构建缓存的担忧,但这些都不会在Dockerfile或容器映像的其他用户之间共享。

yzckvree

yzckvree2#

通常有两种方法:

  • 引用一个vault,您可以在其中获取访问需要放入映像的内容所需的秘密数据(这里是用于访问私有存储库的ssh密钥)

2018年更新:参见“How to keep your container secrets secure”,其中包括:

  • 使用卷装载在运行时将机密传递到容器
  • 有一个轮换秘密的计划
  • 确保你的秘密被加密了
  • 或挤压技术(不推荐,见评论)

关于第二种方法,请参见“Pulling Git into a Docker image without leaving SSH keys behind

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

Dockerfile:

ADD ~/.ssh/mykey /tmp/  
RUN ssh-agent /tmp  
# RUN bundle install or similar command
RUN rm /tmp/mykey

现在让我们构建图像:

$ docker build -t original .
  • 挤压层:
docker save original | sudo docker-squash -t squashed | docker load
w8ntj3qf

w8ntj3qf3#

我能想到的策略有几个:

选项A:Dockerfile中的单级:

ADD ssh-private-key /root/.ssh/id_rsa
RUN git clone git@host:repo/path.git

这有几个显著的缺点:

  • 你的私钥在docker镜像中。
  • 该步骤将从先前的构建缓存到后续的构建中,即使您的存储库发生了更改,除非您在较早的步骤中中断该高速缓存。这是因为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
...

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

选项C:从配置项服务器:

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

COPY path /path

这有几个优点:

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

诚然,这个选项是在对你的问题说“不要这样做”,但它也是我所见过的面临这一挑战的人最受欢迎的选择,这是有充分理由的。

选项D:使用BuildKit:

BuildKit有几个可能有用的实验特性。这些需要较新版本的Docker,可能不是每个构建主机上都有,并且注入选项的语法不是向后兼容的。主要的两个选项是secrets或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

然后构建看起来像:

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

这是相当复杂的,但有几个优点:

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

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

提示:对特定行进行缓存破坏:

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

ARG CACHE_BUST

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

--build-arg "CACHE_BUST=$(date +%s)"

为每个构建注入唯一变量。这可确保生成始终运行该步骤,即使该命令在其他方面未发生更改。build arg作为环境变量注入到RUN中,因此docker会看到此命令已更改,并且无法该高速缓存中重用。
理想情况下,您可以克隆一个特定的标签或提交ID,这允许您缓存使用与以前构建相同的git克隆的构建。但是,如果您正在克隆主服务器,则需要使用此缓存破坏技术。

x0fgdtte

x0fgdtte4#

添加

ADD https://github.com/youraccount/myscript.git#main .
这会将整个repo复制到您的容器中。下面是一个工作示例:

FROM node:latest
WORKDIR /usr/src/app
ADD https://github.com/youraccount/myscript.git#main .
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080

您的存储库中的.dockerignore文件决定了哪些文件夹/文件被过滤/忽略。ADD也可以抓取单个文件。
如果是私有回购,文档中会说:

添加私有git仓库

要通过SSH添加私有存储库,请使用以下格式创建Dockerfile:

# syntax=docker/dockerfile:1-labs
FROM alpine
ADD git@git.example.com:foo/bar.git /bar

相关问题