Tìm hiểu về lỗi
Ít có điều gì gây khó chịu hơn việc thấy ứng dụng của bạn bị sập vì nó không thể tìm thấy nút điều phối (leader) trong cụm MongoDB cluster của bạn. Nói một cách dễ hiểu, lỗi này có nghĩa là driver của bạn đã kết nối tới các nút mà bạn liệt kê, nhưng nó không tìm thấy nút Primary nào để giao tiếp. Vì MongoDB chỉ cho phép các thao tác ghi (write operations) trên nút Primary, driver sẽ từ bỏ thay vì mạo hiểm gây ra tình trạng không nhất quán dữ liệu.
Hãy tưởng tượng nó giống như một cuộc họp hội đồng quản trị mà không ai có thể thống nhất bầu ra chủ tịch. Nếu không có người lãnh đạo, không có quyết định nào được đưa ra. Điều này thường xảy ra vì một trong bốn lý do sau:
- Khủng hoảng danh tính: Tên replica set trong mã nguồn của bạn không khớp với tên trên máy chủ.
- Mất đi đa số: Bạn đã mất quá nhiều nút để có thể tổ chức một cuộc bầu chọn hợp lệ.
- Sự im lặng đáng sợ: Các nút vẫn đang hoạt động nhưng không thể giao tiếp với nhau qua mạng.
- Nhầm lẫn về tên gọi: Ứng dụng của bạn có thể thấy các nút, nhưng không thể phân giải các hostname mà các nút sử dụng để tự định danh.
Các giải pháp từng bước
1. Khớp tên Replica Set
Nếu bạn yêu cầu driver tìm kiếm một tập hợp có tên là production-rs, nhưng cấu hình nội bộ của bạn lại tên là rs0, driver sẽ bỏ qua các nút mà nó tìm thấy. Nó giả định rằng nó đã kết nối nhầm cụm cluster.
Mở mongosh trên một trong các nút của bạn và chạy:
rs.conf()._id
Nếu đầu ra là "rs0", hãy đảm bảo chuỗi kết nối của bạn có dạng mongodb://host1:27017/?replicaSet=rs0. Ngay cả một lỗi đánh máy nhỏ ở đây cũng sẽ làm hỏng kết nối.
2. Kiểm tra Quorum và Quyền bầu chọn
MongoDB dựa trên nguyên tắc đa số tuyệt đối (50% + 1) để bầu ra leader. Trong một cụm 3 nút tiêu chuẩn, bạn cần ít nhất 2 nút trực tuyến. Nếu hai nút bị sập—có lẽ trong một cửa sổ bảo trì đồng thời—nút cuối cùng còn lại sẽ tự hạ cấp xuống thành SECONDARY vì nó không còn giữ được đa số.
Kiểm tra tình trạng cụm cluster của bạn bằng:
rs.status()
Kiểm tra danh sách "members" để tìm các dấu hiệu cảnh báo sau:
health: 0: Nút không thể truy cập.stateStr: Nếu tất cả đều làSECONDARY, không có ai đang điều phối.lastHeartbeatMessage: Đây là nơi MongoDB giải thích lý do tại sao nó không thể thấy các nút đồng cấp (ví dụ: "Connection refused").
3. Khắc phục khoảng cách mạng
Trong môi trường Docker hoặc Kubernetes, các nút thường không giao tiếp được với nhau vì chúng sử dụng ID container nội bộ hoặc localhost thay vì các hostname có thể truy cập được. Nếu Nút A không thể ping Nút B, chúng không thể bầu chọn.
Kiểm tra đường truyền giữa các nút của bạn một cách thủ công:
# Từ nút mongo đầu tiên, thử kết nối tới nút thứ hai
nc -zv mongo-node-2 27017
Nếu kết nối bị hết thời gian chờ (timeout), có khả năng tường lửa hoặc các security group của bạn đang chặn cổng 27017.
4. Tái cấu hình khẩn cấp (Sử dụng thận trọng)
Nếu bạn đang trong một kịch bản thảm họa khi hầu hết các nút đã mất vĩnh viễn, bạn có thể cần phải ép buộc nút còn lại đảm nhận quyền điều phối. Đây là một bước đi "chỉ dùng trong trường hợp khẩn cấp" vì nó có thể gây ra tình trạng rollback dữ liệu nếu các nút cũ đột ngột xuất hiện trở lại.
Trên nút còn sống sót, chạy:
let cfg = rs.conf();
cfg.members = [cfg.members[0]]; // Loại bỏ tất cả các thành viên khác khỏi cấu hình
rs.reconfig(cfg, {force: true});
Lệnh này yêu cầu nút ngừng tìm kiếm đa số và bắt đầu hoạt động như một Primary ngay lập tức.
5. Giải quyết các vấn đề về DNS Hostname
Đây là một cái bẫy phổ biến. Khi ứng dụng của bạn kết nối lần đầu, nó sử dụng IP mà bạn cung cấp. Tuy nhiên, cụm cluster sau đó sẽ đưa cho ứng dụng một danh sách các hostname từ cấu hình nội bộ của nó. Nếu máy chủ ứng dụng của bạn không thể phân giải các hostname cụ thể đó (như mongo-db-0.internal.local), nó sẽ không tìm thấy Primary.
Kiểm tra xem cụm cluster đang phát đi những tên nào:
rs.conf().members.map(m => m.host)
Hãy thử ping chính xác các chuỗi đó từ máy chủ ứng dụng của bạn. Nếu thất bại, bạn cần cập nhật DNS hoặc tệp /etc/hosts của mình.
Cách xác minh bản sửa lỗi
Sau khi bạn đã tinh chỉnh cấu hình, hãy xác minh tình trạng của cụm cluster. Chạy rs.status() và đảm bảo một nút hiển thị "stateStr": "PRIMARY". Sau đó, hãy thử kết nối trực tiếp từ máy chủ ứng dụng của bạn bằng shell:
mongosh "mongodb://user:pass@host1:27017,host2:27017/?replicaSet=rs0"
Nếu bạn thấy một dấu nhắc như rs0 [direct: primary]>, bạn đã hoạt động trở lại bình thường.
Các mẹo bảo trì chủ động
- Luôn sử dụng số lẻ: Luôn triển khai 3, 5 hoặc 7 nút. Số lượng nút chẵn làm cho việc "hòa" trong bầu chọn dễ xảy ra hơn, dẫn đến việc không có Primary.
- Sử dụng Arbiter một cách khôn ngoan: Nếu một nút dữ liệu thứ ba quá tốn kém, hãy thêm một instance Arbiter nhỏ. Nó không chứa dữ liệu nhưng cung cấp phiếu bầu phá vỡ thế cân bằng cần thiết cho các cuộc bầu chọn.
- Kiểm tra quyền ưu tiên: Kiểm tra xem bạn có vô tình đặt
priority: 0trên mọi nút hay không. Một nút có quyền ưu tiên bằng 0 sẽ bị cấm trở thành Primary.

