使用Terraform进行分层部署

时间:2018-02-06 19:10:36

标签: terraform terraform-provider-azure terraform-template-file

我是Terraform的新手,所以甚至不能确定这样的事情是可能的。举个例子,假设我有一个模板,可以在其中部署Azure资源组和密钥保管库。然后让我说我有另一个模板将虚拟机部署到同一个资源组中。是否可以在不破坏密钥保管库和资源组的情况下使用虚拟机模板进行销毁?我们试图将大型解决方案的各个部分划分,而不必将其全部放在一个模板中,我们希望能够分别管理每个部分而不影响其他部分。

在相关说明中......我们将状态文件存储在Azure存储帐户中。如果我们将部署分解为多个分区部署...每个部署是否应该有自己的状态文件,还是应该使用相同的状态文件?

2 个答案:

答案 0 :(得分:5)

对于大型系统,通常将基础架构拆分为多个单独的配置,并分别应用它们。这是使用共享模块的独立想法(并且是互补的):模块允许许多不同的配置具有它们自己的特定基础设施的单独“副本”,而下面描述的模式允许由一个配置管理的对象,通过引用传递给另一个配置。

如果某些配置取决于其他配置的结果,则必须将这些结果存储在某个数据存储中,该数据存储可由其生产者写入并由其使用者读取。在Terraform状态远程存储且可广泛读取的环境中,the terraform_remote_state data source是一种常用的入门方式:

data "terraform_remote_state" "resource_group" {
  # The settings here should match the "backend" settings in the
  # configuration that manages the network resources.
  backend = "s3"
  config {
    bucket = "mycompany-terraform-states"
    region = "us-east-1"
    key    = "azure-resource-group/terraform.tfstate"
  }
}

resource "azurerm_virtual_machine" "example" {
  resource_group_name = "${data.terraform_remote_state.resource_group.resource_group_name}"
  # ... etc ...
}

此示例中resource_group_name数据源导出的terraform_remote_state属性假定使用output管理资源组的配置公开了该名称的值。

这将两种配置分离,以便它们具有完全独立的生命周期。您首先在创建资源组的配置中terraform apply,然后在包含上面显示的terraform apply数据资源的配置中terraform_remote_state。然后,您可以根据需要多次应用后一种配置,而不会对共享资源组或密钥库造成风险。

虽然terraform_remote_state数据源可以快速开始使用已经使用remote state的任何组织(建议使用),但有些组织更愿意通过引入像{{{{}这样的中间数据存储来进一步解耦配置。 3}},然后允许更明确地在配置之间传递数据。

为此,“生成”配置(管理您的资源组的配置)使用Consul在知名位置发布有关其创建到Consul的必要信息:

resource "consul_key_prefix" "example" {
  path_prefix = "shared/resource_group/"
  subkeys = {
    name = "${azurerm_resource_group.example.name}"
    id   = "${azurerm_resource_group.example.id}"
  }

resource "consul_key_prefix" "example" {
  path_prefix = "shared/key_vault/"
  subkeys = {
    name = "${azurerm_key_vault.example.name}"
    id   = "${azurerm_key_vault.example.id}"
    uri  = "${azurerm_key_vault.example.uri}"
  }
}

使用集中管理的资源组和密钥保管库的单独配置将使用the consul_key_prefix resource读取它:

data "consul_keys" "example" {
  key {
    name = "resource_group_name"
    path = "shared/resource_group/name"
  }
  key {
    name = "key_vault_name"
    path = "shared/key_vault/name"
  }
  key {
    name = "key_vault_uri"
    path = "shared/key_vault/uri"
  }
}

resource "azurerm_virtual_machine" "example" {
  resource_group_name = "${data.consul_keys.example.var.resource_group_name}"
  # ... etc ...
}

作为对运行另一个服务以存储这些中间值的额外复杂性的回报,除了Consul中商定的密钥命名方案之外,这两个配置现在彼此都不知道,这提供了灵活性,例如,如果未来您决定重构这些Terraform配置,以便密钥保险库也有自己独立的配置。使用像Consul这样的通用数据存储也可能使这些数据可供应用程序本身使用,例如通过the consul_keys data source

Consul只是数据存储的一个例子,它恰好在Terraform中得到了很好的支持。使用Terraform可以读写的任何其他数据存储也可以获得类似的结果。例如,您甚至可以将值存储在consul-template中的TXT条记录中,并使用a DNS zone来阅读,作为避免运行其他服务的“开箱即用”解决方案。

像往常一样,这里需要在简单性(“一站式配置中最简单”)和灵活性(使用单独的配置存储)之间进行权衡,因此您需要评估这些方法中的哪一种最适合你的情况。

作为一些额外的背景:我已经为中等复杂度系统记录了the DNS provider。在这种情况下,我们使用了Consul和DNS的混合来创建“环境”抽象,允许我们分别为临时环境,生产等部署相同的应用程序。但是,使用的确切技术不如模式重要。这种方法并不完全适用于所有其他情况,但希望有一些想法可以帮助其他人思考如何在他们的环境中最好地利用Terraform。

答案 1 :(得分:0)

您可以使用terraform destroy -target path.to.resource销毁特定资源。 Docs

大型解决方案的不同部分可以分为modules,这些模块甚至不必是同一代码库的一部分,可以远程引用。根据您的解决方案,您可能希望将部署分解为模块,并从包含所有内容的“主”状态文件中引用它们。