Sao chép Kafka giữa hai vùng là nền của mọi kế hoạch phục hồi thảm hoạ. Bài này dựng hai cụm thật, chạy MirrorMaker 2, và tìm ra rằng phần dễ chạy hoàn hảo còn phần khó thì im lặng không chạy.
Dựng
Hai cụm độc lập A và B, sao chép topic don từ A sang B:
clusters = A, B
A.bootstrap.servers = kA:9092
B.bootstrap.servers = kB:9092
A->B.enabled = true
A->B.topics = don
sync.group.offsets.enabled = true
connect-mirror-maker.sh mm2.props
Dữ liệu sang rất nhanh
| chép hết 200.000 tin đã có | 8,7 giây |
| độ trễ khi đang chạy | 1.467 / 498 / 1.457 / 1.436 ms |
Khoảng một giây rưỡi cho một tin mới (đã bao gồm khoảng 200–300 ms chạy công cụ). Trên mạng cục bộ thì đây gần như là mức sàn; giữa hai vùng thật, cộng thêm thời gian đi về của mạng.
Tên topic ở cụm đích mang tiền tố cụm nguồn:
cụm A: don -> cụm B: A.don
Tiền tố là cố ý và quan trọng: nó ngăn vòng lặp vô tận khi bạn sao chép hai chiều. Không có nó, don ở B sẽ được chép ngược về A thành don, rồi lại sang B, mãi mãi.
Đổi lại, mọi consumer ở cụm B phải biết tên có tiền tố. Đây là chỗ kế hoạch chuyển vùng hay bị hụt: ứng dụng đọc don ở A, và khi chuyển sang B nó phải đọc A.don — một thay đổi cấu hình mà ai đó phải nhớ, đúng vào lúc đang có sự cố.
Offset thì không sang
Đây là phần quan trọng nhất, và nó không hoạt động.
Nhóm gfail ở cụm A đã đọc tới:
p0 offset 16.200 p1 offset 16.900 p2 offset 16.900
Ở cụm B, sau khi bật sync.group.offsets.enabled=true và chờ:
topic A.checkpoints.internal:0:0 không có bản ghi nào
nhóm gfail: không tồn tại
Tôi thử thêm A->B.groups=.*, emit.checkpoints.enabled=true, sync.group.offsets.interval.seconds=5, khởi động lại MirrorMaker, và thử cả với một nhóm đang hoạt động thay vì nhóm đã dừng. Vẫn 0 bản ghi.
Trong thời gian tôi dành cho nó, tôi không tìm ra nguyên nhân, và ghi lại đúng như vậy thay vì đoán.
Có một dòng lỗi trong log nhưng nó thuộc chiều ngược lại (B->A, chiều tôi không bật):
ERROR [Worker clientId=B->A] Failed to reconfigure connector's tasks
(MirrorCheckpointConnector), retrying after backoff.
RetriableException: Timeout while loading consumer groups.
Điều đáng nói nhất không phải là nó hỏng, mà là cách nó hỏng: topic checkpoint được tạo ra đàng hoàng, rỗng, và im lặng. Không có gì trên bảng theo dõi cho biết việc dịch offset đang không chạy.
Bài học vận hành
Kế hoạch chuyển vùng dựa vào offset đã dịch chỉ đúng nếu offset thật sự được dịch. Hãy kiểm chứ đừng cấu hình rồi tin:
kafka-get-offsets.sh --bootstrap-server kB:9092 --topic A.checkpoints.internal
Ra 0 nghĩa là khi cụm A chết, mọi consumer khởi động ở cụm B sẽ không tìm thấy offset nào và rơi về auto.offset.reset:
earliest→ xử lý lại toàn bộ lịch sử còn giữ trên topiclatest→ bỏ qua mọi thứ chưa xử lý
Cả hai đều là hỏng, và cả hai đều xảy ra trong im lặng đúng vào lúc tệ nhất.
Câu tổng kết: sao chép dữ liệu là phần dễ. Chuyển vùng có trạng thái mới là phần khó.
Bảy topic nội bộ
MM2 tạo ra khá nhiều thứ ở cả hai cụm:
cụm B: A.don bản sao dữ liệu
A.checkpoints.internal offset đã dịch
heartbeats kiểm tra đường sống
mm2-configs.A.internal cấu hình Connect
mm2-offsets.A.internal tiến độ của chính MM2
mm2-status.A.internal trạng thái connector
cụm A: mm2-offset-syncs.B.internal ánh xạ offset A <-> B
Đáng biết vì hai lý do: chúng chiếm chỗ và cần chính sách lưu trữ, và khi dọn dẹp một thiết lập MM2, xoá connector không xoá chúng.
Ba kiến trúc, chọn cái nào
Chủ động – bị động. Ghi vào A, sao chép sang B, B chỉ dùng khi A chết. Đơn giản nhất và là lựa chọn đúng cho phần lớn trường hợp. Điểm yếu là chính bài toán offset ở trên.
Chủ động – chủ động. Ghi vào cả hai, sao chép hai chiều. Tiền tố topic ngăn vòng lặp, nhưng ứng dụng phải đọc cả don lẫn B.don để thấy đủ dữ liệu. Và không có thứ tự toàn cục giữa hai cụm — hai sự kiện cùng khoá ghi ở hai nơi sẽ không có quan hệ trước-sau nào.
Cụm trải vùng. Một cụm Kafka duy nhất với broker ở nhiều vùng, dùng rack awareness để rải bản sao. Không cần MM2, không có tiền tố, offset đúng tự nhiên. Đổi lại acks=all phải chờ qua liên vùng — phần 4 đo được p50 434 ms trên mạng cục bộ, và liên vùng thì cộng thêm 50–150 ms mỗi chiều.
Kiến trúc thứ ba là kiến trúc duy nhất giải quyết được bài toán offset một cách sạch sẽ, và nó chỉ khả thi khi độ trễ giữa các vùng đủ thấp.
Thử ba mươi giây
Nếu bạn đang chạy MM2, kiểm ba con số:
# dữ liệu có sang không
kafka-get-offsets.sh --bootstrap-server kB:9092 --topic A.ten-topic
# offset có được dịch không
kafka-get-offsets.sh --bootstrap-server kB:9092 --topic A.checkpoints.internal
# nhóm consumer có xuất hiện ở cụm đích không
kafka-consumer-groups.sh --bootstrap-server kB:9092 --list | grep ten-nhom
Con số đầu tiên tăng còn hai con số sau không đổi nghĩa là bạn có bản sao dữ liệu nhưng không có kế hoạch chuyển vùng — và khác biệt đó chỉ lộ ra vào ngày cụm chính chết.
Phần sau đo quy mô cụm: bao nhiêu broker là đủ, và dấu hiệu nào nói đã đến lúc thêm.