暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

GrowingIO Terraform 实践

GrowingIO技术团队 2021-12-09
824

↑ 点击关注 ↑ 更多技术干货


背景


为满足 GrowingIO 客户多样性的需求,在公有云设施上使用 Terraform 作资源管理。采取 Terrform 具有以下相关优势:

  • 多云支持,主流云厂商均提供对应的 Provider 支持。

  • 自动化管理基础结构,可重复对资源进行编排使用。

  • 基础架构即代码(Infrastructure as Code),允许保存基础设施状态,便于追踪管理。

  • 统一的语法来管理不同的云服务,实现标准化管理。


Terraform 介绍


概念

Terraform 是一个开源 IAS 工具,提供一致的 CLI 工作流,可管理数百个云服务。Terraform 通过将云厂商提供的 API 编写为声明式配置文件,通过 Terraform 的命令行接口,可将资源调度配置应用到任意支持的云上,并实现版本控制。

Terraform 通过不同的 Provider 来支持不同云平台。国外云服务商如 Azure、AWS、GoogleCloud、DigtalOcean,国内云服务商如 Aliyun、TencentCloud、Ucloud、BaiduCloud 均有提供官方的 Provider。

架构



Terraform 通过解析用户书写的 HCL(HashiCorp Configuration Language) 格式的 DSL 文件,然后通过 Terraform core 与各云厂商提供的 Providers 进行交互,从而进行相关资源的调度。各云厂商依 HCL 代码风格,将自家资源调用 API 重新封装,以生成对应的 Providers。


项目实践


项目设计

  • 客户项目存在多个同构环境,环境交付需要一致。

  • 每个环境中存在中多个项目,各项目对资源调度需求各异。

  • 每个项目需要使用 EC2、ELB、EBS、 EMR 等多种资源。

项目实现

项目结构

# tree -L 3 .
├── README.MD
├── module
│ ├── app1
│ │ ├── config.tf
│ │ ├── locals.tf
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── variables.tf
│ ├── global
│ │ └── config.tf
└── dev
├── main.tf
└── output.tf

目录结构拆解:

  • module 中依项目名进行封装。其中 app1 为项目名。

  • global 为全局参数设置。

  • dev 为各环境对 module 的引用封装。

module 细节:

  • config.tf 为配置的相关参数,如 Providers 的相关参数设定。

  • locals.tf 为变量的计算生成与其它变量引用,如 module 的引用与一些复杂变量的重生成。

  • main.tf 为 resource 与 data 相关的资源编排调用。

  • outputs.tf 为项目输出值,如创建 ecs 之后主机的 ip 等。

  • variables.tf 为传参的相关设置,如需创建 ecs 的数量。

module 封装

module 的资源引用

在对资源调度的实施过程中,往往需要多次重复作业,故需将多个原子操作,统一封装成 module,后续在外部引用 module 并传入对应参数即可。

apply 构建文件代码:

# dev/main.tf
...
module "app1" {
source = "../module/app1"
aws_ec2_create_number = 3
...
}

module 的条件判断

在 Terraform 中,往往将循环与判断结合使用,主要使用场景有两种:

  • 确认变量形式

  • 确认资源是否创建

如创建 ECS 时的主机名设置,当创建多台主机时,主机名需数字后缀以区分,而只有一台主机时,不需要数字后缀。

相关实例代码如下:

