最近在搞devops
,有一点对Infrastructure as code
代码风格的感悟
直接从一个例子展开吧
假如需要将原来单账号下以下多网络分别创建到单独的账号下
1 2 3 4 5 6 7 8 9 10 11 12 vpcs = { network1 = { name = "vpc-1" } network2 = { name = "vpc-2" } network3 = { name = "vpc-3" } }
为了网络创建复用自然需要使用module
去按账号构建
(别想动态指定provider
,terraform
不支持!)
那问题是怎么将vpc
的配置按账号分组传递给对应的module
来看两种方案
方案一: 动态分组 给每个vpc
配置加acct_key
, 然后代码动态分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 vpcs = { network1 = { acct_key = "a" name = "vpc-1" } network2 = { acct_key = "a" name = "vpc-2" } network3 = { acct_key = "b" name = "vpc-3" } } locals { grouped_networks_as_array = { for k, v in var.vpcs : v.acct_key => { "${k} " = v }... } grouped_networks_as_object = { for k, v in local.grouped_networks_as_array : k => merge({}, v...) } } output "group_result" { value = local.grouped_networks_as_object } provider "external" { alias = "acct_a" } provider "external" { alias = "acct_b" } module "acct_a_vpcs_1" { source = "./vpc" vpcs = local.grouped_networks_as_object.a providers = { external = external.acct_a } } module "acct_b_vpcs_1" { source = "./vpc" vpcs = local.grouped_networks_as_object.b providers = { external = external.acct_b } }
聚合那里代码需要两段,主要是terraform
的默认聚合,只会按key
相同合并成array
,但我们其实是要把array
的每个元素合并成一个object
, 方便后续按网络的key
去索引资源创建的结果
(比如module
输出了vpc
资源的创建结果vpcs
,就可以用module.acct_a_vpcs.vpcs.network1
拿到network1
的结果)
聚合后结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + group_result = { + a = { + network1 = { + acct_key = "a" + name = "vpc-1" } + network2 = { + acct_key = "a" + name = "vpc-2" } } + b = { + network3 = { + acct_key = "b" + name = "vpc-3" } } }
这不是程序员最擅长的代码封装么,配置没怎么变,代码动态一聚合就完成了变更的需求。
等等,再来看一个方案
方案二: 静态分组 就是配置按账号重新拆分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 accounts = { acct_a = { vpcs = { network1 = { name = "vpc-1" } network2 = { name = "vpc-2" } } } acct_b = { vpcs = { network3 = { name = "vpc-3" } } } }
然后使用时按账号获取配置就是一目了然的事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 module "acct_a_vpcs" { source = "./vpc" vpcs = var.acct_a.vpcs providers = { external = external.acct_a } } module "acct_b_vpcs" { source = "./vpc" vpcs = var.acct_b.vpcs providers = { external = external.acct_b } }
整体看下来两种方案好像都差不多,但如果考虑代码的简洁与配置聚合的粒度的话,第二种就更胜一筹
毕竟对于IaC而言,一目了然的简洁比复杂的代码抽象更易于维护,基础设施的配置文件本来就不应该搞复杂。
哈哈, 代码封装也有碰壁的时候。
当然也有需要代码封装的时候,比如把多个账号的vpcs结果合并起来,便于其他资源跨账号按vpc key查询资源id,路由表id啥的
1 2 3 4 5 6 locals { combined_acct_vpcs = merge( module .acct_a_vpcs .vpcs , module .acct_b_vpcs .vpcs ) }
如有疑问,请文末留言交流或邮件:newbvirgil@gmail.com
本文链接 : https://newbmiao.github.io/2024/08/17/iac-loves-simple.html