1. Kiến trúc cụm MinIO Distributed Mode với Docker
Mục tiêu là mô phỏng một cụm (cluster) gồm 4 node vật lý độc lập trên một máy chủ Ubuntu 24.04 duy nhất bằng cách sử dụng 4 container Docker riêng biệt, mỗi container đóng vai trò là một node trong cụm phân tán.
Trong Distributed Mode, dữ liệu được phân tán và sao chép (qua Erasure Coding) trên tất cả các node. Nếu 1 hoặc nhiều node bị lỗi, dữ liệu vẫn có thể phục hồi và truy cập được, đảm bảo tính sẵn sàng cao (High Availability).
Chúng ta sẽ tạo một thư mục làm root cho dự án và cấu trúc thư mục dữ liệu riêng biệt cho từng node để mô phỏng các ổ cứng vật lý khác nhau.
mkdir -p ~/minio-distributed
cd ~/minio-distributed
mkdir -p data/node{1,2,3,4}/disk{1,2}
Kết quả mong đợi: Thư mục ~/minio-distributed được tạo với 4 thư mục con (node1 đến node4), mỗi thư mục chứa 2 thư mục con (disk1, disk2) để lưu trữ dữ liệu.
2. Cấu hình file docker-compose.yml cho Distributed Mode
File docker-compose.yml sẽ định nghĩa 4 service, mỗi service tương ứng với một node MinIO. Mỗi node sẽ mount 2 disk vào container để tăng tính dư thừa dữ liệu bên trong node đó.
Quan trọng nhất là biến môi trường MINIO_VOLUMES (hoặc MINIO_STORAGE_CLASS trong các phiên bản mới hơn, nhưng dùng volumes là cách chuẩn cho distributed) và biến MINIO_ROOT_USER, MINIO_ROOT_PASSWORD phải giống nhau trên tất cả các node.
Để các node giao tiếp được với nhau, chúng cần biết địa chỉ của nhau. Trong Docker Compose, tên service sẽ được dùng làm tên host. Chúng ta sẽ dùng 4 drive (2 drive/node * 4 nodes = 8 drives) để đạt được độ bền dữ liệu cao (tỉ lệ 4:4, chịu mất 4 drive mà không mất dữ liệu).
Đường dẫn file: ~/minio-distributed/docker-compose.yml
version: '3.8'
services:
minio1:
image: minio/minio:latest
container_name: minio-node1
command: server http://minio{1...4}/data/disk{1...2} --console-address :9001
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin123
MINIO_VOLUMES: "http://minio{1...4}/data/disk{1...2}"
MINIO_DEFAULT_BUCKET_CLASS: STANDARD
ports:
- "9000:9000"
- "9001:9001"
volumes:
- ./data/node1/disk1:/data/disk1
- ./data/node1/disk2:/data/disk2
networks:
- minio-net
restart: always
minio2:
image: minio/minio:latest
container_name: minio-node2
command: server http://minio{1...4}/data/disk{1...2} --console-address :9001
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin123
MINIO_VOLUMES: "http://minio{1...4}/data/disk{1...2}"
MINIO_DEFAULT_BUCKET_CLASS: STANDARD
ports:
- "9002:9000"
- "9003:9001"
volumes:
- ./data/node2/disk1:/data/disk1
- ./data/node2/disk2:/data/disk2
networks:
- minio-net
restart: always
minio3:
image: minio/minio:latest
container_name: minio-node3
command: server http://minio{1...4}/data/disk{1...2} --console-address :9001
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin123
MINIO_VOLUMES: "http://minio{1...4}/data/disk{1...2}"
MINIO_DEFAULT_BUCKET_CLASS: STANDARD
ports:
- "9004:9000"
- "9005:9001"
volumes:
- ./data/node3/disk1:/data/disk1
- ./data/node3/disk2:/data/disk2
networks:
- minio-net
restart: always
minio4:
image: minio/minio:latest
container_name: minio-node4
command: server http://minio{1...4}/data/disk{1...2} --console-address :9001
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin123
MINIO_VOLUMES: "http://minio{1...4}/data/disk{1...2}"
MINIO_DEFAULT_BUCKET_CLASS: STANDARD
ports:
- "9006:9000"
- "9007:9001"
volumes:
- ./data/node4/disk1:/data/disk1
- ./data/node4/disk2:/data/disk2
networks:
- minio-net
restart: always
networks:
minio-net:
driver: bridge
Kết quả mong đợi: File được lưu thành công. Lưu ý rằng mỗi node expose cổng S3 (9000) và Console (9001) ở các cổng host khác nhau để tránh xung đột, nhưng trong mạng nội bộ Docker, chúng giao tiếp qua tên service (minio1, minio2...).
3. Khởi động cụm và kiểm tra tính sẵn sàng
Thực thi lệnh docker compose để khởi động tất cả 4 node cùng lúc. MinIO Distributed Mode yêu cầu các node phải khởi động đồng thời để hình thành quorum (bầu cử leader).
Chúng ta sẽ sử dụng lệnh docker compose up -d để chạy ở chế độ background.
cd ~/minio-distributed
docker compose up -d
Kết quả mong đợi: Docker tạo 4 container và trạng thái là "running".
Để xác minh cụm đã hình thành đúng cách, kiểm tra log của node đầu tiên. MinIO sẽ hiển thị thông báo về việc các node đã join vào cụm và tổng số drives được phát hiện.
docker logs minio-node1
Kết quả mong đợi: Bạn sẽ thấy dòng log: "Starting erasure code on 8 drives" (hoặc tương tự) và thông báo "Ready: endpoint http://minio1:9000". Nếu thấy thông báo lỗi về số lượng drive không đủ (ví dụ: cần 8 nhưng chỉ thấy 4), cụm sẽ không hoạt động.
Tiếp theo, kiểm tra trạng thái của tất cả các container để đảm bảo không có node nào bị crash ngay sau khi khởi động.
docker compose ps
Kết quả mong đợi: 4 container đều ở trạng thái Up và không có thông báo lỗi (Exit 0).
4. Kiểm tra tính năng Erasure Code và độ bền dữ liệu
Erasure Code (EC) trong MinIO hoạt động dựa trên thuật toán Reed-Solomon. Với cấu hình 8 drives (4 nodes x 2 drives), chúng ta thiết lập tỉ lệ parity 4:4. Điều này có nghĩa là dữ liệu gốc được chia thành 4 phần (data shards) và tạo thêm 4 phần parity (parity shards).
Lợi ích: Bạn có thể mất tối đa 4 drives (nửa số drives) mà vẫn có thể khôi phục toàn bộ dữ liệu. Dữ liệu vẫn nguyên vẹn ngay cả khi mất cả một node (2 drives).
Tạo một bucket để kiểm tra hoạt động ghi dữ liệu trên cụm phân tán.
mc alias set mycluster http://localhost:9000 minioadmin minioadmin123
mc mb mycluster/my-bucket-ec
Kết quả mong đợi: Bucket my-bucket-ec được tạo thành công trên cụm phân tán.
Để xác nhận cấu hình Erasure Code đang hoạt động, kiểm tra thông tin bucket. MinIO sẽ hiển thị số lượng drives đang phục vụ và cấu hình parity.
mc admin info mycluster
Kết quả mong đợi: Trong phần "Drives", bạn sẽ thấy 8 drives được liệt kê với trạng thái Online. Phần "Storage Class" sẽ hiển thị cấu hình STANDARD (mặc định 4:4 cho 8 drives).
Mô phỏng sự cố: Dừng một node (ví dụ node2) để kiểm tra tính sẵn sàng. Cluster vẫn phải hoạt động.
docker stop minio-node2
Kết quả mong đợi: Node2 dừng. Kiểm tra lại bucket.
mc ls mycluster/my-bucket-ec
Kết quả mong đợi: Bucket vẫn truy cập được, không báo lỗi. Dữ liệu vẫn an toàn nhờ Erasure Code trên các node còn lại.
Khởi động lại node2 để khôi phục cụm về trạng thái đầy đủ.
docker start minio-node2
Kết quả mong đợi: Node2 khởi động lại, tự động đồng bộ dữ liệu (healing) và join lại vào cụm. Log của node2 sẽ hiện thông báo "Healing data" hoặc "Node is healthy".
Điều hướng series:
Mục lục: Series: Triển khai Database Object Storage với MinIO và Ubuntu 24.04
« Phần 5: Tối ưu hóa hiệu năng và bảo mật cho MinIO
Phần 7: Cấu hình Backup, Restore và giám sát (Monitoring) »