Saravan Gnanaguru最初发表在InfraCloud的博客上的客座文章
Terraform介绍和概述
Terraform是一种基础设施即代码的技术,它被用来创建不可改变的基础设施。它允许基础设施以一种简单的、人类可读的语言HCL(HashiCorp Configuration Language)来表达代码。它支持管理所有主要云供应商的资源。Terraform用于创建、管理和更新基础设施资源,如物理机、虚拟机、网络交换机、容器、Kubernetes等。几乎任何基础设施类型都可以在Terraform中表现为资源。
- 使用Terraform的声明式
.tf
配置文件,将基础设施作为代码交付。 - 计划和预测变化。Terraform为操作者提供了优雅的用户体验,使其能够安全、可预测地对基础设施进行修改。它读取配置文件,并提供更改的执行计划,可以审查其安全性,然后应用和配置。
- 可扩展的
供应商
使Terraform能够管理广泛的资源,包括IaaS、PaaS、SaaS和硬件服务。 - 创建可重复的基础设施。Terraform使类似基础设施的配置易于重复使用,有助于避免错误并节省时间。
这篇文章是为那些对Terraform及其使用有基本了解,并可能愿意开发自定义Terraform提供者的用户准备的。让我们开始吧!
什么是Terraform提供程序?
使用Terraform创建和维护资源依赖于被称为提供者的插件。每个提供者插件负责与云供应商、SaaS供应商和其他API进行交互。大多数供应商配置特定的基础设施平台(无论是云还是自我托管的)。提供商还可以提供本地的实用程序,以完成诸如为独特的资源名称生成随机数字的任务。
每个供应商都会增加一组资源类型和/或Terraform可以管理的数据源。每种资源类型都由提供者实现;没有提供者,Terraform就不能管理任何类型的基础设施。Terraform提供者不仅可以为云计算基础设施提供扩展性,而且还可以管理通过暴露的API调用创建的对象。
Terraform自定义提供者
以下是编写自定义Terraform提供者的一些可能的情况,例如。
- 内部私有云,其功能是专有的或对开源社区无益。
- 一个 "正在进行中 "的提供者,在向注册中心贡献之前在本地进行测试。
- 现有提供商的扩展。
Terraform和提供商插件如何工作?
根据Terraform文档。
Terraform核心
Terraform核心是一个用Go编程语言编写的静态编译的二进制文件。编译后的二进制文件是命令行工具(CLI)terraform
,这是任何人使用Terraform的入口。
Terraform核心的主要职责是。
- 基础设施即代码:读取和插值配置文件和模块
- 资源状态管理
- 资源图谱的构建
- 计划的执行
- 通过RPC与插件进行通信
Terraform插件
Terraform插件是用Go语言编写的,是由Terraform核心通过RPC调用的可执行二进制文件。
源于此。Terraform文档
每个插件都为特定的服务提供一个实现,例如。AWS,或供应者,如bash。Terraform配置中使用的所有供应商和供应者都被称为插件。Terraform核心提供了一个高层次的框架,抽象出了插件发现和RPC通信的细节,所以开发者不需要管理这些细节。
提供者插件的主要职责是。
- 初始化任何用于进行API调用的内置库。
- 与基础设施提供者进行认证。
- 定义映射到特定服务的资源
供给者插件的主要职责是:。
- 在创建后或销毁时对指定的资源执行命令或脚本。
请注意,我们的文章重点是如何开发供应者插件
Terraform CLI文件和供应者插件的安装
Terraform 0.13+使用.terraformrc
CLI配置文件来处理提供者的安装行为。因此,我们需要在$HOME/.terraformrc
路径下创建配置文件,并添加以下内容。
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"
disable_checkpoint = true
有两种方法可以进行提供者的安装(从Terraform 0.13+开始)。
显式安装方法
CLI配置中的提供者_installation
块允许覆盖Terraform的默认安装行为,因此你可以强制Terraform为你打算使用的部分或全部提供者使用本地镜像。在显式安装方法中,我们需要有一个提供者_installation
块。
隐式本地镜像方法
如果CLI配置文件中没有provider_installation
块,那么Terraform会产生一个隐式配置。
我们将使用隐式本地镜像方法来安装我们的自定义提供者。
Terraform init
的默认行为,通常是试图从互联网上的Terraform注册表中下载提供者。由于我们模仿的是自定义提供者的情况,我们可以用隐式方法覆盖这一行为。使用隐式方法,Terraform将隐式地尝试在Linux系统的插件目录~/.terraform.d/plugins
和Windows系统的%APPDATA%\terraform.d\plugins
中找到提供者。
开发自定义提供程序需要什么条件?
- 只要有基本的Go开发知识就可以开始了。
- 服务提供者公开的API细节,用于管理资源。
如何安装和配置Terraform
请参考这里来安装Terraform
Windows。
- 下载并提取Terraform的可执行文件
- 在ENV PATH变量中添加Terraform可执行路径
在Linux系统中,提取并复制Terraform的可执行文件到/usr/bin路径中,以便在任何目录下执行它。
安装Go并设置开发环境
按照Go官方网站中提到的Go安装步骤,开始使用Go。
自定义提供者的源代码细节
进入$HOME/go/src
路径并创建代码。
cd $HOME/go/src
mkdir tf_custom_provider
自定义提供者所需的源文件是。
main.go
provider.go
resource_server.go
代码布局看起来像这样。
.
├── main.go
├── provider.go
├── resource_server.go
提供者的功能
我们将创建一个具有以下功能的提供者。由于这将是一个例子,我们将模拟Terraform资源的创建和删除功能。我们还将利用随机UUID生成器API,并将其作为创建功能的一部分加入,以显示调用API的能力。该API可以在以后进行修改,为云提供商、预置服务提供商或任何作为服务提供商
的API提供实际的资源创建API。
main.go
Go的入口点函数是main.go
。
// main.go
包main
输入 (
"github.com/hashicorp/terraform-plugin-sdk/plugin"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)
func main() {
plugin.Serve(&plugin.ServeOpts{)
ProviderFunc: func() terraform.ResourceProvider {
返回Provider()
},
})
}
provider.go
provider.go
将有资源服务器的函数调用。
// provider.go
包main
输入 (
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
func Provider() *schema.Provider {
返回 &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"example_server": resourceServer(),
},
}
}
resource_server.go
所有的资源创建都必须在resource_server.go
中进行编码。这个文件将有资源功能声明和定义,如创建、删除等,它也会获得创建资源所需的输入参数。
作为本示例提供者的一部分,资源服务器具有以下功能。
- 创建
- 删除
// resource_server.go
包main
输入 (
"net/http"
"日志"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
func resourceServer() *schema.Resource {
返回 &schema.Resource{
Create: resourceServerCreate,
读取: resourceServerRead,
更新: resourceServerUpdate,
删除:resourceServerDelete。
模式:map[string]*schema.Schema{
"uuid_count": &schema.Schema{
类型:schema.TypeString。
需要: true,
},
},
}
}
func resourceServerCreate(d *schema.ResourceData, m interface{}) error {
uuid_count := d.Get("uuid_count"). (string)
d.SetId(uuid_count)
// https://www.uuidtools.com/api/generate/v1/count/uuid_count
resp, err := http.Get("https://www.uuidtools.com/api/generate/v1/count/" + uuid_count)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
返回 resourceServerRead(d, m)
}
func resourceServerRead(d *schema.ResourceData, m interface{}) error {
返回 nil
}
func resourceServerUpdate(d *schema.ResourceData, m interface{}) error {
返回 resourceServerRead(d, m)
}
func resourceServerDelete(d *schema.ResourceData, m interface{}) error {
d.SetId("")
返回nil
}
我们的示例代码为名为 "exampleprovider "的提供者实现了模拟资源创建。在实际执行中,必须将其改为各自云或企业内部服务器的提供者名称。大多数供应商都有API调用,用于资源操作,如创建/更新/删除等。因此,我们需要定义资源操作的逻辑,如使用自定义提供者的API调用来创建和删除,以应用Terraform模板。
在resource_server.go
中加入资源操作的逻辑后,我们的自定义提供者就可以开始测试了。
构建自定义提供者的代码
go mod init
转到fmt
转到mod tidy
去构建 -o terraform-provider-example
将提供者的可执行文件复制到插件目录的步骤
为了复制和使用我们创建的自定义提供者,我们需要在plugins目录下创建以下目录结构。
- 基于Linux的系统 -
~/.terraform.d/plugins/${host_name}/${namespace}/${type}/${version}/${target}
- Windows系统
%APPDATA%\terraform.d\plugins\${host_name}/${namespace}/${type}/${version}/${target}
。
其中。
- host_name-> somehostname.com
- namespace-> 自定义提供者名称空间
- type-> 自定义提供者类型
- version-> 提供者的语义版本(例如:1.0.0)。
- target-> 目标操作系统
我们的定制提供程序应放在如下目录中。
~/.terraform.d/plugins/terraform-example.com/exampleprovider/example/1.0.0/linux_amd64/terraform-provider-example
因此,作为第一步,我们需要创建这个目录作为我们提供者安装的一部分。
mkdir -p ~/.terraform.d/plugins/terraform-example.com/exampleprovider/example/1.0.0/linux_amd64
然后,将terraform-provider-example
二进制文件复制到该位置。
cp terraform-provider-example ~/.terraform.d/plugins/terraform-example.com/exampleprovider/example/1.0.0/linux_amd64
创建Terraform.tf
文件
让我们通过提供资源输入,创建main.tf
来测试该提供者。为了演示的目的,我们添加了服务器数量(uuid_count
)作为一个输入参数。
main.tf
创建带有代码的main.tf
文件,以创建自定义提供者资源。
资源 "example_server" "my-server-name" {
uuid_count = "1"
}
version.tf
创建一个名为versions.tf
的文件,并添加自定义提供者名称和版本的路径。
Terraform {
所需供应商 {
示例 = {
版本 = "~> 1.0.0"
来源 = "terraform-example.com/exampleprovider/example"
}
}
}
测试提供者和输出值
执行以下Terraform命令,以验证我们添加的自定义提供者的功能。
Terraform初始化
当我们运行terraform init
命令时,Terraform核心会从本地路径获取提供者插件,因为我们已经在version.tf
文件中配置了提供者。在Terraform初始化过程中,定制的提供者已经被缓存到~/.terraform.d/plugin-cache
目录中,以便在下次运行时重新使用该提供者。
$ terraform init
初始化后端...
初始化提供者的插件...
- 寻找与"~> 1.0.0 "相匹配的terraform-example.com/exampleprovider/example版本...
- 使用共享缓存目录中的terraform-example.com/exampleprovider/example v1.0.0。
Terraform创建了一个锁文件.terraform.lock.hcl,以记录上面的提供者
的选择。在你的版本控制库中包括这个文件
这样,Terraform就可以保证在运行 "terraform init "时,默认做出同样的选择。
运行 "terraform init "时,可以保证做出同样的选择。
Terraform已经成功初始化了!
你现在可以开始使用Terraform。尝试运行 "terraform plan",查看
你的基础设施需要的任何变化。所有的Terraform命令
现在应该可以工作了。
如果你曾经为Terraform设置或改变模块或后台配置。
重新运行这个命令,重新初始化你的工作目录。如果你忘记了,其他
命令会检测到它,并在必要时提醒你这样做。
Terraform计划
Terraform plan
命令,使用main.tf
文件中定义的服务器定义。
$ terraform plan
Terraform使用选定的提供者,生成以下执行计划。资源行动用以下符号表示。
+ 创建
Terraform将执行以下行动。
# example_server.my-server-name将被创建
+ 资源 "example_server" "my-server-name" {
+ id = (应用后已知)
+ uuid_count = "1"
}
计划。1添加,0改变,0销毁。
Terraform应用
Terraform apply
命令调用resourceServerCreate
函数,我们已经在resource_server.go
文件中定义了这个函数。
$ terraform apply -auto-approve=true
Terraform使用选定的提供者,生成以下执行计划。资源行动用以下符号表示。
+ 创建
Terraform将执行以下行动。
# example_server.my-server-name将被创建
+ 资源 "example_server" "my-server-name" {
+ id = (应用后已知)
+ uuid_count = "1"
}
计划。1添加,0改变,0销毁。
example_server.my-server-name:创建......。
example_server.my-server-name: 0秒后创建完成 [id=1]
应用完成!资源。1个添加,0个改变,0个销毁。
清理完毕
Terraform destroy
Terraform destroy
命令调用resourceServerDelete
函数,我们已经在resource_server.go
文件中定义了这个函数。
$ terraform destroy -auto-approve=true
example_server.my-server-name:正在刷新状态...[id=1]。
Terraform使用所选的提供者来生成以下执行计划。资源动作用以下符号表示。
- 销毁
Terraform将执行以下行动。
# example_server.my-server-name将被销毁
- 资源 "example_server" "my-server-name" {
- id = "1" -> null
- uuid_count = "1" -> null
}
计划。0添加,0改变,1销毁。
example_server.my-server-name: 正在销毁...[id=1]
example_server.my-server-name:0秒后销毁完毕
销毁完成!资源。1个被销毁。
结论
在这篇技术博文中,我们涵盖了以下的主题。
- Terraform提供者如何工作。
- 什么是Terraform自定义提供者。
- 创建和建立一个Terraform提供器的例子的步骤。
- 使用自定义提供者的步骤。
- 调用Terraform CLI命令时发生了什么。
本文的读者可以使用上面给出的示例代码,用自己的API修改API调用,以管理自己的资源。此外,这里还有GitHub repo的源代码链接。希望你喜欢这篇文章,如果你有任何疑问或反馈,让我们在LinkedIn上联系并开始对话。