如何用Docker多阶段构建缓存宝石

366 阅读2分钟

在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使用了构建缓存,在复制源代码之前将GemfileGemfile.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-

  1. gem-cache 为Gem Caching创建一个目录
  2. base 安装bundler
  3. gem 从现有镜像中复制宝石并运行bundler
  4. deploy 添加源代码

如果没有设置CACHE_IMAGE,/usr/local/bundle 目录的内容将是空的,我们将无法复制任何宝石。 如果它被设置为一个有宝石的镜像,那些宝石将被复制过来。

现在可以用以下方法建立图像

docker build .

标记后,CACHE_IMAGE ,可以设置为:

docker build .  --build-arg CACHE_IMAGE=saeloun:rails7:gem-cache app