您如何使用 Packer 和 Terraform 管理图像版本?

How do you manage image versions with Packer and Terraform?

我目前正在使用 Ansible 配置的裸机节点上使用 Kubernetes 集群 运行。有计划迁移到云端,我正在阅读有关 Terraform 和 Packer 的信息,为此做准备。撇开数据迁移不谈,我们似乎有一条非常直接的迁移路径:

  1. 使用我们现有的 Ansible 脚本通过 Packer 构建映像
  2. 使用 Terraform 将构建的映像部署到云端
  3. 使用我们当前的工具部署我们的 Kubernetes 资源

太好了。我们现在拥有不可变的基础架构,使用最先进的工具。

我正在努力寻找的是如何对使用 Packer 构建的图像进行版本控制。在某个地方,我们将不得不升级这些映像中的一些软件。有时 Ansible 脚本会发生变化,但有时只是在映像中进行最新的安全更新。无论哪种方式,Packer 都必须为我们构建一个新镜像,而我们必须使用 Terraform 部署它。如果新图像最终造成问题,我们将不得不恢复到旧图像。

我可以想象这是如何手动完成的,方法是在 运行 之前编辑模板,然后编辑 terraform 配置以获取新版本,但这不适用于 [=30= 】 管道。另一个问题是我们可能会在不同地区和供应商之间移动。因此图像的一个版本可能存在于一个区域,但不存在于另一个区域,理想情况下,如果图像不存在,管道应该创建图像,如果图像已经存在,则使用现有图像。这可能会导致不同地区或云中的图像不同,特别是因为它们可能是在不同的日期构建的并且应用了不同的安全更新。

所有这些都内置于 Docker 工作流程中,但是对于 Packer,要做什么远非显而易见。我还没有找到涵盖该主题的任何文档或教程。 Packer 和 Terraform 中是否有任何内置的版本控制功能?如果图像丢失,Terraform 是否能够调用 Packer?是否有公认的最佳实践?

我可以想象在执行 Terraform 之前,通过使用来自云提供商的 API 检查所需图像是否存在并为任何丢失的图像调用 Packer 来实现自动化。这可行,但我不想为每个云提供商编写自定义集成,这听起来像是 Terraform 应该已经提供的东西。我以前没有使用过 Terraform,所以也许我只是不知道去哪里看,也许在 Terraform 中实现起来并不难,但是为什么没有任何教程告诉我如何实现?

因此,就其价值而言,图像版本控制很有用,因为您可以为 kubernetes 主机节点(预下载 docker 图像等)保存一些默认值,以便在它通过 AWS 检查时, 它已经加入集群。

我没有为许多应用程序这样做,发现通常最好执行如下操作

vendor-app-appversion-epoch

这种方法允许您对 Am​​i 和应用程序进行版本控制,然后您可以像对待牛(待宰)和宠物(终生照顾)一样对待您的实例。

data "aws_ami" "amazon_linux2" {
  most_recent = true
  filter {
    name = "name"
    values = ["amzn2-ami-*-x86_64-gp2"]
  }

  filter {
    name = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["amazon"]
}

这将在您应用 terraform 时提取 linux2 的最新映像。

这在很大程度上取决于提供商,您没有指定您正在使用的云提供商,但 AWS 在这里提供了一个很好的示例用例。

Terraform 和 Packer 都有办法select获取与过滤器匹配的最新 AMI。

Packer 的 AWS AMI 构建器使用 source_ami_filter that can be used to select the most recent image to base your image from. An example is given in the amazon-ebs builder documentation:

{
  "source_ami_filter": {
    "filters": {
      "virtualization-type": "hvm",
      "name": "ubuntu/images/\*ubuntu-xenial-16.04-amd64-server-\*",
      "root-device-type": "ebs"
    },
    "owners": ["099720109477"],
    "most_recent": true
  }
}

这里的一个典型案例是始终使用最新的官方 Ubuntu 映像进行构建。如果您正在为不同的用例(例如 Kubernetes 工作节点与 etcd 节点)生成多个 AMI,那么您可以从那里构建,这样您就可以创建一个具有已知命名方案(例如 ubuntu/20.04/base/{{isotime | clean_resource_name}})的黄金基础映像,其中包含所有内容您希望在您生成的每个 AMI 中,然后其他 AMI 也可以使用 source_ami_filter 到 select 作为您发布的最新基础 AMI。

Terraform 的 AWS 提供商具有以相同方式工作的 aws_ami data source,可用于自动 select 与过滤器匹配的最新 AMI,以便发布新的 AMI,然后 运行 Terraform 将生成一个计划来替换您的实例或启动引用 AMI 数据源的 configuration/template。

例子在aws_instance resource documentation:

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "web" {
  ami           = "${data.aws_ami.ubuntu.id}"
  instance_type = "t2.micro"

  tags = {
    Name = "HelloWorld"
  }
}

一般来说,您应该依靠这些机制来自动 select 最近发布的 AMI 并使用它而不是在代码中硬编码 AMI ID。


管理图像的生命周期超出了 Packer 本身的范围,它应该被用作更大系统的一部分。如果你想回滚图像,那么你有两个可用的选项:

  • 如果您的构建是可重现的并且问题出在该重现中 然后您可以使用旧代码构建并注册一个新图像,这样您的最新图像现在与 2 个图像之前的图像相同
  • 注销最新图像,这样您在搜索最新图像时将再次开始选择旧图像。这取决于云提供商,但 AWS 可以通过编程方式完成,例如通过 aws ec2 deregister-image command line

虽然 Packer 可以自动将图像复制到不同的区域(请参阅 ami_regions for AWS) and different accounts (use ami_users to share the created AMI with the other account or a post processor 在不同的帐户中创建单独的副本),但如果您没有为每种方式组合使用不同的 Packer 配置文件,它就无法轻松地有条件地执行操作想要分享东西并且不能单独推出,所以你先发布到非生产帐户,然后再发布到生产帐户等。

如果您想在某些帐户和区域中推出 AMI,但不是全部,那么您将需要将该逻辑置于更高顺序的位置,例如像您的 CI/CD 系统这样的编排机制。

我写了一篇关于这个主题的博客 post,Keeping Packer and Terraform images versioned, synchronized and DRY

总结:

我们的目标

  • Packer 构建的映像必须进行版本控制。
  • 图像版本控制必须是 DRY(保存在一个地方)并在 Packer 和 Terraform 之间共享,以避免错误 out-of-sync。
  • Packer、Terraform 和图像版本控制信息的配置文件必须存储在 git 中,以便检查特定提交并执行 Terraform 应用应该足以执行回滚。
  • Terraform 必须仅根据本地信息自动检测是否存在一个或多个图像的更新版本,或者应该构建更新版本。
  • 必须有 N 个独立的 development/staging 环境,其中图像版本控制独立于生产。
  • 方法必须与 IaaS 无关(必须与任何云提供商合作)。

方法总结

使用类似

的命名约定
<IMAGE-NAME> ::= <ROLE>__<BRANCH>-<REVISION>

在单独的文件中定义变量的值,packer/versions.pkvars.hcl:

service-a-img-name = "service-a__main-3"

构建图像:

$ packer build -var-file=versions.pkrvars.hcl minimal.pkr.hcl

在 Terraform 方面,由于文件 packer/versions.pkvars.hcl 在 HCL 中,我们可以从 Terraform 读取它:

$ terraform apply -var-file=../../packer/versions.pkrvars.hcl

所有细节都在上面提到的博客post中。