前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >什么时候使用Dockerfiles(什么时候不使用……)

什么时候使用Dockerfiles(什么时候不使用……)

作者头像
CNCF
发布2021-12-15 20:48:17
1.7K0
发布2021-12-15 20:48:17
举报
文章被收录于专栏:CNCFCNCF

在这篇文章中,我们将讨论一些使用 Dockerfile 的最佳实践,探索一些注意事项,并使用 Dockerfile 和云原生 Buildpacks 构建应用。你将了解每种工具最擅长的工作是什么,以及如何决定何时使用它们。

Dockerfiles 是什么?

Dockerfile 是一个包含命令的文本文件,Docker 将执行这些命令来构建一个容器镜像。Dockerfiles 总是以一个 FROM 指令开始,指定从基本镜像开始。后续命令构建并修改该基本镜像。

让我们通过使用 Dockerfile 构建一个小的“hello world”,一个文件的 Go 应用程序来更好地了解 Dockerfile。你不需要安装 Go 以跟随教程,Docker 会照顾依赖。

代码语言:javascript
复制
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", sayHello)
http.ListenAndServe(":8080", nil)
}
func sayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}

现在,让我们创建一个简单的 Dockerfile。

代码语言:javascript
复制
FROM golang:1.16.5
WORKDIR /app
COPY main.go .
RUN go build main.go
CMD ./main

为了让我们的容器运行起来,我们需要通过从 docker.com 安装 Docker CLI 来设置 Docker。然后,运行以下命令来构建应用程序。

代码语言:javascript
复制
docker build -t hello .

我们新构建的镜像的大小是 868.3 MB

代码语言:javascript
复制
REPOSITORY  TAG       IMAGE ID       CREATED         SIZE
hello       latest    005c27e8cd40   7 minutes ago   868.3MB

现在我们可以使用以下命令运行镜像:

代码语言:javascript
复制
docker run -it hello

这是一个很好的开始,但是镜像没有得到优化。

编写更好的 Dockerfile

我们开始使用 golang:1.16.5 作为我们的 Go 应用程序的基本镜像。但我们实际上可以从以下两个镜像中选择:

代码语言:javascript
复制
1.16.5              862MB
1.16.5-alpine       302MB

golang:1.16.5-alpine 指定 Go 基准镜像的 Alpine 版本。Alpine 是一个专门为容器设计的微型 Linux 发行版。所以 Docker、Go 和 Alpine 是天生一对!

我们也可以在 Dockerfile 中添加一个 FROM scratch 行,它告诉 Docker 以一个全新的、完全空的容器镜像(这被称为 scratch 容器)重新开始,并将编译后的程序复制到其中。这是我们稍后将继续运行的容器镜像。

使用 scratch 镜像也节省了大量空间,因为我们实际上不需要 Go 工具或其他工具来运行编译后的程序。使用一个容器用于构建,另一个容器用于最终镜像,这称为多阶段构建。

我们更好的 Dockerfile 看起来像这样

代码语言:javascript
复制
FROM golang:1.14-alpine AS build
COPY main.go .
RUN CGO_ENABLED=0 go build -o /bin/demo
FROM scratch
COPY --from=build /bin/demo /bin/demo
ENTRYPOINT ["/bin/demo"]

当我们再次运行 docker 构建后,我们的镜像将会变小,新构建的镜像的大小大约为 8MB。

利用构建缓存

因为镜像是在构建过程的最后阶段构建的,所以你可以通过利用构建缓存[1]来最小化镜像层。

如果构建包含多个层,则可以将其从更改频率较低的层排序为更改频率较高的层,这确保了构建缓存是可重用的。

遵循以下步骤:

  • 安装构建应用程序所需的工具
  • 安装和更新依赖项。
  • 生成的应用程序。

多阶段构建[2]允许你大幅减少最终镜像的大小,而不必费劲地减少中间层和文件的数量。下面是 Dockerfile 的示例。

代码语言:javascript
复制
FROM golang:1.16-alpine AS build

# Install tools required for project
# Run `docker build --no-cache .` to update dependencies
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# List project dependencies with Gopkg.toml and Gopkg.lock
# These layers are only re-built when Gopkg files are updated
COPY Gopkg.lock Gopkg.toml /go/src/demo/
WORKDIR /go/src/demo/
# Install library dependencies
RUN dep ensure -vendor-only

# Copy the entire project and build it
# This layer is rebuilt when a file changes in the project directory
COPY . /go/src/demo/
RUN go build -o /bin/demo

# This results in a single layer image
FROM scratch
COPY --from=build /bin/demo /bin/demo
ENTRYPOINT ["/bin/demo"]
CMD ["--help"]

然而,Dockerfile 缓存是脆弱的,你必须小心如何编写 Dockerfile。如果,你不需要写呢?

让我们用 Buildpacks

构建包(buildpack)是将源代码转换为可运行的容器镜像的程序。通常,构建包封装了单一语言生态系统工具链。有针对 Ruby、Go、Node.js、Java、Python 等的构建包。

用构建包构建我们的 Go 应用

要设置构建包,请遵循Pack CLI 安装说明[3]。让我们使用下面的命令来构建应用程序

代码语言:javascript
复制
pack build hello --builder=paketobuildpacks/builder:tiny

该镜像的大小大约为 30 MB。

