Как я могу заставить поставщика Docker в Terraform ждать, пока адрес станет доступным, прежде чем пытаться подключиться к нему?

В Terraform у меня есть следующий ресурс:

provider "docker" {
    host = "tcp://${digitalocean_droplet.docker_server.ipv4_address}:2376/"
}

Это зависит от значения ipv4_addressбыть известным до того, как он сможет подключиться к докеру. Это значение неизвестно, пока не будет предоставлен другой ресурс:

resource "digitalocean_droplet" "docker_server" {
    image = "docker-18-04"
    name = "docker_server"
    region = "nyc2"
    size = "512mb"
    private_networking = true
    ssh_keys = [
      var.ssh_fingerprint
    ]

    connection {
        user = "root"
        type = "ssh"
        private_key = file(var.pvt_key)
        timeout = "2m"
    }
}

Когда я бегу terraform plan, Я получаю следующую ошибку:

Ошибка: ошибка инициализации клиента Docker: невозможно проанализировать хост докера ''

в строке 1 docker.tf в провайдере "docker": 1: provider "docker" {

Похоже, что ipv4_addressпусто, потому что подключаемый модуль докера пытается подключиться к докеру до его подготовки. Как мне сказать ему дождаться подготовки машины, прежде чем пытаться подключиться к ней?


Я пробовал одну вещь:

provider "docker" {
    host = "tcp://${digitalocean_droplet.docker_server.ipv4_address}:2376/"
    depends_on = [
        digitalocean_droplet.docker_server.ipv4_address,
    ]
}

Когда я это сделаю, я получаю такую ​​ошибку:

Ошибка: зарезервированное имя аргумента в блоке поставщика

в строке 4 docker.tf в "docker" провайдера: 4: depends_on = [

Имя аргумента поставщика "зависит_он" зарезервировано для использования Terraform в будущей версии.

Но читая больше в depends_on, Я все равно не думаю, что это решение.

1 ответ

Решение

К сожалению, блок поставщика не поддерживает выражения, относящиеся к атрибуту ресурса.

Это ограничение объясняется в документации по конфигурации провайдера:

Аргументы конфигурации, определенные поставщиком, могут быть назначены с помощью выражений, которые, например, могут позволить параметризовать их с помощью входных переменных.

Однако, поскольку конфигурации поставщика должны быть оценены для выполнения любого действия типа ресурса, конфигурации поставщика могут относиться только к значениям, которые известны до применения конфигурации.

В частности, избегайте ссылок на атрибуты, экспортируемые другими ресурсами, если их значения не указаны непосредственно в конфигурации.

Например, это сработает (но не решит вашу проблему):

variable "docker_host" {
  type = string
}

provider "docker" {
  host = "tcp://${var.docker_host}:2376/"
}

Но выход есть.

Решение состоит из двух шагов:

  1. разделите вашу конфигурацию terraform на две части (каждая должна находиться в своем собственном каталоге), где одна с поставщиком докеров зависит от того, который развертывает дроплет. Обратите внимание, что это означает, что вам придется вводить команды терраформирования отдельно (вам нужно применить дважды).
  2. Установите однонаправленное "соединение" только для чтения между двумя состояниями с помощью функции, называемой удаленным состоянием:

Получает данные о состоянии из серверной части Terraform. Это позволяет вам использовать выходные данные корневого уровня одной или нескольких конфигураций Terraform в качестве входных данных для другой конфигурации.

Если вы еще не используете "настоящий" удаленный сервер, такой как S3 + DynamoDB, вы все равно можете легко экспериментировать с локальным сервером, как показано ниже.

Макет каталога:

├── docker                   <== this performs docker operation
│   ├── main.tf
│   └── terraform.tfstate
└── server                   <== this deploys the droplet
    ├── main.tf
    └── terraform.tfstate

В приведенных ниже фрагментах используется AWS, но адаптировать его к DO несложно.

Файловый сервер /main.tf содержит нечто похожее на

resource "aws_instance" "server" {     <= equivalent to the Droplet
  ...
}

output "ipv4_address" {
  value = aws_instance.server.public_ip
}

Файл docker/main.tf содержит нечто похожее на

data "terraform_remote_state" "docker_server" {
  backend = "local"

  config = {
    path = "${path.module}/../server/terraform.tfstate"
  }
}

provider "docker" {
  host = "tcp://${data.terraform_remote_state.docker_server.outputs.ipv4_address}:2376/"
}

В заключение:

cd server
terraform apply
cd ../docker
terraform apply

Помните: вы должны выполнять также отдельные terraform destroy, в порядке LIFO: сначала уничтожить docker, затем уничтожить server.

Другие вопросы по тегам