Thông báo lỗi
Việc chuẩn bị một node Kubernetes để bảo trì—cho dù là để vá lỗi kernel, thay thế phần cứng hay thu hẹp cluster—thường bắt đầu bằng lệnh kubectl drain. Nhưng thông thường, quá trình này sẽ dừng lại với lỗi cụ thể sau:
error: cannot delete DaemonSet-managed Pods (use --ignore-daemonsets to ignore): kube-system/kube-proxy-xxxx, kube-system/calico-node-xxxx
Điều này xảy ra vì kubectl đã phát hiện các pod được quản lý bởi một DaemonSet, chẳng hạn như kube-proxy, calico-node, hoặc một agent thu thập log như Fluentd. Hệ thống từ chối tiếp tục vì nó không muốn chấm dứt các dịch vụ cấp hệ thống mà không có sự cho phép rõ ràng từ bạn.
Nguyên nhân gốc rễ: Tại sao Kubernetes bảo vệ DaemonSets
Lệnh kubectl drain hoạt động bằng cách trục xuất (evict) các pod để chúng có thể được lập lịch lại trên các node khác. DaemonSets là ngoại lệ đối với quy tắc này. Chúng được thiết kế để chạy chính xác một bản sao của pod trên mọi node trong cluster.
Nếu Kubernetes "trục xuất" một pod DaemonSet, nó sẽ không còn nơi nào để đi. Mọi node khác đều đã có bản sao riêng của nó. Vì các pod này thường xử lý các tác vụ mạng hoặc giám sát quan trọng, Kubernetes buộc bạn phải xác nhận rằng các dịch vụ này sẽ dừng trên node cụ thể này và sẽ không di chuyển sang nơi khác trong thời gian dừng hoạt động.
Hướng dẫn khắc phục từng bước
1. Cách xử lý tiêu chuẩn: --ignore-daemonsets
Cách khắc phục nhanh nhất thường là cách được gợi ý ngay trong chính thông báo lỗi. Bằng cách thêm cờ này, bạn cấp quyền cho Kubernetes chấm dứt các pod DaemonSet trên node đích mà không cố gắng lập lịch lại chúng ở nơi khác.
# Thay thế <node-name> bằng ID node thực tế của bạn
kubectl drain <node-name> --ignore-daemonsets
Điều này an toàn cho 99% các cluster. Sau khi node khởi động lại và bạn bỏ chặn (uncordon) nó, controller của DaemonSet sẽ tự động khởi chạy lại các pod đó.
2. Xử lý lưu trữ cục bộ (emptyDir)
Sau khi vượt qua lỗi DaemonSet, bạn có thể gặp phải trở ngại thứ hai: cannot delete Pods with local storage. Điều này xảy ra khi các pod sử dụng các volume emptyDir làm không gian tạm thời hoặc bộ đệm. Vì dữ liệu emptyDir sẽ bị xóa khi pod bị xóa, Kubernetes tạm dừng để ngăn chặn việc mất dữ liệu vô tình.
Nếu bạn chắc chắn rằng dữ liệu cục bộ chỉ là tạm thời, hãy sử dụng cả hai cờ cùng nhau:
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
3. Xử lý các Pod "trần" với --force
Đôi khi, bạn có thể có các pod không được tạo bởi một controller như Deployment hay StatefulSet. Những pod "trần" (naked pods) này không có cách nào để tự tạo lại nếu chúng bị xóa. Nếu bạn chắc chắn rằng những pod này có thể bỏ đi hoặc bạn đã có bản sao lưu thủ công, hãy sử dụng cờ --force để dọn dẹp chúng.
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data --force
Xác minh: Đảm bảo Node đã sẵn sàng
Kiểm tra trạng thái của node ngay sau khi lệnh kết thúc. Bạn cần tìm trạng thái SchedulingDisabled.
kubectl get nodes
Kết quả mong đợi sẽ trông như thế này:
NAME STATUS ROLES AGE VERSION
worker-node-1 Ready,SchedulingDisabled worker 12d v1.28.2
Chạy một kiểm tra nhanh để xem còn gì trên node. Chỉ các pod DaemonSet mới được phép còn lại, và chúng thường sẽ ở trạng thái Terminating hoặc Running tùy thuộc vào cách chúng được xử lý.
kubectl get pods -A -o wide | grep worker-node-1
Phòng ngừa và Thực hành tốt nhất
Cấu hình Pod Disruption Budgets (PDB)
Đừng để lệnh drain vô tình làm toàn bộ ứng dụng của bạn ngừng hoạt động. Hãy định nghĩa một PDB để cho Kubernetes biết số lượng bản sao tối thiểu phải duy trì hoạt động trong quá trình bảo trì. Nếu lệnh drain vi phạm giới hạn này, nó sẽ thất bại một cách an toàn.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-app-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: my-web-app
Thiết lập Drain Timeouts
Trong các pipeline tự động, một pod bị kẹt có thể làm treo toàn bộ quá trình CI/CD của bạn trong nhiều giờ. Hãy sử dụng timeout để đảm bảo quá trình thất bại nhanh chóng nếu một pod từ chối chấm dứt do các finalizer phức tạp hoặc sự cố phần cứng.
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data --timeout=300s
Đưa Node trở lại hoạt động
Sau khi các tác vụ bảo trì hoàn tất, hãy thông báo cho Kubernetes rằng node đã sẵn sàng hoạt động trở lại. Bước này rất dễ quên nhưng lại cực kỳ quan trọng để cân bằng tải cho cluster của bạn.
kubectl uncordon <node-name>
Sau khi uncordon, các pod DaemonSet của bạn sẽ là những pod đầu tiên ổn định, sau đó là các workload tiêu chuẩn sẽ di chuyển ngược trở lại node.