pack 使用构建包来帮助你轻松创建可以在任何地方运行的 OCI 镜像。

代码语言:javascript
复制
===> DETECTING
4 of 6 buildpacks participating
google.go.runtime  0.9.1
google.go.gopath   0.9.0
google.go.build    0.9.0
google.utils.label 0.0.1
===> ANALYZING
Previous image with name "go-app" not found
===> RESTORING
===> BUILDING
=== Go - Runtime (google.go.runtime@0.9.1) ===

构建包运行以下一组进程来构建应用程序的镜像。

  • CLI 检测项目的主要语言。例如,如果你的源代码目录有一个 Gemfile,构建包就会把它识别为 Ruby 项目;pom.xml 文件将其标识为 Java 项目,等等。
  • 然后执行环境分析之前的构建,以确定是否有任何步骤可以在后续构建中重用。
  • 构建包运行构建,下载所有依赖项并准备应用程序在生产环境中运行。
  • 最后,它将构建的结果导出为 Docker 镜像

除了构建镜像,pack 还让你为容器镜像生成一个材料清单。软件物料清单(Software Bill-of-Materials,BOM)提供了必要的信息,以了解容器中是什么以及它是如何构造的。

让我们为使用构建包构建的镜像运行下面的程序。

代码语言:javascript
复制
pack inspect-image your-image-name --bom

对我们的示例 Go 应用程序镜像运行它会得到以下结果。

代码语言:javascript
复制
{
  "remote": null,
  "local": [
    {
      "name": "go",
      "metadata": {
        "version": "1.17.1"
      },
      "buildpacks": {
        "id": "google.go.runtime",
        "version": "0.9.1"
      }
    }
  ]
}

云原生 Buildpacks 提供了两种形式的材料清单。

  1. 构建包可以填充关于它们所提供的依赖项的材料清单信息。
  2. 用于构建应用程序的构建包列表。

可复制的构建

构建包为容器镜像创建“可复制的构建(reproducible builds)”。以可复制的方式创建镜像。可复制构建意味着无论何时运行:

代码语言:javascript
复制
pack build hello --builder=paketobuildpacks/builder:tiny

它将产生完全相同的镜像 ID(也称为 sha / digest),假设你有:

  • 同样的源代码
  • 相同的构建器镜像
  • 底层的构建包/语言支持可复制的构建(例如,go 二进制文件在默认情况下是可复制的)

让我们为最近构建的容器演示一下

同一个 Go 应用的两个镜像使用相同的构建器镜像和构建包有相同的哈希值。

我们为什么需要它?

镜像 sha 考虑镜像层的内容,包括元数据,例如镜像生成的日期。可复制构建可以作为信任链的一部分;源代码可以被签名,确定性编译可以证明二进制文件是从可信的源代码编译的。

现在,尝试将新镜像部署到你最喜欢的云上,这里有一些文档[4]可以帮助你!

适合每项工作的工具

到目前为止,我们已经讨论了云原生 Buildpacks、Dockerfiles 以及使用它们构建的应用程序。对于 Dockerfiles 来说,它们的灵活性使它们熠熠发光。你构建的镜像只受限于你编写 Dockerfile 脚本的能力;你可以安装系统包,允许或限制根访问,从头开始,增加一个现有的镜像,使用任何一个 Docker 的认证镜像,天空是唯一限制!然而,真正的挑战在于同样的灵活性。你的 Dockerfile 将成为你必须维护的另一段代码。随着时间的推移,操作系统或运行时配置可能需要补丁或更新。标准化、维护和构建镜像的自动化完全取决于你。

云原生 Buildpacks 解决了 Dockerfiles 操作上的复杂性,并提供了大规模创建和维护镜像所需的结构,提供了简单的用户体验。从选择和维护基本镜像到为其余层提供内容,提供与镜像大小和分层、缓存和安全性相关的优化,以及特定于给定编程语言的标准和优化,Buildpacks 可以完成所有这些工作。生成的应用程序镜像通过元数据进行了丰富,使其易于检查,你还可以获得详细的软件材料清单(Software Bill of Materials,SBOM),包括运行时版本、应用程序依赖关系和其他细节。

虽然 buildpack 为大多数用例提供了解决方案,但在某些情况下,你可能需要更大的灵活性,例如,如果你正在使用当前的 Buildpacks 生态系统不支持的语言构建应用程序,那么在这种情况下,你可能必须编写自定义的 Buildpacks。在 Buildpacks 不能处理某些需求的情况下,你可能必须创建一个一次性的 Dockerfile。

现在,轮到你探索这些工具并找出最适合你需要的工具了!

鸣谢

Javier Romero 和 Joe Kutner。

参考资料

[1]

利用构建缓存: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache

[2]

多阶段构建: https://docs.docker.com/develop/develop-images/multistage-build/

[3]

Pack CLI 安装说明: https://github.com/buildpacks/pack#getting-started

[4]

文档: https://docs.docker.com/language/golang/deploy/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-12-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CNCF 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Dockerfiles 是什么?
    • 编写更好的 Dockerfile
      • 利用构建缓存
      • 让我们用 Buildpacks
        • 用构建包构建我们的 Go 应用
          • 可复制的构建
          • 适合每项工作的工具
          • 鸣谢
            • 参考资料
            相关产品与服务
            容器镜像服务
            容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档