<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>IaC | 伪架构师</title>
    <link>/tags/iac/</link>
      <atom:link href="/tags/iac/index.xml" rel="self" type="application/rss+xml" />
    <description>IaC</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Sun, 07 Jul 2024 06:58:46 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>IaC</title>
      <link>/tags/iac/</link>
    </image>
    
    <item>
      <title>介绍一个小工具：Terranetes</title>
      <link>/post/terranetes-controller-intro/</link>
      <pubDate>Sun, 07 Jul 2024 06:58:46 +0800</pubDate>
      <guid>/post/terranetes-controller-intro/</guid>
      <description>

&lt;h2 id=&#34;iac-不只是-terraform&#34;&gt;IaC 不只是 Terraform&lt;/h2&gt;

&lt;p&gt;虽然几年前的一次讨论中，我嘲讽过某同事说，Terraform 目前最靠谱的 Provider，也就只有 Kubernetes 一个而已，相对于顾头不顾尾的 Provider 来说，Kubernetes + Operator 才是正道；然而形势比人强，目前 Terraform 的确是能帮用户踏上 IaC 旅程的方便法门。&lt;/p&gt;

&lt;p&gt;就像 Kubernetes 之于云原生，对于 IaC 来说，Terraform 也同样有着 Day2 的问题。并且由于面对更大范围、更大成本的调度能力，Terraform 的管控也面临更大的挑战。例如工作流、合规、安全、成本等方面的考虑，以及偏差检测、模块安全等特性的应用，都是摆在管理员面前的明显问题。围绕这一工具，想要构建一个稳健的自助平台，是个颇为复杂的困难过程。&lt;/p&gt;

&lt;h2 id=&#34;terranetes-简介&#34;&gt;Terranetes 简介&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;appvia.io&lt;/code&gt; 是一个位于伦敦的解决方案厂商，它的开源项目 &lt;code&gt;Terranetes&lt;/code&gt; 就尝试解决 Terraform 的 Day2 问题。这个 golang 项目在 22 年开源，到现在为止，发布了一百多个 Release，Star 数量只有 135 个，上次代码更新是在两个月前，从运作成绩来看，似乎不太成功。然而从平台工程的角度来看，这个项目提供的众多特性，却是非常值得关注和致敬的。&lt;/p&gt;

&lt;p&gt;Terranetes 首页有这样一张图，形象地描述了这一产品的 IaC 治理思路：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/terranetes-controller-arch.png&#34; alt=&#34;governerce&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;工作流&#34;&gt;工作流&lt;/h3&gt;

&lt;p&gt;图里简单地描述了一个工作流，平台团队制定规则、开发团队申请资源，控制器接收申请、引用凭据、执行规范，按照流程中设定的审批、Lint 等环节，完成资源申请过程。&lt;/p&gt;

&lt;p&gt;他把用户明确地区分为平台和开发两种角色：&lt;/p&gt;

&lt;h4 id=&#34;平台团队&#34;&gt;平台团队&lt;/h4&gt;

&lt;p&gt;平台用户的主要职责是为开发者用户提供自助服务的基础支持：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;接入资源供应商&lt;/strong&gt;：用 CRD 的形式定义资源供应商，以此来支撑开发者使用指定供应商的资源。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;凭据管理&lt;/strong&gt;：管理接入资源供应商的身份凭据，开发者无需自行管理凭据，即可获得操作资源的权限。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;策略管理&lt;/strong&gt;：从成本、合规、安全等方面，提供策略支持，保障开发者对资源的有效使用。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;开发团队&#34;&gt;开发团队&lt;/h4&gt;

&lt;p&gt;开发者的职责相对集中一些：引用平台团队维护的凭据、规则、模板等，按照既定工作流进行资源的申请和使用。&lt;/p&gt;

&lt;h2 id=&#34;上手一试&#34;&gt;上手一试&lt;/h2&gt;

&lt;p&gt;官方提供了 Quick Start 文档，网络条件允许的话，几分钟就可以完成第一次资源分配。下面的例子我使用 AWS 作为资源供应商，本机的 OrbStack 提供给 Terranetes 作为控制器的 Kubernetes 运行平台。&lt;/p&gt;

