Courseiva
CKAChapter 8 of 16Objective Workloads & Scheduling - 15%

Configuring Scheduling and Affinity

The Workloads and Scheduling domain, worth 15% of the CKA exam, is all about controlling where your applications (pods) run in a Kubernetes cluster. Configuring scheduling and affinity is the way you tell Kubernetes to place pods on specific servers (nodes) based on hardware, software, or policy needs—like ensuring a database pod only runs on a node with a fast SSD, or that two instances of the same app are never on the same node to avoid a single point of failure.

12 min read
Intermediate
Updated Jul 23, 2026
Editorial oversight: Johnson Ajibi· Security Systems Architect · MSc IT Security · IEEE Senior Member

A simple way to picture Configuring Scheduling and Affinity

The Restaurant Seating Analogy

Have you ever walked into a busy restaurant and been told you can only sit at a specific table, even though there are empty tables elsewhere? That is Kubernetes scheduling. A Kubernetes cluster is like a restaurant with many tables (nodes) and a host (the scheduler). The host decides which table each customer (pod) sits at. Sometimes a customer has a strong preference for a booth by the window (node selector). Other times a table might have a 'reserved' sign (taint) that says 'only parties with a reservation (toleration) can sit here.'

Now imagine a large party wants to sit near the kitchen for quick service, but not on top of it (node affinity). And maybe the party wants to sit far away from a crying baby (pod anti-affinity) or right next to their friends (pod affinity). The host juggles all these rules to seat everyone optimally. If the host ignores the rules, the party might be miserable—split across separate floors or stuck next to a noisy dishwasher. That is what happens when scheduling goes wrong: pods end up on the wrong nodes, causing performance issues or even downtime. This analogy maps directly to how the Kubernetes scheduler uses node selectors, taints, tolerations, and affinities to place pods on the most suitable nodes in the cluster.

How It Actually Works

Kubernetes is an open-source system for automating deployment, scaling, and management of containerised applications. A container is a lightweight, standalone package that includes everything needed to run a piece of software. A pod is the smallest deployable unit in Kubernetes—it can contain one or more containers that share storage and network. A node is a worker machine in the cluster (a virtual or physical server). The scheduler is a built-in component that decides which node each newly created pod should run on.

By default, the scheduler spreads pods evenly across nodes based on resource availability (CPU, memory). But often you need more control. For example, you might have nodes with specialised hardware (GPUs for machine learning) or you might want to isolate sensitive workloads. This is where scheduling constraints come in.

