介绍一个小工具:Terranetes
IaC 不只是 Terraform
虽然几年前的一次讨论中,我嘲讽过某同事说,Terraform 目前最靠谱的 Provider,也就只有 Kubernetes 一个而已,相对于顾头不顾尾的 Provider 来说,Kubernetes + Operator 才是正道;然而形势比人强,目前 Terraform 的确是能帮用户踏上 IaC 旅程的方便法门。
就像 Kubernetes 之于云原生,对于 IaC 来说,Terraform 也同样有着 Day2 的问题。并且由于面对更大范围、更大成本的调度能力,Terraform 的管控也面临更大的挑战。例如工作流、合规、安全、成本等方面的考虑,以及偏差检测、模块安全等特性的应用,都是摆在管理员面前的明显问题。围绕这一工具,想要构建一个稳健的自助平台,是个颇为复杂的困难过程。
Terranetes 简介
appvia.io
是一个位于伦敦的解决方案厂商,它的开源项目 Terranetes
就尝试解决 Terraform 的 Day2 问题。这个 golang 项目在 22 年开源,到现在为止,发布了一百多个 Release,Star 数量只有 135 个,上次代码更新是在两个月前,从运作成绩来看,似乎不太成功。然而从平台工程的角度来看,这个项目提供的众多特性,却是非常值得关注和致敬的。
Terranetes 首页有这样一张图,形象地描述了这一产品的 IaC 治理思路:
工作流
图里简单地描述了一个工作流,平台团队制定规则、开发团队申请资源,控制器接收申请、引用凭据、执行规范,按照流程中设定的审批、Lint 等环节,完成资源申请过程。
他把用户明确地区分为平台和开发两种角色:
平台团队
平台用户的主要职责是为开发者用户提供自助服务的基础支持:
接入资源供应商:用 CRD 的形式定义资源供应商,以此来支撑开发者使用指定供应商的资源。
凭据管理:管理接入资源供应商的身份凭据,开发者无需自行管理凭据,即可获得操作资源的权限。
策略管理:从成本、合规、安全等方面,提供策略支持,保障开发者对资源的有效使用。
开发团队
开发者的职责相对集中一些:引用平台团队维护的凭据、规则、模板等,按照既定工作流进行资源的申请和使用。
上手一试
官方提供了 Quick Start 文档,网络条件允许的话,几分钟就可以完成第一次资源分配。下面的例子我使用 AWS 作为资源供应商,本机的 OrbStack 提供给 Terranetes 作为控制器的 Kubernetes 运行平台。
先决条件
- Kubernetes 集群
- AWS 账号,有能够创建 S3 Bucket 的 AK/SK
部署
基于 Helm 的老套部署方式:
$ helm repo add appvia https://terranetes-controller.appvia.io
$ helm repo update
$ helm install -n terraform-system terranetes-controller appvia/terranetes-controller --create-namespace
...
运行后可以看到多出了 terraform-system
命名空间,其中运行了两个控制器 Pod。
CRD
看看多出了哪些 CRD:
$ kubectl api-resources | grep terraform
cloudresources terraform.appvia.io/v1alpha1 true CloudResource
configurations terraform.appvia.io/v1alpha1 true Configuration
contexts terraform.appvia.io/v1alpha1 false Context
plans terraform.appvia.io/v1alpha1 false Plan
policies terraform.appvia.io/v1alpha1 false Policy
providers terraform.appvia.io/v1alpha1 false Provider
revisions terraform.appvia.io/v1alpha1 false Revision
官网文档用下图来描述对象之间的关系:
CloudResources:这个对象用来描述 Terraform 中的云资源,CloudResources 会选择性的向用户公开属性,从而减少开发者的心智负担,并确保选项符合组织意图。
Revisions:Revision 是云资源的模板,在实际环境中,应该是经过策划和测试的版本化资产,其中会包含组织所需的默认设置,并只公开与上下文相关的功能:
- Revision 指向 Terraform 模块。
- 包含平台希望模块默认设置的所有默认选项,并向消费者公开部分可见选项。
- 可以跟踪 Terraform 模块,也可独立演进。
- 所有 CloudResources 都会引用集群中的 Revision。
- 要升级 CloudResource 消费者,需要更新其指向的 Revision。
Configurations:另一种描述云资源的方法,相对于 CloudResource,它相对传统一些,采用了和 Module 进行一对一连接的方式。
Providers:用来连接云资源提供方的定义。
Plans:无需主动创建,随 Revision
对象的定义自然产生。
Policies:对策略的引用,其中包含了 Checkov
规则的引用方法、对 Module 的限制。
Contexts:上下文提供了一种在配置之间共享通用配置的方法。集群中任何组件都可以引用该资源。
配置凭据和 Provider
接下来配置用来连接 AWS:
$ kubectl -n terraform-system create secret generic aws \
--from-literal=AWS_ACCESS_KEY_ID=${AWS_AK} \
--from-literal=AWS_SECRET_ACCESS_KEY=${AWS_SK} \
--from-literal=AWS_REGION=${AWS_AGN}
secret/aws created
$ export PROVIDER="https://raw.githubusercontent.com/appvia/terranetes-controller/master/examples/provider.yaml"
$ kubectl apply -f $PROVIDER
provider.terraform.appvia.io/aws created
provider.terraform.appvia.io/aws-irsa created
可以看到 Provider 对象的 spec
内容:
spec:
preload:
cluster: wayfinder-production
context: default
enabled: false
region: eu-west-2
provider: aws
secretRef:
name: aws
namespace: terraform-system
source: secret
其中指定了 AWS 作为资源供应商,并且使用前面的 Secret 作为连接凭据。
配置 Revision
$ export REV="https://raw.githubusercontent.com/appvia/terranetes-controller/master/examples/revision.yaml"
$ kubectl apply -f ${REV}
revision.terraform.appvia.io/bucket.v1 created
打开 Revision 文件,能够大致观察到其中包含的信息:
- configuration:其中有 Module 的地址、Context 引用、Provider 引用以及默认值。
- inputs:这里定义了用户可以控制的内容(此处只有
bucket
可写)。 - plan:则定义了该对象所属的计划。
Revision
中定义的 Plan
也被自动创建了:
$ kubectl get plan bucket -o yaml
apiVersion: terraform.appvia.io/v1alpha1
kind: Plan
metadata:
...
spec:
revisions:
- name: bucket.v1
revision: v0.0.1
使用 Plan->Revision 创建资源
上面两节完成了平台管理或者资源管理角色的任务,接下来要真正地申请资源了。这里需要创建如下的 CloudService
对象:
apiVersion: terraform.appvia.io/v1alpha1
kind: CloudResource
metadata:
name: bucket
spec:
plan:
name: bucket
revision: v0.0.1
providerRef:
name: aws
writeConnectionSecretToRef:
name: test
variables:
bucket: forever8384
执行之后,我们会看到,系统中有了一个运行成功的 Job
对象,以及一个 CloudResource
对象:
$ kubectl get jobs
NAME COMPLETIONS DURATION AGE
bucket-tgbrl-1-plan 1/1 28s 8m37s
$ kubectl get cloudresources.terraform.appvia.io
NAME PLAN REVISION SECRET CONFIGURATION ESTIMATED UPDATE SYNCHRONIZED AGE
bucket bucket v0.0.1 test bucket-tgbrl Not Enabled None OutOfSync 8m50s
任务已经完成,打开 AWS 控制台,也找不到我们要的 Bucket。看看 Job 对应的 Pod 日志:
kubectl logs -f bucket-tgbrl-1-plan-lj7hd
[info] Checking if required flags have been provided.
[info] Waiting 10 seconds for pod logs to be available (attempt 1/15)..
[info] waiting for the job to be scheduled
...
# aws_s3_bucket_server_side_encryption_configuration.this[0] will be created
+ resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
...
# aws_s3_bucket_versioning.this[0] will be created
+ resource "aws_s3_bucket_versioning" "this" {
...
Plan: 6 to add, 0 to change, 0 to destroy.
搞 Terraform 的读者会看得出来,他似乎只做了 Plan,没有做实际的 Apply。
文档解释说,默认情况下,资源的创建是需要被批准的,除非是使用了注解:terraform.appvia.io/apply=true
,所以我们现在加入这个:
$ kubectl annotate cloudresources bucket "terraform.appvia.io/apply"=true --overwrite
cloudresource.terraform.appvia.io/bucket annotated
接下来会看到一个新的 Job 在运行,类似 bucket-l6jkf-1-apply-xp45d
这样的名称。这个 Pod 运行成功之后,会看到 S3 桶已经创建。
客户端
Terranetes 还有个叫 tnctl
的命令行客户端软件,提供了任务跟踪、审批等能力,避免大量使用 kubectl
来完成这些任务。
其他
偏差检测
CloudResource
有个字段 spec.enableDriftDetection
,设置为 True
就可以启动偏差检测。例如上面的 CloudResource,我们修改这个字段为 True 之后,删除对应的桶,一段时间之后,这个资源就会变成 OutOfSync
状态。
监控
Controller 自带了 Prometheus 的指标抓取端口,提供了请求数、延迟时间等简单的指标。
除了这些基本功能之外,Terranetes 还能对接 Infracost
进行成本数据采集和预测,不过这是个商业产品,就没有进一步尝试了。
总之,Terranetes 提供了一个相对全面的框架,其中展示的工作流、策略、分权等设计,都是很好的范本,很值得工具平台、IaC 相关方案的设计者们参考和学习。