&lt;h3 id=&#34;先决条件&#34;&gt;先决条件&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes 集群&lt;/li&gt;
&lt;li&gt;AWS 账号，有能够创建 S3 Bucket 的 AK/SK&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;部署&#34;&gt;部署&lt;/h3&gt;

&lt;p&gt;基于 Helm 的老套部署方式：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ helm repo add appvia https://terranetes-controller.appvia.io
$ helm repo update
$ helm install -n terraform-system terranetes-controller appvia/terranetes-controller --create-namespace
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;运行后可以看到多出了 &lt;code&gt;terraform-system&lt;/code&gt; 命名空间，其中运行了两个控制器 Pod。&lt;/p&gt;

&lt;h3 id=&#34;crd&#34;&gt;CRD&lt;/h3&gt;

&lt;p&gt;看看多出了哪些 CRD：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ 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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;官网文档用下图来描述对象之间的关系：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/cloudresources.png&#34; alt=&#34;CRDs&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CloudResources&lt;/strong&gt;：这个对象用来描述 Terraform 中的云资源，CloudResources 会选择性的向用户公开属性，从而减少开发者的心智负担，并确保选项符合组织意图。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Revisions&lt;/strong&gt;：Revision 是云资源的模板，在实际环境中，应该是经过策划和测试的版本化资产，其中会包含组织所需的默认设置，并只公开与上下文相关的功能：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revision 指向 Terraform 模块。&lt;/li&gt;
&lt;li&gt;包含平台希望模块默认设置的所有默认选项，并向消费者公开部分可见选项。&lt;/li&gt;
&lt;li&gt;可以跟踪 Terraform 模块，也可独立演进。&lt;/li&gt;
&lt;li&gt;所有 CloudResources 都会引用集群中的 Revision。&lt;/li&gt;
&lt;li&gt;要升级 CloudResource 消费者，需要更新其指向的 Revision。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Configurations&lt;/strong&gt;：另一种描述云资源的方法，相对于 CloudResource，它相对传统一些，采用了和 Module 进行一对一连接的方式。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Providers&lt;/strong&gt;：用来连接云资源提供方的定义。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plans&lt;/strong&gt;：无需主动创建，随 &lt;code&gt;Revision&lt;/code&gt; 对象的定义自然产生。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policies&lt;/strong&gt;：对策略的引用，其中包含了 &lt;code&gt;Checkov&lt;/code&gt; 规则的引用方法、对 Module 的限制。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contexts&lt;/strong&gt;：上下文提供了一种在配置之间共享通用配置的方法。集群中任何组件都可以引用该资源。&lt;/p&gt;

&lt;h3 id=&#34;配置凭据和-provider&#34;&gt;配置凭据和 Provider&lt;/h3&gt;

&lt;p&gt;接下来配置用来连接 AWS：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ 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=&amp;quot;https://raw.githubusercontent.com/appvia/terranetes-controller/master/examples/provider.yaml&amp;quot;
$ kubectl apply -f $PROVIDER
provider.terraform.appvia.io/aws created
provider.terraform.appvia.io/aws-irsa created
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到 Provider 对象的 &lt;code&gt;spec&lt;/code&gt; 内容：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  spec:
    preload:
      cluster: wayfinder-production
      context: default
      enabled: false
      region: eu-west-2
    provider: aws
    secretRef:
      name: aws
      namespace: terraform-system
    source: secret
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中指定了 AWS 作为资源供应商，并且使用前面的 Secret 作为连接凭据。&lt;/p&gt;

&lt;h3 id=&#34;配置-revision&#34;&gt;配置 Revision&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ export REV=&amp;quot;https://raw.githubusercontent.com/appvia/terranetes-controller/master/examples/revision.yaml&amp;quot;
$ kubectl apply -f ${REV}
revision.terraform.appvia.io/bucket.v1 created
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;打开 Revision 文件，能够大致观察到其中包含的信息：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;configuration&lt;/strong&gt;：其中有 Module 的地址、Context 引用、Provider 引用以及&lt;strong&gt;默认值&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;inputs&lt;/strong&gt;：这里定义了用户可以控制的内容（此处只有 &lt;code&gt;bucket&lt;/code&gt; 可写）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;plan&lt;/strong&gt;：则定义了该对象所属的计划。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Revision&lt;/code&gt; 中定义的 &lt;code&gt;Plan&lt;/code&gt; 也被自动创建了：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl get plan bucket -o yaml
apiVersion: terraform.appvia.io/v1alpha1
kind: Plan
metadata:
  ...
