tencent cloud

文档反馈

资源跨地域复制

最后更新时间:2023-05-29 16:04:10
    本文介绍如何将已有资源导入 Terraform,以及如何通过复制文件的方式创建新资源,完成跨地域复制。
    

    已有资源导入到 Terraform

    大部分刚接触 Terraform 的用户,可能在云上已经存在资源并期望将他们导入到 Terraform 中管理,Terraform 支持单个资源的导入,但是碰到多资源多实例的导入,则需要借助开源工具实现,以下介绍这两种场景的导入方法。
    

    导入单个资源

    Terraform 支持 Import 命令导入单个资源,格式 terraform import [资源类型].[名称] [入参]。名称可以自定义,入参则是查询资源必要的字符串(一般为 ID ,部分资源是名字或者多字段组合)。以云服务器实例为例,通过查询 CVM Resource 文档 ,可知导入命令为 :
    $ terraform import tencentcloud_instance.ins ins-jvu2hiw2 -allow-missing-config
    其中 -allow-missing-config 表示允许本地不需要预先声明 block ,否则需要在文件中预先写一段 resource [资源类型].[名称] {} 这样的空块导入完成后,字段不会写入 TF 文件中,需要执行 terraform show 查看导入的资源代码:
    # tencentcloud_instance.ins:
    resource "tencentcloud_instance" "ins" {
    allocate_public_ip = true
    availability_zone = "ap-guangzhou-3"
    create_time = "2022-01-01T01:11:11Z"
    id = "ins-xxxxxxxx"
    image_id = "img-xxxxxxxx"
    instance_charge_type = "POSTPAID_BY_HOUR"
    instance_name = "xxxxxxxx"
    instance_status = "RUNNING"
    instance_type = "S3.MEDIUM2"
    internet_charge_type = "TRAFFIC_POSTPAID_BY_HOUR"
    internet_max_bandwidth_out = 1
    key_name = "skey-xxxxxxxx"
    private_ip = "10.0.1.1"
    project_id = 0
    public_ip = "1.1.1.1"
    running_flag = true
    security_groups = [
    "sg-xxxxxxxx",
    ]
    subnet_id = "subnet-xxxxxxxx"
    system_disk_id = "disk-xxxxxxxx"
    system_disk_size = 50
    system_disk_type = "CLOUD_PREMIUM"
    tags = {}
    vpc_id = "vpc-xxxxxxxx"
    }
    将这段代码填入您的 TF 文件中,还需要去掉只读字段,通过 Attribute Reference 可知,需要去掉 id, create_time, public_ip 后完成导入。
    resource "tencentcloud_instance" "ins" {
    allocate_public_ip = true
    availability_zone = "ap-guangzhou-3"
    # create_time = "2022-01-01T01:11:11Z"
    # id = "ins-xxxxxxxx"
    image_id = "img-xxxxxxxx"
    instance_charge_type = "POSTPAID_BY_HOUR"
    instance_name = "xxxxxxxx"
    # instance_status = "RUNNING"
    instance_type = "S3.MEDIUM2"
    internet_charge_type = "TRAFFIC_POSTPAID_BY_HOUR"
    internet_max_bandwidth_out = 1
    key_name = "skey-xxxxxxxx"
    private_ip = "10.0.1.1"
    project_id = 0
    # public_ip = "1.1.1.1"
    running_flag = true
    security_groups = [
    "sg-xxxxxxxx",
    ]
    subnet_id = "subnet-xxxxxxxx"
    system_disk_id = "disk-xxxxxxxx"
    system_disk_size = 50
    system_disk_type = "CLOUD_PREMIUM"
    tags = {}
    vpc_id = "vpc-xxxxxxxx"
    }
    要获取各个资源的 Import 命令和只读字段,访问 文档 中对应的实例中可以查询。如果未填写,则说明该资源暂不支持导入。
    

    使用 Terraformer 批量导入

    通过上文可以看到使用 Terraform 的导入相当繁琐,仅适合导入少量资源。您可能需要借助 Terraformer 进行批量导入。Terraformer 是一个属于 GoogleCloudPlatform 的命令行工具,可以把账号下大部分云资源标记并导入为 TF 文件。
    1. 安装
    $ brew install terraformer
    2. 执行导入命令,假如我想导入腾讯云广州区下所有的 CVM 和 VPC 资源,那么命令格式如下:
    terraformer import tencentcloud --resources="vpc,cvm" --regions=ap-guangzhou
    命令执行完成后,Terraformer 默认将导入的资源文件写入 ./generated 目录,示例如下:
    .
    └── tencentcloud
    ├── cvm
    │ └── ap-guangzhou
    │ ├── instance.tf
    │ ├── key_pair.tf
    │ ├── outputs.tf
    │ ├── provider.tf
    │ ├── terraform.tfstate
    │ └── variables.tf
    └── vpc
    └── ap-guangzhou
    ├── outputs.tf
    ├── provider.tf
    ├── terraform.tfstate
    └── vpc.tf
    3. 换源:TencentCloudProvider 由我们腾讯云维护而非 Terraform 官方,需要在生成的 provider.tf 中添加 source 字段,值为 tencentcloudstack/tencentcloud
    provider "tencentcloud" {
    version = "~> 1.77.11"
    }
    
    terraform {
    required_providers {
    tencentcloud = {
    source = "tencentcloudstack/tencentcloud" # 添加 source 以指定命名空间
    version = "~> 1.77.11"
    }
    }
    }
    
    当然,不是所有的腾讯云资源 Terraformer 都支持导入,查看已支持导入的资源参考 Terraformer 源码
    

    跨地域复制

    实现原理

    一个简单的 Terraform 工作目录结构如下:
    .
    ├── .terraform
    │ └── providers # 引用到的 Provider
    ├── .terraform.lock.hcl # Provider 锁版本
    ├── main.tf # TF 文件
    ├── vars.tf # TF 文件
    ├── outputs.tf # TF 文件
    └── terraform.tfstate # 状态文件
    
    而本地的工作目录跟腾讯云资源映射结构如下图所示:
    
    
    
    当用户执行 terraform apply 命令且部署完成后。会生成 terraform.tfstate 文件。它是一份 JSON 格式的文件,默认存储在本地,或者配置在远端存储桶中(需要配置 Backend )用来描述 TF 声明的资源和真实云资源的映射关系。如果本地目录或 Backend 中不存在 terraform.tfstate ,或者该文件没有写入云资源数据,Terraform 就会认为资源没有被部署,执行 apply 会进行资源创建操作。
    

    示例:TKE Serverless 集群跨地域复制

    只要没有 tfstate 映射的资源声明都视为创建。基于这一思路,我们通过复制文件并修改地域的方法,再执行 apply 即可完成资源跨地域复制。
    假设我们已经通过 Terraform 在广州部署了一套基于 Serverless 集群服务的应用,目录如下:
    eks-app-guangzhou
    ├── crds.tf
    ├── infra.tf
    ├── main.tf
    ├── terraform.log
    └── terraform.tfstate
    其中 main.tf 指定 Terraform 和 Provider 的元信息。代码如下:
    terraform {
    required_providers {
    tencentcloud = {
    source = "tencentcloudstack/tencentcloud"
    }
    }
    }
    
    provider "tencentcloud" {
    region = "ap-guangzhou"
    }
    infra.tf 指定 TKE Serverless 集群和所需的资源:VPC、子网、安全组、TKE Serverless 集群、负载均衡。代码如下:
    # 服务对外放通测试 IP 地址
    variable "accept_ip" {
    description = "Use EnvVar: $TF_VAR_accept_ip instead"
    }
    
    resource "tencentcloud_vpc" "vpc" {
    name = "eks-vpc"
    cidr_block = "10.2.0.0/16"
    }
    
    resource "tencentcloud_subnet" "sub" {
    vpc_id = tencentcloud_vpc.vpc.id
    name = "eks-subnet"
    cidr_block = "10.2.0.0/20"
    availability_zone = "ap-guangzhou-3"
    }
    
    resource "tencentcloud_security_group" "sg" {
    name = "eks-sg"
    }
    
    resource "tencentcloud_security_group_lite_rule" "sgr" {
    security_group_id = tencentcloud_security_group.sg.id
    ingress = [
    "ACCEPT#10.2.0.0/16#ALL#ALL",
    "ACCEPT#${var.accept_ip}#ALL#ALL"
    ]
    }
    
    resource "tencentcloud_eks_cluster" "foo" {
    cluster_name = "tf-test-eks"
    k8s_version = "1.20.6"
    vpc_id = tencentcloud_vpc.vpc.id
    subnet_ids = [
    tencentcloud_subnet.sub.id,
    ]
    cluster_desc = "test eks cluster created by terraform"
    service_subnet_id = tencentcloud_subnet.sub.id
    enable_vpc_core_dns = true
    need_delete_cbs = true
    public_lb {
    enabled = true
    security_policies = [var.accept_ip]
    }
    internal_lb {
    enabled = true
    subnet_id = tencentcloud_subnet.sub.id
    }
    }
    
    resource "tencentcloud_clb_instance" "ingress-lb" {
    address_ip_version = "ipv4"
    clb_name = "example-lb"
    internet_bandwidth_max_out = 1
    internet_charge_type = "BANDWIDTH_POSTPAID_BY_HOUR"
    load_balancer_pass_to_target = true
    network_type = "OPEN"
    security_groups = [tencentcloud_security_group.sg.id]
    vpc_id = tencentcloud_vpc.vpc.id
    }
    
    crds.tf 指定基于 TKE Serverless 集群的 CRD。代码如下:
    locals {
    kubeconfig = yamldecode(tencentcloud_eks_cluster.foo.kube_config)
    }
    
    provider "kubernetes" {
    host = local.kubeconfig.clusters[0].cluster.server
    cluster_ca_certificate = base64decode(local.kubeconfig.clusters[0].cluster["certificate-authority-data"])
    client_key = base64decode(local.kubeconfig.users[0].user["client-key-data"])
    client_certificate = base64decode(local.kubeconfig.users[0].user["client-certificate-data"])
    }
    
    resource "kubernetes_namespace" "test" {
    metadata {
    name = "nginx"
    }
    }
    
    resource "kubernetes_deployment" "test" {
    metadata {
    name = "nginx"
    namespace = kubernetes_namespace.test.metadata.0.name
    }
    spec {
    replicas = 2
    selector {
    match_labels = {
    app = "MyTestApp"
    }
    }
    template {
    metadata {
    labels = {
    app = "MyTestApp"
    }
    }
    spec {
    container {
    image = "nginx"
    name = "nginx-container"
    port {
    container_port = 80
    }
    }
    }
    }
    }
    }
    
    resource "kubernetes_service" "test" {
    metadata {
    name = "nginx"
    namespace = kubernetes_namespace.test.metadata.0.name
    }
    spec {
    selector = {
    app = kubernetes_deployment.test.spec.0.template.0.metadata.0.labels.app
    }
    type = "NodePort"
    port {
    node_port = 30201
    port = 80
    target_port = 80
    }
    }
    }
    
    resource "kubernetes_ingress_v1" "test" {
    metadata {
    name = "test-ingress"
    namespace = "nginx"
    annotations = {
    "ingress.cloud.tencent.com/direct-access" = "false"
    "kubernetes.io/ingress.class" = "qcloud"
    "kubernetes.io/ingress.existLbId" = tencentcloud_clb_instance.ingress-lb.id
    "kubernetes.io/ingress.extensiveParameters" = "{\\"AddressIPVersion\\": \\"IPV4\\"}"
    "kubernetes.io/ingress.http-rules" = "[{\\"path\\":\\"/\\",\\"backend\\":{\\"serviceName\\":\\"nginx\\",\\"servicePort\\":\\"80\\"}}]"
    "kubernetes.io/ingress.https-rules" = "null"
    "kubernetes.io/ingress.qcloud-loadbalance-id" = tencentcloud_clb_instance.ingress-lb.id
    "kubernetes.io/ingress.rule-mix" = "false"
    }
    # selfLink = "/apis/networking.k8s.io/v1/namespaces/nginx/ingresses/test-ingress"
    }
    spec {
    rule {
    http {
    path {
    backend {
    service {
    name = kubernetes_service.test.metadata.0.name
    port {
    number = 80
    }
    }
    }
    path = "/"
    }
    }
    }
    }
    }
    
    如果想要将这些资源复制一份到其他地域(以新加坡为例) ,那么可以执行以下步骤:
    1. 复制该目录下的所有 .tf 文件到新的目录下,如 eks-app-singapore,断开原目录的 tfstate 引用:
    $ mkdir ../eks-app-singapore
    $ cp *.tf ../eks-app-singapore
    $ cd ../eks-app-singapore
    2. 修改 TencentCloud Provider 的地域。代码如下:
    provider "tencentcloud" {
    # - replace
    # region = "ap-guangzhou"
    # + to
    region = "ap-singapore"
    }
    3. 在新目录 eks-app-singapore 下执行 terraform initterraform plan。由于没有 tfstate 文件, plan 提示即将创建新的资源:
    Plan: 11 to add, 0 to change, 0 to destroy.
    ───────────────────────────────────────
    Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
    4. 确认无误后,执行 terraform apply 即可配置新目录下的云资源在新地域管理。
    

    局限性

    不同产品的业务形态和逻辑差异较大,导致跨地域复制也是一个比较繁琐的操作。主要限制如下:
    

    实例规格和库存限制

    如云服务器、云硬盘、云数据库等实例的资源,各个可用区的实例规格和库存差异较大,很可能出现当前实例规格在其他区域售罄或者不可用的情况,建议使用动态的实例类型,即查询各个资源的 datasource 查询可用的实例规格而非硬编码在文件中,例如:
    在上海四区购买 2 核 2G 的 CVM 实例。代码如下:
    resource "tencentcloud_instance" "cvm" {
    name = "my-instance"
    availability_zone = "ap-shanghai-4"
    image_id = "local.cvm_img_id"
    instance_type = "S5.MEDIUM2"
    }
    切换到广州地域,替换成 datasource 动态获取。代码如下:
    provider "tencentcloud" {
    region = "ap-guangzhou"
    }
    
    # 查询广州地域 CVM 有哪些可用区
    data "tencentcloud_availability_zones_by_product" "cvm" {
    product = "cvm"
    }
    
    # 查询以 Tencent 开头的 CVM 镜像
    data "tencentcloud_images" "img" {
    image_name_regex = "Tencent"
    }
    
    # 查询指定可用区下 2 核 2G 有哪些实例类型
    data "tencentcloud_instance_types" "types" {
    availability_zone = data.tencentcloud_availability_zones_by_product.cvm.zones.0.name
    cpu_core_count = 2
    memory_size = 2
    }
    
    locals {
    # 挑选第可用区列表的第一个结果
    cvm_zone = data.tencentcloud_availability_zones_by_product.cvm.zones.0.name
    # 挑选镜像列表的第一个结果
    cvm_img_id = data.tencentcloud_images.img.images.0.image_id
    # 挑选实例类型的第一个结果
    cvm_type = data.tencentcloud_instance_types.types.instance_types.0.instance_type
    }
    
    resource "tencentcloud_instance" "cvm" {
    name = "my-instance"
    availability_zone = local.cvm_zone
    image_id = local.cvm_img_id
    instance_type = local.cvm_type
    }
    

    资源数量限制

    有些资源在各个地域有数量限制,如 TKE 集群、私有网络、对象存储桶(总量)等,复制之前请确认好目标区域有充足的存量配额,如有配额提升需求,可以通过 提交工单 申请。
    

    不需要复制的资源

    有些资源本身没有地域属性,例如 CAM 用户/角色和策略、SSL 证书、SSH 密钥等。这些资源在进行整体复制操作时需要过滤掉,避免重复创建。
    
    联系我们

    联系我们,为您的业务提供专属服务。

    技术支持

    如果你想寻求进一步的帮助,通过工单与我们进行联络。我们提供7x24的工单服务。

    7x24 电话支持