使用Werf和现有的Dockerfiles改善你的CI/CD体验

迟到总比不到好。该故事讲关于我们因不支持使用常规的Dockerfile来构建镜像导致我们差点犯了一个重大错误。

Werf 是一个GitOps工具,可以很好地集成到任何CI/CD系统中,并提供完整的应用程序生命周期管理,允许你:

  • 构建和推送镜像
  • 部署应用程序到Kubernetes中
  • 根据策略清理未使用镜像

我们工具的理念是:将低级手段组合到一个统一的系统中让DevOps工程师控制应用程序。尽可能使用现有的即用型工具(如Helm和Docker)。但是如果没有合适的任务解决方案呢?答案很简单,我们自己编写并维护工具来完成工作。

背景:自定义镜像构建器

Werf镜像构建器也发生了同样的故事。Dockerfile是描述构建镜像过程的事实标准,但我们的需求受到很大限制。这个问题在我们项目的早期阶段变得至关重要。在开发用于容器化应用的工具时,我们很快意识到Dockerfile不适合以下特定任务:

  1. 遵循构建典型小型Web应用程序的标准工作流程:a)安装系统范围的应用程序依赖项,b)安装特定于应用程序的库,c)构建资产(assets),d)最重要的部分,快速高效地更新镜像中的代码。
  2. 构建器在发生更改时应通过提交修补应用于修改的文件来创建新的镜像层。
  3. 如果某些文件已被修改,则必须重建依赖阶段。

这是我们一开始的需求列表,在今天我们的构建器有着许多额外的功能。

总而言之,我们没花多长时间就开始使用首选编程语言开发自定义DSL(见下文)。它必须满足既定目标,根据文件描述分阶段的构建过程并确定不同阶段间的依赖。它由相应的那些可将DSL转变成最终目标的构建器,即即用型Docker镜像补充。一开始我们使用Ruby实现了DSL,在切换到 Golang 之后我们用YAML文件形式重写了它。

Werf的ruby的配置,这是旧的版本(此时项目被称为dapp)

Werf的YAML形式配置,这是现在的版本

构建器的概念随时间推移一直在变化。在一开始,我们只是简单地使用我们的配置动态地生成一些临时的Dockerfile,然后我们在临时容器中运行构建指令并进行提交。

注意: 目前我们使用YAML配置的Stapel构建器(如上所示)已经发展成一个相当强大的工具。虽然它值得一篇文章来详细描述其本身,但你现在可以先 在该文档 中找到更多详细信息。

等一下!

不久之后,我们发现了一个严重的错误,那就是我们没有添加 使用标准Dockerfiles构建镜像的能力 ,我们将它们集成到已建立的基础架构中以进行完整的应用程序管理(即用于构建,部署和删除镜像)。“我们怎么可能在没有支持Dockerfile的情况下打造为Kubernetes部署镜像的工具,这是否是一种描述大多数项目镜像的流行方式?”这个问题依旧困扰着我们。

我们没有回答该问题,而是提出了解决方案。如果你已经有了一个Dockerfile(或一组Dockerfile)且想使用werf呢?

注意:顺便问一下,你为何要使用werf?至少,它有各种很好的功能来增强和粘合你的CI/CD流程,例如:

  • 完整的应用程序管理周期,包括删除镜像
  • 在单个配置中构建多个镜像的能力
  • 改进的部署Helm兼容图表的流程

完整的功能列表请点击 项目页面 .

因此,直至最近,如果你对使用werf感兴趣,我们还希望你将Dockerfiles移植到我们的配置格式。但是现在我们很高兴地告诉你,“让werf来构建你的Dockerfiles吧!”

用法

此功能的首次完整实现在werf的 v1.0.3-beta.1 版本引入。

一般流程非常简单,即用户在werf配置中指定已存在的 Dockerfile 的路径,然后使用 werf build 命令启动werf。然后就没有然后了,werf将会构建镜像。

以下是一个例子。我们在应用的根目录中定义 Dockerfile
FROM ubuntu:18.04

RUN echo Building ...

然后我们定义 werf.yaml 它将引用上面的 Dockerfile :

configVersion: 1

project: dockerfile-example

image: ~

dockerfile: ./Dockerfile

然后我们就可以 执行 werf build 了:

