在Rails构建的Dockerfile中,bundle的安装占用了大部分时间。
下面是Rails应用程序的Dockerfile的标准和简化版本。
FROM ruby:3.1.2
RUN gem install bundler:2.3.7
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
Dockerfile使用了构建缓存,在复制源代码之前将Gemfile 和Gemfile.lock 分离出来。
这非常有效,因为Gems只有在构建运行时才会被首次安装。
与非Docker化环境不同的是,当Gemfile有变化时,一个Gem被改变,或者新的Gem被添加/删除,所有的Gem都要从头开始安装。
使用缓存图像
RUN gem install bundler:2.3.7
WORKDIR /app
# Copy the gems from a dedicated cache image
COPY --from saeloun:rails7:gem-cache /usr/local/bundle /usr/local/bundle
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
它将saeloun:rails7:gem-cache 镜像中的/usr/local/bundle 目录复制到我们的构建中。
通过COPY --from ,Docker可以将现有注册表中的文件复制到当前构建中。 通过这样做,可以确保Bundle不会从头开始,并有一个所有先前使用的宝石的缓存。
这样做的唯一问题是,必须在注册表中存在一个镜像,这样才能发挥作用。
这种方法的问题是,如果初始构建镜像不存在,它就会失败。
带有宝石缓存的多阶段程序
我们可以使用MultiStage-build来进一步完善dockerfile,并将注册表作为docker build的参数。
ARG BASE_IMAGE=ruby:3.1.2
ARG CACHE_IMAGE=${BASE_IMAGE}
# Build stage for the gem cache
FROM ${CACHE_IMAGE} AS gem-cache
RUN mkdir -p /usr/local/bundle
# Image with Bundler Installed
FROM $BASE_IMAGE AS base
RUN gem install bundler:2.3.7
WORKDIR /usr/src/app
# Copy gems from a gem-cache build stage
FROM base AS gems
COPY --from=gem-cache /usr/local/bundle /usr/local/bundle
COPY Gemfile Gemfile.lock ./
RUN bundle install
# Get the source code in place
FROM base AS deploy
COPY --from=gems /usr/local/bundle /usr/local/bundle
COPY . .
构建阶段有4 Steps-
gem-cache为Gem Caching创建一个目录base安装bundlergem从现有镜像中复制宝石并运行bundlerdeploy添加源代码
如果没有设置CACHE_IMAGE,/usr/local/bundle 目录的内容将是空的,我们将无法复制任何宝石。 如果它被设置为一个有宝石的镜像,那些宝石将被复制过来。
现在可以用以下方法建立图像
docker build .
标记后,CACHE_IMAGE ,可以设置为:
docker build . --build-arg CACHE_IMAGE=saeloun:rails7:gem-cache app