【问题标题】:How to avoid redundancy and time loss when re-building images during development?在开发过程中重建镜像时如何避免冗余和时间损失?
【发布时间】:2014-12-13 09:56:00
【问题描述】:

作为 Vagrant 用户,在尝试使用 Docker 时,我注意到使用 Vagrant 和使用 Docker 的开发工作流程之间存在一个显着差异 - 使用 Docker,我每次都需要从头开始重建我的映像,即使我对代码进行了微小的更改。

这对我来说是个大问题,因为图像重建的过程通常非常冗余且耗时。

也许已经发明了一些使用 Docker 的智能工作流程,如果是,它们是什么?

【问题讨论】:

  • @MarkO'Connor 系统如何知道它们是否发生了变化?
  • @MarkO'Connor 我正在使用 Ansible 使用这样的命令 RUN ansible-playbook provision.yml ... 构建我的图像 - 它永远不会命中缓存。
  • @MarkO'Connor 如果您发布有关缓存的答案,我会接受。

标签: deployment continuous-integration workflow docker


【解决方案1】:

我为 vagrant-cachier 插件提交了一个功能请求,用于保存 docker 构建数据,并为该过程附加了一个 bash 解决方法。如果您可以自行破解,您可以在 vagrant 中实现脚本。

caching docker build data with vagrant

请注意,此过程需要安装 vagrant-cachier 插件,如果是新机器,则必须从磁盘保存和加载 +300MB 的文件。因此,如果你的 dockerfile 只包含 1-5 行代码,这真的很慢,但如果你的 dockerfile 有很多必须从网上下载的 LOC 或图像,它会很快。

另请注意,这种方法节省了每个中间构建步骤。因此,如果您正在构建映像并在 dockerfile 中间更改一行并再次构建,则 docker build 过程将获取所有缓存的中间容器,直到更改的行。

使用 baseimages 仍然是首选方式,但您可以将这两个过程结合起来。

请随时发布改进并订阅,以便 fgrehm 可能会在他的插件中原生实现这一点。

【讨论】:

    【解决方案2】:

    正如 Mark O'Connor 所建议的,其中一个技巧可能是为您的容器构建基础映像。此图像应具有依赖项、包安装、下载...或任何其他消耗活动。该基础镜像的构建频率应该低于其他镜像。类似地,如果 dockerfile 的每一步执行的最终状态没有改变,Docker 就不会再次构建该层。因此,您可以尽可能晚地尝试执行几乎每次运行都会更改此状态的命令(例如:apt-get update),因此 docker 不必重新构建之前的步骤。您也可以尝试在后面的步骤中比在第一步中更好地编辑您的 dockerfile。

    如果您在容器内编译/下载某些内容,另一种选择是将其下载或编译到主机文件夹中,然后使用docker run 中的-v--volume 选项将其附加到容器。

    最后,还有其他方法可以解决这个问题,如 knife container 厨师使用的方法。在这种方法中,您使用厨师食谱构建容器,并且每次构建它(因为您已经编辑了您的食谱......)这些更改将作为新的 docker 层(AUFS 层)应用,您不必重复所有过程。我不推荐这个解决方案,除非你有使用 Chef 的经验并且你有食谱来管理你的软件。您应该更加努力地让它工作,如果您只想让 Chef 管理 docker 容器,我认为这不值得(尽管 Chef 是管理基础设施的绝佳选择)。

    如果您自己有多个图像依赖项,要使构建过程自动化,您可以使用 bash 脚本来帮助您完成该任务(感谢 smola@github):

    #!/bin/bash
    IMAGES="${IMAGES:-stratio/base:test stratio/mesos:test stratio/spark-mesos:test stratio/ingestion:test}"
    LATEST_TAG="${LATEST_TAG:-test}"
    for image in $IMAGES ; do
        USER=${image/\/*/}
        aux=${image/*\//}
        NAME=${aux/:*/}
        TAG=${aux/*:/}
        DIR=${NAME}/${TAG}
        pushd $DIR
        docker build --tag=${USER}/${NAME}:${TAG} .
        if [[ $TAG = $LATEST_TAG ]] ; then
            docker tag ${USER}/${NAME}:${TAG} ${USER}/${NAME}:latest
        fi
        popd
    done
    

    【讨论】:

    • 主要问题,我在这里看到的是如何正确地对这些依赖图像进行版本控制?
    • 您要询问哪种解决方案?如我所见,您可以尝试管理使用不同标签构建的图像之间的依赖关系,将版本称为标签,然后何时可以更新或不更新您的“儿子”Dockerfiles 中的FROM 命令.
    • 想象一下,我有一个带有 deps 的图像,而主图像使用这个带有 FROM 指令的 deps 图像。因此,如果我在 deps 映像中更改版本,我还需要更新主映像的版本。这很容易出错(开发人员可能会忘记这样做)并且很乏味,尤其是在有多个 deps 图像的情况下。
    • 另一个问题 - 想象一下,如果我有两个依赖项图像 - 一个用于系统包,第二个用于 Python 包(例如)。 Docker 不支持“多重继承”——因此第二个 deps 映像将从第一个构建,主映像将从第二个构建。在这种情况下 - 即使第二个图像没有更改,但只更改了第一个 - 仍然需要重建第二个图像,因为它的版本将被更改。
    • 确实,当您更新基础镜像时,您必须管理和重建您的镜像。我在回复中添加了一个执行该帮助的 bash 脚本。
    【解决方案3】:

    有一些技巧可能会改善您的工作流程(非常注重网络)

    Docker 缓存

    始终确保在最后将源代码添加到 Dockerfile 中的 Docker 映像。 示例;

    COPY data/package.json /data/
    RUN cd /data && npm install
    
    COPY data/ /data
    

    这将确保您在构建映像时获得最佳缓存,并且当您更改源时 Docker 不必重新构建 npm 包。

    另外,请确保您没有添加经常更改的文件夹/文件的基本映像(例如 COPY . /data/ 的基本映像

    无花果坐骑

    使用 fig(或其他工具),并在开发时挂载您的源目录。这样一来,您就可以进行即时更改开发,并且在构建映像时仍然使用当前版本的代码。

    开发服务器

    您可以在开发时启动您的开发者网络服务器,而在不开发时启动 nginx(如果您正在开发 www 应用程序,但同样的想法适用于其他应用程序)。

    例如,在您的启动脚本中,执行以下操作:

    if [[ $DEBUG ]]; then
      /usr/bin/supervisorctl start gulp
    else
      /usr/bin/supervisorctl start nginx
    fi
    

    并且在你的 supervisord.conf 文件中有autostart=false

    自动刷新应用

    如果您正在开发网络应用程序,请使用gulpgulp-connect 等工具,如果您正在开发python/django 应用程序,请使用runserver 实用程序。两者都会在检测到文件更改时重新加载服务器。

    如果您使用 if [[ $DEBUG ]] ... 技巧,请让它们在与您的普通实例 (nginx) 相同的端口上侦听。这样,您可以为您的反向代理配置 1 个配置,即,只需将流量发送到示例 www:8080,它就会在生产环境和开发环境中访问您的网页。

    【讨论】:

      【解决方案4】:

      创建一个包含应用程序大部分依赖项的基础映像。这将显着减少您的 docker 构建时间。

      【讨论】:

        猜你喜欢
        • 2021-04-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-02
        • 1970-01-01
        • 2020-07-05
        • 2016-03-21
        • 1970-01-01
        相关资源
        最近更新 更多