顺便说下,你也可以这样定义 werf.yaml 以使用多个Dockerfile同时构建镜像:

configVersion: 1

project: dockerfile-example

image: backend

dockerfile: ./dockerfiles/Dockerfile-backend

image: frontend

dockerfile: ./dockerfiles/Dockerfile-frontend

werf配置中同样支持传递额外的构建参数,例如 --build-arg--add-host 等。以下是完整Dockerfile镜像配置的 链接

它是如何运作的?

在构建镜像期间,本地层的通用Docker缓存处于活动状态。重要的是,werf还 将Dockerfile配置集成到其基础架构中 。这意味着什么?

  1. 所有使用Dockerfile构建的镜像都包含一个特定的名为 dockerfile 的阶段。(可以在werf 文档中 了解“阶段”相关内容)
  2. dockerfile 阶段,werf会根据Dockerfile配置中的内容计算出签名。Dockerfile配置的改变将会引起 dockerfile 阶段的签名改变。在这种情况下,werf使用新的Dockerfile配置启动此阶段的重建。如果签名保持不变,则werf使用缓存的镜像。
  3. 你可是使用 werf publishwerf build-and-publish 发布构建出来的镜像并将其部署到Kubernetes中。已推送到Docker仓库的镜像会通过常规werf机制进行清理。这意味着旧镜像(超过N天)和与不存在的Git分支相关联的镜像将被自动删除,且可以应用其他策略。

你可以在相应的文档中了解有关这些werf特性的更多信息:

提示和警告

  1. 指令 ADD 不支持外部URL

当前 ADD 参数不能支持外部的URL。Werf不会启动重建过程以响应指定URL处的资源更改。我们计划很快添加此功能。

  1. 不能将 .git 目录包含到镜像中

事实上,将 .git 目录添加到你的容器镜像中不是个好主意,原因如下:

  • .git 目录在最终镜像中的存在违反了 12要素应用理念 。最终镜像必须与单个提交链接; 不应允许其在任意提交上执行 git checkout
  • .git 目录增大了镜像的体积(git仓库可能会因为曾经添加删除过大文件而增大)。相反地,每个特定提交对工作树大小将不依赖于Git操作的历史。此外, .git 目录从最终镜像中添加以及后续的删除,文件夹将不再起作用,因为无论如何都将生成新的层次(这正是Docker的工作原理)。
  • 即使正在处理相同的提交(源自不同的工作树),Docker也可能启动不必要的重建。例如,GitLab在 /home/gitlab-runner/builds/HASH/[0-N]/yourproject 启用并行构建时创建单独的克隆文件夹。不必要的重建是由 .git 同一存储库的各种克隆版本中的文件夹的差异引起的(即使我们构建完全相同的提交)。

最后一点直接影响了werf的使用。Werf需要构建缓存来运行某些命令(例如 werf deploy )。执行这些命令时,werf会为 werf.yaml 文件中指定的镜像计算各阶段签名,因此它们必须存在于构建缓存中,否则命令将会失败。 .git 内容在签名阶段的依赖意味着缓存容易受到无关文件的影响,这是werf无法容忍的错误( 更多细节 )。

无论如何,通过 ADDCOPY 指令添加特定和需要的文件仍然是一个很好的做法。它提高了创建的效率和所创建的 Dockerfile 的可靠性,并提高了缓存(通过上述内容构建Dockerfile)对Git中无关的更改的弹性。

总结

我们为特定需求制作自定义构建器之路是艰难的,诚实的和直接的:我们倾向于使用自定义语法开发自己的解决方案,而不是将解决方法置于默认Dockerfile之上。这种方法有其优势:Stapel构建器就做得很好!

但是,在创建自定义镜像构建器时,我们完全忽略了应用现有Dockerfiles的情况。这个漏洞现在已经解决了。在未来,我们计划加强对Dockerfiles的支持以及我们的定制Stapel构建器,用于分布式构建和在Kubernetes集群内构建镜像(即通过使用类似于kaniko的Kubernetes中的运行程序)。

所以,如果你碰巧有一些好的Dockerfiles,不要犹豫,快来尝试 werf 吧!

以下是一份简短的阅读清单,其内容将包含一些可用信息:

【原文链接】翻译:冯旭松

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章