20

我正在使用带有 ansible provisioner 的 packer 来构建一个 ami,并使用 terraform 以该 ami 作为源来设置基础设施 - 有点类似于这篇文章: http: //www.paulstack.co.uk/blog/2016/01/ 02/构建-一个-elasticsearch-cluster-in-aws-with-packer-and-terraform

当命令packer build pack.json成功完成时,我得到以下格式的输出 ami id:

eu-central-1: ami-12345678

在我的 terraform 变量variables.tf中,我需要指定源 ami id、区域等。这里的问题是我不想手动或多次指定它们。对于区域(我事先知道)这很容易,因为我可以在两种情况下使用环境变量,但是输出 ami 呢?是否有一种内置的方式来链接这些产品或一些不那么老套的方法来做到这一点?

编辑:对于任何可能感兴趣的人来说,Hacky 方法。在此解决方案中,我grep从打包程序输出中获取 aws 区域和 ami,并在 perl 中使用正则表达式将结果写入terraform.tfvars文件:

vars=$(pwd)"/terraform.tfvars"
packer build pack.json | \
    tee /dev/tty | \
    grep -E -o '\w{2}-\w+-\w{1}: ami-\w+' | \
    perl -ne '@parts = split /[:,\s]+/, $_; print "aws_amis." . $parts[0] ." = \"" . $parts[1] . "\"\n"' > ${vars}
4

3 回答 3

25

您应该考虑将 Terraform 的数据源用于aws_ami. 有了这个,您可以依赖在创建 AMI 时设置的自定义标签(例如版本号或时间戳)。然后,在 Terraform 配置中,您可以简单地过滤此账户和区域的可用 AMI,以获得您需要的 AMI ID。

https://www.terraform.io/docs/providers/aws/d/ami.html

data "aws_ami" "nat_ami" {
  most_recent = true
  executable_users = ["self"]
  filter {
    name = "owner-alias"
    values = ["amazon"]
  }
  filter {
    name = "name"
    values = ["amzn-ami-vpc-nat*"]
  }
  name_regex = "^myami-\\d{3}"
  owners = ["self"]
}

注意:在上面的示例中(来自文档),过滤器的组合可能过多。您可能可以通过以下方式过得很好:

data "aws_ami" "image" {
  most_recent = true
  owners = ["self"]
  filter {                       
    name = "tag:Application"     
    values = ["my-app-name"]
  }                              
}

output "ami_id" {
  value = "${data.aws_ami.image.id}"
}

这样做的另一个好处是您可以部署到具有相同配置且没有可变映射的多个区域!

于 2017-01-26T19:58:54.473 回答
7

Hashicorp 推荐的“官方”方式是使用他们的产品 Atlas 作为两者之间的“中间人”。您将使用Packer 中的 Atlas 后处理器记录工件(在您的情况下为 AMI id),然后使用Terraform 中atlas_artifact资源再次读取 id 以在 Terraform 中使用。

在这种情况下,您将从资源中获取 id,而不是使用变量传递它们。

除了 Atlas 之外,其他选项相当有限,在某些情况下也很老套。

如果您想在没有任何外部服务的情况下执行此操作,那么您可以尝试使用本地 shell 后处理器作为在您的工件上运行本地命令的一种方式,或者您可以使用机器可读的输出来提取 AMI id 和将它们写入 Terraform 的变量文件中。

另一种选择是编写您自己的后处理器插件,该插件与您已经使用的某些软件交互,作为 Atlas 的替代品。例如,我和我的一些同事一起编写了一个后处理器,将工件记录为 Buildkite 中的元数据,然后我们随后使用Buildkite API进行检索。这需要在 Go 中编写自定义代码。

在撰写本文时,Terraform 版本 0.7 仍在开发中,但计划包括一个新功能,允许直接查询 EC2 API 以获取 AMI,这将(如果它确实登陆 0.7)允许进一步选择标记 AMI使用 Packer,然后使用这些标签直接从 EC2 中找到它。这使用 EC2 本身作为“中间人”,这可能不那么尴尬,因为它已经作为 AMI 的存储参与进来了。

于 2016-05-21T02:33:58.227 回答
2

这是我使用的方法:

  1. 包装打包程序调用并通过解析输出获取 AMI
  2. 使用解析的 AMI 创建一个 Terraform 文件,该文件将值作为变量提供

它类似于已编辑答案中的版本。更详细地说,它可能如下所示:

首先,创建一个名为ami.tf.template

# "ami.tf" was automatically generated from the template "ami.tf.template".
variable "ami" {
  default     = "${AMI_GENERATED_BY_PACKER}"
  description = "The latest AMI."
}

此模板将用于创建ami.tf文件,从而使打包程序中的 AMI 可用于您现有的 Terraform 设置。

其次,创建一个用于运行 packer 的 shell 包装脚本。您可以使用以下想法:

# run packer (prints to stdout, but stores the output in a variable)
packer_out=$(packer build packer.json | tee /dev/tty)

# packer prints the id of the generated AMI in its last line
ami=$(echo "$packer_out" | tail -c 30 | perl -n -e'/: (ami-.+)$/ && print $1')

# create the 'ami.tf' file from the template:
export AMI_GENERATED_BY_PACKER="$ami" && envsubst < ami.tf.template > ami.tf

脚本完成后,它会创建一个ami.tf文件,如下所示:

# "ami.tf" was automatically generated from the template "ami.tf.template".
variable "ami" {
  default     = "ami-aa92a441"
  description = "The latest AMI."
}

最后,将该文件放在现有的 Terraform 设置旁边。然后您可以像这样访问 AMI:

resource "aws_launch_configuration" "foo" {
  image_id = "${var.ami}"
  ...
}
于 2018-06-07T17:40:38.257 回答