Câu hỏi này thường được trả lời bằng niềm tin. Bài này dựng cùng một ứng dụng hai container bằng cả hai cách rồi đo song song.
Bốn chỉ số đầu: gần như hoà
| Compose | Kubernetes | |
|---|---|---|
| Số dòng cấu hình | 10 | 42 |
| Khởi động lần đầu | 0,9 giây | 1,2 giây |
| RAM container (web / api) | 12,9 / 20,9 Mi | 12 / 12 Mi |
| Ứng dụng tự thoát → sống lại | 0,4 giây | 2,1 giây |
Kubernetes không chậm hơn khi khởi động, và không tốn thêm RAM ở tầng container. Hai điều đó ngược với ấn tượng phổ biến.
Bốn lần số dòng cấu hình là thật, nhưng 42 dòng vẫn là 42 dòng — nó không phải rào cản mà người ta hay mô tả.
Chỗ Kubernetes thua rõ là khởi động lại: 2,1 giây so với 0,4. Compose nhanh hơn vì Docker daemon thấy container chết và dựng lại ngay tại chỗ; Kubernetes phải đi qua kubelet, API server, và vòng lặp đối chiếu.
Nhưng Kubernetes mang theo phần không nằm trong ứng dụng
control plane 536 MB (đo ở phần 52)
kubelet + containerd mỗi node 207 MB (đo ở phần 55)
Compose không có gì tương đương — nó dùng chính Docker daemon đã chạy sẵn.
Đây mới là cái giá thật, và nó cố định: 743 MB cho một cụm một node, dù bạn chạy một container hay một trăm. Với ứng dụng chiếm 33 MB như trong phép đo, phần hạ tầng lớn gấp hai mươi lần phần ứng dụng.
Tỷ lệ đó đảo ngược rất nhanh khi số dịch vụ tăng, và đó chính là chỗ ranh giới nằm.
Cập nhật phiên bản: chỗ khác biệt thật
Tôi bắn liên tục vào dịch vụ rồi đổi image giữa chừng.
| Số lỗi | |
|---|---|
| Compose, đổi image | 2 / 408 yêu cầu |
Kubernetes, 1 bản, không probe, không preStop |
1 / 446 |
Kubernetes, 3 bản + readinessProbe + preStop |
0 / 533 |
Dòng thứ hai là dòng đáng dừng lại: Kubernetes trần trụi không tốt hơn Compose. Một bản sao, không probe, không preStop — vẫn mất yêu cầu, đúng như Compose.
Cái cho không đứt quãng là nhiều bản + readinessProbe + preStop, đúng ba thứ đã đo ở phần 51. Kubernetes không tặng bạn thời gian hoạt động; nó tặng bạn cơ chế để đạt được nó — và Compose không có cơ chế nào trong ba cái đó.
Đây là cách trả lời câu hỏi ban đầu chính xác nhất: Kubernetes không tốt hơn, nó có thể tốt hơn nếu bạn cấu hình đủ.
Một chỗ Compose không làm như quảng cáo
docker kill + restart: unless-stopped -> exited, restarts 0
docker kill + restart: always -> exited, restarts 0
Lặp ba lần, đều vậy. Cả hai chính sách khởi động lại đều không dựng container lên sau docker kill.
Lý do: Docker coi docker kill và docker stop là yêu cầu tường minh của người vận hành, và đánh dấu container là "đã dừng có chủ đích". Chính sách khởi động lại không đè lên chủ đích đó.
Nhưng khi ứng dụng tự thoát — tôi chạy nginx -s stop bên trong — thì nó dựng lại sau 0,4 giây, RestartCount từ 0 lên 1. Chính sách hoạt động đúng cho trường hợp nó được thiết kế.
Điều đáng nhớ là cách người ta hay kiểm thử: docker kill để "thử xem nó có tự sống lại không", thấy nó không sống lại, rồi kết luận sai — hoặc tệ hơn, sửa cấu hình cho tới khi phép thử sai đó cho kết quả đúng.
Trong Kubernetes không có sự phân biệt này: pod bị xoá thì ReplicaSet dựng lại, bất kể ai xoá và vì sao.
Chọn thế nào
Từ các con số ở trên, ranh giới khá rõ:
Dùng Compose khi: một máy chủ là đủ; dừng vài giây lúc triển khai chấp nhận được; số dịch vụ đếm trên một bàn tay; và không ai trong đội muốn học Kubernetes.
Dùng Kubernetes khi: cần nhiều hơn một máy; cần triển khai không đứt quãng; có nhiều dịch vụ giống nhau cần vận hành theo cùng một cách; hoặc cần thứ chỉ Kubernetes có — tự mở rộng, tự chữa, phân quyền theo namespace.
Và một lựa chọn thứ ba ít được nhắc: Compose trên nhiều máy với một bộ cân bằng tải phía trước. Nó xử lý được phần lớn nhu cầu "không đứt quãng" mà không cần control plane, đổi lại là bạn tự lo việc điều phối.
Chuyển từ Compose sang Kubernetes
Nếu quyết định chuyển, thứ tự này ít đau nhất:
- Chuyển máy móc trước.
kompose convertdựng bộ khung; đừng kỳ vọng nó đúng, chỉ cần nó đỡ phải gõ. - Thêm
readinessProbengay. Không có nó thì mọi thứ khác vô nghĩa — đã đo ở trên. - Đặt
requests. Không córequeststhì pod làBestEffortvà bị đuổi đầu tiên (phần 12). - Bỏ
depends_on. Kubernetes không có khái niệm tương đương và cố ý không có; ứng dụng phải tự thử lại. Đây là thay đổi tư duy lớn nhất, và nó thuộc về mã ứng dụng chứ không thuộc về YAML. - Volume tính sau cùng. Đây là phần khác nhau nhiều nhất và dễ mất dữ liệu nhất — xem phần 54.
Thử ba mươi giây
Nếu đang dùng Compose, kiểm xem ứng dụng có sống sót qua kiểu hỏng thật hay không:
# KHÔNG dùng docker kill — nó bị coi là chủ đích của bạn
docker compose exec <dich-vu> sh -c 'kill -TERM 1' || \
docker compose exec <dich-vu> <lenh-dung-ung-dung>
sleep 5
docker compose ps
docker inspect <container> -f 'restarts={{.RestartCount}}'
RestartCount tăng nghĩa là chính sách khởi động lại đang hoạt động. Vẫn 0 và container exited nghĩa là bạn vừa kiểm bằng cách Docker coi là chủ đích — thử lại bằng cách làm chính ứng dụng thoát.
Phần cuối: gom mọi phép đo của sáu mươi phần thành một danh sách kiểm.