Scheduling & Affinity - Pod Placement Control
Tổng quan
Kubernetes scheduler quyết định pod placement. Affinity rules và constraints control pod scheduling behavior.
Scheduler
Scheduling Process
- Filtering nodes
- Scoring nodes
- Selecting best node
- Binding pod to node
Scheduling Policies
- Resource requirements
- Node affinity
- Pod affinity/anti-affinity
- Taints và tolerations
Node Affinity
apiVersion: v1
kind: Pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- amd64
Pod Affinity
apiVersion: v1
kind: Pod
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cache
topologyKey: kubernetes.io/hostname
Taints và Tolerations
Taints
kubectl taint nodes node1 key=value:NoSchedule
Tolerations
apiVersion: v1
kind: Pod
spec:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
Node Selectors
apiVersion: v1
kind: Pod
spec:
nodeSelector:
disktype: ssd
Priority Classes
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000
globalDefault: false
description: "High priority class"
Best Practices
- Use affinity cho performance optimization
- Implement anti-affinity cho high availability
- Reserve nodes với taints
- Set resource requests accurately
- Use priority classes appropriately
Advanced Scheduling Scenarios
Custom Schedulers
Bạn có thể triển khai một custom scheduler nếu các chiến lược lập lịch mặc định của Kubernetes không đáp ứng được nhu cầu của bạn. Custom scheduler sẽ lắng nghe các Pod mới có schedulerName được chỉ định và quyết định node nào sẽ chạy Pod đó.
Ví dụ cấu hình Pod sử dụng custom scheduler:
apiVersion: v1
kind: Pod
metadata:
name: my-pod-with-custom-scheduler
spec:
schedulerName: my-custom-scheduler
containers:
- name: my-container
image: nginx
Describing a Custom Scheduler in Java (Conceptual)
Một custom scheduler trong Java sẽ cần sử dụng Fabric8 Kubernetes Client để:
1. Lắng nghe các Pod có schedulerName khớp với tên của nó.
2. Áp dụng các thuật toán lập lịch tùy chỉnh (ví dụ: dựa trên tải của node, chi phí, hoặc các yếu tố kinh doanh).
3. Gửi yêu cầu Binding đến Kubernetes API Server để gán Pod cho một Node.
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.BindingBuilder;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.WatcherException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class CustomScheduler {
private static final String CUSTOM_SCHEDULER_NAME = "my-custom-scheduler";
private KubernetesClient client;
private ScheduledExecutorService executorService;
public CustomScheduler() {
this.client = new DefaultKubernetesClient();
this.executorService = Executors.newSingleThreadScheduledExecutor();
}
public void start() {
System.out.println("Starting custom scheduler: " + CUSTOM_SCHEDULER_NAME);
client.pods().watch(new Watcher<Pod>() {
@Override
public void eventReceived(Action action, Pod pod) {
if (action == Action.ADDED && pod.getSpec().getSchedulerName() != null &&
pod.getSpec().getSchedulerName().equals(CUSTOM_SCHEDULER_NAME) &&
pod.getSpec().getNodeName() == null) {
System.out.println("Found unscheduled pod: " + pod.getMetadata().getName());
executorService.schedule(() -> schedulePod(pod), 1, TimeUnit.SECONDS);
}
}
@Override
public void onClose(WatcherException cause) {
System.err.println("Watcher closed: " + cause.getMessage());
}
});
}
private void schedulePod(Pod pod) {
// Simple scheduling logic: pick the first available node
// In a real scheduler, this would involve complex algorithms
String targetNode = client.nodes().list().getItems().stream()
.filter(node -> !node.getSpec().getUnschedulable())
.map(node -> node.getMetadata().getName())
.findFirst()
.orElse(null);
if (targetNode != null) {
System.out.println("Attempting to schedule pod " + pod.getMetadata().getName() + " to node " + targetNode);
try {
client.pods().inNamespace(pod.getMetadata().getNamespace())
.withName(pod.getMetadata().getName())
.bind(new BindingBuilder()
.withNewMetadata()
.withName(pod.getMetadata().getName())
.endMetadata()
.withNewTarget()
.withApiVersion("v1")
.withKind("Node")
.withName(targetNode)
.endTarget()
.build());
System.out.println("Pod " + pod.getMetadata().getName() + " scheduled to " + targetNode);
} catch (Exception e) {
System.err.println("Failed to bind pod " + pod.getMetadata().getName() + ": " + e.getMessage());
}
} else {
System.out.println("No suitable node found for pod " + pod.getMetadata().getName());
}
}
public static void main(String[] args) {
CustomScheduler scheduler = new CustomScheduler();
scheduler.start();
// Keep the main thread alive
try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { /* ignore */ }
}
}
Nội dung đã được mở rộng với advanced scheduling scenarios và custom scheduler development, cùng các ví dụ Java.