Terraform state là gì

Terraform là một công cụ để build, cập nhật, và quản lý hạ tầng của bạn bằng cách sử dụng code. Giống như Github đã trích dẫn:

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is an open source tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.

Dịch: Terraform cho phép bạn tạo, thay đổi, và nâng cấp cơ sở hạ tầng dễ dàng do có thể xem trước các thay đổi. Nó là một công cụ mã nguồn mở cho phép chuyển đổi các API dưới dạng những tệp cấu hình có thể chia sẻ, được biểu diễn dưới dạng code để xem, chỉnh sửa và sự khác nhau giữa các phiên bản cấu hình.

Vì vậy, phần lợi hại nhất của Terraform là bạn có thể import hạ tầng vốn có của bạn vào trong Terraform configuration state, nhờ đó tất cả các thay đổi trong tương lai có thể theo dõi được. Thứ có thể theo dõi được ở đây chính là toàn bộ thông tin về môi trường procduction của bạn. Trong bài viết hôm nay, mình sẽ đề cập đến vấn đề tại sao phải sử dụng Terraform và làm thế nào để sử dụng nó.

Chuẩn bị môi trường #

Terraform là cloud-agnostic nên bạn có thể chạy với bất kì Kubernetes Cluster, nào trên bất kì Cloud nào sử dụng bằng cách sử dụng các Providers mà được cung cấp sẵn. Một Provider là phần lõi của nền tảng mà Terraform hỗ trợ, có trách nhiệm tương tác với API và cung cấp tài nguyên.

Trong bài viết này, mình sử dụng Terraform v0.13.4 trên tel4vn Server 20.04. Để có thể theo dõi bài viết, bạn đọc hãy chuẩn bị môi trường như sau:

  • Tải xuống và cài đặt Minikube v1.14.0 theo tài liệu hướng dẫn sau: Minikube Documention
  • Tải xuống và cài đặt Helm v3.4.0-rc.1 theo hướng dẫn sau: Helm Documention
  • Tải xuống và cài đặt kubectl để thao tác với Kubernetes Cluster: kubectl Documention
  • Tải xuống và cài đặt Terraform v0.13.4  tại đây. Giải nén rồi move vào /usr/local/bin/terraform

Sơ lược về các câu lệnh trong Terraform #

Để có thể build được với Terraform, bạn cần phải tạo module. Đây sẽ là thư mục gồm các file config để cung cấp thông tin cho việc thực thi. File config của Terraform có đuôi .tf nên trước hết bạn cần tạo file main.tf:

tel4vn@devops:~/terraform_example$ touch main.tf

Dưới đây là một số câu lệnh Terraform thường sử dụng mà bạn đọc cần biết:

  • terraform init

    Khởi tạo một thư mục làm việc với Terraform. Câu lệnh này phải được chạy trong cùng thư mục với các file .tf nếu không thì sẽ không gì diễn ra khi thực thi.

  • terraform validate

    Kiểm trả cú pháp các cài đặt trong file .tf .

  • terraform plan

    Đọc file config và hiển thị những thay đổi nếu thực thi

  • terraform apply

    áp dụng và thực thi các cài đặt trong file config.

  • terraform refresh

    Cập nhật trạng thái hiện tại của các tài nguyên

  • terraform destroy

    Xoá các hạ tầng được quản lý bởi Terraform mà lưu trong file terraform.tfstate . Câu lệnh này sẽ xoá vĩnh viễn bất kì đối tượng, tài nguyên nào được tạo và chỉ định trong file terraform.tfstate

Khởi tạo Minikube Cluster sử dụng Minikube #

Trước khi bắt đầu với Terraform, bạn cần phải tạo một Minikube Cluster.

tel4vn@devops: minikube start

Sau khi chạy câu lệnh trên, kiểm tra lại với kubectl

tel4vn@devops:~/terraform_example$ kubectl get nodes NAME STATUS ROLES AGE VERSION minikube Ready master 20h v1.19.2

Cluster đã lên, vậy tiếp theo, bạn cần thêm cài đặt vào file main.tf . Trước hết, bạn cần một provider . Trong bài viết này, provider của bạn là kubernetes.

provider "kubernetes" {   config_context_cluster   = "minikube" }

Theo cú pháp trên, bạn đã khai báo với Terraform rằng Cluster mà bạn đang chạy là Minikube Cluster.

Việc tiếp theo là phải chỉ định các resource block. Đó là các đoạn chỉ định một hoặc nhiều đối tượng trong hạ tầng của bạn. cụ thể: virtual networks, computer instances, or DNS Records, …

Thực hiện thêm một Kubernetes namespace vào trong cluster:

resource "kubernetes_namespace" "minikube-namespace" {   metadata {     name = "tel4vn-namespace"   } }

Tiếp theo, thực hiện lệnh terraform init để kiểm tra phiên bản và khởi tạo Terraform trong thư mục hiện tại:

