是否可以为不同的资源重用 Terraform 模板,为变量提供不同的值?

Posted

技术标签:

【中文标题】是否可以为不同的资源重用 Terraform 模板,为变量提供不同的值?【英文标题】:Is it possible to reuse Terraform templates for different resources providing different values for variables? 【发布时间】:2018-10-03 15:07:48 【问题描述】:

我正在使用 Terraform 设置多个在 DigitalOcean 上运行 Consul 的液滴。也许我遗漏了一些基本的东西,但为它们提供正确的配置似乎非常困难。

resource "digitalocean_droplet" "prime" 
  count  = 3
  image  = "$data.digitalocean_image.prime.image"
  name   = "$format("%s-%02d-%s", "prime", count.index + 1, var.region)"
  private_networking = true

  # ...

每台机器都有两个网络接口——公共的和私有的。使用此设置,似乎有必要提供指向每个 droplet 的私有 IP 地址的 bind_addr - 否则 consul 退出时会出现错误,指出有多个私有 (?!) 地址。

最直接的解决方案是为每台机器配置一个在每种情况下几乎相同的配置文件,但 bind_addr 字段的值不同,如下所示:


  "server": true,
  "data_dir": "/var/consul/data",
  "ui": true,
  "bind_addr": "$ private_ipv4_address "

这不是模板的用途吗?我不知道如何以这种方式使用它们。似乎模板的变量只能在定义模板时提供一次:

data "template_file" "consul-config" 
  template = "$file("templates/consul-config.json.tpl")"

  vars 
    private_ipv4_address = "10.0.0.1" # At this point the real address is not known
  


resource "digitalocean_droplet" "prime" 
  ...

  provisioner "file" 
    content = "$data.template_file.consul-config.rendered"
    destination = "/etc/consul.d/server.json"

    # At this point the address is known: $ self.private_ipv4_address ,
    # but is it possible to pass it to the template?
  

我尝试将数据块嵌套在资源块中,但随后出现如下错误:

Error: resource 'digitalocean_droplet.prime' provisioner file (#7): unknown resource 'data.template_file.consul-config' referenced in variable data.template_file.consul-config.rendered

我目前使用的解决方法是将配置拆分为两部分(服务器和自定义)并将自定义的内容内联到文件配置器中:

resource "digitalocean_droplet" "prime" 
  # ...

  provisioner "file" 
    content = "$data.template_file.consul-config.rendered"
    destination = "/etc/consul.d/server.json"
  

  # This is a custom configuration for each particular droplet
  provisioner "file" 
    content = " \"bind_addr\": \"$ self.ipv4_address_private \", \"bootstrap\": $ count.index == 0   "
    destination = "/etc/consul.d/custom.json"
  

它可以工作,但由于以下几个原因阻碍了可读性:

    所有引号都必须转义

    所有内容都必须在单行上 (?)

    文本编辑器没有语法高亮或类似帮助

另外,我考虑使用外部程序(如envsubst)来渲染模板或使用built in format function 和file function,但每个都看起来很麻烦。

有没有直接的方法来实现我想要的?

【问题讨论】:

【参考方案1】:

您是否尝试过使用编写模块?

这可能是一个很好的起点: https://blog.gruntwork.io/how-to-create-reusable-infrastructure-with-terraform-modules-25526d65f73d

【讨论】:

谢谢。这是一篇有趣的文章,但坦率地说,我不知道如何将它应用到我的案例中。你能提供一些代码示例吗?【参考方案2】:

模板旨在从 Terraform 中获取值(无论是来自变量文件、本地变量、数据资源等)并将它们插入到模板文件中。一般来说,我看到生成的文件是脚本,将由您的最终资源(AWS 的 EC2 实例,DigitalOcean 的 Droplet)运行。

模块(在另一个答案中提到)用于设置具有单个入口点的资源集合(例如,服务器、负载平衡器、一些网络资源)。我相信你是对的,它不适用于你的情况。

我完成类似于您想要的事情的方式(在创建资源后获取 IP 地址)是模板化一个脚本并将其提供给您的 droplet。然后让该脚本询问液滴它的 IP 是什么。所以你需要在你的 droplet 中定义一个 user_data 资源:

resource "digitalocean_droplet" "prime" 
  user_data = "$data.template_file.some_script.rendered"

那么你还有一个数据模板:

data "template_file" "consul-config" 
  template = "$file("templates/consul-config.json.tpl")"

  vars 
    # Define Script Variables here, excluding IP
  

渲染成 shell(或 python、perl 等)脚本

# Get networking details
ifconfig | #pipe to function of your choice and format results as you please.
# Do stuff to configure server.

【讨论】:

谢谢。可悲的是,我的问题的答案似乎是“不,没有直接的方法可以实现这一目标。”【参考方案3】:

已经一年多了……也许您已经找到了解决问题的方法。

我绝不是 Terraform 方面的专家,但在这种特殊情况下,因为您需要的信息在液滴中...您可以将脚本传递给液滴,该脚本将创建您需要的配置...

$ addr=`ip addr show eth1 | grep "inet\b" | awk 'print $2' | cut -d/ -f1`
$ tee consul.json <<EOF

  "server": true,
  "data_dir": "/var/consul/data",
  "ui": true,
  "bind_addr": "$addr"

EOF

$ echo 'YES!!' ## LOL

这将用作 user_data、remote-exec 供应商等...

【讨论】:

以上是关于是否可以为不同的资源重用 Terraform 模板,为变量提供不同的值?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Terraform 进行分层部署

重用配置在 Terraform 中创建类似的资源

将 ARM 模板转换为 Terraform [关闭]

Terraform常用命令

是否可以在 Terraform 中执行 CloudFormation 文件?

如何使用 Terraform 为 Azure 资源创建警报