Terraform最佳实践

2,870 阅读6分钟
Terraform 又是什么新工具

帮你创建删除资源。它使用起来非常简单,我们希望用户能把精力全都集中开发工作上,而不是考虑管理资源这种琐事上。

我尽量让这篇文章通俗易懂,无论你是从事什么工作,只要你的工作内容跟云资源管理有一点点关系,都能从Terraform中受益

最快的上手的方法永远是先用一下试试,按照下面的方法去做:


Step - 1  准备一些原料

  • Terraform 
    • 在官网下载一个不到15M大小的文件,下载完成以后解压,并添加对应PATH,完成以后在命令行里输入 terraform 有反应则代表安装成功
  • AccessKey / SecretKey
    • 准备 AccessKey SecretKey ,它们可以在京东云控制台的账户栏下找到 。用于鉴别你的身份,创建你账户下的资源。


Step - 2  配置 Terraform

我们会用一个文件用于描述我们需要的资源, 在当前目录下创建一个名叫 jdcloud.tf 的文件,并在这个文件里写入下面的内容:

provier "jdcloud" {
    access_key = "your_access_key"
    secret_key = "your_secret_key"
    region = "cn-north-1"
}

resource "jdcloud_vpc" "local-name" {
  vpc_name = "my-vpc"
  cidr_block = "172.16.0.0/20"
  description = "terraform rules"
}
  • 第一项 provider-jdcloud
    • 代表服务提供商是京东云,写下你的 AccessKey SecretKey 以及区域信息,它们用于鉴别你的身份,并在对应区域内创建你的资源
  • 第二项 resource-jdcloud-vpc
    • 代表我们想创建一个京东云下的VPC资源,写下这个VPC的名称以及CIDR,描述是选填项可以不写,就像在网页上创建资源一样

这份配置文件包含了身份信息以及资源信息,已经是一份完整可用的配置文件了。


Step - 3 初始化 Terraform

在配置文件中我们声明了一个新服务商 - 京东云,为了能使Terraform接入京东云资源管理,Terraform需要去下载京东云插件,这一切都是 Terraform 自动完成的,我们只需要一个命令

$ terraform init

> Initializing provider plugins...

  Downloading plugin for provider "jdcloud" (0.0.1)...

  * provider.jdcloud: version = "~> 0.0"

  Terraform has been successfully initialized!


Step - 4 创建资源

通过一个指令,Terraform会为先为你生成一个行动计划,在本示例中则是创建出一个VPC, 等待你确认这次行动,输入“yes”,才会才开始创建

$ terraform apply

> Terraform will perform the following actions:

  + jdcloud_vpc.jd-vpc-1
      id:          <computed>
      cidr_block:  "172.16.0.0/20"
      description: "terraform rules"
      vpc_name:    "my-vpc"

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?

  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

--------------------------------------------------
  Enter a value: yes
--------------------------------------------------

jdcloud_vpc.jd-vpc-1: Creating...
  cidr_block:  "" => "172.16.0.0/20"
  description: "" => "vpc1"
  vpc_name:    "" => "my-vpc-1"

---------------------------------------------------
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
---------------------------------------------------

创建成功了,我们可以登陆控制台查看这项资源是否被成功创建,果然是在这儿的



Step - 5 修改资源

简单来说,用户需要去做的,只是在 jdcloud.tf 里修改相应资源, 而后通过 terraform apply 即可完成修改,使用上当前的配置。

有的配置例如名称,描述等是可供修改的。在terraform apply的时候会提示一项资源即将修改: 

$ terraform apply

> jdcloud_vpc.jd-vpc-1: Refreshing state... (ID: vpc-qdpipn8mn2)

  ~ jdcloud_vpc.jd-vpc-1
      vpc_name: "my-vpc" => "modified-name"

-------------------------------------------------------
Plan: 0 to add, 1 to change, 0 to destroy
-------------------------------------------------------

jdcloud_vpc.jd-vpc-1: Modifying... (ID: vpc-qdpipn8mn2)
  vpc_name: "my-vpc" => "modified-name"

--------------------------------------------------------
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
--------------------------------------------------------


但是有些配置则是不能修改的,比如VPC的CIDR属性在创建之后就不能修改,如果尝试修改CIDR属性,Terraform会为你销毁当前资源并重建一个。  在下面的日志中 Terraform 会用-/+的方式提示你,terraform会先销毁当前资源并重建,同时提示你 new resource required

$ terraform apply

> jdcloud_vpc.jd-vpc-1: Refreshing state... (ID: vpc-qdpipn8mn2)

-------------------------------------------------------
    -/+ jdcloud_vpc.jd-vpc-1 (new resource required)
-------------------------------------------------------
          id:          "vpc-qdpipn8mn2" => <computed> (forces new resource)
          cidr_block:  "172.16.0.0/20" => "172.16.0.1/20" (forces new resource)
          description: "vpc1" => "vpc1"
          vpc_name:    "modified-name" => "modified-name"

---------------------------------------------------------
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
---------------------------------------------------------


为什么一定要用Terraform ?

