录屏
介绍
terraform 是什么
Terraform是由HashiCorp公司在2014年左右推出的开源资源编排工具, 目前几乎所有的主流云服务商都支持Terraform,包括阿里云、腾讯云、华为云、AWS、Azure、百度云、火山引擎等等。遵循 IaC (基础设施即代码)的理念,编写 Terraform 特定语法的 DSL 配置文件即可轻松部署上百家云厂商的服务。
HashiCorp 目前总共有八款主打产品,囊括了从开发 (Packer, Vagrant)、部署 (Terraform)、安全 (Boundary, Vault)、网络 (Consul)、应用 (Nomad, Waypoint)等各个环节。
解决什么问题
将云资源、服务或者操作步骤以代码的形式定义在模板中,借助编排引擎,实现资源的自动化管理,这就是基础设施即代码(Infrastructure as Code,简称IaC),也是资源编排最高效的实现模式。然而,多种云编排服务带来的是高昂的学习成本、低效的代码复用率和复杂的多云协同工作流程。每一种服务仅限于管理自家的单一云平台上,无法满足对多个云平台,多种层级(如IaaS,PaaS)资源的统一管理,比如AWS CloudFormation。如何解决如上问题,是否可以使用统一的编排工具,共用一套语法实现对多云的统一管理呢?所以这个时候就诞生Terraform,来解决这些问题。
同类型的工具有什么
-
Salt-Stack:自动化编排工具(没有找到火山的module)
-
Ansible:自动化编排工具(没有找到火山的module)
-
AWS CloudFormation: aws 的云资源编排工具,只能在aws 上使用,其他云上无法使用
如何工作
Terraform通过插件调用 target api 在云平台和其他服务上创建和管理资源。
-
编写配置:编写 Terraform 的配置文件
-
生成计划:根据配置生成执行计划,进行review
-
执行:review 通过后执行该计划
核心概念
架构
Terraform本身是基于插件的架构,可扩展性很强,可以方便对Terraform进行扩展。Terraform从逻辑上可以分为两层,核心层(Terraform Core)和插件层(Terraform Provider)。
核心层
核心层其实就是terraform的命令行工具,它是用go语言开发的,它负责:
-
读取.tf配置,进行变量替换
-
资源状态文件管理
-
分析资源关系,绘制图谱
-
对于依赖关系图谱的资源,根据依赖关系创建资源;对于没有依赖关系的资源,会并行进行创建(缺省10个并行进程),这也是Terraform能够高效快速管理云资源的原因。
-
用RPC调用插件层
插件层
插件层也是由go语言开发的,Terraform有超过250个不同的插件,它们负责:
-
接受核心层的RPC调用
-
具体提供某一项服务的执行
插件层又有两种:
Provider
Provider,负责与外界API的集成,比如阿里云Provider就提供了在阿里云创建、修改、删除云资源的功能。这个插件负责和云API的接口交互,并提供一层抽象,这样我们可以在不了解API细节的情况下,通过terraform来编排资源。
常见provider:
ruby
火山引擎:https://github.com/volcengine/terraform-provider-volcengine
阿里云: https://github.com/aliyun/terraform-provider-alicloud
百度云:https://github.com/baidubce/terraform-provider-baiducloud
腾讯云:https://github.com/tencentcloudstack/terraform-provider-tencentcloud
华为云:https://github.com/huaweicloud/terraform-provider-huaweicloud
ucloud:https://github.com/ucloud/terraform-provider-ucloud
qingcloud:https://github.com/yunify/terraform-provider-qingcloud
AWS:https://github.com/hashicorp/terraform-provider-aws
Azure:https://github.com/terraform-providers/terraform-provider-azurerm
GoogleCloud:https://github.com/hashicorp/terraform-provider-google
Provisioner
Provisioner,负责在资源创建或者删除完成后,执行一些脚本。比如Puppet Provisioner就可以在云虚拟机资源创建完成后,在该资源上下载、安装、配置Puppet agent。也可以理解为一种hook。
基本用法
安装
terraform 下载路径: developer.hashicorp.com/terraform/d...
在官网下载并解压,然后放到 /usr/local/bin 下即可。
执行 terraform 查看是否安装成功
基本命令介绍
Go
主要命令:
init 初始化工作空间,为其他命令做准备,在这一步也会下载 provider 插件
validate 验证配置文件是否合法
plan 生成计划进行review
apply 执行计划
destroy 删除之前创建的基础设施
其他命令:
fmt 格式化配置文件
get 安装 terraform 模块
graph 生成Graphviz依赖图,默认为 dot 语言,可以用在线网站查看,也可以安装工具
providers 打印配置文件中的 providers
show 打印当前配置下的状态
Global options (use these before the subcommand, if any):
-chdir=DIR 改变工作目录
快速开始
terraform 相关 demo 参考 : code.byted.org/gaoweizong/...
demo1: 安装 nginx
-
环境准备,确保自己本地环境已经安装了 terraform 和 docker ,启动 docker open -a Docker
-
创建并进入 learn-terraform-docker-container 目录
Go
mkdir learn-terraform-docker-container
cd learn-terraform-docker-container
- 编写 terraform 配置文件 main.tf
PlainText
terraform { # terraform 配置
# 详细用法参考官网
required_providers { # 定义需要的 provider , init 时默认从Terraform Registry 仓库下载安装
docker = {
source = "kreuzwerker/docker" # 完整的是 registry.terraform.io/kreuzwerker/docker
version = "~> 2.13.0"
}
}
}
provider "docker" {} # 指定使用 docker provider 创建和管理资源,可以使用多个 provider 配合使用。
# docker 插件相关文档 : docker docs
# resource 可以是物理机、虚拟机、逻辑资源
resource "docker_image" "nginx" { # 格式: resource resource_type resource_name
# resource_type 的参数
name = "nginx:latest"
keep_locally = false
}
resource "docker_container" "nginx" {
image = docker_image.nginx.latest
name = "tutorial"
ports {
internal = 80
external = 8000
}
}
- 初始化项目,这一步会下载 docker 插件,方便与docker 进行交互
Go
terraform init
- 生成计划,进行review
Go
terraform plan
known after apply: 执行后才能知道。
- 执行计划
Go
terraform apply
- 访问 nginx
- 修改资源
可以对配置文件进行修改,修改后重新执行 terraform apply ,修改端口: 8000 -> 8080
SQL
resource "docker_container" "nginx" {
image = docker_image.nginx.latest
name = "tutorial" hostname = "learn-terraform-docker"
ports { internal = 80
- external = 8000
+ external = 8080 } # 实际配置只需要把端口改下即可
}
- 变量注入
配置文件中可以使用变量,执行时动态注入,适配性更强
- 定义变量
Go
variable "container_name" { # 可以放在 main.tf 或者一个单独的配置文件中 variables.tf
description = "Value of the name for the Docker container"
type = string
default = "ExampleNginxContainer"
}
- 使用变量
PlainText
resource "docker_container" "nginx" {
image = docker_image.nginx.latest
- name = "tutorial"
+ name = var.container_name # 实际配置文件直接替换成这一行
ports {
internal = 80
external = 8080 }
}
- 创建资源
Go
terraform apply -var "container_name=tutorial_dynamic"
# 也可以通过 -var-file=filename 指定变量,把变量写在单独的文件中,默认的 var-file 是 terraform.tfvars
- 数据查询,通过 output 可以更方便的查询和展示数据给用户
定义 output
Go
# outputs.tf
output "container_id" {
description = "ID of the Docker container"
value = docker_container.nginx.id
}
output "image_id" {
description = "ID of the Docker image"
value = docker_image.nginx.id
}
- 销毁资源
Go
terraform destroy
demo2: 创建火山资源
通过terraform 创建火山资源
参考: code.byted.org/gaoweizong/...
terraform DSL代码的核心概念
在上面的几个例子中,用到了一些资源类型,这里介绍一些核心的类型:
-
data 数据源允许查询或计算一些数据以供其他地方使用。使用数据源可以使得Terraform代码使用在Terraform管理范围之外的一些信息,或者是读取其他Terraform代码保存的状态。
-
resource 资源用于定义基础设施资源对象,一个resource可以定义一个或多个基础设施资源对象,例如VPC、虚拟机,或是DNS记录、Consul的键值对数据等。
-
module 简单来讲模块就是包含一组Terraform代码的文件夹,Terraform模块是编写高质量Terraform代码,提升代码复用性的重要手段,可以说,一个成熟的生产环境应该是由数个可信成熟的模块组装而成的。样例参考: github.com/hashicorp/t...
代码开发
插件开发
参考文档:
插件工作原理
插件开发的sdk
官方提供了两种SDK
-
SDKv2 : 是大多数现有提供商的基础,并继续提供稳定的开发体验。
-
Framework : 是一个新的SDK,围绕Terraform的更新架构重新调整了提供商开发体验和抽象。它代表了Terraform插件开发未来的愿景。
本地插件目录
如果不能从 terraform 仓库下载插件,可以从指定本地路径,路径可以通过在 cli 配置中指定
Go
provider_installation {
filesystem_mirror {
path = "/usr/share/terraform/providers"
include = ["example.com/*/*"]
}
direct {
exclude = ["example.com/*/*"]
}
}
如果没有配置,则本地插件默认目录为:
-
~/.terraform.d/plugins/ <math xmlns="http://www.w3.org/1998/Math/MathML"> h o s t _ n a m e / {host\_name}/ </math>host_name/{namespace}/ <math xmlns="http://www.w3.org/1998/Math/MathML"> t y p e / {type}/ </math>type/{version}/${target}
-
%APPDATA%\terraform.d\plugins\ <math xmlns="http://www.w3.org/1998/Math/MathML"> h o s t _ n a m e / {host\_name}/ </math>host_name/{namespace}/ <math xmlns="http://www.w3.org/1998/Math/MathML"> t y p e / {type}/ </math>type/{version}/${target}
本地插件工作原理解析
这里用 terraform 官方 demo 进行演示,假设环境中已经安装了 docker & docker_compose
-
本地启动HashiCups provider 后端服务(类似于 阿里云/火山云的后端接口)
-
创建 HashiCups user (上一步启动好服务,这一步做鉴权)
-
安装HashiCups provider 插件(类似于 实现插件,这里直接用官方实现好的插件)
-
编写 terraform 文件
-
执行
Shell
terraform init
terraform apply
...
如何开发一个插件
开发插件的原因可能有:
-
对于内部云,没有现成的 provider ,如火山私有云 ?
-
扩展已有 provider 的能力
插件开发demo 仓库: github.com/hashicorp/t...
-
环境准备,克隆脚手架代码
-
启动 hashicups 实例,模拟服务端(开发真实插件这一步不是必须的,通常由云厂商提供)
-
定义 data 资源,按照terraform惯例,文件名字需要以 data_source_ 开头,
在本 demo 中 返回的数据为:
Shell
curl localhost:19090/coffees
[
{
"id": 1,
"name": "Packer Spiced Latte",
"teaser": "Packed with goodness to spice up your images",
"description": "",
"price": 350,
"image": "/packer.png",
"ingredients": [
{
"ingredient_id": 1
},
{
"ingredient_id": 2
},
{
"ingredient_id": 4
}
]
}, ## ...
]
则定义的 schema 为:
Go
// hashicups/data_source_coffee.go
Schema: map[string]*schema.Schema{
"coffees": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"teaser": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"price": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
"image": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"ingredients": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"ingredient_id": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
},
},
},
},
},
},
},
-
实现查询方法
-
在 provider 增加刚刚定义的 datasource
-
测试 provider
-
给 provider 增加身份认证
-
实现CRUD,以订单为例
优势与不足
优势
-
基础架构即代码:基础架构是使用高级配置语法描述的。这允许对数据中心的蓝图进行版本化和处理,就像对任何其他代码一样。此外,基础架构可以共享和重用。
-
执行计划:Terraform有一个"计划"步骤,它会生成执行计划。执行计划显示了Terraform在我们调用应用时会做什么。这可以让我们在Terraform操作基础架构时避免任何意外。
-
资源图:Terraform构建所有资源的图,并并行化任何非依赖资源的创建和修改。正因为如此,Terraform尽可能高效地构建基础架构,操作员可以深入了解其基础架构中的依赖关系。
-
变更自动化:复杂的变更集可以应用到我们的基础架构中,只需最少的人工交互。通过前面提到的执行计划和资源图,我们可以准确地知道Terraform将更改什么以及以什么顺序更改,避免了许多可能的人为错误。
-
对多云进行管理。
不足
- Terraform被设计成声明式而非命令式,例如没有常见的if条件语句,后来才加上了count和for_each实现的循环语句(但循环的次数必须是在plan阶段就能够确认的,无法根据其他resource的输出动态决定)