Node Selector is the simplest way to constrain a pod to only run on nodes that have a specific label. A label is a key-value pair attached to a Kubernetes object (like a node) for identification. For instance, you can label a node with 'disktype=ssd' and then add a nodeSelector field to your pod YAML (YAML Ain't Markup Language, a human-readable data serialisation language) that says 'disktype: ssd'. The scheduler will then only consider nodes with that label. It is straightforward but limited—you can only match exact key-value pairs, and you cannot express complex logic like 'disktype=ssd OR disktype=nvme'.

Taints and Tolerations offer more sophisticated control. A taint is a mark placed on a node that repels pods unless those pods have a matching toleration. Think of a taint as a rule like 'this node is only for GPU workloads'. A toleration is a permission granted to a pod that says 'I accept being scheduled on nodes with that taint'. The combination allows you to reserve nodes for specific types of workloads, or to evict pods from a node that is being drained for maintenance. Taints can have three effects:

NoSchedule: the scheduler will not place new pods on the node unless they tolerate the taint. Existing pods are not affected.

PreferNoSchedule: the scheduler tries to avoid the node but will place pods there if no alternative exists.

NoExecute: immediately evicts any running pods that do not tolerate the taint, and will not schedule new pods.

Node Affinity is like a more powerful and flexible version of nodeSelector. It allows you to define rules that express preferences or requirements for pod placement using label expressions. For example, 'requiredDuringSchedulingIgnoredDuringExecution: matchExpressions: key: disktype operator: In values: [ssd, nvme]' would require the node to have a label with key 'disktype' and a value of either 'ssd' or 'nvme'. The 'IgnoredDuringExecution' part means if the node's labels change after the pod is running, the pod stays. There is also 'preferredDuringSchedulingIgnoredDuringExecution' for soft preferences (the scheduler tries but may skip if no matching node).

Pod Affinity and Pod Anti-Affinity control placement relative to other pods, not nodes. Pod affinity says 'schedule this pod near (or far from) pods with label X'. For example, you might want a front-end web pod to be on the same node as its backend cache pod for low latency. Pod anti-affinity says 'do not schedule this pod on the same node as another pod with label Y'. This is critical for high availability—if one node fails, you want your replicas spread across different nodes. Both can be 'required' (hard constraint) or 'preferred' (soft preference). They use the same operator system (In, NotIn, Exists, DoesNotExist) as node affinity.

All these features work together. You might have a node tainted for GPU workloads, with a toleration on your model-training pods, plus node affinity to prefer nodes with 'gpu-type=nvidia', and pod anti-affinity to ensure no two training pods run on the same node. The scheduler evaluates all constraints when placing a pod. If a pod has both a nodeSelector and a node affinity rule, both must be satisfied—they are combined with a logical AND.

This flowchart shows how the Kubernetes scheduler decides where to place a pod, checking constraints like node labels, taints, tolerations, and pod anti-affinity.

Walk-Through

1

Label your nodes

First, assign meaningful labels to your cluster nodes so you can refer to them in scheduling rules. Use 'kubectl label node <node-name> <key>=<value>'. For example, 'kubectl label node worker1 disktype=ssd'. This is the foundation for nodeSelector and nodeAffinity.

2

Taint the nodes you want to reserve

Apply a taint to nodes that should only run specific workloads. For example, 'kubectl taint nodes worker1 gpu=true:NoSchedule'. This prevents any pod without a matching toleration from being scheduled there. Choose the effect based on desired behaviour (NoSchedule, PreferNoSchedule, or NoExecute).

3

Add tolerations to your pod spec

In the pod YAML, under spec.tolerations, add an entry that matches the taint key, value, and effect. For the example above: tolerations: - key: gpu, operator: Equal, value: 'true', effect: NoSchedule. Without this, the pod will never be scheduled on the tainted node.

4

Define node affinity rules

Add an affinity block under spec.affinity.nodeAffinity in your pod or deployment YAML. Use requiredDuringSchedulingIgnoredDuringExecution for hard constraints (e.g., node must have disktype=ssd) or preferredDuringSchedulingIgnoredDuringExecution for soft preferences. Include matchExpressions with the appropriate operator (In, NotIn, etc.).

5

Configure pod affinity or anti-affinity

To control pod-to-pod placement, add podAffinity or podAntiAffinity under spec.affinity. Specify a labelSelector to target certain pods, and a topologyKey (e.g., kubernetes.io/hostname) to define the domain. For high availability, use requiredDuringSchedulingIgnoredDuringExecution with podAntiAffinity to spread replicas across nodes.

6

Apply and verify your configuration

Use 'kubectl apply -f <file>.yaml' to deploy your pod or deployment. Then check scheduling with 'kubectl get pods -o wide' to see which node each pod landed on. Use 'kubectl describe pod <pod-name>' to view events and verify constraints were satisfied. If a pod is stuck in Pending, investigate with 'kubectl describe' to see scheduling errors.

What This Looks Like on the Job

Imagine you work for a financial services company deploying a microservices application on Kubernetes. You have three types of nodes in your cluster: general-purpose nodes (for most workloads), GPU nodes (for machine learning fraud detection), and compliance nodes (located in a specific data centre to meet regulatory requirements). You need to ensure workloads go to the right places.

Step by step, you configure scheduling:

First, you label your nodes. You add labels like 'node-type=general', 'node-type=gpu', and 'compliance=true' to the relevant nodes.

You taint the GPU nodes with 'gpu=true:NoSchedule' to prevent ordinary pods from landing there. Any pod that does not have a toleration for 'gpu=true' will be rejected.

For your fraud-detection pods, you add a toleration for 'gpu=true' and a node affinity rule that requires the node to have 'node-type=gpu'. You also add a preferred pod anti-affinity to spread replicas across different GPU nodes if multiple are available.

For your customer-facing web app, you create a deployment with 3 replicas. You add a required pod anti-affinity rule so that no two replicas run on the same node (to survive node failure). You also add a node affinity preference for 'node-type=general'.

For your database pod that must run in the compliant data centre, you use a nodeSelector for 'compliance=true'.

Now, when you deploy an update or scale up, the scheduler automatically places pods correctly. Without these constraints, a fraud-detection pod might end up on a general node (no GPU, so it would fail) or a customer web pod might land on a GPU node (wasting expensive hardware). The scheduler uses these rules every time a pod is created. You can also update rules on running pods by editing the YAML and reapplying (though changes like nodeSelector only affect future scheduling).

In practice, you would define these constraints in your deployment, statefulset, or pod YAML files. You use command-line tools like kubectl to apply them. For example, you run 'kubectl label node worker1 node-type=gpu' to label a node, and 'kubectl taint nodes worker1 gpu=true:NoSchedule' to taint it. Then in your pod spec, you add the tolerations and affinity fields. You can test your configuration by deploying a pod and checking its status with 'kubectl describe pod' to see if it was scheduled correctly.

How CKA Actually Tests This

The CKA exam tests your ability to configure scheduling and affinity constraints in YAML. You will not be asked theoretical questions—you must write or modify YAML files to achieve specific placement goals. The exam environment gives you kubectl access and the ability to edit files with vi or nano.

Key exam topics:

Node selectors: Know the syntax (selector: key-value under spec.nodeSelector). You must be able to add a nodeSelector to an existing pod or deployment.

Taints and tolerations: You must be able to taint a node (kubectl taint nodes) and add tolerations to a pod spec. They test the effects (NoSchedule, PreferNoSchedule, NoExecute). A common trap is forgetting that a pod with a toleration still may not be scheduled on a tainted node if it does not match other requirements—but it is allowed.

Node affinity: They love to test 'requiredDuringSchedulingIgnoredDuringExecution' vs 'preferredDuringSchedulingIgnoredDuringExecution'. You must write the full matchExpressions structure. Traps: matching multiple values (use operator: In with a list), forgetting to include 'topologyKey' for pod affinity (it is required).

Pod affinity/anti-affinity: Especially anti-affinity for high availability. They will ask you to ensure replicas are spread across nodes. You must set 'topologyKey' to 'kubernetes.io/hostname' for node-level spreading. Common trap: using 'In' operator when you mean 'NotIn'.

Combining constraints: They may give a scenario with both a taint/toleration and a nodeSelector. You must configure both correctly. The scheduler ANDs all constraints.

Trap patterns:

They might present a YAML with mismatched indentation (e.g., affinity under spec instead of spec.template.spec.affinity in a deployment).

They might ask you to update a deployment YAML and accidentally use 'nodeSelector' when they meant 'affinity'.

They often set a taint with effect 'NoSchedule' but then expect you to add a toleration that matches the key and value exactly.

Memory aids:

Taint = node restriction; Toleration = pod permission.

NodeSelector = exact match only; NodeAffinity = flexible logic.

TopologyKey = the domain where spreading happens (node, zone, region).

Key Takeaways

A taint on a node repels pods that do not have a matching toleration, and the toleration must exactly match the taint's key, value, and effect.

NodeSelector only supports exact key-value label matching, while NodeAffinity supports operators like In, NotIn, Exists, and DoesNotExist for flexible rules.

Pod anti-affinity requiredDuringSchedulingIgnoredDuringExecution ensures replicas are spread across different nodes (or zones) to improve high availability.

When combining scheduling constraints, the scheduler applies a logical AND—all constraints must be satisfied for a pod to be placed on a node.

The topologyKey in pod affinity/anti-affinity defines the scope of the constraint, such as kubernetes.io/hostname for node-level spreading.

Tolerations do not guarantee a pod will schedule on a tainted node; they only permit it if other constraints also allow it.

NoExecute taints cause immediate eviction of all pods without a matching toleration, not just blocking new ones.

Easy to Mix Up

These come up on the exam all the time. Here's how to tell them apart.

NodeSelector

Only supports exact key=value label match

Cannot use operators like In or NotIn

Always a hard requirement (required)

NodeAffinity

Supports operators: In, NotIn, Exists, DoesNotExist

Can define hard (required) or soft (preferred) rules

More flexible for complex placement logic

Taint

Applied to a node to repel pods

Has a key, value, and effect

No toleration => pod not scheduled

Toleration

Applied to a pod to permit scheduling on tainted nodes

Must match the taint's key, value, and effect exactly

Toleration does not force scheduling; it only allows it

PodAffinity

Pulls pods together (e.g., same node or zone)

Use for low-latency communication between services

Often used with matching label selectors

PodAntiAffinity

Pushes pods apart (e.g., different nodes)

Use for high availability and failure isolation

Commonly used with 'required' for replicas spreading

NoSchedule

Only affects new pods (no eviction)

Existing pods without toleration remain running

Common for reserving nodes

NoExecute

Evicts existing pods without toleration immediately

Also prevents new pods without toleration

Used for node maintenance or security isolation

Watch Out for These

Mistake

NodeAffinity can override a node taint—if I set a node affinity rule, the pod can schedule on any node that matches the affinity, even if the node is tainted.

Correct

NodeAffinity and taints are independent constraints. A pod must satisfy both the node affinity requirements and have a toleration for any taints on the target node. They do not cancel each other.

Beginners think of affinity as a stronger 'please go here' and assume it bypasses taints, but Kubernetes checks all constraints separately and requires all to pass.

Mistake

If I taint a node with NoExecute, only new pods without tolerations are blocked. Existing pods continue running.

Correct

NoExecute immediately evicts any running pod that does not have a matching toleration. It also prevents future pods without tolerations from being scheduled.

The name 'NoExecute' sounds like it only affects execution (new pods), but it forcefully evicts existing ones. Beginners confuse it with NoSchedule.

Mistake

Pod affinity and anti-affinity rules apply to all pods in the cluster automatically.

Correct

Pod affinity/anti-affinity only consider pods that match a label selector you specify in the rule. They use the 'topologyKey' to define the scope (e.g., same node, same zone).

The word 'affinity' sounds like a global clustering behaviour, but it is scoped and selective. Beginners forget to add the labelSelector.

Mistake

You can only define scheduling constraints when creating a pod. Once the pod is running, you cannot change them.

Correct

You can update scheduling constraints on existing resources like Deployments or StatefulSets by editing their YAML and applying with kubectl apply. However, changes to constraints like nodeSelector or affinity only affect new pods created after the update, not already-running pods.

Some beginners think scheduling is fixed at creation, but Kubernetes allows dynamic policy updates. However, they misunderstand that running pods are not rescheduled.

Do You Actually Know This?

Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.

Frequently Asked Questions

What is the difference between nodeSelector and nodeAffinity?

nodeSelector is a simple exact-match key-value pair. nodeAffinity allows more complex logic using operators like In, NotIn, Exists, and can express hard or soft preferences.

Can a pod have both a nodeSelector and a nodeAffinity rule?

Yes, you can have both. If both are specified, the scheduler will only consider nodes that satisfy both the nodeSelector and the nodeAffinity requirements.

What does topologyKey mean in pod affinity?

The topologyKey is a node label that defines the scope of the affinity constraint. For example, kubernetes.io/hostname means 'same node', while topology.kubernetes.io/zone means 'same cloud availability zone'.

Do tolerations guarantee that my pod will run on a tainted node?

No, tolerations only permit the pod to be scheduled on the tainted node if other constraints (like nodeSelector, affinity, and resource availability) are also satisfied.

How do I update scheduling constraints on an existing deployment?

Edit the deployment YAML using kubectl edit or modify the file and run kubectl apply. The changes take effect for new pods created by the deployment; existing pods are not rescheduled automatically.

What is a NoExecute taint and when should I use it?

NoExecute evicts any running pod that does not tolerate the taint and prevents new pods from being scheduled. It is useful for draining a node for maintenance or for isolating compromised nodes.

Terms Worth Knowing

Keep going

You've finished Configuring Scheduling and Affinity. Continue through the CKA study guide to build a complete picture of the exam.

Done with this chapter?