spec:
  revisions:
  - name: bucket.v1
    revision: v0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;使用-plan-revision-创建资源&#34;&gt;使用 Plan-&amp;gt;Revision 创建资源&lt;/h2&gt;

&lt;p&gt;上面两节完成了平台管理或者资源管理角色的任务，接下来要真正地申请资源了。这里需要创建如下的 &lt;code&gt;CloudService&lt;/code&gt; 对象：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;执行之后，我们会看到，系统中有了一个运行成功的 &lt;code&gt;Job&lt;/code&gt; 对象，以及一个 &lt;code&gt;CloudResource&lt;/code&gt; 对象：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ 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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;任务已经完成，打开 AWS 控制台，也找不到我们要的 Bucket。看看 Job 对应的 Pod 日志：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;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 &amp;quot;aws_s3_bucket_server_side_encryption_configuration&amp;quot; &amp;quot;this&amp;quot; {
...
  # aws_s3_bucket_versioning.this[0] will be created
  + resource &amp;quot;aws_s3_bucket_versioning&amp;quot; &amp;quot;this&amp;quot; {
...
Plan: 6 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;搞 Terraform 的读者会看得出来，他似乎只做了 Plan，没有做实际的 Apply。&lt;/p&gt;

&lt;p&gt;文档解释说，默认情况下，资源的创建是需要被批准的，除非是使用了注解：&lt;code&gt;terraform.appvia.io/apply=true&lt;/code&gt;，所以我们现在加入这个：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl annotate cloudresources bucket &amp;quot;terraform.appvia.io/apply&amp;quot;=true --overwrite
cloudresource.terraform.appvia.io/bucket annotated
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接下来会看到一个新的 Job 在运行，类似 &lt;code&gt;bucket-l6jkf-1-apply-xp45d&lt;/code&gt; 这样的名称。这个 Pod 运行成功之后，会看到 S3 桶已经创建。&lt;/p&gt;

&lt;h3 id=&#34;客户端&#34;&gt;客户端&lt;/h3&gt;

&lt;p&gt;Terranetes 还有个叫 &lt;code&gt;tnctl&lt;/code&gt; 的命令行客户端软件，提供了任务跟踪、审批等能力，避免大量使用 &lt;code&gt;kubectl&lt;/code&gt; 来完成这些任务。&lt;/p&gt;

&lt;h2 id=&#34;其他&#34;&gt;其他&lt;/h2&gt;

&lt;h3 id=&#34;偏差检测&#34;&gt;偏差检测&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CloudResource&lt;/code&gt; 有个字段 &lt;code&gt;spec.enableDriftDetection&lt;/code&gt;，设置为 &lt;code&gt;True&lt;/code&gt; 就可以启动偏差检测。例如上面的 CloudResource，我们修改这个字段为 True 之后，删除对应的桶，一段时间之后，这个资源就会变成 &lt;code&gt;OutOfSync&lt;/code&gt; 状态。&lt;/p&gt;

&lt;h3 id=&#34;监控&#34;&gt;监控&lt;/h3&gt;

&lt;p&gt;Controller 自带了 Prometheus 的指标抓取端口，提供了请求数、延迟时间等简单的指标。&lt;/p&gt;

&lt;p&gt;除了这些基本功能之外，Terranetes 还能对接 &lt;code&gt;Infracost&lt;/code&gt; 进行成本数据采集和预测，不过这是个商业产品，就没有进一步尝试了。&lt;/p&gt;

&lt;p&gt;总之，Terranetes 提供了一个相对全面的框架，其中展示的工作流、策略、分权等设计，都是很好的范本，很值得工具平台、IaC 相关方案的设计者们参考和学习。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
