在没有 Gemfile 的情况下在 Dockerfile 中安装 gems

Installing gems in Dockerfile without Gemfile

我的团队通过 Docker 文件托管我们的 Rails 应用程序。我们有一些缓慢的宝石,它们确实减慢了我们的构建速度。 (我在看着你grpc。)

是否可以在将 Gemfile 复制到我们的 Docker 文件之前安装一些 gem?这将允许 Docker 缓存那些构建步骤,因此我们不必在每次 Gemfile 更改时都重新安装慢速 gem。

我试过了,但是 bundle install 仍在安装 grpcsasscnokogiri

RUN gem install grpc --version 1.28.0
RUN gem install sassc --version 2.2.1
RUN gem install nokogiri --version 1.10.9

WORKDIR /app

ADD Gemfile Gemfile.lock .ruby-version /app/
RUN bundle install

您可以使用选项 bundle install --deployment 或 --path 来指定一个文件夹,您可以在其中安装您的 gems(仅为此目的构建一个容器,然后将文件夹复制到 docker).然后将此目录映射为容器中的卷,映射完成后 运行ning bundle install...或 只需复制文件夹:

# outside actual image

bundle install --path=vendor/cache

# actual docker image
COPY . /app
COPY vendor/cache /app/vendor/cache
COPY .bundle /app/.bundle 

WORKDIR /app

RUN bundle install --path=vendor/cache

带有 --path=vendor/cache 的命令生成一个包配置,就像这个一样:

---
BUNDLE_DEPLOYMENT: "true"
BUNDLE_PATH: "vendor/cache"

只要你的编译扩展 gems 版本在你的 Gemfile 上没有改变,它们就不应该被再次安装。

为了不将 vendor/cache 文件夹提交到源文件,您可以将其托管在某个地方并让开发人员下载它。我认为你可以把这个文件夹放在哪里,以及如何将它复制到图像中,这是一个相当开放的问题,我需要更多关于你的设置的信息来推荐一些东西。您将应用部署到生产环境的具体位置?

示例:

带有本机扩展的 AWS lambda gems 存在问题 - 您可以将它们安装在 OS 系统中,但它们将与 lambda 环境不兼容。

制作 gem 捆绑包的方法是执行以下操作:

1 - 使用 容器以与 lambda 兼容的方式安装具有本机扩展 的 gem:

docker run --rm -v "$PWD":/var/task lambci/lambda:build-ruby2.7 bundle install --deployment

2 - 然后,在另一个容器上,您将 GEMS 映射为一个卷或复制它们,然后 运行 您的应用程序

docker run --rm --env-file=.env -v $PWD:/var/task:ro,delegated lambci/lambda:ruby2.7 send.lambda_handler

我在这里建议你为你的应用程序做一些类似的事情 - 使用一个图像来构建你的平台 gems,然后另一个到 运行 应用程序,传递这些 gem到它了。 只要这两个图像相同 OS,gem 就会兼容。

我的团队在非 docker 部署管道上使用了与此类似的设置,我们在推送新版本和设置之前将 gem 预安装在文件夹中捆绑器从中获取 gems。

你可以考虑拆分你的gem文件,考虑下面的文件。

慢-gems

ruby File.read('.ruby-version').strip

gem 'rubocop'

gem文件

ruby File.read('.ruby-version').strip

# add the "slow" gems to the gem-bundle so we do not have to redefine them.
instance_eval File.read('slow-gems')

gem 'flay'

docker文件

WORKDIR /app

ADD slow-gems slow-gems.lock .ruby-version /app/
RUN bundle install --gemfile=slow-gems

ADD Gemfile Gemfile.lock /app/
RUN bundle install

这也会阻止您重新定义 docker 图像和 gem 文件中的所有 gem。您可能遇到的唯一问题是两个锁定文件中的版本会分开。目前,我还没有解决方案,但是在使用您当前将它们添加到 docker 文件的方法时也会发生这种情况。

第二个 bundle install 将重新使用 slow-gems 文件中已安装的 gem,这需要不到一秒钟的时间。

