etcd là nơi duy nhất Kubernetes lưu trạng thái. Phần trước đo được nó chỉ chiếm 55 MB bộ nhớ — nhỏ hơn nhiều so với tiếng tăm. Bài này mở nó ra xem bên trong có gì.
Toàn bộ cụm nằm trong 2,6 MB
VERSION 3.6.5 DB SIZE 2.6 MB IN USE 2.6 MB IS LEADER true
Hai phẩy sáu megabyte cho một cụm đang chạy vài deployment. etcd không lưu dữ liệu ứng dụng — nó chỉ lưu mô tả những gì cần chạy.
Xem phân bố khoá trong /registry:
| Loại | Số khoá |
|---|---|
events |
173 |
clusterroles |
72 |
clusterrolebindings |
58 |
serviceaccounts |
45 |
apiregistration.k8s.io |
21 |
pods |
15 |
configmaps |
13 |
Event nhiều gấp mười một lần pod.
Phần lớn nội dung etcd không phải ứng dụng của bạn — là RBAC (clusterroles + clusterrolebindings + serviceaccounts = 175 khoá) và event. Trên một cụm rỗng, phần thuộc về bạn gần như không đáng kể.
Event tự hết hạn sau một giờ (--event-ttl mặc định), nên chúng không tích luỹ vô hạn. Nhưng trên cụm bận — nhiều pod khởi động lại, nhiều lần triển khai — chúng là nguồn tăng DB SIZE thường gặp nhất, và cũng là thứ đầu tiên nên nhìn khi etcd phình ra.
Trần kích thước một object: 1.048.576 byte
Tạo ConfigMap với kích thước tăng dần:
100 kB -> created
500 kB -> created
1000 kB -> created
1500 kB -> Too long: may not be more than 1048576 bytes
2000 kB -> Too long: may not be more than 1048576 bytes
Đúng 1 MiB, và nó áp cho mọi loại object — ConfigMap, Secret, CRD, cả pod spec.
Đây là giới hạn hay bị đâm vào theo những cách bất ngờ:
- Nhét chứng chỉ hoặc bộ dữ liệu vào ConfigMap.
- CRD với
statustích luỹ mãi — một số toán tử ghi lịch sử vào đó. - Pod có annotation khổng lồ, ví dụ
kubectl.kubernetes.io/last-applied-configurationcủa một manifest rất dài.
Cách xử lý: dữ liệu lớn không thuộc về etcd. Dùng PersistentVolume, object storage, hoặc initContainer tải về lúc khởi động.
Ngoài trần từng object, etcd còn có trần toàn bộ cơ sở dữ liệu — --quota-backend-bytes, mặc định 2 GB (bảng trên hiện QUOTA 0 B nghĩa là chưa đặt riêng). Vượt là etcd chuyển sang chế độ chỉ đọc, và cả cụm mất khả năng ghi. Đó là một trong những sự cố Kubernetes khó chịu nhất, vì kubectl get vẫn chạy trong khi kubectl apply thì không.
Độ trễ ghi
kubectl create configmap 29 ms
trừ đi kubectl khởi động 24 ms
------------------------------------
đường apiserver -> etcd 5 ms
Ghi vào etcd rẻ: 5 mili giây cho cả chuỗi xác thực, phân quyền, kiểm tra hợp lệ, ghi và đồng bộ.
Cái đắt là chính binary kubectl khởi động. 24 trong 29 mili giây — 83% thời gian của một lệnh — chưa chạm tới cụm.
Đây là phiên bản nhẹ của cái bẫy ở sê-ri Kafka, nơi công cụ dòng lệnh JVM tốn 0,86 giây. Với kubectl thì chỉ 24 ms, nhưng nguyên tắc giống hệt: đừng đo hiệu năng cụm bằng cách gọi lặp lại công cụ dòng lệnh.
Hệ quả thực dụng: một kịch bản chạy kubectl một nghìn lần tốn 24 giây chỉ để khởi động tiến trình. Gộp thành một lời gọi (kubectl apply -f cho cả thư mục) hoặc dùng thư viện client là khác biệt lớn.
Vì sao etcd là thành phần cần cẩn thận nhất
Nó nhỏ, nó nhanh, và mất nó là mất cả cụm. Không có bản sao lưu nào khác — pod đang chạy vẫn chạy (phần trước đã đo), nhưng mọi mô tả về việc nên chạy gì đã biến mất.
Ba việc bắt buộc trên cụm thật:
Sao lưu định kỳ.
etcdctl --endpoints=127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/etcd-$(date +%F).db
Với DB SIZE 2,6 MB thì bản chụp gần như miễn phí. Không có lý do gì để không chạy nó mỗi giờ.
Số node lẻ. etcd dùng Raft, cần đa số. Ba node chịu được một chết; bốn node cũng chỉ chịu được một — thêm node chẵn không thêm khả năng chịu lỗi, chỉ thêm chi phí đồng thuận. Năm node chịu được hai.
Đĩa nhanh. etcd gọi fsync cho mọi lần ghi, nên độ trễ đĩa vào thẳng độ trễ mọi lời gọi API. Đây là chỗ ổ thể rắn không phải tuỳ chọn.
Theo dõi ba chỉ số
etcd_disk_wal_fsync_duration_seconds p99 nên dưới 25 ms
etcd_disk_backend_commit_duration_seconds p99 nên dưới 25 ms
etcd_server_leader_changes_seen_total nên gần như không đổi
Chỉ số thứ ba là chỉ báo sớm tốt nhất: etcd đổi leader liên tục nghĩa là mạng giữa các node đang có vấn đề, và mọi lời gọi API sẽ chậm dần trước khi có gì hỏng hẳn.
Thử ba mươi giây
Xem etcd của bạn đang chứa gì:
kubectl -n kube-system exec etcd-<node> -- etcdctl \
--endpoints=127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get /registry --prefix --keys-only \
| sed 's|^/registry/||;s|/.*||' | grep . | sort | uniq -c | sort -rn | head
Nếu events không đứng đầu, cụm của bạn khác thường. Nếu một loại object nào đó bất ngờ đứng đầu — thường là CRD của một toán tử — đó là chỗ đáng xem trước khi DB SIZE thành vấn đề.
Phần sau đo API server: 83% thời gian một lệnh là chi phí client, vậy 17% còn lại đi đâu.