Cảnh báo PagerDuty lúc 2:15 sáng: Tại sao Local Storage là chưa đủ
Đó là một buổi sáng thứ Ba khi điện thoại của tôi bắt đầu reo inh ỏi. Một worker node trong cụm production của chúng tôi—một instance bare metal 8 lõi tiêu chuẩn—đã gặp lỗi phần cứng hoàn toàn. Trên lý thuyết, Kubernetes được thiết kế cho việc này. Tôi mong đợi scheduler sẽ chỉ đơn giản là di chuyển các pod PostgreSQL và Redis của mình sang một node khỏe mạnh.
Thực tế lại khác. Pod PostgreSQL của tôi bị kẹt ở trạng thái Pending trong hơn mười phút. Các log hiển thị một lỗi đáng sợ: pod có các PersistentVolumeClaims tức thời chưa được liên kết. Vì tôi đang sử dụng các storage class cục bộ gắn liền với ổ NVMe cụ thể đó, dữ liệu đã bị kẹt trên phần cứng đã hỏng. Lời hứa “tự chữa lành” (self-healing) của Kubernetes đã thất bại vì storage không có tính di động.
Nghịch lý của Persistent Storage
Kubernetes không quản lý việc sao chép dữ liệu giữa các node một cách mặc định. Nếu bạn dựa vào hostPath hoặc các local static volume, dữ liệu của bạn chỉ tồn tại trên đúng một ổ đĩa. Nếu ổ đĩa đó hỏng, ứng dụng của bạn cũng hỏng theo. Đối với các microservices có trạng thái (stateful), điều này tạo ra một điểm lỗi duy nhất (single point of failure), làm mất đi lợi ích của một cụm phân tán.
Tôi cần một lớp lưu trữ đáp ứng bốn tiêu chí cụ thể:
- Block Storage: Hiệu suất cao, truy cập với độ trễ thấp cho các tác vụ cơ sở dữ liệu.
- Sao chép đồng bộ (Synchronous Replication): Dữ liệu phải tồn tại trên ít nhất ba node cùng một lúc.
- Gánh nặng vận hành thấp: Một hệ thống không yêu cầu một kỹ sư lưu trữ chuyên dụng để bảo trì.
- Phục hồi sau thảm họa (Disaster Recovery): Các bản snapshot tự động được chuyển đến các bucket S3 bên ngoài.
Đánh giá các lựa chọn
Lưu trữ cloud-native là một thị trường đông đúc. Tôi đã thu hẹp tìm kiếm của mình xuống còn ba con đường chính dựa trên chi phí và độ phức tạp.
1. Managed Cloud Disks (AWS EBS, Azure Disk)
Các giải pháp này đáng tin cậy nhưng đi kèm với “thuế đám mây”. Các volume AWS EBS gp3 tốn khoảng 0,08 USD cho mỗi GB-tháng. Quan trọng hơn, việc gỡ một volume khỏi một node bị lỗi và gắn lại nó vào một node mới có thể mất từ 60 đến 120 giây. Sự chậm trễ đó thường là quá dài đối với các yêu cầu về tính sẵn sàng cao (high-availability).
2. Rook và Ceph
Ceph là một thế lực lớn cho lưu trữ cấp doanh nghiệp. Tuy nhiên, nó nổi tiếng là tiêu tốn nhiều tài nguyên. Một thiết lập Rook/Ceph điển hình yêu cầu ít nhất 2GB RAM cho mỗi OSD (Object Storage Daemon). Đối với một cụm quy mô trung bình, chi phí tài nguyên này thường lớn hơn lợi ích mà chính ứng dụng mang lại.
3. Longhorn
Longhorn là một dự án mã nguồn mở được quản lý bởi SUSE. Nó biến bộ nhớ cục bộ hiện có của các node thành một pool lưu trữ block phân tán. Nó nhẹ, cài đặt thông qua một Helm chart duy nhất và cung cấp một dashboard cực kỳ trực quan. Sau khi chạy các bài kiểm tra hiệu năng (benchmark), Longhorn đã trở thành lựa chọn cân bằng nhất cho kiến trúc microservices của chúng tôi.
Kể từ đó, tôi đã chuyển các workload production của mình sang thiết lập này. Kết quả vô cùng ổn định. Dưới đây là quy trình tôi đã sử dụng để xây dựng một nền tảng lưu trữ bền bỉ.
Bước 1: Chuẩn bị Node
Longhorn sử dụng iscsiadm trên host để quản lý các thiết bị block. Nếu tiện ích này bị thiếu, các volume của bạn sẽ không thể gắn kết, khiến các pod của bạn ở trạng thái pending vĩnh viễn. Đây là nguyên nhân phổ biến nhất dẫn đến thất bại khi cài đặt.
Cài đặt các dependency cần thiết trên tất cả các worker node:
sudo apt update
sudo apt install open-iscsi -y
sudo systemctl enable --now iscsid
Trước khi tiếp tục, hãy kiểm tra môi trường của bạn bằng script kiểm tra chính thức của Longhorn. Nó sẽ phát hiện các vấn đề cấu hình trước khi chúng trở thành lỗi trên production:
curl -sSfL https://raw.githubusercontent.com/longhorn/longhorn/v1.5.x/scripts/environment_check.sh | bash
Bước 2: Triển khai qua Helm
Helm là cách đáng tin cậy nhất để quản lý vòng đời của Longhorn. Nó đơn giản hóa việc nâng cấp và thay đổi cấu hình. Bắt đầu bằng cách thêm repository:
helm repo add longhorn https://charts.longhorn.io
helm repo update
Đối với một cụm 3 node tiêu chuẩn, tôi đặt số lượng bản sao (replica) mặc định là ba. Điều này đảm bảo rằng ngay cả khi hai node bị lỗi, dữ liệu của bạn vẫn có thể truy cập được trên node thứ ba.
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set persistence.defaultClassReplicaCount=3
Bước 3: Giám sát qua Dashboard
Longhorn bao gồm một giao diện người dùng (UI) mạnh mẽ giúp trực quan hóa sức khỏe của dữ liệu. Bạn có thể thấy chính xác node nào đang giữ bản sao nào. Để truy cập mà không cần Ingress, hãy sử dụng port-forward nhanh:
kubectl port-forward -n longhorn-system svc/longhorn-frontend 8080:80
Truy cập http://localhost:8080. Bạn sẽ thấy bảng phân tích các node, mức sử dụng không gian đĩa và sức khỏe của volume. Nó giúp việc khắc phục sự cố lưu trữ nhanh hơn nhiều so với việc phân tích các log thô. It giúp việc khắc phục sự cố lưu trữ nhanh hơn nhiều so với việc phân tích các log thô.
Bước 4: Triển khai các PVC bền bỉ
Khi engine đã chạy, chúng ta có thể định nghĩa một PersistentVolumeClaim (PVC). Longhorn tạo một StorageClass tên là longhorn theo mặc định. Đây là manifest cho một volume PostgreSQL 10GB:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 10Gi
Sau khi áp dụng, Longhorn sẽ phân bổ không gian và sao chép nó trên ba node. Nếu node đang chạy cơ sở dữ liệu của bạn bị crash, Kubernetes sẽ lập lịch lại pod ở nơi khác. Longhorn sau đó sẽ ngay lập tức gắn bản sao dữ liệu cục bộ trên node mới đó.
Bước 5: Sao lưu S3 bên ngoài (Off-site)
Việc sao chép bảo vệ chống lại lỗi phần cứng, nhưng nó không bảo vệ chống lại lỗi do con người. Nếu một script vô tình xóa cơ sở dữ liệu, việc xóa đó sẽ được sao chép ngay lập tức. Bạn cần các bản sao lưu bên ngoài để thực sự an toàn.
Tôi sử dụng AWS S3 cho việc này, mặc dù MinIO hoặc Backblaze B2 cũng hoạt động tốt tương tự. Đầu tiên, hãy lưu thông tin xác thực của bạn trong một secret:
kubectl create secret generic longhorn-s3-secret \
--from-literal=AWS_ACCESS_KEY_ID=KEY_CUA_BAN \
--from-literal=AWS_SECRET_ACCESS_KEY=SECRET_CUA_BAN \
-n longhorn-system
Trong cài đặt của Longhorn, hãy đặt Backup Target thành s3://ten-bucket-cua-ban@vung/. Tôi khuyên bạn nên cấu hình một Recurring Job. Một bản snapshot mỗi 2 giờ và một bản sao lưu đầy đủ mỗi 24 giờ sẽ cung cấp sự cân bằng tốt giữa an toàn và chi phí lưu trữ.
Những bài học từ thực tế triển khai
Chạy Longhorn ở quy mô lớn đã tiết lộ một số hiểu biết thực tế không có trong tài liệu cơ bản:
Giám sát mức tiêu thụ CPU (CPU Overhead)
Mỗi volume Longhorn sẽ khởi chạy một pod instance-manager chuyên dụng. Mỗi pod tiêu thụ khoảng 0,1 lõi CPU và 100MiB RAM. Nếu bạn tạo hàng trăm volume 1GB nhỏ, mức tiêu thụ tích lũy có thể làm giảm hiệu suất của node. Hãy hợp nhất lưu trữ khi có thể.
Tránh cạn kiệt đĩa
Longhorn ngừng lập lịch các bản sao khi đĩa đạt 90% dung lượng. Tôi đã đặt Storage Overprovisioning Tolerance (Khả năng chịu cấp phát vượt mức) thành 150% trong cài đặt. Điều này cho phép tăng trưởng tạm thời, nhưng bạn phải giám sát chặt chẽ mức sử dụng đĩa của host để tránh bị khóa chỉ đọc (read-only locks).
Sử dụng Maintenance Mode
Đừng bao giờ khởi động lại một node mà không chuẩn bị. Hãy sử dụng UI của Longhorn để “Disable Scheduling” (Vô hiệu hóa lập lịch) trên node mục tiêu trước. Điều này ngăn hệ thống xây dựng lại các bản sao một cách không cần thiết trong thời gian bảo trì ngắn.
Lời kết
Lưu trữ có khả năng chịu lỗi là xương sống của bất kỳ triển khai Kubernetes nghiêm túc nào. Bằng cách chuyển từ các local volume sang Longhorn, tôi đã biến một môi trường mong manh thành một môi trường bền bỉ. Các lỗi node giờ đây chỉ là những sự kiện nhỏ thay vì là những trường hợp khẩn cấp lúc 2 giờ sáng. Nếu bạn cần sự cân bằng giữa hiệu suất cao và bảo trì dễ dàng, Longhorn là con đường thực tế nhất để tiến tới.

