Giới thiệu Terraform
Xem thửTình huống: môi trường staging mà không ai dựng lại được
Tám tháng trước, một đồng đội của GDev dựng gdev-staging bằng cách bấm chuột trên console của cloud: một mạng, một security group, một máy chủ chạy gdev-api. Hôm nay đồng đội đó đã chuyển team và sếp yêu cầu dựng thêm một bản cho prod.
Chuyện xảy ra tiếp theo rất quen thuộc:
- Không ai nhớ security group đã mở những cổng nào. Có một cổng được ai đó mở tạm để debug và chưa bao giờ đóng.
- Một bucket lưu log biến mất. Không ai biết ai xóa, vì console không để lại "bản vẽ" nào để so sánh.
- Dựng bản prod bằng cách bấm lại từ trí nhớ mất cả hai ngày, và khi xong, hai môi trường vẫn khác nhau ở những chỗ không ai nhận ra.
Gốc rễ là hạ tầng chỉ tồn tại ở trạng thái hiện tại, không có mô tả nào được lưu, review hay chạy lại được. Infrastructure as Code (IaC) là cách sửa gốc rễ đó: viết hạ tầng thành file văn bản, đưa vào Git như code ứng dụng. Terraform là công cụ IaC phổ biến để làm việc này trên nhiều nhà cung cấp.
Sau bài này bạn làm được các việc sau:
- Viết và chạy một cấu hình Terraform thật, đi qua đủ chu trình
init,plan,apply,destroy. - Đọc
planđể biết Terraform sắp làm gì trước khi nó làm, và phát hiện thay đổi làm tay (drift). - Giải thích state là gì, vì sao nó nhạy cảm và vì sao không được commit lên Git.
Bạn cần có sẵn: dùng được terminal, và (cho lab thứ hai) Docker đã học ở chương Docker.
Ba mô hình cần nhớ
- Khai báo trạng thái mong muốn: bạn viết "tôi muốn có một container tên X chạy image Y", không viết các bước để đạt được. Terraform tự so sánh với thực tế và tính ra việc cần làm. Chạy lần thứ hai không tạo thêm bản thứ hai, vì mọi thứ đã đúng.
- State là trí nhớ của Terraform: file ghi lại "địa chỉ trong code" tương ứng với "ID thật ngoài hạ tầng". Mất state là mất trí nhớ, và Terraform không còn biết những gì nó đã tạo.
- Plan là bản xem trước có thể đọc được: mỗi thay đổi đều đi qua một lần so sánh ba thứ (code, state, hạ tầng thật) rồi in ra diff. Đọc plan là kỹ năng quan trọng nhất của bài này.
Chuẩn bị môi trường lab
Lab chạy được trên máy cá nhân, không cần tài khoản cloud. Bài dùng ba provider (plugin nói chuyện với một hệ thống cụ thể): hashicorp/local để ghi file, kreuzwerker/docker để quản lý container, và provider có sẵn terraform_data.
Cài Terraform trên Linux x86_64 bằng bản nhị phân chính thức (khoảng 90 MB sau khi giải nén):
curl -fsSLO https://releases.hashicorp.com/terraform/1.17.0/terraform_1.17.0_linux_amd64.zip
mkdir -p ~/bin && unzip -o terraform_1.17.0_linux_amd64.zip -d ~/bin
export PATH="$HOME/bin:$PATH"
terraform version
Kết quả (lấy từ lần chạy thật khi viết bài):
Terraform v1.17.0
on linux_amd64
Hệ điều hành khác xem trang tải chính thức trong phần nguồn cuối bài. Bài này chạy bằng Terraform 1.17.0; mọi cấu hình dùng cú pháp ổn định từ 1.6 trở lên.
Với bài Docker, kéo trước một image nhỏ rồi tạo thư mục làm việc:
docker pull alpine:3.20
mkdir -p ~/gdev-iac/lab-local ~/gdev-iac/lab-docker
Terraform và OpenTofu: hai công cụ, cùng cú pháp trong bài này
Tháng 8 năm 2023, HashiCorp đổi giấy phép mã nguồn của các sản phẩm từ Mozilla Public License 2.0 sang Business Source License 1.1 cho các bản phát hành về sau. Theo thông báo của HashiCorp, người dùng vẫn được sao chép, sửa và phân phối mã nguồn cho mục đích thương mại, trừ khi dùng để làm một dịch vụ cạnh tranh trực tiếp với HashiCorp.
Cộng đồng phản ứng bằng cách tạo OpenTofu, một nhánh fork của Terraform do Linux Foundation lưu giữ. Theo trang FAQ của OpenTofu, nó dùng được các provider hiện có (qua registry riêng) và đọc được file state do Terraform tạo ra tới các bản 1.5.x. Hai dự án sau đó phát triển độc lập nên tính năng mới có thể khác nhau.
Với người học, điều thực dụng cần nhớ là: lệnh gọi là terraform hoặc tofu, còn ngôn ngữ cấu hình (HCL) trong các lab của chương này viết giống hệt nhau. Bạn có thể thay terraform bằng tofu trong mọi lệnh. Mọi kết quả trong bài được chạy thật bằng Terraform 1.17.0; riêng OpenTofu chưa được chạy lại trong bài này, nên đừng coi kết quả là bản sao từ OpenTofu. Chọn công cụ nào là quyết định của tổ chức bạn, tùy theo yêu cầu giấy phép và hệ sinh thái.
Lab 1: chu trình đầy đủ với một file
Trước khi gõ lệnh, hãy đặt kỳ vọng: nếu bạn chạy cấu hình dưới đây hai lần liên tiếp, lần thứ hai sẽ tạo thêm một file nữa hay không làm gì cả?
Đây là cấu hình nhỏ nhất vẫn đủ minh họa mọi khái niệm. Lưu vào ~/gdev-iac/lab-local/main.tf:
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.5"
}
}
}
variable "env" {
type = string
default = "staging"
}
resource "local_file" "banner" {
filename = "${path.module}/out/banner.txt"
content = "gdev-api chay o moi truong ${var.env}\n"
}
output "banner_path" {
value = local_file.banner.filename
}
Cấu hình có bốn khối: terraform khai báo provider cần dùng và khoảng phiên bản chấp nhận, variable là đầu vào có giá trị mặc định, resource là thứ cần tồn tại (ở đây là một file), output là giá trị in ra sau khi chạy. Cú pháp chi tiết nằm ở bài sau.
init: tải provider
cd ~/gdev-iac/lab-local
terraform init
Kết quả (lấy từ lần chạy thật, số phiên bản có thể mới hơn):
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/local versions matching "~> 2.5"...
- Installing hashicorp/local v2.9.1...
- Installed hashicorp/local v2.9.1 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
Đọc kết quả: init làm hai việc. Nó tải plugin provider vào thư mục .terraform/ (thư mục này không commit), và ghi phiên bản đã chọn kèm mã băm vào .terraform.lock.hcl. File khóa này nên commit, để đồng đội và pipeline CI dùng đúng cùng phiên bản provider.
validate và fmt: kiểm tra không đụng hạ tầng
terraform validate
terraform fmt -check
Success! The configuration is valid.
validate kiểm tra cú pháp và tính nhất quán nội bộ, không gọi API nào. fmt -check im lặng và thoát với mã 0 khi file đã đúng định dạng; nếu không, nó in tên file cần sửa và bạn chạy terraform fmt để tự canh lề. Hai lệnh này là bước đầu tiên của hầu hết pipeline.
plan: xem trước, chưa thay đổi gì
terraform plan
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# local_file.banner will be created
+ resource "local_file" "banner" {
+ content = <<-EOT
gdev-api chay o moi truong staging
EOT
+ content_sha256 = (known after apply)
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "./out/banner.txt"
+ id = (known after apply)
# (một số thuộc tính băm khác được lược bớt)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ banner_path = "./out/banner.txt"
Đọc từng dòng:
# local_file.banner will be createdlà địa chỉ của resource, ghép từ loại (local_file) và tên (banner). Địa chỉ này là khóa trong state.- Ký hiệu
+ở đầu dòng nghĩa là thuộc tính sẽ được tạo mới. (known after apply)nghĩa là giá trị chỉ có sau khi tạo xong, nhưidhay mã băm nội dung.- Dòng
Plan: 1 to add, 0 to change, 0 to destroy.là tóm tắt bạn phải nhìn trước khi gõyes. Ba con số này cho biết quy mô thay đổi.
apply: thực hiện
terraform apply
Terraform in lại plan rồi hỏi Do you want to perform these actions?. Gõ yes. Kết quả:
local_file.banner: Creating...
local_file.banner: Creation complete after 0s [id=fd8e40c252dff95e440cb7bbde703ca31c27aeda]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
banner_path = "./out/banner.txt"
Kiểm tra bằng cat out/banner.txt bạn thấy dòng chữ đã ghi. Thư mục lúc này có thêm terraform.tfstate. Đây là state.
Chạy lại để thấy tính idempotent
terraform plan
local_file.banner: Refreshing state... [id=fd8e40c252dff95e440cb7bbde703ca31c27aeda]
No changes. Your infrastructure matches the configuration.
Đây là đáp án cho câu hỏi đầu mục: không làm gì cả. Một script Bash echo ... > banner.txt cũng ghi đè được, nhưng một script aws ec2 run-instances chạy hai lần sẽ tạo hai máy chủ. Terraform so sánh với thực tế nên chạy bao nhiêu lần cũng chỉ hội tụ về cùng một trạng thái.
Đổi một giá trị và đọc ký hiệu thay thế
terraform plan -var env=prod
# local_file.banner must be replaced
-/+ resource "local_file" "banner" {
~ content = <<-EOT # forces replacement
- gdev-api chay o moi truong staging
+ gdev-api chay o moi truong prod
EOT
~ content_sha256 = "171a87ec85b3..." -> (known after apply)
~ id = "fd8e40c252dff95e440cb7bbde703ca31c27aeda" -> (known after apply)
# (3 unchanged attributes hidden)
}
Plan: 1 to add, 0 to change, 1 to destroy.
Bảng ký hiệu bạn sẽ gặp mỗi ngày:
| Ký hiệu | Ý nghĩa |
|---|---|
+ | Tạo mới |
- | Xóa |
~ | Sửa tại chỗ (in-place), tài nguyên vẫn giữ nguyên ID |
-/+ | Xóa rồi tạo lại (replace), kèm chú thích forces replacement ở thuộc tính gây ra |
Ở đây content của local_file không thể sửa tại chỗ nên Terraform phải xóa file cũ rồi tạo file mới. Với một file thì vô hại, nhưng với database hay máy chủ, -/+ có nghĩa là mất dữ liệu hoặc gián đoạn. Dòng Plan: 1 to add, 0 to change, 1 to destroy. cho thấy điều đó: đây là thay đổi một resource nhưng gồm một lần xóa và một lần tạo. Luôn tìm forces replacement trong plan trước khi apply.
Nhìn vào state
terraform state list
terraform state show local_file.banner
local_file.banner
Lệnh thứ hai in các thuộc tính đã lưu. Mở thẳng file terraform.tfstate bạn sẽ thấy cấu trúc như sau (rút gọn):
{
"version": 4,
"terraform_version": "1.17.0",
"serial": 1,
"lineage": "77fd06ef-62c9-e776-fafd-fa52908c044d",
"resources": [
{
"mode": "managed",
"type": "local_file",
"name": "banner",
"instances": [ { "attributes": { "filename": "./out/banner.txt", "id": "fd8e40c2..." } } ]
}
]
}
Các trường đáng nhớ: serial tăng sau mỗi lần ghi, lineage là mã định danh của dòng state này, và resources là bảng ghi "địa chỉ trong code" tương ứng với "thuộc tính thật". Mọi thuộc tính xuất hiện ở dạng văn bản thường, kể cả mật khẩu do random_password sinh ra hay mật khẩu database. Khai báo sensitive = true chỉ làm Terraform che giá trị trên màn hình, không mã hóa trong file state. Vì vậy state phải được bảo vệ như một thứ chứa secret.
destroy: dọn dẹp
terraform destroy
# local_file.banner will be destroyed
- resource "local_file" "banner" {
- filename = "./out/banner.txt" -> null
...
}
Plan: 0 to add, 0 to change, 1 to destroy.
Destroy complete! Resources: 1 destroyed.
destroy cũng đi qua plan. Mỗi thuộc tính bị ghi -> null. Trong ngữ cảnh thật, destroy chỉ nên chạy trên môi trường lab bạn chắc chắn, đừng nhầm thư mục.
Cơ chế: plan so sánh ba thứ
Hãy tự trả lời: nếu ai đó sửa tay một resource ngoài Terraform, thông tin đó nằm ở đâu, trong code hay trong state?
Câu trả lời là không nằm ở đâu cả, cho tới khi Terraform hỏi lại hạ tầng thật. Sơ đồ dưới đây mô tả việc đó.
Đang tải sơ đồ…
Cách đọc sơ đồ:
plannhận ba đầu vào: code bạn viết, state là trí nhớ về những gì đã tạo, và hạ tầng thật (qua bước refresh, tức Terraform gọi API đọc giá trị hiện tại).- Đầu ra là diff. Chỉ khi bạn chấp thuận,
applymới gọi API để thay đổi hạ tầng và ghi kết quả mới vào state. - Nhánh
noquan trọng không kém: plan không có tác dụng phụ lên hạ tầng. Bạn có thể chạy bao nhiêu lần cũng được để xem trước.
Kết luận: mọi sai khác giữa code và thực tế, kể cả sai khác do người khác sửa tay, đều hiện ra ở bước diff. Sơ đồ chỉ cho thấy luồng; còn timing và trường hợp lệch thực tế nằm trong lab dưới đây.
Lab 2: drift, có người sửa tay container
Bây giờ mô phỏng đúng sự cố mở đầu bài, nhưng trên một container để bạn tự tay làm được. Lưu vào ~/gdev-iac/lab-docker/main.tf:
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
}
}
data "docker_image" "base" {
name = "alpine:3.20"
}
resource "docker_container" "api" {
name = "gdev-api-lab"
image = data.docker_image.base.id
command = ["sleep", "3600"]
restart = "no"
}
Khối data khác resource: nó chỉ đọc một thứ đã có (image alpine:3.20 trên máy bạn) chứ không tạo. Resource tham chiếu data.docker_image.base.id, nên Terraform tự biết phải đọc data trước rồi mới tạo container. Đây là phụ thuộc ngầm định.
cd ~/gdev-iac/lab-docker
terraform init
terraform apply -auto-approve
Cờ -auto-approve bỏ bước hỏi yes, chỉ dùng trong lab hoặc pipeline đã có bước duyệt riêng. Kết quả cuối:
docker_container.api: Creating...
docker_container.api: Creation complete after 10s [id=496ceaa89079...]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Provider kreuzwerker/docker là provider cộng đồng (partner/community), nên init ghi nhận chữ ký của tác giả thay vì của HashiCorp. Đó là điều bình thường.
Giờ đóng vai người làm tay: đổi chính sách restart của container bằng Docker CLI, hoàn toàn ngoài Terraform.
docker update --restart=always gdev-api-lab
terraform plan
docker_container.api: Refreshing state... [id=496ceaa89079...]
~ update in-place
Terraform will perform the following actions:
# docker_container.api will be updated in-place
~ resource "docker_container" "api" {
id = "496ceaa89079..."
name = "gdev-api-lab"
~ restart = "always" -> "no"
# (49 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Đọc kết quả: dòng Refreshing state là lúc Terraform hỏi Docker giá trị thật. Nó thấy restart đang là always, khác với no trong code, nên plan đề xuất ~ restart = "always" -> "no". Mũi tên -> nghĩa là "từ giá trị đang có thành giá trị mong muốn". Terraform coi code là sự thật, và kéo thực tế về cho khớp.
Nếu chỉ muốn ghi nhận thay đổi mà không sửa ngược lại, có chế độ refresh-only:
terraform plan -refresh-only
Note: Objects have changed outside of Terraform
# docker_container.api has changed
~ resource "docker_container" "api" {
id = "496ceaa89079..."
~ restart = "no" -> "always"
# (41 unchanged attributes hidden)
}
This is a refresh-only plan, so Terraform will not take any actions to undo
these changes.
Chiều mũi tên đảo lại: lần này state là bên "cũ" và thực tế là bên "mới". Có hai cách xử lý, tùy ý định của đội: terraform apply thường sửa ngược lại cho khớp code, hoặc terraform apply -refresh-only chấp nhận thay đổi vào state rồi bạn sửa code cho khớp.
Cách phổ biến nhất là sửa ngược lại:
terraform apply -auto-approve
terraform plan
docker_container.api: Modifying... [id=496ceaa89079...]
docker_container.api: Modifications complete after 0s [id=496ceaa89079...]
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
...
No changes. Your infrastructure matches the configuration.
Đây là cơ chế chống "hạ tầng trôi dạt" mà bài mở đầu thiếu: chạy plan định kỳ (thường trong pipeline), mọi sửa tay đều hiện ra.
Xem lại vòng đời đó theo thời gian. Trước khi bấm, dự đoán: khi ai đó sửa tay container, lệnh nào trong chu trình phát hiện ra, và Terraform lấy thông tin đó từ đâu?
Đang tải Animation…
Failure lab: mất state và khóa state
Lỗi 1: mất file state
Giả sử đồng đội lỡ xóa terraform.tfstate trong thư mục lab-docker, trong khi container vẫn đang chạy. Mô phỏng bằng cách đổi tên file:
mv terraform.tfstate terraform.tfstate.lost
terraform plan
# docker_container.api will be created
+ resource "docker_container" "api" {
+ name = "gdev-api-lab"
...
}
Plan: 1 to add, 0 to change, 0 to destroy.
Triệu chứng đầu tiên: Terraform muốn tạo mới thứ đã tồn tại. Nguyên nhân là không còn state nên Terraform không biết container này của nó. Thử terraform apply:
docker_container.api: Creating...
Error: Unable to create container: Error response from daemon: Conflict. The container name "/gdev-api-lab" is already in use by container "496ceaa89079...". You have to remove (or rename) that container to be able to reuse that name.
Lần này Docker từ chối vì trùng tên, nên thiệt hại dừng lại ở một thông báo lỗi. Với tài nguyên không có ràng buộc tên duy nhất, Terraform có thể tạo ra bản thứ hai song song với bản cũ. Cách khắc phục là khôi phục state từ bản sao lưu (terraform.tfstate.backup, hoặc phiên bản cũ của remote backend), hoặc đưa resource đang tồn tại trở lại vào state bằng terraform import. Bài sau sẽ nói kỹ hơn.
Khôi phục để tiếp tục lab: mv terraform.tfstate.lost terraform.tfstate.
Lỗi 2: hai người chạy apply cùng lúc
Tạo thư mục ~/gdev-iac/lab-lock với một resource chạy chậm có chủ đích:
resource "terraform_data" "slow" {
provisioner "local-exec" {
command = "sleep 20"
}
}
Mở hai terminal. Terminal 1 chạy terraform init rồi terraform apply -auto-approve. Trong 20 giây đó, ở terminal 2 chạy terraform plan:
Error: Error acquiring the state lock
Error message: resource temporarily unavailable
Lock Info:
ID: cd9f7804-dff5-1c7f-fa80-78e43ca50159
Path: terraform.tfstate
Operation: OperationTypeApply
Who: dev@gdev-laptop
Version: 1.17.0
Created: 2026-10-23 23:38:28.008461737 +0000 UTC
Terraform acquires a state lock to protect the state from being written
by multiple users at the same time.
Đây là hành vi đúng, không phải lỗi hệ thống. Khi apply đang chạy, Terraform khóa state; người thứ hai bị chặn để hai lần ghi không giẫm lên nhau và làm hỏng state. Tên người, thời điểm và phép toán đang giữ khóa nằm trong Lock Info. Giá trị Who và Created của bạn sẽ khác.
Cách xử lý: chờ lần chạy kia xong. Nếu tiến trình đó đã chết và khóa bị kẹt, dùng terraform force-unlock <ID> với ID trong thông báo, và chỉ sau khi chắc chắn không còn ai đang chạy. Cờ -lock=false bỏ qua khóa, nhưng gần như luôn là ý tưởng tồi.
Vì sao state phải ở remote khi làm việc nhóm
State cục bộ chỉ nằm trên một máy, nên không chia sẻ được, không có khóa chung và dễ mất. Làm việc nhóm cần remote backend: kho lưu state dùng chung có khóa, có phân quyền và thường có lịch sử phiên bản. Lấy ví dụ backend S3 của AWS, theo tài liệu chính thức:
terraform {
backend "s3" {
bucket = "gdev-tfstate"
key = "staging/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
use_lockfile = true
}
}
Tham số use_lockfile bật khóa bằng một object .tflock đặt cạnh file state ngay trong S3. Cách khóa bằng bảng DynamoDB (dynamodb_table) vẫn chạy nhưng tài liệu đã đánh dấu là deprecated và sẽ bị gỡ trong một bản minor tương lai. Đoạn cấu hình trên chỉ để minh họa, không chạy được trong lab vì cần tài khoản AWS để tạo bucket; tên bucket do bạn tự đặt.
Quy tắc không thương lượng: không commit terraform.tfstate lên Git. Thêm vào .gitignore các mục *.tfstate, *.tfstate.* và .terraform/, nhưng giữ .terraform.lock.hcl.
Dọn dẹp lab:
cd ~/gdev-iac/lab-docker && terraform destroy -auto-approve
cd ~/gdev-iac/lab-local && terraform destroy -auto-approve
Lỗi thường gặp
| Triệu chứng | Nguyên nhân hay gặp | Cách kiểm chứng |
|---|---|---|
| Plan muốn tạo lại thứ đã tồn tại | Mất hoặc trỏ nhầm file state | terraform state list rỗng hoặc thiếu; kiểm tra backend đang dùng |
Error acquiring the state lock | Có lần chạy khác đang giữ khóa, hoặc tiến trình chết để lại khóa | Đọc Lock Info, hỏi chủ khóa, rồi mới force-unlock |
Plan có -/+ ngoài dự kiến | Đổi thuộc tính không sửa tại chỗ được | Tìm chú thích forces replacement trong plan |
| Mật khẩu lộ trong repo | Commit terraform.tfstate hoặc file .tfvars chứa secret | git log -p tìm file, đổi secret ngay, thêm vào .gitignore |
apply mà chưa đọc plan | Dùng -auto-approve làm thói quen | Quy ước đội: duyệt plan trước, chỉ auto-approve trong pipeline đã có bước duyệt |
Một quy tắc thực tế: mỗi lần review thay đổi hạ tầng, hãy xem dòng Plan: X to add, Y to change, Z to destroy. trước tiên. Nếu Z khác 0 mà bạn không chủ ý xóa gì, dừng lại và tìm xem thuộc tính nào gây ra.
Tự kiểm tra
Hãy trả lời trước khi đọc đáp án ngay bên dưới mỗi câu.
Câu 1. Chạy terraform apply hai lần liên tiếp với cùng một cấu hình. Lần thứ hai in gì, và điều đó chứng minh tính chất nào của Terraform?
Đáp án: Lần thứ hai in No changes và không gọi API ghi nào. Đây là tính chất khai báo và idempotent: Terraform so sánh với trạng thái thật rồi chỉ làm phần còn thiếu.
Câu 2. Một đồng đội sửa tay một container qua Docker CLI. Terraform biết điều đó bằng cách nào, khi mà state vẫn ghi giá trị cũ?
Đáp án: Ở bước refresh của plan, Terraform gọi provider đọc giá trị hiện tại từ hạ tầng thật rồi so với code. State chỉ là trí nhớ của lần apply trước, không phải nguồn sự thật về hiện tại.
Câu 3. Plan có một dòng -/+ và chú thích forces replacement. Bạn cần kiểm tra gì trước khi apply, nếu resource là database của gdev-api?
Đáp án: Thuộc tính nào gây thay thế và dữ liệu có bị mất không. Thay thế nghĩa là xóa rồi tạo lại nên database sẽ trống, cần có bản sao lưu hoặc đổi cách làm để tránh thay thế.
Nếu chỉ nhớ 3 điều
- Terraform là khai báo: bạn mô tả đích đến,
planso sánh code, state và hạ tầng thật rồi tính ra việc cần làm, và chạy lại không tạo trùng. - Đọc plan trước khi apply:
+tạo,-xóa,~sửa tại chỗ,-/+thay thế và luôn tìmforces replacement. - State là trí nhớ chứa dữ liệu thật dạng văn bản thường: không commit lên Git, dùng remote backend có khóa khi làm việc nhóm.
Bài tiếp theo đi sâu vào cú pháp HCL: biến, local, output, hàm và cách Terraform báo lỗi khi bạn viết sai.
Tài liệu chính thức: Terraform language: state, S3 backend, cài đặt Terraform, thông báo đổi giấy phép của HashiCorp và OpenTofu FAQ.