5.2. Source-to-Image (S2I) 构建

Source-to-Image (S2I) 是一种用于构建可重复生成的 Docker 格式容器镜像的工具。它通过将应用程序源代码注入容器镜像并汇编新镜像来生成可随时运行的镜像。新镜像融合了基础镜像(构建器)和构建的源代码,并可搭配 buildah run 命令使用。S2I 支持递增构建,可重复利用以前下载的依赖项和过去构建的工件等。

S2I 的优点包括:

镜像灵活性

可以编写 S2I 脚本,将应用程序代码注入到几乎所有现有的 Docker 格式容器镜像,以此利用现有的生态系统。请注意,S2I 目前依靠 tar 来注入应用程序源代码,因此镜像需要能够处理 tar 压缩的内容。

速度

使用 S2I 时,汇编过程可以执行大量复杂操作,无需在每一步创建新层,进而能实现快速的流程。此外,可以编写 S2I 脚本来重复利用应用程序镜像的旧版本,而不必在每次运行构建时下载或构建它们。

可修补性

如果基础镜像因为安全问题而需要补丁,则 S2I 允许基于新的基础镜像重新构建应用程序。

操作效率

通过限制构建操作而不许随意进行 Dockerfile 允许的操作,PaaS 运维人员可以避免意外或故意滥用构建系统。

操作安全性

构建任意 Dockerfile 会将主机系统暴露于 root 特权提升。因为整个 Docker 构建过程都通过具备 Docker 特权的用户运行,这可能被恶意用户利用。S2I 限制以 root 用户执行操作,而能够以非 root 用户运行脚本。

用户效率

S2I 禁止开发人员在应用程序构建期间执行任意 yum install 类型的操作。因为这类操作可能会减慢开发迭代速度。

生态系统

S2I 倡导共享镜像生态系统,您可以将其中的最佳实践运用于自己的应用程序。

可重复生成性

生成的镜像可以包含所有输入,包括构建工具和依赖项的特定版本。这可确保精确地重新生成镜像。

5.2.1. 执行 Source-to-Image (S2I) 增量构建

S2I 可以执行增量构建,也就是能够重复利用过去构建的镜像中的工件。

流程

要创建增量构建,请创建 BuildConfig 并对策略定义进行以下修改:

strategy:
  sourceStrategy:
    from:
      kind: "ImageStreamTag"
      name: "incremental-image:latest" 1
    incremental: true 2
1
指定支持增量构建的镜像。请参考构建器镜像的文档,以确定它是否支持此行为。
2
此标志(flag)控制是否尝试增量构建。如果构建器镜像不支持增量构建,则构建仍将成功,但您会收到一条日志消息,指出增量构建因为缺少 save-artifacts 脚本而未能成功。

其他资源

  • 如需有关如何创建支持增量构建的构建器镜像的信息,请参阅 S2I 要求。

5.2.2. 覆盖 Source-to-Image (S2I) 构建器镜像脚本

您可以覆盖构建器镜像提供的 assemblerunsave-artifacts S2I 脚本。

流程

要覆盖构建器镜像提供的 assemblerunsave-artifacts S2I 脚本,请执行以下任一操作:

  1. 在应用程序源存储库的 .s2i/bin 目录中提供 assemblerunsave-artifacts 脚本;或者
  2. 提供包含脚本的目录的 URL,作为策略定义的一部分。例如:
strategy:
  sourceStrategy:
    from:
      kind: "ImageStreamTag"
      name: "builder-image:latest"
    scripts: "http://somehost.com/scripts_directory" 1
1
此路径会将 runassemblesave-artifacts 附加到其中。如果找到任何或所有脚本,将使用它们代替镜像中提供的同名脚本。
注意

位于 scripts URL 的文件优先于源存储库的 .s2i/bin 中的文件。

5.2.3. Source-to-Image (S2I) 环境变量

可以通过两种方式将环境变量提供给源构建过程和生成的镜像:环境文件和 BuildConfig 环境值。提供的变量将存在于构建过程和输出镜像中。

5.2.3.1. 使用 Source-to-Image (S2I) 环境文件

利用源代码构建,您可以在应用程序内设置环境值(每行一个),方法是在源存储库中的 .s2i/environment 文件中指定它们。此文件中指定的环境变量存在于构建过程和输出镜像。

如果在源存储库中提供 .s2i/environment 文件,则 S2I 会在构建期间读取此文件。这允许自定义构建行为,因为 assembe 脚本可能会使用这些变量。

流程

例如,在构建期间禁用 Rails 应用程序的资产编译:

  • .s2i/environment 文件中添加 DISABLE_ASSET_COMPILATION=true

除了构建之外,指定的环境变量也可以在运行的应用程序本身中使用。例如,使 Rails 应用程序在 development 模式而非 production 模式中启动:

  • .s2i/environment 文件中添加 RAILS_ENV=development

其他资源

  • 使用镜像部分中提供了各个镜像支持的环境变量的完整列表。

5.2.3.2. 使用 Source-to-Image (S2I) BuildConfig 环境

您可以在 BuildConfigsourceStrategy 定义中添加环境变量。这里定义的环境变量可在 assemble 脚本执行期间看到,也会在输出镜像中定义,使它们能够供 run 脚本和应用程序代码使用。

流程

  • 例如,禁用 Rails 应用程序的资产编译:
sourceStrategy:
...
  env:
    - name: "DISABLE_ASSET_COMPILATION"
      value: "true"

其他资源

  • “构建环境”部分提供了更多高级指导。
  • 您也可以使用 oc set env 命令管理 BuildConfig 中定义的环境变量。

5.2.4. 忽略 Source-to-Image (S2I) 源文件

