背景
在之前参与资产市场产品迭代的时候,就资产市场与研发平台的打通,研发团队就提出过使用IaC,实现“一键下行”。一键下行,指的是在下行复用组件资产的时候,将组件所依赖的资源也同时自动化开通,然后自动将组件部署在新开通的资源上。
举个例子:假设4A组件需要依赖RDS、Redis、OSS,就在后台配置的云资源环境自动申请RDS、Redis、OSS。站在研发的视角,隐藏了资源申请的复杂性,加速了复用流程,提升了研发体验。
在资产市场对外输出时,对接的云资源往往没有那么理想,必定是阿里云公共云资源。如果使用IaC,便于适配不同的云环境。
接下来即将开始的工作,可能也会涉及大量而频繁的云资源开通。最近在做Demo的时候,就深感开通和销毁云资源这个繁琐的事情有多浪费时间。
以程序员的偷懒本性,自然想到了使用IaC来提升效率。
概念和快速入门指引可以直接查阅参考资料,就不copy & paste了。只记录一些个人觉得实践时需要关注的点。
由于自家的关系,以下云资源默认为阿里公共云。
2. 工作中可能用到IaC的业务场景
IaC核心是版本控制和可重复,实现提效、降低误操作、一致性与合规安全。
适合IaC的业务场景是什么?企业上云、环境复制、环境重建、合规管控等。
3. 学习时的几个选型
3.1 Terraform vs. 阿里云ROS
对于个人而言,暂时没精力掌握两种IaC语法。考虑到后续工作会涉及跨云平台的资源编排和管理,优先选择了Terraform。
3.2 Terraform vs. Ansible
Ansible于 2015 年被Red Hat收购。从当前的厂商Redhat自己的说法,两者可以结合。
- 使用Terraform创建云资源:使用Terraform创建虚拟机、网络、存储等云资源,确保环境的一致性和可重复性。
- 使用Ansible配置软件:在创建完云资源后,使用Ansible来安装、配置和管理软件,确保每个虚拟机都具有所需的配置。
如果需要在虚拟机之间进行复杂的配置和协调,可以优先考虑使用Ansible,因为它在主机内部执行操作,更适合执行复杂的系统管理任务。
不过我的个人未经实践的粗浅见解是,如果是有很强能力的互联网行业客户,可以考虑分别利用这两套解决方案各自的长处。但对于大量泛企业客户,同时运用两套解决方案对于运维的学习成本会更高(特别是对运维人员能力一般的企业)。
此外,在云原生时代,直接通过镜像拉起容器,在主机内部执行操作是需要尽量减少的运维行为。
以国内的运维条件,直接到主机内部执行操作肯定还是无法避免。不过如果这种是低频偶发操作,是不是靠人工+文档,比引入Ansible成本更低?如果是基于纯阿里公共云的话,还有OOS(系统运维管理)云产品可能也可以替代Ansible。
Anyhow,当前我个人测试和做demo的时候还是以纯Terraform的方案。
参考资料:
3.3 是否将Terraform用于应用部署
阿里云的官方文档有使用Terraform分别在ECS和K8S集群上部署Wordpress的实践教程。典型案例里也有使用Terraform快速拉起幻兽帕鲁服务的案例。但实际搜索应用编排,主要内容是在云效的AppStack文档中。即从阿里云的业务逻辑来看,应用编排从逻辑上更贴合DevOps。
我个人还是认同这个逻辑的。上面案例中,不管是部署Wordpress,还是幻兽帕鲁的服务,都是比较成熟的服务。而对于快速迭代的企业应用,还是适合配合DevOps来落地应用编排,而非ROS(资源编排服务)。
不过也有对Terraform深度探索使用的企业(特别是游戏行业),不仅云资源,DevOps部署应用也使用Terraform编排完成。
讲到了应用编排,顺带一提,在查资料的时候看到了华为对其资源编排的分享的材料,发现一个挺有意思的细节。
在那份材料里,华为将资源编排与应用编排分为两个不同的产品,并按照Shell脚本(命令行级别自动化)->配置管理自动化(Puppet/Chef/Ansible/Salt)->资源供给自动化(Terraform/Docker Compose)->资源编排(AWS-Cloudformation/Ali-ROS/HC-RTS)->应用编排(AWS-CFN/Pivotal-BOSH/HC-AOS)的逻辑,来描述编排服务的发展趋势来描绘云上自动化。但从这张图很容易误解资源编排和应用编排是有发展递进关系。当前,资源编排和应用编排已经都合并到华为的同一个资源编排服务(RFS)的产品里,估计也是做了云产品治理与整合。但应用编排的产品页和产品代号理论上应该随之下线,但还保留着,透露出内部组织调整的影子。
4. 几个知识点
在实践了几个Hello world级的demo后,整理了几个自己关注的知识点。
4.1 身份认证
在提到身份认证之前,记录一下自己遇到过的弱智问题。
使用Terraform在创建VPC的时候,发现可以通过页面创建上海区域可用区的VPC,但没法使用Terraform创建,会报错:”Message: code: 400, Resource you requested is not available in this region or zone. “。但在杭州区域区域,不管是页面还是Terraform都可以成功创建。提了工单,经过售后工程师提醒才发现,Terraform初始配置的时候需要配置一个环境变量:
1 | export ALICLOUD_REGION="cn-hangzhou" |
环境变量是前一个周五配置的,隔了一个周末继续做demo的时候,自己忘记了。。。在写HCL脚本的时候还在奇怪,怎么VPC不需要指定区域的。
这里就涉及到初始化本地Terraform的时候一个设置,就是将AK和SK设置到本机的环境变量里。
Terraform要操作云资源,操作交互自然是需要带身份认证的。
官方文档介绍了几种方案,包括通过terraform命令传参方式(-var参数)、环境变量。如果是在ECS上执行,还可以通过ECS服务角色、角色扮演、OIDC角色扮演。这3个也是生产环境到的建议方式,无需直接暴露AK/SK。
不过话又说回来,我能理解需要将AK和SK放到环境变量里,还是不太理解为什么默认要将区域也放到环境变量里,而不是也放到脚本里维护。盲猜是由于ROS的HCL脚本本身就是按区域分别维护的原因。
4.2 变量
HCL2中,变量可以不用加花括号了。即可以直接用var.region
,而不需要使用"${var.region}"
。(这是IntelliJ IDEA里提示后才知道的)
4.3 模块化
Terraform也支持代码复用,形式就是模块。模块可以理解为包含一个或多个资源的模板,对应Terraform代码里的一个目录。
模块使用的典型场景,包括:
- 需要重复创建多个相同资源(可以配合count或for each语法)
- 在不同的项目或环境,调用相同的模块。通过传入不同项目或环境的参数区分
有最佳实践推荐每个基础module尽可能只包含同一产品的相关资源。
阿里云的常用module其实可以直接从terraform官网引用,参见:Browse Modules | Terraform Registry,可以在线引用,例如:
1 | module "vpc" { |
用在线的还是用自己封装的,就看个人判断了。
用在线版本可能会有2个问题:
- init的时候有可能会连不上github导致失败
- 拉取时间变长
阿里云的alibabacloud-automation
项目里的module的代码风格也多少有些差异,猜测是没有制定统一规范,由不同云产品团队自己写的关系。
个人觉得可以作为参考和学习资料。
4.4 Terraform后台(Backend)存储状态文件
默认情况下,Terraform 状态文件(.tfstate
,即Terraform State,保存了资源配置和生成资源的元数据)会存储在本地文件系统上。然而,在团队环境中,需要更安全、持久或可共享的状态管理。
使用Git托管是不行的,会产生团队多人同时操作情况下产生的状态文件更新及时性问题。例如A刚运行完apply,本地更新了state文件,还没来得及上传git,这时候B也运行的话,就可能导致A创建的资源被销毁。
最佳实践是放到OSS Bucket远程存储,配置后台通常在 Terraform 配置文件(terraform.tf 或 backend.tf)中完成。
参考资料:
4.5 存量资源导入Terraform
除非是新创建或新迁云的企业,不然肯定会有相当数量的存量资源没有通过Terraform管理。此外还会遇到:
- 虽然资源已经使用Terraform管理,但由于某些情况,是通过控制台对云资源做了属性变更
- Terraform模版过于复杂而拆分
这种情况下,就需要使用Terraform的DataSource来获取资源ID,声明要导入的资源,然后通过terraform import命令导入。
如果是测试使用,也可以打上被污染的标记,让terraform销毁后重新创建。
参考资料:
按照Google的建议,要避免导入现有资源。给出的理由是:因为这样做可能会很难完全了解手动创建的资源的来源和配置。应通过 Terraform 创建新资源并删除旧资源。当然这是理想情况,Google自己也没死板地要求一定要采用。
5. 工程最佳实践
虽然在demo的时候,可以用一个main.tf搞定。但既然要系统性地学习,还是学彻底。所以也整合了Terraform工程最佳实践的资料。
5.1 目录结构
从 Terraform目录,可以看到一般会分根模块和子模块。
每个模块里包含:
- CHANGELOG.md
- README.md
- locals.tf
- main.tf
- variables.tf
- outputs.tf
此外根模块里包含providers.tf。
如果资源复杂,资源配置代码较长,可以按照资源类型单独使用一个独立的 .tf 文件来存放,例如,用于 ECS 实例、OSS Bucket 和数据库的配置可以分别放在 instance.tf,oss.tf 和 database.tf 中。(这种情况是不是还不如封module?)
5.2 Google方案参考
按照Google的建议,使用environments目录来方每个环境的配置。
1 | -- SERVICE-DIRECTORY/ |
此外,按照Google的风格,也是建议使用MonoRepo来管理所有的Terraform代码,并且由单个平台工程团队管理这个MonoRepo。
也可以将Terraform配置拆分到不同的代码库。
基础代码库:
特定于应用和团队的代码库:
我将个人测试自用的项目,参考Google的MonoRepo+基础代码库+项目(服务)的目录结构方案,暂时拟定了个自用的目录结构:
1 | -- foundations/ |
foundations(基础设施)和projects里都包含了个template目录。新建模块和子项目的时候,可以从template里快速复制出来一份。
根据实际使用体验再调整。
5.3 代码格式化
代码格式化的规则不用记,运行terraform fmt -recursive
就可以了。
5.4 gitignore文件
.gitignore文件参考:gitignore/Terraform.gitignore at main · github/gitignore
稍微修改了一下,增加了个人用的IDE的IntelliJ Idea的gitignore配置,以及感觉也需要加入的.terraform.lock.hcl文件
1 | Local .terraform directories |
注意点:.terraform.lock.hcl
这个是依赖项锁文件,需要纳入版本控制,有助于跟踪和审核指定配置的提供商选择变化。
5.5 Google的其他最佳实践
变量相关:
- 为变量设置type字段:避免变量类型错误
- 如果参数包含敏感信息,在其对应的变量中将sensitive设置为true
- 表示数值的输入、局部变量和输出(例如磁盘大小或 RAM 大小)必须使用单位命名(例如 ram_size_gb)
- 对于具有与环境无关的值的变量(例如磁盘大小),请提供默认值
- 对于具有特定于环境的值的变量(例如 project_id),请勿提供默认值。这样,调用模块必须提供有意义的值。
- 对于根模块,请使用 .tfvars 变量文件提供变量。为了保持一致性,请将变量文件命名为 terraform.tfvars。命令行选项是临时性的,容易忘记。使用默认变量文件更容易预测。
模块相关: - 在每个模块中,添加 Markdown 格式的 README.md 文件。在 README.md 文件中,添加有关模块的基本文档
- 使用资源各自的文件和描述性名称(例如 network.tf、instance.tf 或 loadbalancer.tf)创建资源的逻辑分组
- 将示例放在 examples/ 文件夹中,并为每个示例提供单独的子目录。对于每个示例,请添加详细的 README.md 文件。
命令相关: - 使用下划线命名所有配置对象,以分隔多个字词
- 将资源名称设为单数形式
- 在资源名称中,请不要重复资源类型。例如避免:
resource "google_compute_global_address" "main_global_address" { … }
输出: - 请勿直接通过输入变量传递输出,因为这样做会阻止输出正确地添加到依赖关系图中
数据源: - 将数据源放在引用它们的资源旁边。
- 如果数据源数量很大,请考虑将它们移动到专用 data.tf 文件中。
自定义脚本: - 限制自定义脚本的使用。Terraform 不会考虑或管理通过脚本创建的资源的状态。
- 将 Terraform 调用的自定义脚本放在 scripts/ 目录中。
其他: - 建议指定Provider版本:防止Provider更新引入问题,保障稳定性
- 将静态文件放在单独的目录中
- 最大限度地减少每个根模块中的资源数量。一般规则:在单个状态下,最好不要超过十几个。
- 不要手动修改 Terraform 状态
5.6 无网络情况下运行
客户专有云环境,特别是生产环境,基本都是无法连互联网的。
首先在有网络环境的机器把当前目录的插件复制到特定目录:
1 | terraform providers mirror /data/terraform/plugins |
拷贝到无网络环境的机器,运行时指定插件目录:
1 | terraform init -plugin-dir=/data/terraform/plugins |
参考资料:《Terraform 101 从入门到实践》 第二章 Providers插件管理 - 南瓜慢说官网
5.7 Terraform故障恢复
和K8S不同,Terraform 不提供自动故障恢复功能,必须依赖故障时运行的脚本。
不过故障的检测能力还是具备的。可以使用terraform state
命令,检查Terraform管理的当前资源状态:
1 | terraform state list |
这将输出一个列表,其中包含了资源类型和资源的唯一标识符(例如aws_instance.example)。
如果想查看某个特定资源的详细状态,可以使用terraform state show
命令,并提供该资源的地址作为参数。例如,如果要查看名为example的AWS EC2实例的状态,可以执行:
1 | terraform state show aws_instance.example |
5.8 快捷命令
用得次数多了之后,也可以自己设置一些alias来简化需要敲的命令:
1 | terraform |
6. 总结
以上内容都只是Demo测试+搜索后的资料整理,未经历实际项目和客户的实践验证,只能算是纸上谈兵。如果后续有机会在真实客户或项目中实践,会修正其中错误的内容。
X. 参考资料
Terraform入门
什么是Terraform_Terraform(Terraform)-阿里云帮助中心
https://help.aliyun.com/document_detail/95820.html
为什么选择Terraform_资源编排(ROS)-阿里云帮助中心
https://help.aliyun.com/zh/ros/user-guide/overview-2
如何在本地安装和配置Terraform_Terraform(Terraform)-阿里云帮助中心
https://help.aliyun.com/document_detail/95825.html
快速手册
Terraform有哪些常用命令_Terraform(Terraform)-阿里云帮助中心
https://help.aliyun.com/document_detail/145531.html
常见的Terraform模板示例有哪些_资源编排(ROS)-阿里云帮助中心
https://help.aliyun.com/zh/ros/user-guide/examples-of-terraform-templates
Terraform 身份认证-阿里云帮助中心
https://help.aliyun.com/document_detail/2837050.html
《Terraform 101 从入门到实践》 第五章 HCL语法 - 南瓜慢说官网
https://www.pkslow.com/archives/terraform-101-hcl
最佳实践参考
Terraform代码的开发方式和建议是什么_资源编排(ROS)-阿里云帮助中心
https://help.aliyun.com/zh/ros/user-guide/methods-and-suggestions-for-terraform-code-development
使用 Terraform 的最佳实践 | Google Cloud
https://cloud.google.com/docs/terraform/best-practices-for-terraform?hl=zh-cn