当我构建基础设施时,我会将其作为代码进行。向基础设施即代码的转变意味着每个更改都是可见的,无论是通过配置管理文件还是完全成熟的GitOps。
Terraform 是一种用于将基础设施作为代码进行构建、升级和维护的工具。正如其 GitHub 页面 所解释的那样:
“Terraform 使您能够安全且可预测地创建、更改和改进基础设施。它是一个开源工具,可将 API 编纂成声明性配置文件,这些文件可以在团队成员之间共享、被视为代码、编辑、审查和版本控制。”
最棒的是,您可以将现有的基础设施导入到 Terraform 配置状态中,并且所有未来的更改都将被跟踪。这种跟踪提供了对您的生产环境(以及任何其他环境)的完整了解,可以备份到本地或远程 Git 存储库,以对整个基础设施进行版本控制。
在本文中,我将解释如何使用 Terraform 来管理 <0xC2><0xA0>Minikube Kubernetes 集群中的状态。
先决条件
Terraform 是云平台无关的,因此您可以使用相关的提供商在任何云平台中运行几乎任何类型的 Kubernetes 集群。提供商 是 Terraform 插件架构的核心,每个提供商都“负责理解 API 交互和暴露资源”,以便 Terraform 主项目可以保持精简,但项目可以扩展到任何系统。
在本示例中,我将在 Linux 桌面使用 Terraform 11。要跟随操作,您还需要 Helm 2.16.7、Minikube 和 kubectl。
- 按照此文档下载并配置 Minikube。
- 使用您的软件包管理器下载并配置 Helm,或者从发布版本手动进行。
- 按照这些步骤下载并安装 kubectl。
- 通过下载 Linux .tar 文件 安装 Terraform,解压文件,并将其移动到
/usr/local/bin/terraform
。
在运行构建之前,了解命令行实用程序提供的功能。运行 terraform
,输出将显示执行计划和应用命令。在底部,有一个升级清单,如果您使用的是旧版本的 Terraform,这将非常方便。开发人员内置了一个很棒的命令来检查版本之间的兼容性。
开始入门
要在 Terraform 中构建某些内容,您需要创建模块,模块是包含一组配置文件的文件夹,可以收集信息并执行您需要完成的操作。Terraform 文件的文件扩展名始终为 .tf
。从一个 main.tf
文件开始,例如:
jess@Athena:~/terraform_doc$ touch main.tf
在集群中开始进行任何操作之前,您还需要了解一些基本的 Terraform 命令和要求。
terraform init
:初始化 Terraform 工作目录
– 它必须与.tf
文件位于同一目录中,否则将不会发生任何事情。terraform validate
:确认 Terraform 文件的语法正确
– 始终运行此命令以确认代码构建正确,并且不会出现错误。terraform plan
:生成并显示在您运行terraform apply
时将发生的变化
– 在apply
之前运行此命令以确认结果符合您的预期。terraform apply
:构建或更改基础设施
– 它将显示执行计划,并要求您输入 yes 或 no 来执行,除非您使用--auto-approve
标志,这将使其自动执行。Terraform refresh
:根据实际资源更新本地状态文件
– 这确保 Terraform 准确了解当前环境中的内容。terraform destroy
:删除和移除 Terraform 管理的基础设施
– 这将永久删除集群中状态文件中创建和存储的任何内容。
对于本示例中的配置,所有由 Terraform 控制的内容都保存在本地状态文件中。根据 Terraform 的 文档:
“默认情况下,此状态存储在名为
terraform.tfstate
的本地文件中,但也可以远程存储,这在团队环境中效果更好。Terraform 使用此本地状态来创建计划并更改您的基础设施。在任何操作之前,Terraform 都会执行刷新以使用实际基础设施更新状态。”
既然您已经了解了这些背景信息,您可以继续编辑您的 main.tf
文件,检查您的集群,并努力使用 Terraform 添加配置。
准备和构建 Minikube
在开始使用 Terraform 之前,您必须创建一个 Minikube 集群。此示例使用 Minikube 版本 v1.9.2。运行 minikube start
。
jess@Athena:~/terraform_doc$ minikube start
? minikube 1.11.0 is available! Download it: https://github.com/kubernetes/minikube/releases/tag/v1.11.0
? To disable this notice, run: 'minikube config set WantUpdateNotification false'
? minikube v1.9.2 on Ubuntu 18.04
✨ Using the kvm2 driver based on existing profile
? Starting control plane node m01 in cluster minikube
? Restarting existing kvm2 VM for "minikube" ...
? Preparing Kubernetes v1.18.0 on Docker 19.03.8 ...
? Enabling addons: default-storageclass, storage-provisioner
? Done! kubectl is now configured to use "minikube"
检查您的新集群并添加命名空间
使用您可靠的 kubectl
命令检查您的新 Minikube 集群。
jess@Athena:~/terraform_doc$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 4m5s v1.18.0
集群已启动并运行,因此将配置添加到您的 main.tf
文件中。首先,您需要一个 提供商,它“负责理解 API 交互和暴露资源”。本示例中的提供商将被(恰如其分地)命名为 Kubernetes。编辑您的 main.tf
文件并添加提供商:
provider "kubernetes" {
config_context_cluster = "minikube"
}
此语法告诉 Terraform 集群正在 Minikube 中运行。
现在您将需要资源块的定义。资源块描述一个或多个基础设施对象,例如虚拟网络、计算实例或更高级别的组件,例如 DNS 记录。
向集群添加一个 Kubernetes 命名空间
resource "kubernetes_namespace" "1-minikube-namespace" {
metadata {
name = "my-first-terraform-namespace"
}
}
接下来,运行 terraform init
命令以检查您的提供商版本并初始化 Terraform。
jess@Athena:~/terraform_doc$ terraform init
Initializing provider plugins...
The following providers do not have any version constraints in configuration,
so the latest version was installed.
To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.
* provider.kubernetes: version = "~> 1.11"
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
运行您的计划以查看将要执行的操作。
jess@Athena:~/terraform_doc$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
+ kubernetes_namespace.1-minikube-namespace
id: <computed>
metadata.#: "1"
metadata.0.generation: <computed>
metadata.0.name: "my-first-terraform-namespace"
metadata.0.resource_version: <computed>
metadata.0.self_link: <computed>
metadata.0.uid: <computed>
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
现在您知道了 Terraform 将要做什么,应用您的配置。
jess@Athena:~/terraform_doc$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
+ kubernetes_namespace.1-minikube-namespace
id: <computed>
metadata.#: "1"
metadata.0.generation: <computed>
metadata.0.name: "my-first-terraform-namespace"
metadata.0.resource_version: <computed>
metadata.0.self_link: <computed>
metadata.0.uid: <computed>
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
-----------------------------------
kubernetes_namespace.1-minikube-namespace: Creating...
metadata.#: "" => "1"
metadata.0.generation: "" => "<computed>"
metadata.0.name: "" => "my-first-terraform-namespace"
metadata.0.resource_version: "" => "<computed>"
metadata.0.self_link: "" => "<computed>"
metadata.0.uid: "" => "<computed>"
kubernetes_namespace.1-minikube-namespace: Creation complete after 0s (ID: my-first-terraform-namespace)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
最后,通过运行 kubectl get ns
确认您的新命名空间存在。
jess@Athena:~/terraform_doc$ kubectl get ns
NAME STATUS AGE
default Active 28d
kube-node-lease Active 28d
kube-public Active 28d
kube-system Active 28d
my-first-terraform-namespace Active 2m19s
通过 Helm Chart 运行
能够手动编写 Terraform 配置文件,运行它,并在 Kubernetes 中看到结果是很棒的。更棒的是什么?能够通过 Helm Chart 重新运行相同的命令。
运行 helm create <name>
命令以生成 Chart。
$ helm create buildachart
Creating buildachart
您需要此练习的另一个提供商块。有一个特定的 Helm 提供商,它需要 Kubernetes 集群名称,以便 Helm 知道在哪里安装其 Chart。将新的提供商(如下所示)添加到您现有的 main.tf
文件中。
provider "helm" {
kubernetes {
config_context_cluster = "minikube"
}
}
现在 Helm 已配置,您需要为此 terraform 模块 添加一个 Helm Chart 以进行安装。为了保持简单,将 Helm Chart 保存在您用于 Terraform 状态的同一文件夹中。
jess@Athena:~/terraform_doc$ ls
buildachart main.tf terraform.tfstate
添加新的 Helm 资源,以便可以使用 helm_release
资源安装 Chart 并通过您的 Terraform 状态进行跟踪。我将此资源命名为 local
,并导入了我的 Chart 名称和 Chart 位置。
resource "helm_release" "local" {
name = "buildachart"
chart = "./buildachart"
}
现在您已经添加了这些部分,再次运行 Terraform 的初始化命令。它将根据您的更改更新状态,包括下载新的提供商。
jess@Athena:~/terraform_doc$ terraform init
Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "helm" (1.2.2)...
The following providers do not have any version constraints in configuration,
so the latest version was installed.
To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.
* provider.helm: version = "~> 1.2"
* provider.kubernetes: version = "~> 1.11"
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
然后计划您的新配置。
jess@Athena:~/terraform_doc$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-first-terraform-namespace)
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
+ helm_release.local
id: <computed>
atomic: "false"
chart: "./buildachart"
cleanup_on_fail: "false"
create_namespace: "false"
dependency_update: "false"
disable_crd_hooks: "false"
disable_openapi_validation: "false"
disable_webhooks: "false"
force_update: "false"
lint: "false"
max_history: "0"
metadata.#: <computed>
name: "buildachart"
namespace: "default"
recreate_pods: "false"
render_subchart_notes: "true"
replace: "false"
reset_values: "false"
reuse_values: "false"
skip_crds: "false"
status: "deployed"
timeout: "300"
verify: "false"
version: "0.1.0"
wait: "true"
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
应用您的配置,这次只添加 --auto-approve
标志,以便它无需确认即可执行。
jess@Athena:~/terraform_doc$ terraform apply --auto-approve
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-first-terraform-namespace)
helm_release.local: Creating...
atomic: "" => "false"
chart: "" => "./buildachart"
cleanup_on_fail: "" => "false"
create_namespace: "" => "false"
dependency_update: "" => "false"
disable_crd_hooks: "" => "false"
disable_openapi_validation: "" => "false"
disable_webhooks: "" => "false"
force_update: "" => "false"
lint: "" => "false"
max_history: "" => "0"
metadata.#: "" => "<computed>"
name: "" => "buildachart"
namespace: "" => "default"
recreate_pods: "" => "false"
render_subchart_notes: "" => "true"
replace: "" => "false"
reset_values: "" => "false"
reuse_values: "" => "false"
skip_crds: "" => "false"
status: "" => "deployed"
timeout: "" => "300"
verify: "" => "false"
version: "" => "0.1.0"
wait: "" => "true"
helm_release.local: Creation complete after 8s (ID: buildachart)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
尽管它说 Chart 已部署,但始终最好再次检查并确认新的 Helm Chart 已就位。使用 kubectl
命令检查您的 Pod 是否已创建。
jess@Athena:~/terraform_doc$ kubectl get pods
NAME READY STATUS RESTARTS AGE
buildachart-68c86ccf5f-lchc5 1/1 Running 0 43s
这确认您的 Pod 正在运行,这意味着您的 Chart 已部署!您还有一个新的备份状态文件。
jess@Athena:~/terraform_doc$ ls
buildachart main.tf terraform.tfstate terraform.tfstate.backup
Terraform 对状态具有保护性,这是一个很棒的功能。它会在每次更新后自动生成一个先前的状态文件。这允许对您的基础设施进行版本控制,您可以始终保存当前和最近的状态。由于这是一个本地构建,因此请坚持使用当前状态和先前状态,而无需版本控制。
回滚更改并导入某些内容
当您运行 Terraform 命令时,备份状态文件会生成和更新,这意味着您可以准确地回滚一次之前的更改,除非您将状态文件保存在其他位置的存储中(例如,数据库),并使用其他配置来管理文件。
在本示例中,您需要回滚您的 Helm Chart 部署。为什么?好吧,因为您可以。
在您执行任何操作之前,花一点时间运行 terraform refresh
命令,以查看集群和当前状态之间是否存在任何差异。
jess@Athena:~/terraform_doc$ terraform refresh
helm_release.local: Refreshing state... (ID: buildachart)
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-first-terraform-namespace)
有一个奇怪的解决方法可以回滚更改:您可以将备份文件覆盖您的状态文件,或者您可以注释掉您通过 Terraform 文件推出的代码更改,并允许 Terraform 销毁它们。
在本示例中,我将注释掉代码并重新运行 Terraform,以便删除 Helm Chart。注释在 Terraform 文件中以 //
开头。
jess@Athena:~/terraform_doc$ cat main.tf
provider "kubernetes" {
config_context_cluster = "minikube"
}
resource "kubernetes_namespace" "1-minikube-namespace" {
metadata {
name = "my-first-terraform-namespace"
}
}
//provider "helm" {
// kubernetes {
// config_context_cluster = "minikube"
// }
//}
//resource "helm_release" "local" {
// name = "buildachart"
// chart = "./buildachart"
//}
在您注释掉所有内容后,运行 terraform apply
。
jess@Athena:~/terraform_doc$ terraform apply
helm_release.local: Refreshing state... (ID: buildachart)
null_resource.minikube: Refreshing state... (ID: 4797320155365789412)
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-terraform-namespace)
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
- helm_release.local
Plan: 0 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
helm_release.local: Destroying... (ID: buildachart)
helm_release.local: Destruction complete after 0s
Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
要查看覆盖文件是什么样的,请重新应用 Helm Chart 并覆盖状态文件。这是正在重新创建 Chart 的代码片段(此文本输出可能很长):
helm_release.local: Still creating... (10s elapsed)
helm_release.local: Creation complete after 15s (ID: buildachart)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Here’s the file overwrite and the plan showing that the helm chart needs to be rerun.
jess@Athena:~/terraform_doc$ cp terraform.tfstate.backup terraform.tfstate
jess@Athena:~/terraform_doc$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
null_resource.minikube: Refreshing state... (ID: 4797320155365789412)
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-terraform-namespace)
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
+ helm_release.local
id: <computed>
atomic: "false"
chart: "./buildachart"
cleanup_on_fail: "false"
create_namespace: "false"
dependency_update: "false"
disable_crd_hooks: "false"
disable_openapi_validation: "false"
disable_webhooks: "false"
force_update: "false"
max_history: "0"
metadata.#: <computed>
name: "buildachart"
namespace: "default"
recreate_pods: "false"
render_subchart_notes: "true"
replace: "false"
reset_values: "false"
reuse_values: "false"
skip_crds: "false"
status: "deployed"
timeout: "300"
verify: "false"
version: "0.1.0"
wait: "true"
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
请注意,如果您在覆盖状态文件时未清理环境,您将遇到问题。该问题在名称使用中显示出来。
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
helm_release.local: Creating...
atomic: "" => "false"
chart: "" => "./buildachart"
cleanup_on_fail: "" => "false"
create_namespace: "" => "false"
dependency_update: "" => "false"
disable_crd_hooks: "" => "false"
disable_openapi_validation: "" => "false"
disable_webhooks: "" => "false"
force_update: "" => "false"
max_history: "" => "0"
metadata.#: "" => "<computed>"
name: "" => "buildachart"
namespace: "" => "default"
recreate_pods: "" => "false"
render_subchart_notes: "" => "true"
replace: "" => "false"
reset_values: "" => "false"
reuse_values: "" => "false"
skip_crds: "" => "false"
status: "" => "deployed"
timeout: "" => "300"
verify: "" => "false"
version: "" => "0.1.0"
wait: "" => "true"
Error: Error applying plan:
1 error occurred:
* helm_release.local: 1 error occurred:
* helm_release.local: cannot re-use a name that is still in use
Terraform does not automatically rollback in the face of errors.
Instead, your Terraform state file has been partially updated with
any resources that successfully completed. Please address the error
above and apply again to incrementally change your infrastructure.
由于注释掉和重新引入资源,这会创建一个重用问题。要解决此问题,请执行状态导入,这允许您获取环境中已存在的内容,并让 Terraform 再次开始跟踪它。
为此,您需要要导入的命名空间和 Chart 名称以及您正在导入的模块名称。在本例中,模块是 helm.local
,它从您的资源代码中生成,该资源以 "helm_release" "local"
开头。
对于重新导入,您将需要资源当前部署到的命名空间,因此导入将类似于 default/buildachart
。对于涉及命名空间的任何内容,都需要此格式。
jess@Athena:~/terraform_doc$ terraform import helm_release.local default/buildachart
helm_release.local: Importing from ID "default/buildachart"...
helm_release.local: Import complete!
Imported helm_release (ID: buildachart)
helm_release.local: Refreshing state... (ID: buildachart)
Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
重新导入的过程可能很棘手,但对于状态管理非常重要。
清理
Terraform 的优点在于,您可以在测试后快速清理自身。如果您不小心在哪里运行 follow
命令,则该命令是破坏您一天的另一种好方法。
jess@Athena:~/terraform_doc$ terraform destroy
helm_release.local: Refreshing state... (ID: buildachart)
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-first-terraform-namespace)
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
- helm_release.local
- kubernetes_namespace.1-minikube-namespace
Plan: 0 to add, 0 to change, 2 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
helm_release.local: Destroying... (ID: buildachart)
kubernetes_namespace.1-minikube-namespace: Destroying... (ID: my-first-terraform-namespace)
helm_release.local: Destruction complete after 1s
kubernetes_namespace.1-minikube-namespace: Destruction complete after 7s
Destroy complete! Resources: 2 destroyed.
运行一次 terraform destroy
即可删除您的 Pod 和命名空间,但您的集群仍然存在。您又回到了起点。
jess@Athena:~/terraform_doc$ kubectl get pods
No resources found in default namespace.
jess@Athena:~/terraform_doc$ kubectl get ns
NAME STATUS AGE
default Active 28d
kube-node-lease Active 28d
kube-public Active 28d
kube-system Active 28d
jess@Athena:~/terraform_doc$ minikube status
m01
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
最终说明
说您可以使用 Terraform 创建任何东西都是轻描淡写的。它是一种可以管理环境创建和销毁各个方面的工具。它具有强大而简单的状态管理概念,可以使团队与组织预期的基础设施保持同步。
但是,如果您不小心,此工具可能会非常不宽容。如果您移动状态文件并且不注意,您可能会导致 Terraform 认为它需要管理的内容出现一些问题。当您使用此工具时,请注意不要过度扩展自己或一次编写太多,因为如果您不注意,您可能会将自己编码到死胡同中。
Terraform 最适合用于基础设施配置。它可以最大限度地减少管理状态时发生的问题,并允许为配置和部署设计的工具来补充其功能。
您使用 Terraform 和 Kubernetes 完成了什么?在下面的评论中分享您的经验。
3 条评论