Source-to-Image 支持 .s2iignore 文件,该文件包含了需要被忽略的文件列表。构建工作目录中的文件(由各种输入源提供)若与 .s2iignore 文件中指定的文件匹配,将不会提供给 assemble 脚本使用。

如需有关 .s2iignore 文件格式的更多详细信息,请参阅 Source-to-Image 文档。

5.2.5. 使用 S2I 从源代码创建镜像

Source-to-Image (S2I) 是一种框架,它可以轻松地将应用程序源代码作为输入,生成可运行编译的应用程序的新镜像。

使用 S2I 构建可重复生成的容器镜像的主要优点是便于开发人员使用。作为构建器镜像作者,您必须理解两个基本概念,才能让您的镜像提供最佳的 S2I 性能:构建过程和 S2I 脚本。

5.2.5.1. 了解 S2I 构建过程

构建过程包含以下三个基本元素,这些元素组合成最终的容器镜像:

  • 源代码
  • S2I 脚本
  • 构建器镜像

在构建过程中,S2I 必须将源代码和脚本放在构建器镜像中。为此,S2I 创建一个包含源和脚本的 tar 文件,然后将该文件流传输到构建器镜像中。在执行 assemble 脚本前,S2I 解压缩该文件并将其内容放到构建器镜像的 io.openshift.s2i.destination 标签中指定的位置,默认位置为 /tmp 目录。

为了使这个过程能够发生,您必须提供 tar 存档实用程序(可在 $PATH 中使用的 tar 命令)和命令行解释器(/bin/sh 命令);这样,您的镜像能够使用最快捷的构建路径。如果 tar/bin/sh 命令不可用,则强制 s2i build 过程自动执行额外容器构建,将源和脚本放进镜像中,然后才运行常规构建。

参见下图中的基本 S2I 构建工作流:

图 5.1. 构建工作流

S2I 工作流

运行构建的过程包括解压源代码、脚本和工件(若存在),并且调用 assemble 脚本。如果这是二次运行(在捕获了“未找到 tar/bin/sh”错误后),它将仅负责调用 assemble 脚本,因为脚本和源代码都已就位。

5.2.5.2. 编写 S2I 脚本

您可以使用任何编程语言编写 S2I 脚本,只要脚本可在构建器镜像中执行。S2I 支持多种提供 assemble/run/save-artifacts 脚本的选项。每次构建时按以下顺序检查所有这些位置:

  1. BuildConfig 中指定的脚本
  2. 在应用程序源 .s2i/bin 目录中找到的脚本
  3. 在默认镜像 URL(io.openshift.s2i.scripts-url 标签)中找到的脚本

镜像中指定的 io.openshift.s2i.scripts-url 标签和 BuildConfig 中指定的脚本都可以采用以下形式之一:

  • image:///path_to_scripts_dir - 镜像中 S2I 脚本所处目录的绝对路径
  • file:///path_to_scripts_dir - 主机上 S2I 脚本所处目录的相对或绝对路径
  • http(s)://path_to_scripts_dir - S2I 脚本所处目录的 URL

表 5.1. S2I 脚本

脚本描述

assemble(必需)

assemble 用来从源代码构建应用程序工件,并将其放置在镜像内部的适当目录中的脚本。此脚本的工作流为:

  1. 恢复构建工件。如果要支持增量构建,请确保同时定义了 save-artifacts(可选)。
  2. 将应用程序源放在所需的位置。
  3. 构建应用程序工件。
  4. 将工件安装到适合它们运行的位置。

run(必需)

run 脚本将执行您的应用程序。

save-artifacts(可选)

save-artifacts 脚本将收集所有可加快后续构建过程的依赖项。例如:

  • 对于 Ruby,由 Bundler 安装的 gem
  • 对于 Java,.m2 内容。

这些依赖项收集到一个 tar 文件中,再传输到标准输出。

usage(可选)

借助 usage 脚本,可以告知用户如何正确使用您的镜像。

test/run(可选)

借助 test/run 脚本,可以创建一个简单流程来检查镜像是否正常工作。该流程的建议工作流是:

  1. 构建镜像。
  2. 运行镜像以验证 usage 脚本。
  3. 运行 s2i build 以验证 assemble 脚本。
  4. 再次运行 s2i build,以验证 save-artifactsassemble 脚本的保存和恢复工件功能(可选)。
  5. 运行镜像,以验证测试应用程序是否正常工作。
注意

建议将 test/run 脚本构建的测试应用程序放置到镜像存储库中的 test/test-app 目录。如需更多信息,请参阅 S2I 文档

5.2.5.2.1. S2I 脚本示例

以下示例 S2I 脚本采用 Bash 编写。每个示例都假定其 tar 内容解压缩到 /tmp/s2i 目录中。

例 5.1. assemble 脚本:

#!/bin/bash

# restore build artifacts
if [ "$(ls /tmp/s2i/artifacts/ 2>/dev/null)" ]; then
    mv /tmp/s2i/artifacts/* $HOME/.
fi

# move the application source
mv /tmp/s2i/src $HOME/src

# build application artifacts
pushd ${HOME}
make all

# install the artifacts
make install
popd

例 5.2. run 脚本:

#!/bin/bash

# run the application
/opt/application/run.sh

例 5.3. save-artifacts 脚本:

#!/bin/bash

pushd ${HOME}
if [ -d deps ]; then
    # all deps contents to tar stream
    tar cf - deps
fi
popd

例 5.4. usage 脚本:

#!/bin/bash

# inform the user how to use the image
cat <<EOF
This is a S2I sample builder image, to use it, install
https://github.com/openshift/source-to-image
EOF