我们如何在多个 Terraform 提供者中创建相同的资源?

How can we create same resource in multiple terraform providers?

我正在使用 terraform 创建 Kubernetes 命名空间。下面的示例

resource "kubernetes_namespace" "test1" {
  metadata {
    name = local.ns_name
  }

}

我正在尝试使用此 link 之后的 terraform 创建 Blue/Green 类型的部署。作为其中的一部分,我现在创建了两个 kubernetes 集群。一个是蓝色的,另一个是绿色的,现在我有两个 kubernetes 供应商

provider "kubernetes" {
  alias                  = "kubernetes_blue"
}

provider "kubernetes" {
  alias                  = "kubernetes_green"
}

我想知道是否有办法,我可以在 kubernetes_namespace 上设置一些条件,这样,根据标志 var.enable_green_sidevar.enable_blue_side,我可以创建多个 kubernetes 集群中的相同命名空间,而不必像下面这样重复整个资源块

resource "kubernetes_namespace" "test1" {
  metadata {
    name = local.ns_name
  }

  provider = kubernetes.kubernetes_blue  
}

resource "kubernetes_namespace" "test2" {
  metadata {
    name = local.ns_name
  }

  provider = kubernetes.kubernetes_green  
}

提前致谢。

从问题中不清楚为什么需要两个提供者定义。但为了示例,我们可以考虑以下 blue/green 部署的常见用例:

  • 针对不同的集群
  • 针对不同的上下文

在这两种情况下,根据变量中设置的标志使用具有不同提供程序配置的单个提供程序会更容易。

考虑像这样的变量定义(使用 string 类型,但也可以使用 bool):

variable "kubernetes_deployment" {
  type    = string
  default = "blue"
}

假设我想针对不同的集群:

  • cluster1 为蓝色
  • 绿色集群 2

然后可以根据变量的值配置提供程序(例如使用 kubeconfig)。为此,我们可以使用 ternary conditional.

provider "kubernetes" {
  config_path = var.kubernetes_deployment == "blue" ? "~/.kube/cluster1.config" : "~/.kube/cluster2.config"
}

resource "kubernetes_namespace" "mynamespace" {
  metadata {
    name = local.ns_name
  }

  provider = kubernetes
}

这将确保将在“cluster1”上为蓝色部署创建资源(在本例中为命名空间),为绿色部署在“cluster2”上创建资源。

此条件可应用于提供程序的其他配置参数。使用相同集群但不同上下文的示例:

provider "kubernetes" {
  config_path = "~/.kube/cluster1.config"
  config_context = var.kubernetes_deployment == "blue" ? "context1" : "context2"
}

Terraform 的模型要求每个 resource 块恰好属于一个提供者配置,因此无法避免两次声明资源,但您至少可以通过分解它来减少重复的数量进入一个模块并调用该模块两次,而不是直接复制 resource 块:

provider "kubernetes" {
  alias = "blue"
}

provider "kubernetes" {
  alias = "green"
}

module "blue" {
  source = "../modules/bluegreen"

  # (any settings the module needs from the root)

  providers = {
    kubernetes = kubernetes.blue
  }
}

module "blue" {
  source = "../modules/bluegreen"

  # (any settings the module needs from the root)

  providers = {
    kubernetes = kubernetes.green
  }
}

The special providers argumentmodule 块中允许您为子模块提供与调用者所声明的提供者配置不同的“视图”。在上面的 module "blue" 块中,providers 参数表示:“在此模块实例中,对默认 kubernetes 提供程序配置的任何引用都意味着使用 kubernetes.blue 配置来电者”。

在模块内部,你可以只写普通的 resource "kubernetes_...." 块而不需要任何特殊的 provider 参数,因为这会导致它们从角度来看附加到默认提供者 该模块实例,并且两个模块实例中的每一个都绑定了不同的配置。

这种分解到模块中的方法是否有用当然取决于子模块最终需要调用模块的多少上下文。如果你的 module 块最终包含的参数几乎与你要分解的 resource 块一样多,那么最好只保留 resource 块在顶层并避免间接访问。