tel4vn@devops:~/terraform_example$ terraform init Initializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/kubernetes... - Installing hashicorp/kubernetes v1.13.2... - Installed hashicorp/kubernetes v1.13.2 (signed by HashiCorp) 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, we recommend adding version constraints in a required_providers block in your configuration, with the constraint strings suggested below. * hashicorp/kubernetes: version = "~> 1.13.2" 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.

Tiếp thục terraform plan để kiểm tra những thay đổi có thể xảy ra:

tel4vn@devops:~/terraform_example$ 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.minikube-namespace will be created + resource "kubernetes_namespace" "minikube-namespace" { + id = (known after apply) + metadata { + generation = (known after apply) + name = "tel4vn-namespace" + resource_version = (known after apply) + self_link = (known after apply) + uid = (known after apply) } } 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.

Sau khi xem xét những thay đổi, thực hiện apply các thay đổi đó:

tel4vn@devops:~/terraform_example$ 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.minikube-namespace will be created + resource "kubernetes_namespace" "minikube-namespace" { + id = (known after apply) + metadata { + generation = (known after apply) + name = "tel4vn-namespace" + resource_version = (known after apply) + self_link = (known after apply) + uid = (known after apply) } } 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.minikube-namespace: Creating... kubernetes_namespace.minikube-namespace: Creation complete after 0s [id=tel4vn-namespace] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Cuối cùng, xác nhận lại các thay đổi đã được áp dụng hay chưa sử dụng kubectl get ns

tel4vn@devops:~/terraform_example$ kubectl get ns NAME STATUS AGE default Active 20h kube-node-lease Active 20h kube-public Active 20h kube-system Active 20h tel4vn-namespace Active 93s

Sử dụng Helm chart với Terraform #

Từ phần trên, bạn đã biết các viết file config cho Terraform và chạy nó. Tiếp theo sẽ là chạy sử dụng qua Helm chart, dùng helm create để tạo một chart:

tel4vn@devops:~/terraform_example$ helm create tel4vnchart Creating tel4vnchart

Như vậy, bạn cần chỉ định một provider để có thể làm việc được với Helm chart và chỉ định tên Kubernetes Cluster để Helm biết nơi nó sẽ cài đặt các chart. Thực hiện thêm một block mới trong main.tf như sau:

provider "helm" {   kubernetes {         config_context_cluster   = "minikube"         } }

Sau đó cần phải thêm một Helm resource để chart này có thể được cài đặt và theo dõi bởi Terraform của bạn bằng cách sử dụng tài nguyên mà provider Helm cung cấp đó là helm _release.  mình đặt tên resource này là local. Bạn đọc có thể đặt tên bất kì.

resource "helm_release" "local" {   name          = "tel4vnchart"   chart         = "./tel4vnchart" }

Sau khi cập nhật thêm config trong main.tf, bạn chạy lại phần khởi tạo để cập nhập trạng thái thay đổi, bao gồm cả việc tải mới provider.

tel4vn@devops:~/terraform_example$ terraform init Initializing the backend... Initializing provider plugins... - Using previously-installed hashicorp/kubernetes v1.13.2 - Finding latest version of hashicorp/helm... - Installing hashicorp/helm v1.3.2... - Installed hashicorp/helm v1.3.2 (signed by HashiCorp) 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, we recommend adding version constraints in a required_providers block in your configuration, with the constraint strings suggested below. * hashicorp/helm: version = "~> 1.3.2" * hashicorp/kubernetes: version = "~> 1.13.2" 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.

Thống kê sự thay đổi sẽ được thực thi:

