4

我在 Terraform 中有以下资源:

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

这依赖于在ipv4_address连接到 docker 机器之前要知道的值。在供应另一个资源之前,该值是未知的:

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 客户端时出错:无法解析 docker 主机 ``

在 docker.tf 第 1 行,在提供者“docker”中:1:提供者“docker”{

这似乎ipv4_address是空的,因为 docker 插件在配置之前尝试连接到 docker 机器。我如何告诉它在尝试连接之前等待机器配置好?


我试过的一件事:

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

当我这样做时,我收到此错误:

错误:提供程序块中的保留参数名称

在 docker.tf 第 4 行,在提供者“docker”中:4:depends_on = [

提供者参数名称“depends_on”保留供 Terraform 在未来版本中使用。

但是阅读更多内容depends_on,我认为这不是解决方案。

4

1 回答 1

5

不幸的是,提供程序块不支持引用资源属性的表达式

提供程序配置文档中解释了此限制:

提供者定义的配置参数可以使用表达式进行分配,例如可以允许它们通过输入变量进行参数化。

但是,由于必须评估提供程序配置才能执行任何资源类型操作,因此提供程序配置可能仅引用在应用配置之前已知的值。

特别是,避免引用其他资源导出的属性,除非它们的值直接在配置中指定。

例如,这会起作用(但不能解决您的问题):

variable "docker_host" {
  type = string
}

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

但有一条出路。

解决方案分为两个步骤:

  1. 将您的 terraform 配置分为两部分(每部分都必须位于其自己的目录中),其中具有 docker 提供程序的部分取决于部署 droplet 的部分。请注意,这意味着您必须单独发出 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 是微不足道的。

文件 server/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

于 2020-05-14T18:03:12.817 回答