Sägetstrasse 18, 3123 Belp, Switzerland +41 79 173 36 84 info@ict.technology

      Terraform @ Scale - 第1b部分:用于模块化云基础架构的多租户架构示例

      在本系列的上一篇文章中,我们解释了Terraform中的远程状态(Remote State)概念的基础知识,以及如何在多租户(Multi-Tenancy)环境中利用它来继承信息。现在,我们将通过一个具体的架构示例对此进行说明。

      引言

      一个有效的多租户架构应遵循企业的组织结构,同时考虑云平台的最佳实践。现代云平台提供了多种方式来映射层次化的组织结构 - 无论是Compartments(OCI)、Organizations和Accounts(AWS)、资源组(Azure)还是项目和文件夹(GCP)。

      架构概念介绍

      Datacenter View DE

      在我们的架构示例中,我们将一个“客户的全球数据中心”视为最高的组织单元。在此单元内存在不同的区域:

      全球系统和服务: 包括IAM、监控、计费和其他中央服务。
      区域数据中心: 面向不同需求的地理分布式基础架构。
      组织性分区: 例如按部门、安全区域或其他标准划分。

      此模型的特别之处在于:每个组织单元代表一个独立的逻辑区域,具有明确的责任范围。这些单元构成了不同租户或组织单元之间的自然边界,同时提供了权限和配置继承的框架。

      作为自然多租户模型的层次化组织结构

      云基础架构的层次化结构为多租户场景提供了多个优势:

      层次化组织: 单元可以嵌套,类似于文件夹结构。
      策略继承: 权限可以从较高级别继承到较低级别。
      资源管理: 可以为每个组织单元定义配额限制。
      成本核算: 计费和测量在组织单元级别进行。

      举例来说:一家公司可以为每个业务部门设置一个顶层单元。在这些单元内,可能存在生产、开发和测试等进一步的子单元。最底层可能是与具体项目相关的单元。
      这种层次结构不仅反映了组织结构,还简化了对访问权限和资源的管理。例如,管理员可以获得对特定部门单元的访问权限,而无需访问其他部门的资源。

      不同的分区层次

      我们可以沿着不同的维度来划分基础架构:

      技术性分区: 例如“站点A区域数据中心”,其中包含不同的技术区(DC1、DC2、DC3、DC4)。
      组织性分区: 例如“站点B区域数据中心”,其中包含不同的部门(部门1、部门2等)。
      安全区域: 例如“站点n区域数据中心”,其中包括高度安全(High Security)、中等(Medium)、低级(Low)和公共(Public)区域。
      价值流: 跨越其他结构的业务流程。

      这些不同的分区维度可能会重叠和互补。挑战在于如何在Terraform中清晰地表示它们,并同时实现它们之间的信息流。
      特别重要的是通过远程状态(Remote State)继承数据。我们看到信息如何从较高层(全球数据中心)传递到较低层(区域数据中心及其子单元)。这正是我们在上一部分中讨论的远程状态模式的体现。

      使用Terraform模块的实际实现

      在Terraform中实现此类架构需要采用结构化的模块方法。我们为每个层次结构级别创建专用的Terraform项目。

      提示: 远程状态数据源的具体配置根据所使用的后端而有所不同。对于常见选项(如S3、GCS或Consul),需要进行相应的调整。

      1. 全局基础架构的Root模块:

      Glabl Services DE


      # 1. 全局基础架构 (Root级别)
      module "global_datacenter" {
        source = "./modules/organization"
        
        name        = "global-datacenter-customer"
        description = "客户的全球数据中心"
        parent_id   = var.root_organization_id
      }
      
      module "global_network" {
        source = "./modules/network"
        
        organization_id = module.global_datacenter.id
        cidr_block      = "10.0.0.0/16"
        name            = "global-network"
      }
      
      # 全局必要的系统和服务
      module "global_services" {
        source = "./modules/organization"
        
        name        = "global-services"
        description = "客户所需的全局系统和服务"
        parent_id   = module.global_datacenter.id
      }
      
      # IAM、监控、计费服务模块将在此处实现...
      
      output "global_datacenter_id" {
        value = module.global_datacenter.id
      }
      
      output "global_network_id" {
        value = module.global_network.id
      }
      
      output "global_services_id" {
        value = module.global_services.id
      }

      2. 按技术分区的区域基础架构:

      Rrgional Datacenter ZN


      # 2. 位置A的区域数据中心 (多个数据中心)
      data "terraform_remote_state" "global" {
        backend = "remote"
        config = {
          organization = "my-org"
          workspaces = {
            name = "global-infrastructure"
          }
        }
      }
      
      module "location_a" {
        source = "./modules/organization"
        
        name        = "location-a-regionales-datacenter"
        description = "位置A的区域数据中心"
        parent_id   = data.terraform_remote_state.global.outputs.global_datacenter_id
      }
      
      # 配置4个数据中心作为子单元
      module "dc1" {
        source = "./modules/organization"
        
        name        = "dc1"
        description = "数据中心 1"
        parent_id   = module.location_a.id
      }
      
      module "dc2" {
        source = "./modules/organization"
        
        name        = "dc2"
        description = "数据中心 2"
        parent_id   = module.location_a.id
      }
      
      module "dc3" {
        source = "./modules/organization"
        
        name        = "dc3"
        description = "数据中心 3"
        parent_id   = module.location_a.id
      }
      
      module "dc4" {
        source = "./modules/organization"
        
        name        = "dc4"
        description = "数据中心 4"
        parent_id   = module.location_a.id
      }
      
      # 为位置A的全局网络配置子网
      module "subnet_dc1" {
        source = "./modules/subnet"
        
        organization_id = module.dc1.id
        network_id      = data.terraform_remote_state.global.outputs.global_network_id
        cidr_block      = "10.0.1.0/24"
        name            = "subnet-dc1"
      }
      
      # 更多的子网配置 (DC2、DC3、DC4...)
      
      # 在其他Terraform项目中使用的输出
      output "location_a_id" {
        value = module.location_a.id
      }
      
      output "dc1_id" {
        value = module.dc1.id
      }
      
      output "subnet_dc1_id" {
        value = module.subnet_dc1.id
      }
      
      # 其他输出项

      3. 按组织分区的区域基础架构:

      Regional Organizations ZN


      # 3. 位置B的区域数据中心 (组织架构)
      data "terraform_remote_state" "global" {
        backend = "remote"
        config = {
          organization = "my-org"
          workspaces = {
            name = "global-infrastructure"
          }
        }
      }
      
      module "location_b" {
        source = "./modules/organization"
        
        name        = "location-b-regional-datacenter"
        description = "位置B的区域数据中心"
        parent_id   = data.terraform_remote_state.global.outputs.global_datacenter_id
      }
      
      # 组织部门
      module "department_1" {
        source = "./modules/organization"
        
        name        = "department-1"
        description = "部门 1"
        parent_id   = module.location_b.id
      }
      
      module "department_2" {
        source = "./modules/organization"
        
        name        = "department-2"
        description = "部门 2"
        parent_id   = module.location_b.id
      }
      
      module "department_3" {
        source = "./modules/organization"
        
        name        = "department-3"
        description = "部门 3"
        parent_id   = module.location_b.id
      }
      
      module "department_4" {
        source = "./modules/organization"
        
        name        = "department-4"
        description = "部门 4"
        parent_id   = module.location_b.id
      }
      
      # 部门专属子网
      module "subnet_department_1" {
        source = "./modules/subnet"
        
        organization_id = module.department_1.id
        network_id      = data.terraform_remote_state.global.outputs.global_network_id
        cidr_block      = "10.0.10.0/24"
        name            = "subnet-department-1"
      }
      
      # 在其他Terraform项目中使用的输出
      output "location_b_id" {
        value = module.location_b.id
      }
      
      output "department_1_id" {
        value = module.department_1.id
      }
      
      output "subnet_department_1_id" {
        value = module.subnet_department_1.id
      }

      4. 按安全分区的区域基础架构:

      REgional Security Zones ZN


      # 4. 位置n的区域数据中心 (安全区域)
      data "terraform_remote_state" "global" {
        backend = "remote"
        config = {
          organization = "my-org"
          workspaces = {
            name = "global-infrastructure"
          }
        }
      }
      
      module "location_n" {
        source = "./modules/organization"
        
        name        = "location-n-regional-datacenter"
        description = "位置n的区域数据中心"
        parent_id   = data.terraform_remote_state.global.outputs.global_datacenter_id
      }
      
      # 安全区域作为子单元
      module "high" {
        source = "./modules/organization"
        
        name        = "high"
        description = "高级安全区域"
        parent_id   = module.location_n.id
      }
      
      module "med" {
        source = "./modules/organization"
        
        name        = "med"
        description = "中级安全区域"
        parent_id   = module.location_n.id
      }
      
      module "low" {
        source = "./modules/organization"
        
        name        = "low"
        description = "低级安全区域"
        parent_id   = module.location_n.id
      }
      
      module "pub" {
        source = "./modules/organization"
        
        name        = "pub"
        description = "公共区域"
        parent_id   = module.location_n.id
      }
      
      # 安全区域特定的子网
      module "subnet_high" {
        source = "./modules/subnet"
        
        organization_id       = module.high.id
        network_id            = data.terraform_remote_state.global.outputs.global_network_id
        cidr_block            = "10.0.20.0/24"
        name                  = "subnet-high"
        prohibit_public_ip    = true  
      }
      
      # 其他安全资源,如Security Lists和Network Security Groups...
      
      # 输出
      output "location_n_id" {
        value = module.location_n.id
      }
      
      output "high_security_id" {
        value = module.high.id
      }
      
      output "subnet_high_id" {
        value = module.subnet_high.id
      }

      5. 部门或团队特定的基础架构:

      Department Datacenter ZN


      # 5. 示例:部门中的资源使用远程状态信息
      data "terraform_remote_state" "standort_b" {
        backend = "remote"
        config = {
          organization = "my-org"
          workspaces = {
            name = "regional-location-b"
          }
        }
      }
      
      module "vm_instances" {
        source = "./modules/compute"
        
        instances = {
          app_server_department_1 = {
            organization_id = data.terraform_remote_state.location_b.outputs.department_1_id
            subnet_id       = data.terraform_remote_state.location_b.outputs.subnet_department_1_id
            size            = "medium"
            image           = var.app_image_id
          }
          # 其他虚拟机实例 ...
        }
      }

      动态模块配置以实现灵活的部署模式

      为了进一步提高灵活性,我们还可以使用动态配置。这在我们需要管理大量相似资源时特别有用:


      module "regional_datacenters" {
        source   = "./modules/regional_datacenter"
        for_each = var.datacenter_configs
        
        name            = each.key
        description     = each.value["description"]
        parent_id       = data.terraform_remote_state.global.outputs.global_datacenter_id
        network_cidr    = each.value["network_cidr"]
        security_level  = each.value["security_level"]
        subunits        = each.value["subunits"]
      }
      
      # 使用示例,如何从根模块调用此模块
      # datacenter_configs = {
      #   "europe-west" = {
      #     description    = "欧洲西部数据中心"
      #     network_cidr   = "10.1.0.0/16"
      #     security_level = "high"
      #     subunits       = ["prod", "staging", "dev", "test"]
      #   },
      #   "us-east" = {
      #     description    = "美国东海岸数据中心"
      #     network_cidr   = "10.2.0.0/16"
      #     security_level = "medium"
      #     subunits       = ["prod", "dev"]
      #   }
      # }

      模块化结构的优势


      这种模块化结构使我们能够将组织的逻辑层次结构转换为独立的Terraform项目,同时确保这些项目之间的信息交换:

      明确的责任划分: 每个团队管理其自己的Terraform代码和状态。
      自动的信息继承: 管理全局基础架构的团队可以进行更改,而无需区域或部门级别的团队调整其代码。
      独立的部署: 某个区域出现的错误不会自动影响其他区域。
      可扩展性: 新的组织单元可以轻松添加,而无需更改现有结构。
      一致的治理: 通过输出的继承,确保所有层级的一致配置。

      完整的多租户设置示例

      以下是一个示例,展示如何在实际项目中结合这些模式:


      module "tenant_environments" {
        source = "./modules/tenant_environment"
        
        for_each = {
          for tenant in var.tenants : tenant.name => tenant
        }
        
        name            = each.key
        parent_id       = module.tenant_root_organization.id
        environments    = each.value["environments"]
        network_cidr    = each.value["network_cidr"]
        security_policy = each.value["security_policy"]
      }
      
      # tenant_environments 可用于不同的客户、
      # 业务部门或项目。
      # 
      # 示例:
      # tenants = [
      #   {
      #     name = "finance-department"
      #     environments = ["prod", "dev", "test"]
      #     network_cidr = "10.10.0.0/16"
      #     security_policy = "high"
      #   },
      #   {
      #     name = "marketing-department"
      #     environments = ["prod", "staging"]
      #     network_cidr = "10.20.0.0/16"
      #     security_policy = "medium"
      #   }
      # ]


      借助这种结构化的方法,我们能够以技术上稳健且组织上合理的方式管理复杂的多租户环境。通过明确的责任划分,结合远程状态(Remote State)的精准信息继承,使我们即便在大型、分布式团队中也能高效地使用Terraform。

      远程状态(Remote-State)信息的安全性考量

      尽管远程状态(Remote States)非常有用,但它们可能包含潜在的敏感信息。尤其是在多租户(Multi-Tenancy)环境中,保护这些信息至关重要。
      为确保仅授权的团队能够访问远程状态的输出,您应采取以下措施:

      • 访问控制限制: 使用您的后端的访问控制机制(例如S3的IAM策略、Consul的ACL,或Terraform Cloud/Enterprise中的基于角色的访问控制)。
      • 最小化输出: 仅将绝对必要的信息定义为输出。每个额外的输出属性都会增加敏感数据泄露的风险。
      • 避免不必要的输出: 具有安全关键值(例如密码、私钥)的输出应尽可能避免作为输出提供,而应通过机密管理服务(如HashiCorp Vault或Secrets Manager)来管理。
      • 信息汇总: 与其单独提供数据库密码、API密钥或其他敏感详细信息作为输出,不如优先使用结构化输出,仅聚合相关参数。
      • 使用代理状态文件(Proxy-Statefiles):过滤远程状态的输出,仅包含与特定租户相关的信息,并将其作为新的状态提供。该租户仅允许访问该代理状态。

      远程状态依赖的错误处理

      在多租户架构中使用远程状态会带来特殊的错误处理挑战。错误来源可能包括不可用的后端、输出不一致或意外更改,这些都可能导致严重问题。为了尽量减少这些风险,您应采取以下措施:

      • 可用性检查: 在Terraform的terraform_remote_state块中结合查询结构(如lookup())使用,以减少因缺失输出引发的错误。
      • 输出正确性检查: 在模块和CI/CD流水线中添加测试,确保远程状态输出始终包含预期的结构和内容。
      • 回退机制: 如果远程状态暂时无法访问,可以使用本地缓存的值(例如通过环境变量)作为临时解决方案。

      以下是一个带有错误处理的稳健远程状态查询示例:


      data "terraform_remote_state" "network" {
        backend = "remote"
        config = {
          organization = "my-org"
          workspaces = {
            name = "global-infrastructure"
          }
        }
      }
      
      locals {
        private_subnet_id = lookup(data.terraform_remote_state.network.outputs.subnets, "private", null)
      }
      
      resource "example_vm" "app_server" {
        subnet_id = local.private_subnet_id != null ? local.private_subnet_id : var.default_subnet_id
      }

      在下一部分中,我们将探讨远程后端(Remote Backends),并了解如何以最佳方式对远程状态进行层次化组织。