测试基于 Go 的 S2I 构建器镜像

在本系列关于 Golang 应用的 Source-to-Image 的第三篇文章中,构建您的应用程序镜像并试用一下。
182 位读者喜欢这篇文章。
gopher illustrations

Renee French。CC BY 3.0

在本系列的前两篇文章中,我们探讨了 Source To Image (S2I) 系统的通用要求 并准备了一个环境,专门用于 Go (Golang) 应用程序。现在让我们试用一下。

构建构建器镜像

一旦 Dockerfile 和 Source-to-Image (S2I) 脚本准备就绪,就可以使用 docker build 命令创建 Golang 构建器镜像

docker build -t golang-builder .

这将生成一个名为 golang-builder 的构建器镜像,其上下文为当前目录。

构建应用程序镜像

没有要构建的应用程序,golang-builder 镜像用途不大。在本练习中,我们将构建一个简单的 hello-world 应用程序。

GoHelloWorld

让我们认识一下我们的测试应用程序 GoHelloWorld。如果您想继续学习,请下载最新版本的 Go。此存储库中有两个重要的(对于本练习而言)文件

// goHelloWorld.go
package main

import "fmt"

func main() {
  fmt.Println("Hello World!")
}

这是一个非常基础的应用程序,但它对于测试构建器镜像来说已经足够好了。我们还为 GoHelloWorld 准备了一个基本的测试

// goHelloWorld_test.go
package main

import "testing"

func TestMain(t *testing.T) {
  t.Log("Hello World!")
}

构建应用程序镜像

构建应用程序镜像需要运行 s2i build 命令,并带有用于指定包含要构建代码的存储库的参数(或者使用 . 从当前目录构建代码),要使用的构建器镜像的名称,以及要创建的结果应用程序镜像的名称。

$ s2i build https://github.com/clcollins/goHelloWorld.git golang-builder go-hello-world

要从文件系统上的本地目录构建,请将 Git URL 替换为句点 以表示当前目录。例如

$ s2i build . golang-builder go-hello-world

注意: 如果在当前目录中初始化了 Git 存储库,则 S2I 将从存储库 URL 获取代码,而不是使用本地代码。这会导致在构建镜像时未使用本地、未提交的更改(如果您不熟悉我所说的“未提交的更改”,请复习一下Git 术语)。未进行 Git 初始化存储库的目录的行为符合预期。

运行应用程序镜像

一旦构建了应用程序镜像,就可以使用 Docker 命令运行它进行测试。Source-to-Image 已将镜像中的 CMD 替换为先前创建的运行脚本,因此它将执行构建过程中创建的 /go/src/app/app 二进制文件

$ docker run go-hello-world
Hello World!

成功!我们现在有一个编译后的 Go 应用程序,它位于 Docker 镜像内部,该镜像通过将 Git 存储库的内容传递给 S2I 而创建,并且无需为我们的应用程序使用特殊的 Dockerfile。

我们刚刚构建的应用程序镜像不仅包含应用程序,还包含其源代码、测试代码、S2I 脚本、Golang 库以及大部分 Debian Linux 发行版(因为 Golang 镜像基于 Debian 基础镜像)。生成的镜像并不小

$ docker images | grep go-hello-world
go-hello-world      latest      75a70c79a12f      4 minutes ago      789 MB

对于用运行时解释并在链接库上运行的语言(如 Ruby 或 Python)编写的应用程序,拥有所有 源代码和操作系统是运行所必需的。构建镜像的结果将非常大,但至少我们知道它可以运行。对于这些语言, 我们可以就此停止我们的 S2I 构建。 

但是,还有一种选择可以更明确地定义应用程序的生产要求。

由于生成的应用程序镜像将是运行生产应用程序的同一镜像,我想确保 所需的端口、卷和环境变量已添加到构建器镜像的 Dockerfile 中。通过以声明方式编写这些内容,我们的应用程序更接近 十二要素应用 推荐的做法。例如,如果我们使用构建器镜像为运行 Puma 的 Ruby on Rails 应用程序创建应用程序镜像,我们将需要打开一个端口来访问 Web 服务器。我们应该在构建器 Dockerfile 中添加 PORT 3000 行,以便它可以被从中生成的所有镜像继承。

但对于 Go 应用程序,我们可以做得更好。

构建运行时镜像

