✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes Node Affinity Placement

Kubernetes Node Affinity Placement ensures workloads are scheduled based on node attributes, optimizing resource usage and enhancing cluster efficiency.

Kubernetes Node Affinity Placement is the more expressive successor to nodeSelector, allowing Pods to specify node-targeting rules using a richer set of operators (In, NotIn, Exists, DoesNotExist, Gt, Lt) and, critically, distinguishing between hard requirements that must be satisfied and soft preferences that merely influence scoring without excluding a node outright. Where nodeSelector can only express "this exact label must be present," node affinity can express "any of these several values," "this label must be absent," or "prefer this node, but don't require it" — covering placement logic that would otherwise be impossible or require multiple redundant Pod specs.

Node affinity is configured through spec.affinity.nodeAffinity, and its two variants — required and preferred — map directly onto the scheduler's two-phase decision flow: required rules participate in candidate filtering, while preferred rules participate only in scoring.


requiredDuringSchedulingIgnoredDuringExecution

Hard Constraints with Expressive Operators

This variant behaves like nodeSelector in that it is a hard requirement eliminating non-matching nodes from the candidate set entirely, but supports a much richer expression syntax through nodeSelectorTerms and matchExpressions.

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: node.kubernetes.io/instance-type
              operator: In
              values: ["m5.large", "m5.xlarge", "m5.2xlarge"]

OR Semantics Across Terms, AND Within a Term

Multiple entries in nodeSelectorTerms are combined with OR logic (a node matching any one full term is a candidate), while multiple matchExpressions within a single term are combined with AND logic — this two-level structure is what allows expressing genuinely disjunctive placement rules that nodeSelector cannot represent at all.

nodeSelectorTerms:
  - matchExpressions:
      - key: zone
        operator: In
        values: ["us-east-1a"]
  - matchExpressions:
      - key: zone
        operator: In
        values: ["us-east-1b"]

This expresses "zone is us-east-1a OR zone is us-east-1b" — something a flat nodeSelector map has no way to represent.

The "IgnoredDuringExecution" Qualifier

The suffix indicates this rule is evaluated only at scheduling time; if a node's labels change after the Pod has already been placed there, the Pod is not automatically evicted even though it would no longer satisfy the rule if re-evaluated — a distinction that matters when reasoning about label changes made after Pods are already running.


preferredDuringSchedulingIgnoredDuringExecution

Weighted Soft Preferences

This variant does not filter out any node; instead, each preference term carries a weight (1-100), and a node's total affinity score is the sum of weights from every preference term it satisfies, factored into the scheduler's overall scoring alongside other scoring plugins.

affinity:
  nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
            - key: disktype
              operator: In
              values: ["ssd"]
      - weight: 20
        preference:
          matchExpressions:
            - key: zone
              operator: In
              values: ["us-east-1a"]

A node matching the SSD preference but not the zone preference still receives a meaningful score boost (80 out of the possible 100), and remains a viable placement target even if no node satisfies every preference perfectly.

Choosing Weights Deliberately

Because weights are summed and compared relatively across nodes, their absolute values matter less than their relative proportions — a preference the workload cares about significantly more than another should carry a proportionally higher weight, and weights should be reviewed together as a set rather than assigned independently per rule.


Combining Required and Preferred Rules

A single Pod can use both variants together — required rules narrow the candidate set to nodes that are viable at all, and preferred rules then rank those remaining candidates by desirability, giving a two-tier placement policy: "must satisfy X, and among nodes satisfying X, prefer those that also satisfy Y."

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/os
              operator: In
              values: ["linux"]
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 50
        preference:
          matchExpressions:
            - key: disktype
              operator: In
              values: ["ssd"]

Example

apiVersion: v1
kind: Pod
metadata:
  name: codartium-affinity-example
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: node.kubernetes.io/instance-type
                operator: In
                values: ["m5.large", "m5.xlarge"]
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 70
          preference:
            matchExpressions:
              - key: zone
                operator: In
                values: ["us-east-1a"]
  containers:
    - name: app
      image: codartium/app:latest