Node Maintenance
A comprehensive guide to Kubernetes node maintenance covering draining, cordoning, OS updates, cluster upgrades, node taints/tolerations, and practical implementation strategies for production clusters.
Node maintenance is essential for keeping Kubernetes clusters secure, performant, and reliable. Regular maintenance includes:
- Security: Apply OS and Kubernetes security patches
- Reliability: Replace failing hardware or upgrade components
- Performance: Optimize node configurations
- Availability: Maintain cluster health without downtime
- Compliance: Meet regulatory and security standards
- Upgrades: Update Kubernetes version for new features
- Zero Downtime: Maintain application availability during maintenance
- Graceful Handling: Allow pods to terminate gracefully
- Orderly Process: Follow a structured maintenance workflow
- Testing: Always test maintenance procedures in staging
- Documentation: Document maintenance procedures and runbooks
Cordoning marks a node as unschedulable, preventing new pods from being scheduled on it. Existing pods continue to run.
# Cordon a node (mark unschedulable)
kubectl cordon node-1
# Verify node status
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# node-1 Ready,SchedulingDisabled 10d v1.28.0
# Check node details
kubectl describe node node-1
# View nodes with cordon status
kubectl get nodes -o custom-columns=NAME:.metadata.name,STATUS:.status.conditions[-1].type
# Cordon multiple nodes
for node in node-1 node-2 node-3; do
kubectl cordon $node
done
# Why cordon first?
# 1. Prevents new pods from being scheduled during maintenance
# 2. Allows existing pods to continue running
# 3. Prepares for draining without impacting new workloads
# 4. Provides a controlled maintenance window
- Always cordon a node before draining
- Cordon during maintenance windows or off-peak hours
- Monitor cluster capacity during cordoning
- Uncordon after maintenance is complete
- Document cordoning procedures
Draining evicts all pods from a node, allowing them to terminate gracefully and be rescheduled elsewhere. This is essential for node maintenance without downtime.
# Basic drain
kubectl drain node-1 --ignore-daemonsets
# Drain with grace period
kubectl drain node-1 --ignore-daemonsets --grace-period=60
# Drain with timeout
kubectl drain node-1 --ignore-daemonsets --timeout=300s
# Force drain (ignore PodDisruptionBudget)
kubectl drain node-1 --ignore-daemonsets --force
# Drain without deleting emptyDir data
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
# Drain specific pods (with pod-selector)
kubectl drain node-1 --ignore-daemonsets --pod-selector="app=nginx"
# Check drain status
kubectl get pods --all-namespaces --field-selector spec.nodeName=node-1
# Monitor drain progress
kubectl get events --field-selector involvedObject.kind=Node --watch
# Drain all nodes in a node group
for node in node-1 node-2 node-3; do
kubectl cordon $node
kubectl drain $node --ignore-daemonsets --delete-emptydir-data
done
kubectl cordon node-1 - Prevent new pods from being scheduledkubectl drain node-1 --ignore-daemonsets - Evict all pods gracefullykubectl uncordon node-1 - Allow scheduling again- Always use
--ignore-daemonsets(DaemonSets stay on the node) - Use
--delete-emptydir-datafor pods with emptyDir volumes - Set appropriate grace periods for termination
- Monitor cluster capacity to ensure pods can be rescheduled
- Test drain procedures in staging before production
- Use
--timeoutto avoid indefinite hanging
Regular OS updates are critical for security and performance. They should be applied during maintenance windows using a controlled process.
# Node maintenance workflow for OS updates
# 1. Before maintenance: Check cluster health
kubectl get nodes
kubectl get pods --all-namespaces
# 2. Cordon and drain the node
kubectl cordon node-1
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
# 3. Perform OS updates (Ubuntu example)
# SSH to the node
ssh node-1
sudo apt-get update
sudo apt-get upgrade -y
sudo reboot
# 4. Wait for node to come back
kubectl get node node-1
# 5. Verify node status and uncordon
kubectl uncordon node-1
# 6. Verify pods are running
kubectl get pods --all-namespaces --field-selector spec.nodeName=node-1
# Automated OS update script
#!/bin/bash
NODES=("node-1" "node-2" "node-3")
for node in "${NODES[@]}"; do
echo "Maintaining $node"
kubectl cordon $node
kubectl drain $node --ignore-daemonsets --delete-emptydir-data --timeout=300s
# Perform updates via SSH
ssh $node "sudo apt-get update && sudo apt-get upgrade -y && sudo reboot"
sleep 60
kubectl uncordon $node
echo "Node $node maintenance complete"
done
# Security updates (critical)
sudo apt-get update && sudo apt-get install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Kernel updates require reboot
# Check if reboot is needed
if [ -f /var/run/reboot-required ]; then
sudo reboot
fi
- Apply security patches promptly (critical CVEs)
- Schedule regular maintenance windows for updates
- Test updates in staging before production
- Use automation for consistent updates
- Monitor node health after updates
- Have rollback procedures for failed updates
Upgrading Kubernetes versions is essential for new features, security patches, and performance improvements. Always follow a phased approach.
# Cluster upgrade workflow (kubeadm)
# 1. Check current version
kubectl version --short
# 2. Check available versions
kubeadm upgrade plan
# 3. Upgrade control plane
kubeadm upgrade apply v1.28.0
# 4. Upgrade kubelet and kubectl on control plane nodes
sudo apt-get update && sudo apt-get install -y kubelet=1.28.0-00 kubectl=1.28.0-00
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# 5. Upgrade worker nodes (one at a time)
# Cordon and drain the node
kubectl cordon node-1
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
# Upgrade kubelet on worker node
ssh node-1 "sudo apt-get update && sudo apt-get install -y kubelet=1.28.0-00"
ssh node-1 "sudo systemctl daemon-reload && sudo systemctl restart kubelet"
# Uncordon the node
kubectl uncordon node-1
# Repeat for all worker nodes
# Managed cluster upgrades (EKS)
eksctl upgrade cluster --name my-cluster --version 1.28
# Managed cluster upgrades (GKE)
gcloud container clusters upgrade my-cluster --zone us-central1-a --master
# Managed cluster upgrades (AKS)
az aks upgrade --resource-group my-rg --name my-cluster --kubernetes-version 1.28
# Check node versions after upgrade
kubectl get nodes -o wide
# Verify cluster health
kubectl get nodes
kubectl get pods --all-namespaces
- Always test upgrades in a staging environment
- Have a rollback plan
- Upgrade one node at a time
- Verify API compatibility before upgrading
- Schedule upgrades during maintenance windows
- Monitor cluster health during and after upgrades
Taints and tolerations allow you to control which pods can be scheduled on which nodes. Taints are applied to nodes; tolerations are applied to pods.
# Add a taint to a node
kubectl taint nodes node-1 key=value:NoSchedule
kubectl taint nodes node-1 dedicated=db:NoSchedule
# Remove a taint
kubectl taint nodes node-1 key=value:NoSchedule-
# View taints on nodes
kubectl describe nodes | grep Taints
# Types of taints:
# - NoSchedule: Pods without toleration won't be scheduled
# - PreferNoSchedule: Cluster tries to avoid scheduling
# - NoExecute: Pods without toleration will be evicted
# Pod toleration for the taint
apiVersion: v1
kind: Pod
metadata:
name: db-pod
spec:
containers:
- name: db
image: postgres:13
tolerations:
- key: dedicated
operator: Equal
value: db
effect: NoSchedule
# Node with NoExecute taint (for maintenance)
kubectl taint nodes node-1 maintenance=upgrade:NoExecute
# Pod toleration with NoExecute (allow pod to stay during maintenance)
apiVersion: v1
kind: Pod
metadata:
name: critical-pod
spec:
tolerations:
- key: maintenance
operator: Equal
value: upgrade
effect: NoExecute
tolerationSeconds: 3600 # Pod can stay for 1 hour
# Taint with PreferNoSchedule
kubectl taint nodes node-1 node-type=spot:PreferNoSchedule
# Use taints for node groups (dedicated nodes)
kubectl taint nodes node-gpu gpu=true:NoSchedule
kubectl taint nodes node-db db=true:NoSchedule
# Check pods with tolerations
kubectl get pods -o json | jq '.items[] | {name: .metadata.name, tolerations: .spec.tolerations}'
- Use taints for dedicated node pools (GPU, DB, high-memory)
- Use NoExecute taints for maintenance (with tolerationSeconds)
- Apply taints before cordoning for controlled maintenance
- Document all taints and their purposes
- Combine with node selectors for precise scheduling
# Automated node rotation script
#!/bin/bash
NODE_GROUPS=("node-1" "node-2" "node-3")
MAINTENANCE_WINDOW="2h"
for node in "${NODE_GROUPS[@]}"; do
echo "Starting maintenance for $node"
# Cordon the node
kubectl cordon $node
# Drain the node
kubectl drain $node --ignore-daemonsets --delete-emptydir-data --timeout=300s
# Perform maintenance
# ... your maintenance tasks ...
# Wait for maintenance to complete
sleep 60
# Uncordon the node
kubectl uncordon $node
echo "Maintenance complete for $node"
echo "Waiting for pods to stabilize..."
sleep 30
done
# Maintenance with node conditions
apiVersion: v1
kind: Pod
metadata:
name: maintenance-pod
annotations:
cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
spec:
containers:
- name: maintenance
image: alpine
command: ["sleep", "infinity"]
# Use kubectl plugin for node maintenance
# Install krew
kubectl krew install node-maintenance
# Use node-maintenance plugin
kubectl node-maintenance start node-1 --reason="OS Update"
kubectl node-maintenance status node-1
kubectl node-maintenance stop node-1
- Automate node rotation for regular maintenance
- Implement monitoring and alerting during maintenance
- Use maintenance windows to minimize impact
- Test automation in staging before production
- Document automation scripts and procedures
- Have manual fallback procedures
--ignore-daemonsets to allow DaemonSet pods to remain. This is usually the desired behavior.--delete-emptydir-data to delete emptyDir data. For persistent volumes, pods should be rescheduled. Local storage with hostPath may require additional handling.--force to bypass PDBs if absolutely necessary.--timeout to set a limit. Consider using --force as a last resort.Regular node maintenance is essential for cluster security, performance, and reliability. Follow these best practices to maintain your nodes without disrupting applications.