Giới thiệu
Trong một thập kỷ qua, hệ sinh thái Terraform đã phải vật lộn với một bất cập kiến trúc cơ bản: chúng ta đang sử dụng các phép toán trên hệ thống tệp để giải quyết một vấn đề của hệ thống phân tán. Điều này dẫn đến những khó khăn và đau đớn có thể đoán trước được. Bài viết này sẽ khám phá cách Stategraph có thể cải thiện quản lý trạng thái Terraform bằng cách tiếp cận vấn đề như một đồ thị rời rạc với các giao dịch ACID và cách ly dưới đồ thị.
Tóm tắt nhanh
- Trạng thái Terraform cho thấy các vấn đề về phối hợp phân tán nhưng lại sử dụng các phép toán trên tệp.
- Tệp blob (đọc/khóa 100%) so với nón thay đổi (~3%).
- Stategraph → trạng thái đồ thị, giao dịch ACID, cách ly dưới đồ thị.
Vấn đề với trạng thái dựa trên tệp
Trạng thái Terraform, ở cốt lõi, là một vấn đề phối hợp. Nhiều tác nhân (kỹ sư, hệ thống CI, phát hiện drift) cần đọc và sửa đổi các tập con chồng chéo của trạng thái hạ tầng đồng thời. Đây là một vấn đề đã được nghiên cứu tốt trong các hệ thống phân tán, với các giải pháp đã được thiết lập xoay quanh việc khóa tinh vi, kiểm soát đồng thời nhiều phiên bản, và cách ly giao dịch.
Thay vào đó, Terraform thực hiện giải pháp đơn giản nhất: một mutex toàn cục trên một tệp JSON.
Nhận xét
Xác suất xảy ra xung đột khóa trong một tệp trạng thái chia sẻ tăng lên siêu tuyến tính với cả kích thước nhóm và số lượng tài nguyên. Với 100 tài nguyên và 5 kỹ sư, bạn đang phối hợp 500 điểm tương tác tiềm năng thông qua một mutex duy nhất.
Mô hình hiện tại
tfstate.json (2.3MB)
Đọc: 100%
Khóa: 100%
Sửa đổi: 0.5%
Yêu cầu thực tế
Các nút đồ thị: VPC → Subnet → RDS → ALB → ASG → SG
Đọc: 3%
Khóa: 3%
Sửa đổi: 3%
Sự không khớp giữa độ chi tiết của hoạt động và độ chi tiết của khóa là nguyên nhân gốc rễ của mọi vấn đề mở rộng của Terraform. Nó vi phạm nguyên tắc cơ bản về cách ly trong các hệ thống đồng thời: các hoạt động không chồng chéo không nên chặn nhau.
Phản ứng tiêu chuẩn
Phản ứng tiêu chuẩn là chia tệp trạng thái không giải quyết được vấn đề. Nó chỉ phân phối lại nó. Bây giờ bạn có N vấn đề phối hợp thay vì một, cộng với độ phức tạp bổ sung trong việc quản lý các phụ thuộc giữa các trạng thái. Bạn đã trao đổi sự cạnh tranh giả tạo cho việc phối hợp giao dịch phân tán, điều này có thể tồi tệ hơn.
Trạng thái dưới dạng Đồ thị: Cách biểu diễn tự nhiên
Trạng thái hạ tầng vốn dĩ là một đồ thị có hướng. Các tài nguyên có phụ thuộc, hình thành các cạnh. Những thay đổi lan truyền dọc theo các cạnh này. Terraform đã biết điều này: đại diện nội bộ là một đồ thị, và trình lập kế hoạch thực hiện việc duyệt đồ thị. Nhưng ở tầng lưu trữ, chúng ta làm phẳng cấu trúc giàu có này thành một blob.
stategraph> -- Tìm kiếm đồ thị con tài nguyên cho thay đổi dự kiến
WITH RECURSIVE affected AS (
SELECT id, type, name FROM resources
WHERE name = 'prod-api-cluster'
UNION
SELECT r.id, r.type, r.name FROM resources r
JOIN dependencies d ON r.id = d.dependent_id
JOIN affected a ON d.resource_id = a.id
) SELECT * FROM affected;
→ 12 tài nguyên trong phạm vi thay đổi (0.003s)
→ So với: 2,847 tài nguyên trong trạng thái đầy đủ (1.2s)
Khi trạng thái được chuẩn hóa đúng cách vào một cơ sở dữ liệu đồ thị, một số thuộc tính xuất hiện tự nhiên:
Cách ly dưới đồ thị: Các hoạt động trên các đồ thị con không liên quan là có thể thực hiện song song. Nếu Nhóm A đang sửa đổi các phiên bản RDS và Nhóm B cập nhật các phân phối CloudFront, không có trạng thái chung nào để phối hợp.
Khóa chính xác: Chúng ta có thể thực hiện khóa theo hàng trên các tài nguyên và khóa theo cạnh trên các phụ thuộc. Việc lấy khóa theo thứ tự phụ thuộc sẽ ngăn chặn tình trạng deadlock thông qua thứ tự nhất quán.
Làm mới theo từng phần: Với một tập hợp thay đổi, chúng ta có thể tính toán tập hợp làm mới tối thiểu bằng cách duyệt đồ thị phụ thuộc. Hầu hết các thay đổi ảnh hưởng đến một nón nhỏ các tài nguyên, không phải toàn bộ không gian trạng thái.
Kiểm soát đồng thời thông qua các trừu tượng thích hợp
Cộng đồng hệ thống phân tán đã giải quyết những vấn đề này từ nhiều thập kỷ trước. Kiểm soát đồng thời nhiều phiên bản (MVCC) cho phép người đọc tiếp tục mà không bị chặn bởi người viết. Ghi trước cung cấp độ bền mà không hy sinh hiệu suất. Các cấp độ cách ly giao dịch cho phép các nhà vận hành chọn các đảm bảo nhất quán của họ.
Stategraph thực hiện những mẫu này ở tầng trạng thái Terraform:
Truyền thống: Khóa toàn cục
$ terraform apply
Đang lấy khóa toàn cục… chờ
Tất cả các tài nguyên đều bị khóa (100%)
Stategraph: Cách ly dưới đồ thị
$ stategraph apply
Đang khóa đồ thị con (3 tài nguyên)… sẵn sàng
Chỉ các tài nguyên bị ảnh hưởng được khóa (3%)
Mỗi hoạt động chỉ lấy khóa trên đồ thị con của nó. Trình quản lý khóa sử dụng đồ thị phụ thuộc để đảm bảo thứ tự nhất quán, ngăn chặn deadlocks. Người đọc sử dụng MVCC để truy cập các bản sao nhất quán mà không chặn người viết.
Chi tiết triển khai
Việc lấy khóa theo thứ tự tuân theo một thứ tự một phần nghiêm ngặt được lấy từ đồ thị phụ thuộc tài nguyên. Các tài nguyên được khóa theo thứ tự topo, với các nút bằng nhau được giải quyết bởi ID tài nguyên. Điều này đảm bảo không có deadlock mà không cần phối hợp toàn cục.
Kết quả là sự cải thiện đáng kể về thông lượng đồng thời:
Thực hiện song song
Giao dịch A
- Khóa: RDS:prod-db
- Khóa: SG:prod-db-sg
- Áp dụng thay đổi
Giao dịch B
- Khóa: CF:cdn-dist
- Khóa: S3:static-assets
- Áp dụng thay đổi
Giao dịch C
- Khóa: ASG:workers
- Khóa: LC:worker-config
- Áp dụng thay đổi
Ba nhóm, ba giao dịch, không có sự cạnh tranh. Điều này không thể xảy ra với trạng thái dựa trên tệp, bất kể bạn chia tách như thế nào.
Vấn đề làm mới
Việc làm mới của Terraform có độ phức tạp O(n) theo số lượng tài nguyên, bất kể phạm vi thay đổi. Thay đổi một quy tắc trong nhóm bảo mật và bạn vẫn phải duyệt toàn bộ trạng thái. Đây là một nút thắt thuật toán, không chỉ là một chi tiết triển khai.
Trạng thái dựa trên tệp
- Thay đổi 1 tài nguyên
- Làm mới tất cả 30
Trạng thái đồ thị
- Thay đổi 1 tài nguyên
- Làm mới chỉ 3
Với một đại diện đồ thị, công việc làm mới có thể được giới hạn trong đồ thị con bị ảnh hưởng thay vì toàn bộ trạng thái. Hầu hết các thay đổi chỉ ảnh hưởng đến một phần nhỏ tài nguyên, không phải mọi thứ.
Tại sao chúng tôi xây dựng điều này
Tại Terrateam, chúng tôi đã chứng kiến hàng trăm nhóm vật lộn với những vấn đề cơ bản giống nhau. Họ bắt đầu với một tệp trạng thái duy nhất, đạt đến giới hạn mở rộng, chia tách trạng thái của họ, phát hiện độ phức tạp phối hợp, xây dựng các lớp điều phối, và cuối cùng chấp nhận sống với nỗi đau đó.
Đây là một vấn đề có thể giải quyết được. Khoa học máy tính đã được hiểu rõ. Việc triển khai là dễ dàng một khi bạn thừa nhận rằng quản lý trạng thái là một vấn đề của hệ thống phân tán, không phải là một vấn đề lưu trữ tệp.
Stategraph không phải là một điều cách mạng. Nó là việc áp dụng các nguyên tắc hệ thống phân tán đã được thiết lập vào một vấn đề đã bị hiểu sai từ khi bắt đầu. Chúng tôi không phát minh ra các thuật toán mới; chúng tôi đang áp dụng các thuật toán đúng.
Nguyên tắc thiết kế
Tầng lưu trữ nên phù hợp với các mẫu truy cập. Trạng thái Terraform thể hiện các mẫu duyệt đồ thị, mẫu cập nhật từng phần, và mẫu truy cập đồng thời. Tầng lưu trữ nên là một cơ sở dữ liệu đồ thị với giao dịch ACID và khóa tinh vi. Bất cứ điều gì khác là sự không khớp.
Ngành hạ tầng đã chấp nhận trạng thái dựa trên tệp như một ràng buộc không thể thay đổi quá lâu. Nó không phải. Đó là một lựa chọn, và đó là lựa chọn sai cho các hệ thống quy mô lớn.
Triển khai kỹ thuật
Stategraph được triển khai dưới dạng một sơ đồ PostgreSQL với một backend giao tiếp với giao thức backend từ xa Terraform/OpenTofu. Chúng tôi chọn PostgreSQL vì khả năng MVCC mạnh mẽ, khả năng mở rộng đã được chứng minh và sự quen thuộc trong vận hành. Sơ đồ chuẩn hóa trạng thái thành ba quan hệ chính:
- resources: một hàng cho mỗi tài nguyên, với các cột loại, nhà cung cấp và thuộc tính.
- dependencies: bảng cạnh đại diện cho đồ thị phụ thuộc tài nguyên.
- transactions: nhật ký append-only của tất cả các biến đổi trạng thái với đầy đủ thuộc tính.
Backend mở rộng giao thức của Terraform với các hoạt động có ý thức về đồ thị. Việc lấy khóa và truy vấn trạng thái hoạt động trực tiếp trên đại diện cơ sở dữ liệu của đồ thị, cho phép độ chính xác và đồng thời mà các backend dựa trên tệp không thể cung cấp.
Đây không phải là một lớp wrapper hay một công cụ điều phối. Đây là một sự thay thế cho tầng lưu trữ bảo toàn mô hình thực thi của Terraform trong khi khắc phục các vấn đề phối hợp của nó.
Đường dẫn áp dụng
Stategraph đọc các tệp tfstate hiện có và tự động xây dựng đại diện đồ thị. Không cần thay đổi cấu hình Terraform. Giao thức backend không thay đổi. Từ quan điểm của Terraform, Stategraph chỉ là một backend khác, như S3 hoặc GCS.
Nhưng từ quan điểm vận hành, mọi thứ đều thay đổi. Sự cạnh tranh khóa biến mất. Thời gian làm mới giảm xuống hàng triệu lần. Các nhóm ngừng chặn nhau. Trạng thái trở nên có thể truy vấn, có thể kiểm toán và dễ hiểu.
Chúng tôi không yêu cầu các nhóm viết lại hạ tầng của họ. Chúng tôi yêu cầu họ lưu trữ nó một cách đúng đắn.
Câu hỏi không phải là liệu trạng thái Terraform có nên là một đồ thị hay không. Nó đã là như vậy. Câu hỏi là liệu chúng ta có tiếp tục giả vờ rằng nó là một tệp hay không.
Giới thiệu kỹ thuật
Stategraph đang trong quá trình phát triển tích cực. Chúng tôi đang làm việc với các đối tác thiết kế để xác thực cách tiếp cận ở quy mô lớn.
Hãy theo dõi để nhận thông tin cập nhật tại https://stategraph.dev.