tel4vn@devops:~/terraform_example$ 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.minikube-namespace: Refreshing state... [id=tel4vn-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 will be created + resource "helm_release" "local" { + atomic = false + chart = "./tel4vnchart" + cleanup_on_fail = false + create_namespace = false + dependency_update = false + disable_crd_hooks = false + disable_openapi_validation = false + disable_webhooks = false + force_update = false + id = (known after apply) + lint = false + max_history = 0 + metadata = (known after apply) + 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 = (known after apply) + 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.

Sau khi xem xét thay đổi, cập nhật các thay đổi ngay lập tức bằng cách sử dụng –auto-approve

tel4vn@devops:~/terraform_example$ terraform apply --auto-approve kubernetes_namespace.minikube-namespace: Refreshing state... [id=tel4vn-namespace] helm_release.local: Creating... helm_release.local: Still creating... [10s elapsed] helm_release.local: Still creating... [20s elapsed] helm_release.local: Creation complete after 28s [id=tel4vnchart] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Kiểm tra lại chart đã được thực sự triển khai bằng cách sử dụng kubectl

tel4vn@devops:~/terraform_example$ kubectl get pods NAME READY STATUS RESTARTS AGE tel4vnchart-59bdc8554d-8l4w7 1/1 Running 0 2m8s

Bạn có thể thấy, chart đã thực sự được Terraform deploy và trong thư mục cũng đồng thời có file backup state được tạo ra.

tel4vn@devops:~/terraform_example$ ls tel4vnchart main.tf terraform.tfstate terraform.tfstate.backup

Như bạn thấy, Terraform cung cấp tính năng sao lưu cho trạng thái của hạ tầng. Một bản sao lưu trạng thái hạ tầng gần nhất sẽ được tạo ra mỗi khi một trạng thái mới được khởi tạo. Đồng thời cho phép người dùng có thể quản lý các phiên bản trạng thái.

Rollback phiên bản trạng thái trước đó #

Như đã nói trước đó, khi bạn cập nhật các cài đặt mới sử dụng Terraform, file backup state sẽ được tạo và cập nhật liên tục mình phiên bản gần nhất. Vì một lý do nào đó, bạn phải quay lại phiên bản trạng thái hạ tầng trước đó. cụ thể: phiên bản mới có xung đột dẫn đến một ứng dụng nào đó không hoạt động đúng.

Trước hết, hãy chạy câu lệnh terraform refresh để xem sự khác nhau giữa trạng thái hiện tại và cluster

tel4vn@devops:~/terraform_example$ terraform refresh helm_release.local: Refreshing state... [id=tel4vnchart] kubernetes_namespace.minikube-namespace: Refreshing state... [id=tel4vn-namespace]

Sau đó, để rollback lại được phiên bản trước, có 2 cách:

  • Ghi dè file state hiện tại bằng file backup state
  • Comment những block gây ra sự thay đổi giữa trạng thái hiện tại với trạng thái trước trong file .tf

Trong bài viết này, mình sẽ thực hiện comment những thay đổi, cụ thể là block đã deploy helm chart:

tel4vn@devops:~/terraform_example$ cat main.tf provider "kubernetes" { config_context_cluster = "minikube" } resource "kubernetes_namespace" "minikube-namespace" { metadata { name = "tel4vn-namespace" } } //provider "helm" { // kubernetes { // config_context_cluster = "minikube" // } //} //resource "helm_release" "local" { // name = "tel4vnchart" // chart = "./tel4vnchart"

Sau khi commnent, chạy lại terraform apply để rollback:

tel4vn@devops:~/terraform_example$ terraform apply kubernetes_namespace.minikube-namespace: Refreshing state... [id=tel4vn-namespace] helm_release.local: Refreshing state... [id=tel4vnchart] 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 will be destroyed - resource "helm_release" "local" { - atomic = false -> null - chart = "./tel4vnchart" -> null - cleanup_on_fail = false -> null - create_namespace = false -> null - dependency_update = false -> null - disable_crd_hooks = false -> null - disable_openapi_validation = false -> null - disable_webhooks = false -> null - force_update = false -> null - id = "tel4vnchart" -> null - lint = false -> null - max_history = 0 -> null - metadata = [ - { - app_version = "1.16.0" - chart = "tel4vnchart" - name = "tel4vnchart" - namespace = "default" - revision = 1 - values = "null" - version = "0.1.0" }, ] -> null - name = "tel4vnchart" -> null - namespace = "default" -> null - recreate_pods = false -> null - render_subchart_notes = true -> null - replace = false -> null - reset_values = false -> null - reuse_values = false -> null - skip_crds = false -> null - status = "deployed" -> null - timeout = 300 -> null - verify = false -> null - version = "0.1.0" -> null - wait = true -> null } 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=tel4vnchart] helm_release.local: Destruction complete after 0s Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

Xoá trạng thái triển khai bởi Terraform #

Để xoá trạng thái và tất cả những đối tượng, tài nguyên đã được khởi tạo và theo dõi bởi Terraform, ta sử dụng câu lệnh terraform destroy

tel4vn@devops:~/terraform_example$ terraform destroy kubernetes_namespace.minikube-namespace: Refreshing state... [id=tel4vn-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: # kubernetes_namespace.minikube-namespace will be destroyed - resource "kubernetes_namespace" "minikube-namespace" { - id = "tel4vn-namespace" -> null - metadata { - annotations = {} -> null - generation = 0 -> null - labels = {} -> null - name = "tel4vn-namespace" -> null - resource_version = "51354" -> null - self_link = "/api/v1/namespaces/tel4vn-namespace" -> null - uid = "6121e451-528c-4ca7-926f-46f87cc4d219" -> null } } Plan: 0 to add, 0 to change, 1 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 kubernetes_namespace.minikube-namespace: Destroying... [id=tel4vn-namespace] kubernetes_namespace.minikube-namespace: Destruction complete after 7s Destroy complete! Resources: 1 destroyed.

Bạn có thể kiểm tra lại rằng các đối tượng đã thực sự được xoá đi bằng cách sử dụng kubectl

tel4vn@devops:~/terraform_example$ kubectl get ns NAME STATUS AGE default Active 22h kube-node-lease Active 22h kube-public Active 22h kube-system Active 22h tel4vn@devops:~/terraform_example$ kubectl get pods No resources found in default namespace.

Lời kết #

Vừa rồi mình đã chia sẻ cho mọi người về Terraform và 2 ví dụ sử dụng với Minikube Cluster và Helm. Bất kì câu hỏi nào trong quá trình theo dõi bài đọc, bạn có thể comment bên dưới để mình trợ giúp. Cảm ơn bạn đã theo dõi.

Nguồn bài viết: https://opensource.com