由于我们的构建器镜像创建了一个带有我们应用程序的静态编译 Go 二进制文件,我们可以创建一个最终的“运行时”镜像,其中包含二进制文件,而不包含其他冗余内容。

一旦创建了应用程序镜像,就可以提取已编译的 GoHelloWorld 应用程序,并使用 save-artifacts 脚本将其放入新的空镜像中。

运行时文件

仅需要应用程序二进制文件和一个 Dockerfile 即可创建运行时镜像。

应用程序二进制文件

在应用程序镜像内部,save-artifacts 脚本被编写为将应用程序二进制文件的 tar 存档流式传输到 stdout。我们可以使用 tar 的 -vt 标志检查 save-artifacts 创建的 tar 存档中包含的文件

$ docker run go-hello-world /usr/libexec/s2i/save-artifacts | tar -tvf -
-rwxr-xr-x 1001/root   1997502 2019-05-03 18:20 app

如果这导致类似“这似乎不是 tar 存档”的错误,则 save-artifacts 脚本可能除了 tar 流之外还输出了其他数据,如上所述。我们必须确保抑制除 tar 流之外的所有输出。

如果一切看起来正常,我们可以使用 save-artifacts 将二进制文件复制出应用程序镜像

$ docker run go-hello-world /usr/libexec/s2i/save-artifacts | tar -xf -

这会将 app 文件复制到当前目录中,准备添加到其自己的镜像中。

Dockerfile

Dockerfile 非常简单,只有三行。FROM scratch 源表示它使用一个空的、空白的父镜像。Dockerfile 的其余部分指定将 app 二进制文件复制到镜像中的 /app ,并将该二进制文件用作镜像 ENTRYPOINT

FROM scratch
COPY app /app
ENTRYPOINT ["/app"]

将此 Dockerfile 另存为 Dockerfile-runtime

为什么是 ENTRYPOINT 而不是 CMD?我们可以两者都做,但由于镜像中没有其他内容(没有文件系统,没有 shell),无论如何我们都无法运行任何其他内容。

构建运行时镜像

有了 Dockerfile 和二进制文件,我们就可以构建新的运行时镜像了

$ docker build -f Dockerfile-runtime -t go-hello-world:slim .

新的运行时镜像要小得多——只有 2MB!

$ docker images | grep -e 'go-hello-world *slim'
go-hello-world      slim      4bd091c43816      3 minutes ago     2 MB

我们可以使用 docker run 测试它是否仍然按预期工作

$ docker run go-hello-world:slim
Hello World!

使用 s2i create 引导 s2i

虽然我们在此示例中手动创建了所有 S2I 文件,但 s2i 命令有一个子命令可以帮助搭建我们可能需要的 Source-to-Image 构建的所有文件:s2i create

使用 s2i create 命令,我们可以在 ./ghw2 目录中生成一个名为 go-hello-world-2 的新项目

$ s2i create go-hello-world-2 ./ghw2
$ ls ./ghw2/
Dockerfile  Makefile  README.md  s2i  test

create 子命令创建一个占位符 Dockerfile、一个包含有关如何使用 Source-to-Image 的信息的 README.md、一些示例 S2I 脚本、一个基本测试框架和一个 Makefile。Makefile 是自动化构建和测试 Source-to-Image 构建器镜像的好方法。开箱即用,运行 make 将构建我们的镜像,并且可以扩展它以执行更多操作。例如,我们可以添加步骤来构建基本应用程序镜像、运行测试或生成运行时 Dockerfile。

结论

在本教程中,我们学习了如何使用 Source-to-Image 构建自定义 Golang 构建器镜像,使用 s2i build 创建应用程序镜像,以及提取应用程序二进制文件以创建超精简运行时镜像。

在本系列的未来扩展中,我想看看如何使用我们使用 OKD 创建的构建器镜像,通过 buildConfigsimageStreamsdeploymentConfigs 自动部署我们的 Golang 应用程序。如果您有兴趣让我继续这个系列,请在评论中告诉我,感谢您的阅读。

Chris Collins
Chris Collins 是 Red Hat 的 SRE 和 OpenSource.com 特约撰稿人,他对自动化、容器编排及其周围的生态系统充满热情,并且喜欢在家中重新创建企业级技术以获得乐趣。

2 条评论

source image when you put all the code from good programming and dynamically

做得好 :)

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.