关于Terraform v0.12中HCL描述发生的变化
这篇文章是在第6次名古屋年轻Web工程师交流会上根据演讲内容进行修订和补充而成的。我展示了Terraform v0.11和v0.12中资源描述的不同之处。
使用Terraform可以做的事情。
-
- インフラのコード化
例えばAWSコンソールを使わずにインスタンスを生成できる
JSON互換のHCLという記法でコードを記述
HCL(HashiCorp Configuration Language)
プログラミング言語ではない。設定とかを記述するためのもの
AWS,Azure,GCPといった様々なクラウドインフラに対応
インフラの状態を管理できる(S3とかに保存できる)
为什么选择使用Terraform?
-
- AWSコンソールでの作業に限界を感じたから
例えばVPC、サブネット、セキュリティグループの設定
インスタンスを使うために必要な設定なので、何度やっても一回で出来る気がしない
ステージング環境と本番環境の差異をゼロにしたいから
v0.10からworkspaceという機能があり、単一のtfファイルから異なる環境のインフラを構築出来る
アラート等の設定漏れをなくしたいから
1台目のサーバーにあるcloudwatchの設定が2台目にはない
使用Terraform后感到满意
-
- インフラを構成をコード管理出来るのと、現在のインフラ状態をS3等に保存できるので、安心出来る。
- 複数のサービスを構築したいときは一回Terraformで作ると展開しやすい
使用Terraform的困境之苦
-
- 既存のリソースを管理できない
いつの間にかコードにないリソースが作られてる
緊急避難的にコンソールで変更した内容がTerraformの実行でもとに戻る
複雑な記法
複雑なことをやろうとすると大変
repl環境が使いにくい
output周りや、既存のリソースを他のリソースで使うときの適切な式表現を見つけるのに難しいときがある
Terraform 0.12 中 HCL 的改进之处是什么?
-
- First-ClassExpressions
-
- Expressions with Lists and Maps
-
- For expressions
-
- Dynamic blocks
-
- Generalized Splat Operator
-
- Conditional Operator Improvements
-
- Rich Value Types
- Reliable JSON Syntax
安装
由于尚未正式发布,请从下方链接下载并安装。
在本文撰写时,最新版本为v0.12.0-alpha4,所以我尝试了v0.12.0-alpha4。
如果有与提供者相关的错误,请将提供者文件复制到.terraform/plugins/darwin_amd64/中。
不在清单中的更改。
从0.12版本开始,现在可以通过terraform console获取本地变量。以前是这样的。
locals {
region = "ap-northeast-1"
}
定义了像上面那样的变量,执行terraform console将会发生什么。
$ terraform console
> local.region
unknown values referenced, can't compute value
尽管出现了这样的错误,从0.12开始
$ terraform console
> local.region
ap-northeast-1
只要按照上述的方式,会返回正确的结果。然而,当尝试使用 terraform console 获得已创建资源时,
$ terraform console
> aws_security_group.this.id
>
Error: Result depends on values that cannot be determined until after "terraform apply".
出现了错误。由于无法找到这种变化的说明,可能是由于环境依赖问题或者是由于尝试的Terraform v0.12.0-alpha4版本引起的错误。
错误的原因可能是由于环境依赖问题或者是由于尝试的Terraform v0.12.0-alpha4版本引起的。
一流表达
不再需要用双引号将表达式括起来。
"${var.foo}"
请将以下内容用中文进行本地化,只需要一个选项:
↓
var.foo
在安装了版本为0.12的Terraform的情况下,执行命令terraform 0.12upgrade会将所有0.11版本的hcl文件内容替换为新的描述。
列表和映射的表达方式
现在可以直接在表达式中使用列表和映射。
ids = ["${local.sg_1}", "${local.sg_2}"]
请用中文本地方式改写以下句子,只需要一种版本:
↓
ids = ids
在Terraform 0.11之前(具有一流表达式和列表与映射的表达式)
locals {
sg_a = "sg-xxxxxxxx"
sg_b = "sg-zzzzzzzz"
sg_c = "sg-yyyyyyyy"
ami = "ami-016ad6443b4a3d960"
instance_type = "t3.micro"
}
resource "aws_instance" "example" {
ami = "${local.ami}"
instance_type = "${local.instance_type}"
vpc_security_group_ids = ["${local.sg_a}", "${local.sg_b}", "${local.sg_c}"]
}
在Terraform 0.12之后(第一类表达式和列表和映射中的表达式),
因为不再需要双引号来表示式的表达,而且List的定义也可以直接在表达式中使用,所以我认为它的外观变得非常清晰简洁。
locals {
vpc_security_group_ids = ["sg-xxxxxxxx","sg-zzzzzzzz","sg-yyyyyyyy"]
ami = "ami-016ad6443b4a3d960"
instance_type = "t3.micro"
}
resource "aws_instance" "example" {
ami = local.ami
instance_type = local.instance_type
vpc_security_group_ids = local.vpc_security_group_ids
}
关于表达方式
-
- For文を使えるようになり、ListやMapの内容を参照するのが簡単になりました。
-
- ourputの整形にも使えます。
- 待望の機能だけど、resourceの複数定義にはまだ使えなさそう
在Terraform 0.11之前(用于表达式)
resource "aws_instance" "this" {
count = 3
ami = "${local.ami}"
instance_type = "${local.instance_type}"
availability_zone = "${format("%s%s", "${local.region}", "${local.azs[count.index]}")}"
associate_public_ip_address = true
vpc_security_group_ids = ["${local.sg_a}", "${local.sg_b}", "${local.sg_c}"]
subnet_id = "${local.subnets[count.index]}"
tags = {
Name = "${format("terraform-0.11-for-demo-%d", "${count.index}")}"
}
credit_specification {
cpu_credits = "unlimited"
}
}
output "instance_private_ip_addresses" {
value = "${aws_instance.this.*.private_ip}"
}
>
Outputs:
instance_private_ip_addresses = [
10.2.0.22,
10.2.4.162,
10.2.9.38
]
在Terraform 0.12版本之后,有了For表达式。
现在的版本只能显示关于List类型的描述,但是据写着也可以将其描述为map类型。然而,由于目前出现错误,我们只放置了关于List类型的描述。
resource "aws_instance" "this" {
count = 3
ami = local.ami
instance_type = local.instance_type
availability_zone = format("%s%s", local.region, local.azs[count.index])
associate_public_ip_address = true
vpc_security_group_ids = local.vpc_security_group_ids
subnet_id = local.subnets[count.index]
tags = {
Name = format("terraform-0.12-for-demo-%d", count.index)
}
credit_specification {
cpu_credits = "unlimited"
}
}
output "instance_private_ip_addresses" {
value = [
for instance in aws_instance.this:
instance.private_ip
]
}
>
Outputs:
instance_private_ip_addresses = [
"10.2.1.79",
"10.2.7.42",
"10.2.11.106",
]
动态区块
・现在我们可以动态地表示反复描述的块,如标签等。
・可以用于重复描述的AutoScaling标签和SecurityGroup入口设置。
在Terraform 0.11之前(使用表达式)
resource "aws_autoscaling_group" "example" {
# ...
tag {
key = "Name"
value = "example-asg-name"
propagate_at_launch = false
}
tag {
key = "Component"
value = "user-service"
propagate_at_launch = true
}
tag {
key = "Environment"
value = "production"
propagate_at_launch = true
}
}
在Terraform 0.12之后(对于表达式)
locals {
standard_tags = {
Component = "serviceA"
Environment = "production"
}
}
resource "aws_autoscaling_group" "example" {
max_size = 1
min_size = 1
tag {
key = "Name"
value = "example-asg-name"
}
dynamic "tag" {
for_each = local.standard_tags
content {
key = tag.key
value = tag.value
}
}
}
广义的星号运算符
-
- スプラット演算子が複数存在するリソースだけでなく、一般的なListにも使えるようになった
今まではcountで作成したものしか使えなかった
スプラット演算子の記法が直感的になった
aws_instance.this.*.public_dns
aws_instance.this[*]public_dns
ただし0.12−alpha4ではエラーになる
条件操作符的改进
-
- 条件演算子の改善、今まではプリミティブな型にしか使えなかった。
- 今までは両方の式の値を評価していたが、参考演算子的に使えるようになった。
在 Terraform 0.11 之前(有关条件运算符的改进)
output "first_id" {
value = "${"${length("${aws_instance.this.*.private_ip}")}" > 0 ? "${element("${aws_instance.this.*.id}", 0)}" : "2"}"
}
>
Error: Error refreshing state: 1 error(s) occurred:
* output.first_id: element: element() may not be used with an empty list in:
${"${length("${aws_instance.this.*.private_ip}")}" > 0 ? "${element("${aws_instance.this.*.id}", 0)}" : "2"}
在Terraform 0.12之后(条件运算符的改进)
因为现在可以访问以前无法通过数组索引访问的部分,所以处理的可视性得到了很大改善。
output "first_id" {
value = length(aws_instance.this) > 0 ? aws_instance.this[0].id : ""
}
>
Outputs:
first_id =
丰富多样的数值类型
-
- Terraform 0.11以前では変数の型として単純なlistとmapしか使えなかった。
-
- 今後はもっと複雑な型を表現出来るようになる
- localはvariablesの記述をきれいにできる
– 在输出中轻松地输出资源信息。
在Terraform 0.12版本之后(丰富的值类型)
输出时直接描述output的resource,不会出错并输出。
在确认使用下一个资源时,确认已创建的资源的内容非常方便。
resource "aws_security_group" "this" {
name = "ingress"
description = "Allow some inbound traffic"
vpc_id = local.vpc
ingress {
from_port = 8200
to_port = 8200
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 8500
to_port = 8500
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
output "sg" {
value = aws_security_group.this
}
>
sg = {
"arn" = "arn:aws:ec2:ap-northeast-1:000000000000:security-group/sg-099659d43b1534ffa"
"description" = "Allow some inbound traffic"
"egress" = []
"id" = "sg-099659d43b1534ffa"
"ingress" = [
{
"cidr_blocks" = [
"0.0.0.0/0",
]
"description" = ""
"from_port" = 8500
"ipv6_cidr_blocks" = []
"prefix_list_ids" = []
"protocol" = "tcp"
"security_groups" = []
"self" = false
"to_port" = 8500
},
{
"cidr_blocks" = [
"0.0.0.0/0",
]
"description" = ""
"from_port" = 8200
"ipv6_cidr_blocks" = []
"prefix_list_ids" = []
"protocol" = "tcp"
"security_groups" = []
"self" = false
"to_port" = 8200
},
]
"name" = "ingress"
"owner_id" = "000000000000"
"revoke_rules_on_delete" = false
"timeouts" = {}
"vpc_id" = "vpc-xxxxxxxx"
}
模板语法
-
- テンプレート構文の中でもFor文が使えるように
- 引数展開のための変数の定義を少なくできる
在Terraform 0.11之前
"environment": [
{
"name": "AWS_S3_BUCKET",
"value": "${s3_bucket}"
},
{
"name": "AWS_S3_URL",
"value": "${s3_url}"
},
{
"name": "ENCRYPT_SECURE_KEY",
"value": "${secure_key}"
},
{
"name": "MYSQL_HOST",
"value": "${rds_endpoint}"
},
{
"name": "MYSQL_ROOT_PASSWORD",
"value": "${password}"
}
]
在Terraform 0.12之后
在使用fargate或ECS的容器定义时,通常会使用json格式。在这种情况下,我认为将环境依赖的变量等作为模板变量放入容器内使用的环境变量中是常见的做法。通过使用for循环来描述可以减少重复的写法。
locals {
fg_env = [
{
"name": "AWS_S3_BUCKET",
"value": "my.bukect"
},
{
"name": "AWS_S3_UR",
"value": "assets.localhost"
},
{
"name": "ENCRYPT_SECURE_KEY",
"value": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
},
]
fargate_config = <<EOT
%{ for instance in local.fg_env ~}
{
"name": local.env.name,
"value": local.env.value
},
%{ endfor }
EOT
}
> local.fg_env
[
{
"name" = "AWS_S3_BUCKET"
"value" = "my.bukect"
},
{
"name" = "AWS_S3_UR"
"value" = "assets.localhost"
},
{
"name" = "ENCRYPT_SECURE_KEY"
"value" = "xxxxxxxxxxxxxxxxxxxxxxxxxx"
},
]
可靠的JSON语法
– HCL和JSON完全1:1兼容。
– 提升错误信息。
– 在JSON格式中支持注释。
在Terraform 0.11之前(可靠的JSON语法:エラー文)
{
"variable": {
"example": "foo"
}
}
$ terraform apply
Error: Error loading /home/ec2-user/terraform-0.11-example/variables.tf.json: -: "variable" must be followed by a name
在Terraform 0.11之前(可靠的JSON语法:エラー文)
当描述无效结构的JSON时,错误消息变得更加易于理解了。
Error: Incorrect JSON value type
on variable.tf.json line 3, in variable:
3: "example": "foo"
Either a JSON object or a JSON array is required, representing the contents of
one or more "variable" blocks.
在Terraform 0.12版本之后,它具备了可靠的JSON语法:评论功能。
由于在0.11版本中尝试该语法会导致错误,因此我们决定省略此部分。但是现在您可以使用特殊的”//”键来添加注释。这个”//”键在执行Terraform时会被忽略。
{
"locals": {
"example": {
"//": "This property is comment",
"default": "foo"
}
}
}
总结起来
在Terraform v0.12中,HCL进行了重大升级,并且描述语法发生了相当大的变化。
然而,尽管不推荐使用过去的描述方式,但仍可以使用许多旧的项目。
我认为对于升级现有项目来说,并不会带来太大的负担。
补充说明:
Terraform 0.12已经发布。现在提供了一个叫做0.12upgrade的命令,执行该命令可以自动修复一些轻微变化,
比如在First-Class Expressions或者标签定义中需要使用”=”号的部分。