在刚刚的例子中我们只是创建一个简单的资源,terraform看似平平无奇。只是做了一些所有SDK都能做的事。但是在实际生产中我们的结构往往是比较复杂的环境,VPC / 子网 / 云主机 / 路由等等穿插交织在一起,相互依赖。

这种情况下,就算只是想调整一个VPC的网段都是一件非常复杂的事。首先我们需要理清有哪些资源建在这个VPC上,除去重建的步骤,理清脉络就是一件非常头痛的事。

Terraform 真正的威力在于【生成执行计划】的步骤上 。 当前的云端架构会被存在本地的 terraform.tfstate的文件里。 当配置文件被修改的时候,它会立刻计算出当前的架构,与你期望达到的状态之间的差异。并告诉你有哪些资源只是修改一下属性即可,哪些则需要删除重建。 等待你确认之后才开始执行。

于是用户就避免了繁琐的”理清脉络的过程“,可以将工作重心放在开发工作上,而不是理清繁琐的依赖关系。 通过 terraform apply 确认一下,等待执行完成即可。


Terraform是怎么识别依赖关系的? 

事实上,除了直接写,你还可以通过"引用"的方式表明这个属性的值,在一些场景下巧妙的引用会为你省下很多时间,我们以构建一台云主机为例,为了节省空间我们省略了一些细节

resource "jdcloud_subnet" "my-subnet" {
    ... 
}
resource "jdcloud_network_security_group" "my-sg" {
    ... 
}

resource "jdcloud_instance" "my-vm" {

  ...
  image_id = "${var.ubuntu_image}"
  password = "${var.vm_password}"
  subnet_id = "${jdcloud_subnet.my-subnet.id}"
  security_group_ids = ["${jdcloud_network_security_group.my-sg.id}"]
  ...
}
  • 我们先是创建了一个名为"my-subnet"的子网资源  +  "my-sg"的安全组资源
  • ${ jdcloud_subnet.my-subnet.id }  -  通过引用的方式说明这台云主机将会构建在名为my-subnet的子网上。 相似的,这台云主机还会使用名为 my-sg 的安全组

于是 terraform 便得到了一组依赖关系 :  云主机 my-vm 依赖 my-sg & my-subnet ,下次如果my-subnet 面临删除重建,terraform会提示你它会先重建构建在它之上的 my-vm

以此类推,子网:  my-subnet 可能也构建在另一个VPC资源上,相互依赖的网络就这样被构建出来,如果你尝试删除VPC, terraform 会计算出所有同时也需要删除的资源


成为开发者是一件很难的事情吗

你们Terraform支持XX资源吗?

这个问题的答案是: 只要有SDK就是支持的,想要在 terraform 上管理一项新品类的资源,你只需要准备好SDK即可,接下来我们以 VPC 为例介绍如何引入一项新资源

1. 定义资源的属性

Schema: map[string]*schema.Schema{

     "vpc_name": {                                                                                                                                                 
        Type:     schema.TypeString,
        Required: true,
     },

     "cidr_block": {
        Type:     schema.TypeString,
        Required: true,
        ForceNew: true,
     },

     "description": {
        Type:     schema.TypeString,
        Optional: true,
     },
},

  • 定义VPC资源的三种属性: vpc_name / cidr_block / description 为 string类型
  • 设定 vpc_name + cidr_block 为必填项: Required = true
  • 设定 description 为选填项 : Optional = true
  • 设定 cidr_block 为不可修改字段,修改则导致删除重建: ForceNew = true


2. 定义资源的增删改查函数

func resourceVpcCreate(d *schema.ResourceData, m interface{}) error {

    config := m.(*JDCloudConfig)
    vpcName := d.Get("vpc_name").(string)
    req := apis.NewCreateVpcRequest(config.Region, vpcName)
    conn := client.NewVpcClient(config.Credential)

    if _, ok := d.GetOk("cidr_block"); ok {
        req.AddressPrefix = GetStringAddr(d, "cidr_block")
    }

    if _, ok := d.GetOk("description"); ok {
        req.Description = GetStringAddr(d, "description")
    }

    resp,err := conn.CreateVpc(req)    
    if err != nil {
        return e
    }

    return resourceVpcRead(d, m)
}

以上是VPC的创建函数,每当terraform认为需要创建VPC需求的时候,就会调用函数

  • 函数参数
    •  d 可以理解为用户填写的jdcloud.tf 里面包含了我们需要的配置
    • m 则是用户身份信息等,用于创建客户端
  • 第一步:  我们需要从用户填写的 jdcloud.tf 中读回创建 VPC 所需要的 vpc_name + cidr_block + description 信息
  • 第二步:  生成一个创建VPC的请求,并使用客户端发送,如果返回的 error 不为空则代表创建失败,系统不会记录下这笔资源,否则为空,系统会在当前状态里记录下这个资源,创建完成
  • 相似的我们可以做到读,改,删函数,只要我们能拿出相对应的SDK来配合terraform对云资源的修改即可


推荐阅读

Terraform X 京东云插件 - 更多资源的示例

Terraform X 京东云插件 - Github , 关注我们,一起开发

Terraform X 京东云插件 - 使用手册

Terraform官网文档,更详细,介绍Terraform是怎么工作的