Можно ли повторно использовать шаблоны Terraform для разных ресурсов, предоставляя разные значения для переменных?
Я использую Terraform для настройки нескольких капелек, работающих под управлением Consul на DigitalOcean. Возможно, мне не хватает чего-то базового, но кажется удивительно сложным обеспечить правильную конфигурацию для них.
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
# ...
}
У каждой машины есть два сетевых интерфейса - публичный и приватный. При такой настройке представляется необходимым обеспечить bind_addr
указание на частный IP-адрес каждой капли - в противном случае консул завершает работу с ошибкой, указывающей, что существует несколько частных (?!) адресов.
Наиболее простым решением было бы предоставить каждой машине файл конфигурации, который в каждом случае почти одинаков, но с разным значением для 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
) для рендеринга шаблона или использования встроенного format
функционировать вместе с файловой функцией, но каждый из них кажется громоздким.
Есть ли прямой способ достичь того, чего я хочу?
2 ответа
Вы пытались использовать написание модуля?
Это может быть хорошей отправной точкой: https://blog.gruntwork.io/how-to-create-reusable-infrastructure-with-terraform-modules-25526d65f73d
Шаблоны предназначены для получения значений от Terraform (будь то из переменных файлов, локальных источников, ресурсов данных и т. Д.) И включения их в шаблонный файл. Как правило, я видел, что полученные файлы представляют собой сценарии, которые будут выполняться вашим возможным ресурсом (экземпляром EC2 для AWS, Droplet для DigitalOcean).
Модули (упомянутые в другом ответе) используются для настройки набора ресурсов (скажем, сервера, балансировщика нагрузки, некоторых сетевых ресурсов) с единой точкой входа. Я считаю, что вы правы, что это не относится к вашей ситуации.
Я бы выполнил что-то похожее на то, что вы хотите (получить IP-адрес после создания ресурса), - создать шаблон сценария и передать его вашей капле. Затем попросите этот скрипт спросить дроплет, какой у него IP. Так что вам нужно определить ресурс 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
}
}
который отображает в оболочке (или Python, Perl, что угодно) скрипт
# Get networking details
ifconfig | #pipe to function of your choice and format results as you please.
# Do stuff to configure server.
Прошло больше года... возможно, вы уже нашли решение своей проблемы.
Я ни в коем случае не эксперт по 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 provisioner и т. Д.