Terraform Remote State Backends — Nền tảng của mọi dự án Terraform Production
Tìm hiểu Terraform Remote State Backend, lý do vì sao Local State không phù hợp cho production và cách Remote Backend trở thành nền tảng cho mọi dự án Terraform chuyên nghiệp.

Terraform Remote State Backends — Nền tảng của mọi dự án Terraform Production
Ở bài trước, chúng ta đã tìm hiểu cách đưa những hạ tầng đang tồn tại vào Terraform bằng terraform import.
Sau khi import thành công, Terraform bắt đầu quản lý toàn bộ Infrastructure thông qua Terraform State.
Nhưng lúc này sẽ xuất hiện một câu hỏi quan trọng hơn rất nhiều.
Terraform State nên được lưu ở đâu?
Nếu chỉ làm việc một mình trên máy cá nhân, câu trả lời khá đơn giản.
terraform.tfstate
Nhưng khi project bắt đầu có:
- nhiều developer
- nhiều môi trường
- CI/CD Pipeline
- nhiều repository
- nhiều team cùng quản lý Infrastructure
thì việc lưu State trên laptop gần như chắc chắn sẽ dẫn tới vấn đề.
Đó là lý do gần như mọi dự án Terraform production đều bắt đầu bằng Remote State Backend.
Terraform State quan trọng đến mức nào?
Ở bài 3 chúng ta đã biết Terraform luôn hoạt động theo quy trình:
Configuration
↓
Terraform State
↓
Refresh
↓
Execution Plan
↓
Apply
Terraform không đọc trực tiếp Infrastructure.
Terraform luôn đọc State trước.
State chứa toàn bộ thông tin Terraform cần để hiểu Infrastructure hiện tại.
Ví dụ:
resource "aws_s3_bucket" "assets" {
bucket = "company-assets"
}Sau lần Apply đầu tiên, State sẽ lưu những thông tin tương tự:
{
"resources": [
{
"type": "aws_s3_bucket",
"name": "assets",
"instances": [
{
"attributes": {
"id": "company-assets",
"arn": "arn:aws:s3:::company-assets"
}
}
]
}
]
}Terraform không cần đi tìm bucket nào thuộc resource nào.
Nó chỉ cần đọc State.
Có thể nói:
Configuration là mong muốn của chúng ta.
State là trí nhớ của Terraform.
Nếu State sai, Terraform sẽ đưa ra Execution Plan sai.
Local Backend hoạt động như thế nào?
Nếu chúng ta không cấu hình gì cả, Terraform sẽ sử dụng Local Backend.
terraform init
Sau lần Apply đầu tiên, project sẽ có thêm:
terraform.tfstate
Ví dụ:
infra/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfstate
Khi chạy:
terraform applyTerraform sẽ thực hiện:
Read local state
↓
Refresh infrastructure
↓
Execution Plan
↓
Apply
↓
Update local state
Toàn bộ State chỉ tồn tại trên máy đang chạy Terraform.
Điều này hoàn toàn ổn khi:
- học Terraform
- sandbox
- demo
- PoC
- project cá nhân
Nhưng production thì hoàn toàn khác.
Điều gì xảy ra khi có nhiều người cùng làm?
Giả sử team có hai DevOps.
Alice
Bob
Cả hai cùng clone repository.
terraform-project/
├── main.tf
├── variables.tf
└── outputs.tf
State không được commit lên Git.
Sau khi chạy Terraform.
Alice sẽ có:
main.tf
terraform.tfstate
Bob cũng có:
main.tf
terraform.tfstate
Nhìn thì giống nhau.
Nhưng thực tế đây là hai file hoàn toàn độc lập.
Alice chạy:
terraform applyTerraform tạo thêm một EC2.
AWS thay đổi.
State của Alice được cập nhật.
EC2
ID = i-0123456789
State của Bob vẫn chưa biết điều này.
Bob tiếp tục chạy:
terraform applyTerraform sẽ đọc:
Configuration
+
State của Bob
Sau đó mới Refresh Infrastructure.
Lúc này Terraform phải cố gắng reconcile giữa:
- Infrastructure thật
- Configuration
- State đã cũ
Nếu project lớn, việc tính toán này hoàn toàn có thể tạo ra Execution Plan không còn chính xác.
Thậm chí trong nhiều trường hợp Terraform sẽ nghĩ rằng một số Resource cần được tạo lại hoặc thay thế.
Không phải vì Terraform sai.
Mà vì State giữa các thành viên đã không còn giống nhau nữa.
Có nên commit terraform.tfstate lên Git?
Đây gần như là ý tưởng đầu tiên của mọi người khi gặp vấn đề này.
Nếu mọi người đều dùng chung State.
Vậy chỉ cần commit State lên Git.
Nghe khá hợp lý.
Ví dụ:
Alice vừa Apply.
terraform.tfstate
được cập nhật.
Alice commit:
git add terraform.tfstate
git commit
git pushBob pull về.
Mọi thứ có vẻ ổn.
Nhưng thực tế đây là một trong những cách tệ nhất để quản lý Terraform State.
Git không phải Database
Git được thiết kế để quản lý Source Code.
Không phải để quản lý State.
Giả sử Alice và Bob cùng pull phiên bản mới nhất.
State hiện tại:
Version 25
Alice chạy:
terraform applyBob cũng chạy:
terraform applyHai người đều bắt đầu từ cùng một State.
Version 25
Alice Apply xong.
State trở thành:
Version 26
Bob vẫn đang chạy dựa trên Version 25.
Một lúc sau Bob cũng hoàn thành.
State của Bob ghi đè lên Version 26.
Version 25
├── Alice
│ ↓
│ Version 26
│
└── Bob
↓
Version 26
Git chỉ phát hiện conflict khi commit.
Nhưng Infrastructure đã thay đổi từ trước đó.
Git hoàn toàn không thể ngăn hai tiến trình Terraform cùng sửa State.
Đây chính là vấn đề mà Remote Backend sẽ giải quyết bằng State Locking.
Terraform State còn chứa nhiều dữ liệu nhạy cảm
Rất nhiều người nghĩ State chỉ lưu Resource ID.
Thực tế không phải vậy.
State có thể chứa:
- ARN
- Internal IP
- Database Endpoint
- Resource ID
- Outputs
- Provider Metadata
- Connection String
- Username
- Certificate
- Password được generate
- Access Token
Ví dụ:
resource "random_password" "database" {
length = 32
special = true
}Cho dù Output được đánh dấu là:
output "database_password" {
value = random_password.database.result
sensitive = true
}Terraform vẫn phải lưu giá trị thật vào State.
Nếu không lưu.
Terraform sẽ không thể biết Password hiện tại là gì.
Điều đó có nghĩa:
- bất kỳ ai đọc được State đều có thể đọc Secret
- commit lên Git sẽ lưu Secret vào History
- việc xóa Secret khỏi Git sau này gần như không đơn giản
Đó cũng là lý do HashiCorp luôn khuyến nghị:
Không sử dụng Git để lưu Terraform State.
Production cần điều gì?
Một Production-grade Terraform project cần nhiều hơn một file JSON.
State cần phải:
- có một nguồn dữ liệu duy nhất
- nhiều người cùng truy cập được
- tự động khóa khi Apply
- có Versioning
- có Backup
- được mã hóa
- có phân quyền
- hoạt động tốt với CI/CD
Đó chính là mục tiêu của Terraform Remote State Backend.
Remote State Backend hoạt động như thế nào?
Ý tưởng của Remote State Backend thực ra khá đơn giản.
Thay vì lưu State trên máy của từng developer.
Terraform sẽ lưu State vào một nơi dùng chung.
Terraform Apply
│
▼
Read Remote State
│
▼
Refresh Infrastructure
│
▼
Execution Plan
│
▼
Apply Changes
│
▼
Update Remote State
Developer không còn sở hữu State nữa.
State trở thành tài nguyên dùng chung của toàn bộ team.
Ví dụ:
Alice
│
│
Bob
│
│
GitHub Actions
│
▼
Remote State Backend
│
▼
terraform.tfstate
Bất kỳ ai chạy Terraform cũng sẽ đọc cùng một State.
Điều này giúp mọi người luôn làm việc trên cùng một phiên bản Infrastructure.
Backend chỉ lưu State
Đây là một hiểu nhầm khá phổ biến.
Remote Backend không chạy Terraform.
Remote Backend cũng không tạo Infrastructure.
Nó chỉ chịu trách nhiệm lưu trữ Terraform State.
Terraform vẫn chạy trên:
- máy developer
- GitHub Actions
- GitLab CI
- Jenkins
- Azure DevOps
- hoặc bất kỳ Runner nào
Backend chỉ đóng vai trò giống như một "database" dành cho State.
Terraform CLI
│
├── AWS API
├── Azure API
├── GCP API
│
▼
Infrastructure
Terraform CLI
│
▼
Remote Backend
│
▼
Terraform State
Đây cũng là lý do nếu Backend gặp sự cố.
Infrastructure của bạn vẫn tồn tại.
Chỉ có Terraform không thể tiếp tục quản lý Infrastructure đó cho đến khi State được khôi phục.
State Locking là gì?
Đây là tính năng quan trọng nhất của Remote Backend.
Giả sử Alice đang chạy:
terraform applyTerraform sẽ thực hiện:
Acquire Lock
↓
Read State
↓
Refresh
↓
Plan
↓
Apply
↓
Update State
↓
Release Lock
Trong toàn bộ quá trình này.
State bị khóa.
Nếu Bob cũng chạy:
terraform applyTerraform sẽ nhận được thông báo tương tự:
Error acquiring the state lock
Bob phải đợi cho đến khi Alice hoàn thành.
Điều này đảm bảo rằng tại một thời điểm chỉ có một tiến trình được phép cập nhật State.
Vì sao State Locking quan trọng?
Giả sử không có Lock.
Alice và Bob cùng chạy:
terraform applyCả hai đều đọc:
State Version 100
Alice tạo thêm:
EC2 Instance
Bob tạo thêm:
Security Group
Alice ghi State mới.
Version 101
Bob cũng ghi State mới.
Version 101
State cuối cùng chỉ chứa thay đổi của người ghi sau cùng.
Một trong hai thay đổi sẽ biến mất khỏi State.
Infrastructure thật vẫn tồn tại.
Nhưng Terraform không còn biết chính xác Infrastructure đang ở trạng thái nào.
Đây được gọi là State Corruption.
Một khi State bị hỏng.
Việc khôi phục thường rất tốn thời gian.
Nhiều trường hợp phải Import lại toàn bộ Infrastructure.
Force Unlock có nên dùng không?
Đôi khi bạn sẽ gặp lỗi:
Error acquiring the state lockNguyên nhân có thể là:
- tiến trình Terraform vẫn đang chạy
- CI/CD bị hủy giữa chừng
- mất kết nối mạng
- runner bị crash
Terraform cung cấp:
terraform force-unlock LOCK_IDLệnh này xóa Lock thủ công.
Tuy nhiên.
Đây là lệnh chỉ nên dùng khi chắc chắn không còn tiến trình Terraform nào đang chạy.
Nếu force unlock trong khi một tiến trình khác vẫn đang Apply.
Bạn có thể khiến hai tiến trình cùng ghi State.
Kết quả cũng giống như không có State Locking.
Vì vậy.
Đừng xem force-unlock là cách sửa lỗi mặc định.
Hãy luôn kiểm tra xem tiến trình Terraform trước đó đã thực sự kết thúc hay chưa.
Versioning của Remote Backend
Một ưu điểm rất lớn của Remote Backend là State có thể được lưu theo nhiều phiên bản.
Ví dụ.
State hiện tại là:
Version 52
Sau khi Apply.
Backend tạo:
Version 53
Nếu phát hiện State mới bị lỗi.
Bạn vẫn có thể lấy lại Version trước đó.
Điều này đặc biệt hữu ích khi:
- import sai Resource
- move Resource nhầm
- apply sai Workspace
- xóa nhầm Resource khỏi State
- migration Backend thất bại
Tất nhiên.
Khôi phục State luôn cần thực hiện cẩn thận.
Nhưng việc có Version History vẫn tốt hơn rất nhiều so với chỉ có một file trên laptop.
Các Remote Backend phổ biến
Terraform hỗ trợ rất nhiều Backend.
Trong thực tế, phổ biến nhất là:
| Backend | Cloud |
|---|---|
| Amazon S3 | AWS |
| Azure Blob Storage | Azure |
| Google Cloud Storage | Google Cloud |
| HCP Terraform | HashiCorp |
Ngoài ra còn có:
- Consul
- PostgreSQL
- Kubernetes
- HTTP Backend
- S3-compatible Storage (MinIO, Cloudflare R2...)
Tuy nhiên.
Khoảng 90% dự án production hiện nay sẽ sử dụng một trong bốn Backend đầu tiên.
Amazon S3 Backend
Đây là Backend phổ biến nhất.
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "production/network/terraform.tfstate"
region = "ap-southeast-1"
}
}Thông thường người ta sẽ kết hợp thêm:
- S3 Versioning
- Server-side Encryption
- IAM Policy
- DynamoDB (Terraform < 1.10)
hoặc S3 Native Locking trên các phiên bản Terraform mới.
Azure Blob Storage
Nếu làm việc trên Azure.
Thông thường Backend sẽ là Azure Blob Storage.
terraform {
backend "azurerm" {
resource_group_name = "terraform"
storage_account_name = "tfstateprod"
container_name = "state"
key = "production.tfstate"
}
}Azure Blob hỗ trợ Locking thông qua Blob Lease.
Developer không cần cấu hình thêm.
Google Cloud Storage
Google Cloud cũng cung cấp Backend riêng.
terraform {
backend "gcs" {
bucket = "terraform-state-production"
prefix = "network"
}
}GCS hỗ trợ Versioning rất tốt và được sử dụng khá phổ biến trong các dự án chạy trên Google Cloud.
HCP Terraform (Terraform Cloud)
Nếu bạn không muốn tự quản lý Backend.
HashiCorp cung cấp HCP Terraform (trước đây là Terraform Cloud).
Thay vì tự tạo S3, Azure Blob hay GCS.
Terraform sẽ lưu State trực tiếp trên nền tảng của HashiCorp.
Ví dụ:
terraform {
cloud {
organization = "my-company"
workspaces {
name = "production-network"
}
}
}Ngoài việc lưu State, HCP Terraform còn hỗ trợ:
- Remote Execution
- Workspace Management
- Variables Management
- Policy as Code
- Cost Estimation
- Team Access Control
- Audit Log
- Run History
Nếu team đã sử dụng HCP Terraform thì gần như không cần tự xây dựng hạ tầng Backend nữa.
Chuyển từ Local State sang Remote Backend
Một điều khiến nhiều người lo lắng là:
Nếu project đang dùng Local State thì có phải tạo lại Infrastructure không?
Câu trả lời là không.
Terraform hỗ trợ migrate State rất dễ dàng.
Ví dụ:
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "production/network.tfstate"
region = "ap-southeast-1"
}
}Sau khi thêm Backend.
Chạy lại:
terraform initTerraform sẽ phát hiện Backend đã thay đổi.
Backend configuration changed.
Do you want to copy the existing state to the new backend?
Chỉ cần xác nhận:
yes
Terraform sẽ:
Read Local State
↓
Upload State
↓
Configure Backend
↓
Switch to Remote Backend
Infrastructure hoàn toàn không bị ảnh hưởng.
Chỉ có vị trí lưu State được thay đổi.
Những lưu ý khi migrate
Quá trình migrate khá đơn giản.
Tuy nhiên vẫn có một vài nguyên tắc quan trọng.
Chỉ migrate khi không có ai đang Apply
Đừng migrate trong khi:
- CI/CD đang chạy
- một developer khác đang Apply
- có nhiều Pull Request cùng deploy
Thời điểm tốt nhất là:
- maintenance window
- sau khi merge xong
- hoặc trước khi team bắt đầu làm việc
Backup Local State
Trước khi migrate.
Hãy luôn tạo một bản sao.
Ví dụ:
cp terraform.tfstate terraform.tfstate.backupTerraform vốn đã tạo file backup trong nhiều trường hợp.
Tuy nhiên việc tự tạo thêm một bản backup gần như không tốn chi phí nhưng có thể giúp bạn tránh rất nhiều rủi ro.
Không chỉnh sửa State bằng tay
State là dữ liệu nội bộ của Terraform.
Nếu mở file .tfstate bạn sẽ thấy đây chỉ là một file JSON.
Nhưng điều đó không có nghĩa chúng ta nên chỉnh sửa trực tiếp.
Ví dụ:
{
"resources": [
...
]
}Chỉ cần thay đổi sai một Resource Address hoặc Resource ID.
Terraform có thể:
- tạo lại Resource
- mất Mapping
- không còn quản lý đúng Infrastructure
Nếu cần thao tác với State.
Hãy sử dụng các lệnh mà Terraform cung cấp.
Ví dụ:
terraform state listterraform state mvterraform state rmterraform state showThay vì sửa JSON bằng tay.
Best Practices cho Remote State
Sau khi làm việc với Terraform trong nhiều dự án production.
Đây là những nguyên tắc gần như luôn được áp dụng.
Mỗi môi trường nên có State riêng
Đừng để:
dev
staging
production
dùng chung một State.
Thay vào đó.
dev.tfstate
staging.tfstate
production.tfstate
hoặc:
dev/network.tfstate
staging/network.tfstate
production/network.tfstate
Điều này giúp cô lập hoàn toàn từng môi trường.
Bật Versioning
Nếu Backend hỗ trợ Versioning.
Hãy luôn bật.
Ví dụ với Amazon S3.
Bucket Versioning
Enabled
Nếu State bị ghi sai.
Bạn vẫn có thể lấy lại phiên bản cũ.
Bật Encryption
Terraform State có thể chứa dữ liệu nhạy cảm.
Vì vậy Backend nên được mã hóa.
Ví dụ:
- S3 Server-side Encryption
- Azure Storage Encryption
- Google Cloud Encryption
Đây gần như là yêu cầu bắt buộc đối với production.
Phân quyền truy cập
Không phải ai cũng cần quyền ghi State.
Ví dụ.
Developer chỉ cần:
Read
Plan
CI/CD mới là nơi thực hiện:
Apply
Việc giới hạn quyền sẽ giảm đáng kể nguy cơ thao tác nhầm trên production.
Không commit State lên Git
Đây là nguyên tắc quan trọng nhất.
Repository chỉ nên chứa:
- Terraform Configuration
- Modules
- Variables
- Documentation
Không nên chứa:
terraform.tfstate
terraform.tfstate.backup
Thông thường các file này sẽ được đưa vào:
*.tfstate
*.tfstate.*Tổng kết
Terraform State chính là "bộ não" của Terraform.
Nếu Configuration mô tả Infrastructure mong muốn, thì State mô tả Infrastructure mà Terraform đang quản lý.
Khi làm việc một mình, Local State có thể là đủ.
Nhưng khi dự án bắt đầu có nhiều developer, CI/CD và nhiều môi trường, Remote State Backend gần như trở thành một thành phần bắt buộc.
Nó mang lại:
- Single Source of Truth
- State Locking
- Versioning
- Encryption
- Team Collaboration
- CI/CD Integration
Có thể nói:
Một dự án Terraform production gần như luôn bắt đầu bằng việc thiết lập Remote State Backend trước khi quản lý bất kỳ Infrastructure nào.
Ở bài tiếp theo, chúng ta sẽ không nói về thêm tính năng mới của Terraform nữa, mà sẽ nhìn lại những sai lầm phổ biến mà mình từng mắc phải khi sử dụng Terraform trong các dự án thực tế, từ quản lý State, cấu trúc project cho đến cách tổ chức Module và Workflow.
Tiếp tục series
Bình luận
Đang tải bình luận…