添加: 不要忘记使用 docker 的内置缓存,否则这不会更快而且对你没有帮助。

问题:

所以我们先了解一下问题。每当您尝试启动您的应用程序时,它都会花费很多时间。我想我很理解你的问题。

  1. 从互联网下载 gem
  2. 安装和启动应用程序

可能的解决方案:

这是docker个文件的表现。我们有 2 个问题产生的区域,以及 2 个不同的解决方案

解决方案:

  • 下载 gem 本地文件夹中的文件,然后在 docker 文件中将这些 gem 文件复制到容器中,然后安装。

  • 为您的应用程序创建基础映像。从 Ruby/任何你喜欢的其他人创建一个基本图像并使用上述方法安装你的 gems

    1. 从本地文件夹复制 gems然后粘贴container
    2. 使用RUN命令安装gems。随你喜欢。

这将是一次性过程。这样,您就可以安装已经包含耗时 gem 的基础映像。现在在你的应用程序 dockerfile(负责启动应用程序)中,你只需要使用你自己创建的基础图像而不是 RubyLinux 来自 Docker Hub

我们将了解如何构建您自己的基本预配置映像。
让我们一步步来看。

让我们关注这个 GitHub 回购:https://github.com/dupinder/docker-ruby-gem-game

文件夹结构(这有助于理解本文)

  • application/dockerfile
  • gems/download 并放置本地 gems
  • docker文件

  • 创建基础映像并在 docker 容器中安装本地 gems。

dockerfile


    FROM ruby:latest
    RUN mkdir -p /gems 
    COPY /gems/grpc-1.28.0-universal-darwin.gem /gems/grpc-1.28.0-universal-darwin.gem
    COPY /gems/sassc-2.2.1.gem /gems/sassc-2.2.1.gem
    WORKDIR /gems
    RUN gem install --force --local *.gem

使用以下命令从这个docker文件构建图像

docker build --rm -f "dockerfile" -t ruby-gem-base-image:latest "."

  • 第一步: 我正在使用基本图像 ruby,如果你使用 Linux,你可以随心所欲,然后在下一步中你需要安装 Ruby

  • 第二步:在容器中创建文件夹名称gems

  • 步骤3 & 4:将容器内需要安装的Gems从本地目录复制到[=23内的目录=] container.
  • Step 5 & 6: 将工作目录更改为gems,因为我们要安装gems 放在这个目录中,所以下一个命令是 gem install --force --local *.gem,这有助于在本地目录中安装 gems。

这样,我们解决了 50% 的时间消耗。现在 docker 永远不会每次都从互联网下载 gems 并安装。

现在让我们检查一下是否安装了我们需要的 gems。为此:

  • 运行 命令 docker images 我们将拥有新构建的镜像 ruby-gem-base-image

  • 运行 容器处于分离模式,以便我们稍后可以 exec docker run -it -d ruby-gem-base-image

  • 运行 docker ps获取容器ID。

  • 运行 exec as bash inside container docker exec -it d28234630343 bash.
  • 运行 gem list 这将打印已安装的 gem 的列表,您将在那里看到所需的 gem。

看到你的 gems 是从本地目录安装的。


如果您按照这些步骤操作,您的问题就会得到解决。但是现在你需要如果在启动你的应用程序之前 docker 容器已经安装 gems.

对于这个问题,我们可以使用ruby-gem-base-image图像作为我们的ruby应用程序基础图像。如果你还记得 GitHub 回购协议,我们有一个应用程序目录,如果我们看到它有一个 dockerfile

FROM ruby-gem-base-image:latest
CMD ["gem", "list"]

这是您的 docker 文件,您可以在部署应用程序时使用该文件,使用包含您的 gem 的预构建 docker 图像。 我写了任务 gem list 来检查这个容器是否有来自父图像的 gems。

我觉得这个有点清楚了。您的问题将由此得到解决。

如果您需要任何其他帮助或需要帮助来理解此过程,请询问。

------------杜宾德。