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),并了解如何以最佳方式对远程状态进行层次化组织。