Sửa lỗi Terraform "Saved plan is stale" trong CI/CD Pipeline

intermediate🏗️ Terraform2026-06-15| Terraform CLI, các nền tảng CI/CD (GitHub Actions, GitLab CI), và Remote Backends như AWS S3 với DynamoDB hoặc Terraform Cloud.

Error Message

Error: Saved plan is stale The given plan file can no longer be applied because the state was changed by another operation after the plan was created.
#terraform#devops#iac#ci-cd#khắc phục sự cố

Kịch bảnBạn vừa hoàn thành việc xem xét kỹ lưỡng lệnh terraform plan -out=tfplan. Kết quả trông rất hoàn hảo, vì vậy bạn kích hoạt lệnh terraform apply "tfplan". Nhưng thay vì thấy các tài nguyên được khởi tạo, terminal của bạn lại hiển thị một thông báo lỗi cụ thể. Quá trình triển khai của bạn bị dừng lại trước khi nó kịp bắt đầu.

Lỗi chính xác```

Error: Saved plan is stale

The given plan file can no longer be applied because the state was changed by another operation after the plan was created.


Trong các đội ngũ có tốc độ làm việc cao, trạng thái hạ tầng luôn là một đối tượng biến động. Vào thời điểm bạn cố gắng áp dụng plan của mình, có khả năng một đồng nghiệp hoặc một kịch bản tự động đã sửa đổi file state. Sự không khớp này khiến plan đã lưu của bạn ngay lập tức trở nên lỗi thời.
## Tại sao điều này xảy raTerraform dựa vào một file trạng thái (`terraform.tfstate`) như là nguồn sự thật tuyệt đối. Mỗi file state bao gồm một số `serial` — một số nguyên bắt đầu từ 0 và tăng thêm 1 mỗi khi state bị sửa đổi.
Khi bạn tạo một file plan bằng flag `-out`, Terraform sẽ đóng dấu số `serial` và `lineage` của trạng thái hiện tại vào file đó. Ví dụ, nếu state của bạn đang ở serial 45, `tfplan` của bạn sẽ mong đợi số 45.

Nếu một đồng nghiệp chạy lệnh `terraform apply` hoặc thậm chí là `terraform state rm` trong khi bạn đang xem xét code của mình, số serial trong backend sẽ nhảy lên 46. Cuối cùng khi bạn chạy lệnh apply, Terraform phát hiện ra rằng 45 nhỏ hơn 46 và dừng lại. Bước kiểm tra an toàn này ngăn bạn áp dụng các thay đổi dựa trên dữ liệu cũ, điều mà nếu không có thể dẫn đến việc xóa các tài nguyên mới tạo hoặc làm hỏng môi trường của bạn.
Các nguyên nhân phổ biến bao gồm:
- Một đồng nghiệp đã merge một bản vá nhanh (hotfix) làm cập nhật state.- Một job CI/CD đồng thời đã hoàn thành việc triển khai trên một branch khác.- Bạn đã tinh chỉnh state một cách thủ công bằng lệnh `terraform import` giữa các bước plan và apply.## Cách khắc phục nhanh: Phương pháp "Bắt đầu lại"Bạn không thể "làm mới" hoặc ép buộc một plan đã lỗi thời hoạt động. Cách duy nhất là đồng bộ hóa với trạng thái hiện tại và tạo một plan mới.
- **Lấy trạng thái mới nhất:** Terraform thường thực hiện việc này trong giai đoạn plan, nhưng bạn có thể chạy `terraform refresh` để chắc chắn.- **Tạo một plan mới:**```
terraform plan -out=tfplan
```- **Áp dụng plan mới ngay lập tức:**```
terraform apply "tfplan"
```Nếu bạn lại gặp lỗi tương tự ngay lập tức, hạ tầng của bạn đang thay đổi nhanh hơn mức quy trình thủ công của bạn có thể theo kịp. Điều này cho thấy có một nút thắt cổ chai trong việc phối hợp của đội ngũ hoặc lỗi trong logic xử lý đồng thời của pipeline.
## Cách khắc phục lâu dài: Quy trình & Kiến trúcCác lỗi plan lỗi thời thường xuyên là triệu chứng của một quy trình triển khai lỏng lẻo. Sử dụng ba chiến lược sau để kiểm soát chặt chẽ quy trình của bạn.
### 1. Bắt buộc khóa trạng thái (State Locking)Nếu backend của bạn không hỗ trợ khóa, bạn đang tạo điều kiện cho các tình trạng tranh chấp (race conditions). Đối với AWS S3 backend, một bảng DynamoDB là yếu tố bắt buộc để khóa trạng thái. Nó đảm bảo rằng chỉ có một người hoặc quy trình có thể ghi vào state tại một thời điểm.

terraform { backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "us-east-1" dynamodb_table = "terraform-lock-table" # Cần thiết cho sự an toàn của đội ngũ } }


### 2. Thu hẹp khoảng thời gian từ Plan đến ApplyNhiều pipeline tạo ra một plan và sau đó đợi hàng giờ để được phê duyệt thủ công. Khoảng thời gian đó càng dài, cơ hội xảy ra xung đột càng cao. Để giảm thiểu rủi ro:
- Sử dụng **giới hạn đồng thời (concurrency limits)** trong GitHub Actions (`concurrency: group_name`) để đảm bảo chỉ có một pipeline chạy trên mỗi môi trường.- Tự động áp dụng (auto-apply) các thay đổi trong các môi trường thấp hơn như `dev` hoặc `staging` để giữ cho state luôn được cập nhật nhanh chóng.- Thiết lập thời gian hết hạn 30 phút cho các bước phê duyệt thủ công.### 3. Cấm can thiệp thủ công vào StateKhông khuyến khích việc chạy các lệnh như `terraform state rm` hoặc `terraform import` từ máy cá nhân. Các thao tác này làm tăng số serial ngay lập tức. Nếu cần thay đổi thủ công, hãy phối hợp một "khung giờ bảo trì" nơi các pipeline tạm thời bị tạm dừng.
## Các bước xác minhSử dụng các bước sau để xác nhận trạng thái của bạn đã được đồng bộ hóa trở lại:
- **Kiểm tra serial hiện tại:** Lấy state và kiểm tra phiên bản một cách thủ công.```
terraform state pull | jq '.serial'
```- **Chạy thử (dry-run):** Thực hiện một lệnh `terraform plan` mới. Nếu nó báo "No changes," thì một quy trình khác đã áp dụng các thay đổi mà bạn định thực hiện.- **Xác thực việc khóa:** Thử chạy hai lệnh `plan` trong các terminal riêng biệt. Nếu lệnh thứ hai thất bại với lỗi "Lock acquired by...", thì lưới an toàn của bạn đang hoạt động.## Lời kếtLỗi "Saved plan is stale" không phải là một bug — đó là Terraform đang đóng vai trò như một rào chắn bảo vệ. Nó bảo vệ môi trường production của bạn khỏi các kịch bản "ghi đè cuối cùng thắng" (last-write-wins) có thể vô tình xóa sạch công sức làm việc hàng giờ liền. Mặc dù việc tạo lại plan là cách khắc phục nhanh, nhưng việc triển khai khóa trạng thái nghiêm ngặt và quản lý đồng thời pipeline là cách duy nhất để loại bỏ vấn đề này vĩnh viễn.

Related Error Notes