使用 Terraform 进行分层部署

Posted

技术标签:

【中文标题】使用 Terraform 进行分层部署【英文标题】:Layered deployments with Terraform 【发布时间】:2018-07-16 22:38:54 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

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

如果某些配置将依赖于其他配置的结果,则有必要将这些结果存储在某个数据存储中,该数据存储可由其生产者写入并由其消费者读取。在 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 ...

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

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


虽然terraform_remote_state 数据源对于已经使用remote state(推荐)的任何组织都可以快速上手,但一些组织更喜欢通过引入像Consul 这样的中间数据存储来进一步解耦配置,然后允许更明确地在配置之间传递数据。

为此,“生产”配置(管理您的资源组的配置)使用the consul_key_prefix resource 将有关其创建内容的必要信息发布到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_keys data source 读取它:

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 这样的 generic 数据存储也可能使这些数据可供应用程序本身使用,例如通过consul-template。

Consul 只是恰好在 Terraform 中得到良好支持的数据存储的一个示例。使用 Terraform 可以读写的任何其他数据存储也可以实现类似的结果。例如,您甚至可以将值存储在a DNS zone 中的TXT 记录中并使用the DNS provider 进行读取,作为避免运行额外服务的“开箱即用”解决方案。


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

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

【讨论】:

感谢您抽出宝贵时间提供如此有用的详细信息 Terragrunt 在这方面做得很好,虽然它不是按层分隔而是按您的产品特定的“组件/模块”来分隔【参考方案2】:

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

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

【讨论】:

感谢您的回复。仍然试图让我的头脑围绕您的模块方法。我曾尝试使用存储库中的几个模块,但对它们的运气为 0,所以我一直在部署使用资源编写自己的模板的东西。到目前为止,我不喜欢 Terraform 的一件事是文档在某些方面很薄弱。您链接的有关销毁的文档并没有让我了解您引用的方式使用它。

以上是关于使用 Terraform 进行分层部署的主要内容,如果未能解决你的问题,请参考以下文章

需要说明如何使用 Terraform 管理 Google Cloud 项目

使用 Terraform 删除以前的快照并创建 EBS 卷的新快照

使用 github-script@v5 操作提取 terraform 资源更改

如何在Ansible set_fact中使用fact?

Terraform 是不是支持带有少量操作的 CloudFormation 模板

Terraform - 创建 EBS 的快照,然后将快照转换为 EBS 并附加到 EC2