当我构建基础设施时,我将其作为代码来完成。向基础设施即代码的运动意味着每个更改都是可见的,无论是通过配置管理文件还是全面的 GitOps。
Terraform 是一种用于构建、升级和维护您的基础设施即代码的工具。正如其 GitHub 页面 所解释的那样
“Terraform 使您能够安全且可预测地创建、更改和改进基础设施。它是一个开源工具,可将 API 编纂成声明性配置文件,这些文件可以在团队成员之间共享、作为代码处理、编辑、审查和版本控制。”
最好的部分是您可以将现有基础设施导入 Terraform 配置状态,并且所有未来的更改都将被跟踪。这种跟踪提供了对您的生产环境(和任何其他环境)的完整理解,可以备份到本地或远程 Git 存储库以对整个基础设施进行版本控制。
在本文中,我将解释如何使用 Terraform 在 Minikube Kubernetes 集群中管理状态。
先决条件
Terraform 是云无关的,因此您可以使用相关的提供商在任何云中运行几乎任何类型的 Kubernetes 集群。提供商是 Terraform 插件架构的核心,每个提供商都“负责理解 API 交互和公开资源”,以便主要的 Terraform 项目可以保持精简,但该项目可以扩展到任何系统。
在本示例中,我将在 Linux 桌面使用 Terraform 11。要跟随操作,您还需要 Helm 2.16.7、Minikube 和 kubectl
- 按照此文档下载并配置 Minikube。
- 使用您的软件包管理器下载并配置 Helm,或者从版本手动完成。
- 按照这些步骤下载并安装 kubectl。
- 通过下载 Linux .tar 文件、解压文件并将其移动到
/usr/local/bin/terraform
来安装 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 条评论