参考
- 使用 Terraform 在 AWS 中国区域实现自动化部署指南系列1
- 使用 Terraform 在 AWS 中国区域实现自动化部署指南系列2
- https://lonegunmanb.github.io/introduction-terraform/
- 加速 Terraform init
terraform 相关概念
terraform是对标aws cloud formation的iac工具,但是提供了多云基础设施编排的能力(provider)。实际上terraform和cloudformation相比有几个关键点来理解terraform的逻辑
provider
Terraform使用的是HashiCorp自研的go-plugin库(https://github.com/hashicorp/go-plugin),本质上各个Provider插件都是独立的进程,与Terraform进程之间通过rpc进行调用。terraform引擎首先读取并分析用户编写的Terraform代码,形成一个由data与resource组成的图(Graph),再通过rpc调用这些data与resource所对应的Provider插件。Provider插件的编写者根据Terraform所制定的插件框架来定义各种data和resource,并实现相应的CRUD方法。
查找provider的方式,Terraform 官方 Provider 文档,前往registry.terraform.io搜索
terraform的aws文档,https://registry.terraform.io/providers/hashicorp/aws/latest/docs
terraform init下载provider的流程如下(来自:https://developer.aliyun.com/article/723935)
状态管理
terraform执行过程,Terraform 会在项目根目录下加载所有的 *.tf
文件,读取配置信息,生成一个.terraform
目录
terraform init
会分析代码中所使用到的Provider,并尝试下载Provider插件到本地.terraform
文件夹。在命令行下执行 terraform apply
命令,terraform 会根据 tf脚本对远程IT资源进行更新或者创建,资源的状态信息会保存在terraform.tfstate
文件中,这是一个自动创建的 json
文件,可以理解为一个缓存文件。每次通过 terraform 操作远端时,会读取该文件的状态信息进行对比,然后操作,再将状态信息写回到terraform.tfstate
中。
文件和资源加载
在调用加载Terraform配置的任何命令时,Terraform将按字母顺序加载指定目录中的所有配置文件。
Terraform的配置语言是声明性的,所以块的顺序通常并不重要。Terraform根据配置中定义的资源之间的关系自动以正确的顺序处理资源,因此您可以采用对基础架构有意义的任何方式将资源组织到源文件中
启用缓存,每个单独项目中的插件使用符号链接到缓存目录
export TF_PLUGIN_CACHE_DIR="$HOME/.terraform.d/plugin-cache"
//或者修改terraform配置文件
plugin_cache_dir = "$HOME/.terraform.d/terraform-plugin-cache"
disable_checkpoint = true
查看缓存
$ tree terraform-plugin-cache/
terraform-plugin-cache/
└── registry.terraform.io
└── hashicorp
└── aws
└── 4.48.0
└── linux_amd64 -> /usr/share/terraform/providers/registry.terraform.io/hashicorp/aws/4.48.0/linux_amd64
所有的包含terraform文件的文件夹都是一个terraform模块(封装好的代码文件)。使用模块有利于terraform代码的复用和传播。
terraform安装和provider加载
terraform官方提供了与aws集成的quick start,https://developer.hashicorp.com/terraform/tutorials/aws-get-started
安装terraform
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
sudo yum -y install terraform
terraform -install-autocomplete
terraform -help
目录布局
provider.tf ### provider 配置
terraform.tfvars ### 配置 provider 要用到的变量
variables.tf ### 通用变量
resource.tf ### 资源定义
data.tf ### 包文件定义
output.tf ### 输出
main.tf ### 模块入口
terraform {}块包含terraform的设置,可以进行版本约束。
aws provider是terraform用来创建和管理资源的插件,在其中设置共享凭证。aws provider的源被定义为hashicorp/aws
,是 registry.terraform.io/hashicorp/aws
的简写
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "4.48.0"
}
}
}
provider "aws" {
region = "cn-north-1"
profile = "admin"
}
创建简单的s3桶
resource "aws_s3_bucket" "s3_bucket" {
bucket = "my-tf-testbucket"
acl = "private"
}
terraform的常用命令
terraform init #安装插件和初始化
terraform fmt #格式化
terrafrom validate #验证配置
terraform plan #查看计划操作
terraform apply #部署模板
terrafrom show #检查当前资源状态
terraform destroy #清除资源
由于国内没有terraform provider源,因此可能会下载超时。
Error: Failed to query available provider packages
Could not retrieve the list of available versions for provider hashicorp/aws: could not connect to
registry.terraform.io: Failed to request discovery document: Get
"https://registry.terraform.io/.well-known/terraform.json": net/http: request canceled (Client.Timeout
exceeded while awaiting headers)
没有什么太好的办法,可以手动下载压缩包然后从本地加载,需要配置terraform
下载链接,https://releases.hashicorp.com/terraform-provider-aws
$ vim ~/.terraformrc
provider_installation {
filesystem_mirror {
path = "/usr/share/terraform/providers"
include = ["registry.terraform.io/*/*"]
}
}
将压缩包按照路径解压到对应目录下
sudo mkdir -p /usr/share/terraform/providers/registry.terraform.io/hashicorp/aws/4.48.0/linux_amd64
sudo unzip terraform-provider-aws_4.48.0_linux_amd64.zip -d /usr/share/terraform/providers/registry.terraform.io/hashicorp/aws/4.48.0/linux_amd64
或者直接指定目录,加载provider
terraform init -plugin-dir /usr/share/terraform/providers
使用terraform创建eks集群
- AWS EKS Terraform module
hasiicorp给我们提供了一个简单的demo
下载kubernates providers
wget https://releases.hashicorp.com/terraform-provider-kubernetes/2.16.1/terraform-provider-kubernetes_2.16.1_linux_amd64.zip
sudo mkdir -p /usr/share/terraform/providers/registry.terraform.io/hashicorp/kubernetes/2.16.1/linux_amd64
sudo unzip terraform-provider-kubernetes_2.16.1_linux_amd64.zip -d /usr/share/terraform/providers/registry.terraform.io/hashicorp/kubernetes/2.16.1/linux_amd64
加载和使用模块
- https://lonegunmanb.github.io/introduction-terraform/4.2.%E4%BD%BF%E7%94%A8%E6%A8%A1%E5%9D%97.html
模块就是包含一组Terraform代码的文件夹。直接在一个文件夹内执行terraform apply
或者terraform plan
命令,那么当前所在的文件夹就被称为根模块(root module)
由于众所周知的网络原因我们无法从官方的公共仓库下载module
从本地加载模块
mkdir -p /usr/share/terraform/moudles/eks
mkdir -p /usr/share/terraform/moudles/vpc
sudo wget https://github.com/terraform-aws-modules/terraform-aws-eks/archive/refs/tags/v19.4.2.zip
sudo wget https://github.com/terraform-aws-modules/terraform-aws-vpc/archive/refs/tags/v3.18.1.zip
sudo unzip v19.4.2.zip -d /usr/share/terraform/moudles/eks/
sudo unzip v3.18.1.zip -d /usr/share/terraform/moudles/vpc/
修改module的source为本地路径,注意要去掉版本号
module "eks" {
source = "/usr/share/terraform/moudles/eks/terraform-aws-eks-19.4.2"
# version = "19.4.2"
...
}
module "vpc" {
source = "/usr/share/terraform/moudles/vpc/terraform-aws-vpc-3.18.1"
# version = "3.18.1"
...
}
之后任何依赖模块都可以通过以上方式手动加载,例如eks模块会依赖kms模块,网络问题真的增加了不少负担。。
mkdir -p /usr/share/terraform/moudles/eks
sudo wget https://github.com/terraform-aws-modules/terraform-aws-kms/archive/refs/tags/v1.3.0.zip
sudo unzip v1.3.0.zip -d /usr/share/terraform/moudles/kms/
修改eks中的kms模块source,这里一定要把路径写对了,不然会报错参数找不到
module "kms" {
source = "/usr/share/terraform/moudles/kms/terraform-aws-kms-1.3.0"
# source = "terraform-aws-modules/kms/aws"
# version = "1.1.0"
...
}
eks模块需要依赖其他provder
- aws (hashicorp/aws)
>= 4.47
- cloudinit (hashicorp/cloudinit)
>= 2.0
- kubernetes (hashicorp/kubernetes)
>= 2.10
- tls (hashicorp/tls)
>= 3.0
仍旧手动加载其他的provider
wget https://releases.hashicorp.com/terraform-provider-tls/4.0.4/terraform-provider-tls_4.0.4_linux_amd64.zip
sudo mkdir -p /usr/share/terraform/providers/registry.terraform.io/hashicorp/tls/4.0.4/linux_amd64
sudo unzip terraform-provider-tls_4.0.4_linux_amd64.zip -d /usr/share/terraform/providers/registry.terraform.io/hashicorp/tls/4.0.4/linux_amd64
wget https://releases.hashicorp.com/terraform-provider-cloudinit/2.2.0/terraform-provider-cloudinit_2.2.0_linux_amd64.zip
sudo mkdir -p /usr/share/terraform/providers/registry.terraform.io/hashicorp/cloudinit/2.2.0/linux_amd64
sudo unzip terraform-provider-cloudinit_2.2.0_linux_amd64.zip -d /usr/share/terraform/providers/registry.terraform.io/hashicorp/cloudinit/2.2.0/linux_amd64
wget https://releases.hashicorp.com/terraform-provider-random/3.4.3/terraform-provider-random_3.4.3_linux_amd64.zip
sudo mkdir -p /usr/share/terraform/providers/registry.terraform.io/hashicorp/random/3.4.3/linux_amd64
sudo unzip terraform-provider-random_3.4.3_linux_amd64.zip -d /usr/share/terraform/providers/registry.terraform.io/hashicorp/random/3.4.3/linux_amd64
当出现以下结果,说明终于可以用了
$ terraform init -plugin-dir /usr/share/terraform/providers
Terraform has been successfully initialized!
创建eks集群
- learn-terraform-provision-eks-cluster
接下来只需要按照demo中的文件修改即可,例如以下eks模块(当然也可以不用模块,直接创建eks resource)
引用创建的vpc资源,然后创建一个托管节点组,具体的参数和用法在eks module的文档中也有详细说明,按需修改即可
为了避免创建新资源可以直接指定vpc和子网
vpc_id = "vpc-1234556abcdef"
subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"]
control_plane_subnet_ids = ["subnet-xyzde987", "subnet-slkjf456", "subnet-qeiru789"]
以下是demo中的eks模块配置
module "eks" {
source = "/usr/share/terraform/moudles/eks/terraform-aws-eks-19.4.2"
cluster_name = local.cluster_name
cluster_version = "1.24"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
cluster_endpoint_public_access = true
eks_managed_node_group_defaults = {
ami_type = "AL2_x86_64"
}
eks_managed_node_groups = {
one = {
name = "node-group-1"
instance_types = ["t3.small"]
min_size = 1
max_size = 3
desired_size = 2
}
}
}
官方仓库提供了不同的示例,可以按需拿来用
https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/examples
还有个ecs pipeline的demo示例可以之后试试
https://github.com/aws-samples/cicd-for-ecs-workshop-code/blob/main/lab-terraform/tf/app-pipeline/app-pipeline.tf
创建过程中出现的错误
创建角色失败
MalformedPolicyDocument: Invalid principal in policy: "SERVICE":"eks.amazonaws.com.cn"
Invalid principal in policy: "SERVICE":"eks.amazonaws.com.cn"
在控制台查看具体的eks服务的信任策略,service的全名应该是eks.amazonaws.com
,显然是对中国区的适配有问题。直接把模块的源码改了就行
principals {
type = "Service"
identifiers = ["eks.amazonaws.com"]
# identifiers = ["eks.${local.dns_suffix}"]
}
最后终于创建完成了