# module/app1/main.tf
...
resource "aws_instance" "aws-ec2-create" {
count = var.aws_ec2_create_number
...
tags = {
Name = var.aws_ec2_create_number < 2 ? "${var.env}-${var.aws_ec2_name}" : "${var.env}-${var.aws_ec2_name}-${count.index + 1}"
...
}
...

如在 ECS 的创建之中,无法判断用户是否需要数据盘。

相关实例代码如下:

# module/app1/main.tf
...
resource "aws_instance" "aws-ec2-create" {
count = var.aws_ec2_create_number
...
dynamic "ebs_block_device" {
for_each = var.aws_ebs_block_device_volume_size != 0 ? [1] : []
content {
delete_on_termination = true
device_name = var.aws_ebs_block_device_name
volume_size = var.aws_ebs_block_device_volume_size
volume_type = var.aws_ebs_block_device_volume_type
}
}
...
}
...

当用户传入

var.aws_ebs_block_device_volume_size 的值为0时,即循环一个空列表,即不创建该资源,亦即不创建数据盘。

module 的复杂循环

在 Terraform 中,循环主要依赖于 count 与 for_each,这两种方法均只支持简单的循环,而 for 循环更多的是参与计算,并不会直接在 resource 中直接进行使用。

如在 app1 项目中,需要创建 5 台实例,同时实例需分布在不同的 subnet 之中,但 subnet 只有3个。在该情况下,我们无法简单的以 subnet 的 id 作循环,更为重要的是,如果后期 subnet 的数量也可能会变化,所以无法固定循环列表。

对于复杂的循环需求,一般将其置于 locals 中作相关计算,其后在 resource 中进行引用 。

在 locals 中的计算,相关代码如下:

# module/app1/locals.tf
...


//case for the rc2 number is more than the number of zone for subnet
locals {
times = ceil(var.aws_ec2_create_number / length(var.aws_subnet_id_list))
}


locals {
// loop two list to generate a new list
subnet_list_combine = flatten([
for p in range(local.times) : [
for q in var.aws_subnet_id_list : [join(",", [q])]
]
]
)
}


...

通过在 locals 中的计算,我们可以得到一个名为 subnet_list_combine 的 list,其后在 resource 中进行引用即可。

resource 相关代码如下:

# module/app1/main.tf
...
resource "aws_network_interface" "aws-network-interface" {
count = var.aws_ec2_create_number
subnet_id = local.subnet_list_combine[count.index]
...

全局变量

在 Terraform 中,官方为了层级的简洁,默认不推荐使用全局变量,因为全局变量的设置,会出现所见非所得的现象。

但在实际生产中,却有相关需求,如 aws_profile_name 在每个项目中均一致,同时后期因为用户的 profile 设置不一致而需要统一变更。

我们可以将此类参数写入一个 module 之中。

# module/global/config.tf
...
output "aws_profile_name" {
value = "default"
}
...

在各项目中的 module,再次对 global 的 module 作引用。

# module/application/locals.tf
...
// In order to make global variables --beginning
module "global" {
source = "../global"
}


locals {
...
aws_profile_name = module.global.aws_profile_name
}
// In order to make global variables --end
...

最后在项目中,作对应自身 module 的 locals 值作相关的引用。

# module/application/config.tf
...
Provider "aws" {
profile = var.aws_profile_name == "" ? local.aws_profile_name : var.aws_profile_name
...
}
...

环境隔离

在 Terraform 中,隔离一般有两种:

  • workspace 隔离

  • 目录隔离

在 workspace 隔离中,需要使用 terraform workspace 子命令,与 git branch 类似,但是 terraform workspace 中的隔离,并不直观,在生产中容易出现误操作,所以对于不同环境的 module 调用,本项目中采用了目录隔离。

# dev/main.tf
...
module "app1" {
source = "../module/app1"
aws_ec2_create_number = 3
...
}


# stage/main.tf
...
module "app1" {
source = "../module/app1"
aws_ec2_create_number = 3
...
}


项目心得

Terraform 在基础资源编排中,使用方便,语法简洁,但由于各云厂商提供 Provider 的风格并不完全统一,一定程度上增加了多云混合使用的成本。特别是对于国内非 AWS 用户而言,国内部分云厂商提供的 Provider,支持的资源种类相较于 AWS 偏少,部分场景可能无法实现。

同时也因为 Terraform 的语法对于一些高级特性的支持欠缺,导致在部分复杂的场景中,有些捉襟见肘,而更多需要 Provider 去提供对应功能。虽然有 module 的设计,可以进行代码复用,但也有因部分参数无法动态区分,而不得不创建多个 module 以区分,这点在国内云厂商提供的 Provider 中尤为明显。


小结


本文简单介绍了 Terraform 的基本概念以及采用 Terraform 的原由。

同时例举了在生产实践中 Terraform 的目录结构编排与环境隔离,详细说明如何通过传参来动态调整资源编排的实际传参与调度,如何通过多次组合计算以动态生成新的参数来规避 Terraform 对高级特性支持的欠缺,以及如何构建全局变量以解决全局动态传参。基于篇幅限制, Terraform 的使用无法逐一说明,对 Terraform 有兴趣的同学可自行学习了解。


参考


  1. Mikael Krief. Terraform Cookbook:

    Efficiently define, launch, and manage Infrastructure as Code across various cloud platforms. Packt Publishing 2020

  2. Scott Winkler. Terraform in Action. Manning 2021

  3. Yevgeniy Brikman. Terraform: Up & Running: Writing Infrastructure as Code. O'Reilly Media 2019

  4. https://www.terraform.io/docs/language/index.html

  5. https://github.com/hashicorp/terraform/issues/5480




关于 GrowingIO

作为国内领先的一站式数据增长引擎整体方案服务商,GrowingIO以数据智能分析为核心,通过构建客户数据平台,打造增长营销闭环,帮助企业提升数据驱动能力,赋能商业决策、实现业务增长

GrowingIO专注于零售、电商、保险、酒旅航司、教育、内容社区等行业,成立以来,累计服务超过1500家企业级客户,获得LVMH集团、百事、达能、老佛爷百货、戴尔、lululemon、美素佳儿、宜家、乐高、美的、海尔、安踏、汉光百货、上汽集团、广汽蔚来、理想汽车、招商仁和人寿、飞鹤、红星美凯龙、东方航空、滴滴、新东方、喜茶、每日优鲜、奈雪的茶、永辉超市等客户的青睐。

招聘信息

GrowingIO技术团队是一个活力四射、对技术充满激情的团队,多个岗位持续招聘中!诚招前端工程师/大数据工程师/Java工程师等,欢迎有兴趣的同学投递简历至:jianli@growingio.com(邮件标题请注明具体岗位名称),更多职位及信息可进入招聘官网查看。


点击「阅读原文」获取 GrowingIO 15 天免费试用!

↓↓↓

文章转载自GrowingIO技术团队,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论