Câu hỏi "tự dựng hay trả tiền" hầu như luôn được trả lời bằng cảm tính: tự dựng thì tốn máy và tốn người, dịch vụ thì tốn hoá đơn. Nghe hợp lý đến mức ít ai đi đo. Nhưng cảm tính về chi phí tài nguyên thường lệch xa thực tế cả chục lần, và tệ hơn, nó khiến người ta canh chừng nhầm chỗ. Phần này tôi đo vế "tốn máy" cho thật chính xác, rồi để vế "tốn người" lại thành một phép tính mà bạn tự điền giá của mình vào.
Nói trước cho sòng phẳng: tôi không đo được dịch vụ trả tiền. Không có tài khoản, và gửi dữ liệu ra một dịch vụ bên ngoài không phải việc tôi tự ý làm. Nên mọi con số về SaaS trong bài này là số học trên giá bạn tra được, không phải phép đo — và tôi sẽ nói rõ chỗ nào là đo, chỗ nào là tính. (Phần đo chạy Prometheus chính hãng trong container một-lần với --memory=1g, tức đúng cỡ VM rẻ nhất mà các nhà cung cấp bán; không có phần cứng đặc biệt nào.)
Bảng số liệu
Prometheus chính hãng trong container dùng một lần, thu thập mỗi 15 giây từ một exporter phơi ra đúng N chuỗi. Mỗi mức đo trong 180 giây sau khi để ổn định 45 giây, ba lần. Có kiểm chứng đàng hoàng: truy vấn count(chi_so_ung_dung) phải trả về đúng N — nếu Prometheus nuốt hụt thì con số tài nguyên vô nghĩa.
| Số chuỗi | Prometheus thấy | CPU (một lõi) | RAM |
|---|---|---|---|
| 900 | 900 ✓ | 0,03% | 28,3 MB |
| 5 000 | 5 000 ✓ | 0,07% | 37,4 MB |
| 20 000 | 20 000 ✓ | 0,12% | 68,9 MB |
| 100 000 | 100 000 ✓ | — | 219,9 MB |
| 300 000 | 300 000 ✓ | — | 633,9 MB |
| 1 000 000 | bị OOM giết | — | vượt 1 GB |
Ba mức cuối chạy với giới hạn cứng --memory=1g, tức đúng một VM rẻ nhất trên thị trường.
Hai mươi nghìn chuỗi — cỡ một cụm Kubernetes trăm pod, theo con số 187 chuỗi mỗi pod đo được ở phần 36 — tốn 0,12% một lõi CPU và 69 MB RAM. Đọc lại con số đó một lần nữa, vì nó là chìa khoá của cả bài.
Ba con số, và một cái bẫy tôi suýt sập
Chi phí tài nguyên nhỏ đến mức nó không phải là lý do để trả tiền. Với 0,12% một lõi, phải cần tới 16,7 triệu chuỗi mới bão hoà nổi một lõi CPU. Không đội nào trong tầm bài viết này có ngần ấy chuỗi. Cái lập luận quen thuộc "tự dựng tốn máy" đơn giản là sai ở quy mô vừa và nhỏ — sai không phải chút ít, mà sai vài bậc độ lớn.
RAM tăng tuyến tính, và cái mô hình tuyến tính ấy đoán đúng chỗ chết. Bốn mức đo cho 2,27 / 2,18 / 1,98 / 2,07 KB mỗi chuỗi — lấy 2,07 KB. Từ đó suy ra trần của 1 GB là 491 767 chuỗi. Nên 300 000 chuỗi phải sống (đo được: 633,9 MB, 62%) và 1 000 000 chuỗi phải chết. Cả hai lần chạy đều đúng y như vậy. Một mô hình đoán trước được điểm gãy là một mô hình đáng tin — và đây là chỗ dễ dừng lại mà tự mãn.
Nhưng RAM không phải thứ gãy trước. Đây mới là chỗ dễ tính sai nhất trong cả bài, và là thứ suýt lừa cả tôi. Lấy 2,16 byte mỗi mẫu — hằng số đo được ở phần 19 — và chu kỳ 15 giây:
| 20 000 chuỗi | 300 000 chuỗi | |
|---|---|---|
| Mẫu mỗi ngày | 115,2 triệu | 1,73 tỷ |
| Đĩa mỗi ngày | 248,8 MB | 3,73 GB |
| Giữ 30 ngày | 7,5 GB | 112,0 GB |
Một VM 1 GB RAM thường đi kèm 25 GB đĩa. Với thời gian giữ 30 ngày, 25 GB chỉ chứa nổi 66 980 chuỗi.
Trần RAM là 491 767 chuỗi, trần đĩa là 66 980 chuỗi. Đĩa chạm trần sớm hơn 7,3 lần. Nếu bạn chọn máy theo RAM — thứ ai cũng liếc đầu tiên — bạn sẽ mua đúng một cỗ máy chết vì cạn đĩa trước khi kịp dùng một phần bảy số RAM đã trả tiền. Cái bẫy nằm ở chỗ RAM là con số hiện ra ngay, còn đĩa thì âm thầm dồn lại theo ngày.
Vì sao ba tài nguyên lừa ta khác nhau
Ba tài nguyên co giãn theo ba cách khác nhau, và đó là gốc rễ của mọi tính sai:
- CPU theo tốc độ — số mẫu nuốt vào mỗi giây. Nó không tích luỹ. Tắt máy rồi bật lại, CPU về như cũ.
- RAM theo số chuỗi đang hoạt động — Prometheus giữ khối đang ghi của mỗi chuỗi trong bộ nhớ. Cũng không tích luỹ theo thời gian: chuỗi ngừng phát thì sau một lúc nó rời khỏi bộ nhớ.
- Đĩa theo tốc độ nhân với thời gian giữ — và chỉ có nó tích luỹ. Đây là tài nguyên duy nhất mà con số hôm nay chẳng nói gì về con số tháng sau.
Chính vì thế mọi phép thử nhanh đều cho cảm giác sai lệch. Đo trong ba phút thì CPU và RAM đã ở giá trị cuối cùng, còn đĩa mới xài chưa tới một phần vạn cái nó sẽ xài. Tôi cố ý không đo dung lượng đĩa bằng cách nhìn thư mục dữ liệu: ở phần 19 tôi đã sập đúng cái bẫy ấy và nhận chunks = 0,00 MB, vì Prometheus cần 120 mẫu mới ghi xuống một khối. Con số đĩa ở trên tính từ hằng số byte-mỗi-mẫu, chứ không phải từ một mẫu ba phút.
Phép tính bạn tự điền giá vào
Chi phí tự dựng mỗi tháng có hai phần, và chỉ phần đầu là tiền máy:
tu dung = gia VM + so gio van hanh x don gia gio cua ban
tra tien = hoa don theo khoi luong
Điểm hoà vốn không nằm ở khối lượng dữ liệu, mà ở số giờ:
so gio hoa von = (hoa don SaaS thang - gia VM) / don gia gio
Một VM nhỏ nuốt được tới 67 000 chuỗi trong giới hạn đĩa, tức khoảng 350 pod Kubernetes. Nếu hoá đơn SaaS cho lượng đó là X mỗi tháng và VM tốn 5, thì tự dựng chỉ đắt hơn khi bạn đổ vào đó nhiều hơn (X − 5) / đơn giá giờ giờ mỗi tháng.
Với đa số đội, con số ấy rơi vào khoảng một đến ba giờ mỗi tháng. Đó mới là câu hỏi thật: bạn có tin mình vận hành được cụm này trong dưới ba giờ mỗi tháng không?
Chính sê-ri này là dữ liệu để trả lời. Trong hai mươi phần đo đạc vừa qua, tôi đã đâm vào: cửa sổ truy vấn chồng lấn của Loki, tệp thưa của Badger khiến du báo sai 133 lần, bốn giá trị mặc định của Tempo làm truy vấn trả rỗng bốn lần liên tiếp, hàng đợi span âm thầm bỏ 62,5% dữ liệu, sổ đăng ký chỉ số gộp của k3s, probe eBPF gắn nhầm syscall, và nhịp tim vẫn đập ba phút sau khi Prometheus đã chết. Không cái nào báo lỗi. Đó là hình dạng thật của "giờ vận hành" — không phải cài đặt, mà là phát hiện ra thứ đang im lặng sai.
Cùng một cái bẫy đo lường, đổi ngăn xếp
Chuyện "ba tài nguyên co giãn ba kiểu, và cái tích luỹ mới là cái giết bạn" không riêng gì Prometheus — nó là một sai lầm dung lượng kinh điển hiện ra ở khắp nơi.
Trong một ứng dụng Java, bạn có thể tinh chỉnh heap JVM đến từng megabyte và tự tin là đã lo xong bộ nhớ — rồi cỗ máy chết vì thư mục log GC, heap dump, hay chính file log ứng dụng dồn đầy đĩa. Heap là con số bạn nhìn mỗi ngày; đĩa là con số dồn lại sau lưng. Cùng một sự lệch pha giữa "cái hiện ra" và "cái tích luỹ".
Trong Go, một dịch vụ có thể ổn định về RAM nhờ GC, nhưng nếu nó ghi metric hay trace ra đĩa cục bộ (hay đẩy vào một volume), thì bottleneck thật là tốc-độ-ghi-nhân-thời-gian-giữ, đúng công thức trên. pprof cho bạn thấy CPU và heap ngay lập tức, nhưng chẳng có profiler nào cảnh báo "30 ngày nữa đĩa đầy".
Và ở cơ sở dữ liệu, đây gần như là bài học vỡ lòng của DBA: một CSDL có thể thừa RAM và CPU mà vẫn ngã gục vì WAL/redo log, index phình, hay bảng audit tăng tuyến tính theo thời gian. Dung lượng bảng là thứ tích luỹ; ai lên kế hoạch dung lượng CSDL đều biết phải chiếu con số ghi-mỗi-ngày ra vài tháng chứ không nhìn ảnh chụp hôm nay.
Mẫu số chung: trong bất kỳ hệ nào, tài nguyên nguy hiểm nhất không phải cái lớn nhất, mà là cái tích luỹ theo thời gian — vì nó là cái duy nhất mà một phép đo ngắn không nhìn thấy. CPU và RAM là ảnh chụp; đĩa (và mọi thứ dồn lại như nó) là một cuốn phim. Nên khi ước lượng dung lượng, câu hỏi đúng không phải "bây giờ nó ăn bao nhiêu" mà là "cái nào trong đám này còn phình sau ba mươi ngày nữa".
Muốn tự trả lời cho hệ của mình thì lấy số pod của bạn nhân với 187 ra số chuỗi, rồi chia 66 980 cho con số đó: ra số VM nhỏ nhất bạn cần. Nếu kết quả nhỏ hơn 1, tiền máy không phải lý do để bạn trả tiền cho ai cả — và bạn nên hỏi một câu khác hẳn: mỗi tháng bạn muốn tiêu bao nhiêu giờ cho việc này.