我在开发和生产中都使用Docker,有一件事让我很困扰,那就是Docker缓存的简单性。我有一个ruby应用程序,它需要bundle install
来安装依赖项,所以我从下面的Dockerfile开始:ADD Gemfile Gemfile ADD Gemfile.lock Gemfile.lock RUN bundle install --path /root/bundle
所有依赖项都被缓存,在我添加一个新的gem之前,它工作得很好。即使我添加的gem只有0.5MB,从头开始安装所有应用程序gem仍然需要10-15分钟。由于依赖文件夹的大小(约300 MB),然后再花10分钟部署它。
我在使用node_modules和npm时也遇到了同样的问题。我在想,有没有人找到解决这个问题的方法?
目前为止的研究成果:
- Source to image-跨增量构建缓存任意文件。不幸的是,由于它的工作方式,它需要将整个300 MB推送到注册表,即使gems没有改变。更快的构建->更慢的部署,即使gem没有更新。
- tip -将Gemfile分割成两个不同的文件,只在其中一个文件中添加gem。非常具体的解决方案,以bundler和我不相信它是要扩大超越添加1-2宝石。
- Harpoon-如果不是他们强制放弃Dockerfile并切换到他们自己的格式,那将是一个很好的选择。这对团队中的所有新开发人员来说都意味着额外的痛苦,因为这个工具集需要时间从docker中单独学习。
- 临时包缓存。这只是一个想法,我不确定这是可能的。在安装软件包之前,以某种方式将软件包管理器缓存(而不是依赖文件夹)带到机器上,然后将其删除。根据我的破解,它显著加快了bundler和npm的包安装速度,而不会用不必要的缓存文件使机器膨胀。
4条答案
按热度按时间n3ipq98p1#
我发现了两种可能的解决方案,使用外部数据卷进行gem存储:one和two。
简单地说
docker-compose.yml
中,您通过volumes_from
指定BUNDLE_PATH
的挂载点。bundle check || bundle install
,一切就绪。这是一个可能的解决方案,但对我来说,它似乎有点违背Docker的方式。具体来说,
bundle install
听起来应该是构建过程的一部分,而不应该是运行时的一部分。其他依赖于bundle install
(如asset:precompile
)的任务现在也是运行时任务。这是一个可行的解决方案,但我期待着一些更强大的东西。
6rqinv9w2#
我将gem缓存到应用程序tmp目录中的tar文件中。然后,在安装bundle之前,我使用
ADD
命令将gem复制到一个层中。关于Dockerfile.yml
:确保你正在将gem缓存发送到你的docker机器。我的gemcache是118MB,但由于我是在本地构建的,所以它复制得很快。我的
.dockerignore
:您需要缓存构建映像中的gem,但最初可能没有映像。像这样创建一个空缓存(我在一个rake任务中有这个):
镜像构建完成后,将gem复制到gem缓存中。我的图片被标记为
app
。我从镜像创建了一个docker容器,使用docker cp
命令将/var/lib/gems/2.2.0
复制到我的gemcache中,然后删除容器。下面是我的rake任务:在随后的图像构建中,gemcache被复制到调用
bundle install
之前的层。这需要一些时间,但它比从头开始的bundle install
快。之后的构建甚至更快,因为docker已经缓存了
ADD tmp/gemcache.tar.bz2 /var/lib/gems/
层。如果对Gemfile.lock
有任何更改,则仅构建这些更改。没有理由在每个
Gemfile.lock
更改时重建gem缓存。一旦该高速缓存和Gemfile.lock
之间存在足够的差异,bundle install
很慢,您就可以重建gem缓存。当我想重建gem缓存时,它是一个简单的rake cache_gems
命令。irtuqstp3#
“复制本地依赖项”方法(接受的答案)是一个坏主意IMO。将你的环境对接的全部意义在于拥有一个隔离的、可复制的环境。
我们是这样做的。
这与@EightyEight解释的概念类似,但它不会将
bundle install
放入主服务的启动中,而是由不同的服务管理更新。无论哪种方式,都不要在生产中使用这种方法。如果在构建步骤中没有安装依赖项的情况下运行服务,至少会导致不必要的停机时间。myss37ts4#
这个问题很老了,但我最近遇到了这个问题。我正在做一个项目,它有几个依赖项和原生扩展,这使得每当Gemfile中发生更改时,构建一个新的docker镜像需要大约15分钟。
我并不是真的想用外部卷将依赖项安装推迟到运行步骤,所以我做了更多的研究,经过一些尝试和错误,我设法让捆绑包缓存在构建过程中工作。
我所做的是使用以下代码将缓存挂载添加到我的Dockerfile:
(Note路径可能会有所不同,具体取决于您使用的ruby版本)
我想在提交其他答案时,这可能不是一个受支持的docker功能。
希望这能帮助那些努力让bundler与docker cache合作的人!