Understanding Node Selectors and Affinity in Kubernetes (with Examples)

Updated: January 30, 2024 By: Guest Contributor Post a comment

Introduction

As Kubernetes clusters grow in size and complexity, scheduling pods to the appropriate nodes becomes increasingly crucial. Node Selectors and Affinity are two Kubernetes features designed to control how pods are scheduled and assigned to nodes. This tutorial will take a deep dive into Node Selectors and Affinity, providing you with examples ranging from the basic to advanced to ensure a thorough understanding of these concepts.

What are Node Selectors?

Node selectors are the simplest way to constrain pods to specific nodes. They work by labeling nodes with key-value pairs and specifying the required labels in the pod configuration.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mycontainer
    image: myimage
  nodeSelector:
    disktype: ssd

This example sets a nodeSelector that tells the Kubernetes scheduler to run this pod on a node with the label ‘disktype=ssd’. To label a node, use:

kubectl label nodes <node-name> disktype=ssd

Understanding Affinity

Affinity in Kubernetes is a more expressive way to manage pod placement policies. There are two types of affinity:

  • Node Affinity: Controls which nodes a pod can be placed on, based on labels on nodes.
  • Inter-Pod Affinity: Controls which nodes a pod can be placed on, based on labels on pods that are already running on those nodes.

Node Affinity:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mycontainer
    image: myimage
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
            - key: 'disktype'
              operator: In
              values:
              - ssd

This configuration specifies that the pod can only be scheduled onto nodes with the label ‘disktype=ssd’. It uses the requiredDuringSchedulingIgnoredDuringExecution field, which means the rule must be met during scheduling, but will not evict the pod if the label changes after the pod starts running.

Inter-Pod Affinity:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mycontainer
    image: myimage
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        labelSelector:
          matchLabels:
            app: webserver
        topologyKey: 'kubernetes.io/hostname'

The above podAffinity rule states that the pod should be scheduled on a node with a pod running that has the label ‘app=webserver’, assuming that pod resides in the same hostname topology.

Advanced users can specify a preferred scheduling policy using preferredDuringSchedulingIgnoredDuringExecution, which allows the scheduler to weigh the importance of the affinity rules when making scheduling decisions.

Combining Multiple Affinity and AntiAffinity Rules:

It’s possible to granularly control pod placement by combining nodeAffinity and podAntiAffinity rules to spread the pods across different nodes or zones for high availability and fault tolerance.

Here is an example where multiple conditions are combined:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mycontainer
    image: myimage
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
            - key: 'region'
              operator: In
              values:
              - us-west-1
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        labelSelector:
          matchExpressions:
          - key: 'app'
            operator: In
            values:
            - webserver
        topologyKey: 'failure-domain.beta.kubernetes.io/zone'

This pod is required to schedule in the us-west-1 region and must not be co-located with other pods labeled with ‘app=webserver’ in the same zone to ensure it’s highly available in case of a zone failure.

Practical Use-Cases

Node Selectors and Affinity are especially useful in several scenarios such as data locality, licensing restrictions, or hardware constraints. For instance, running a machine learning workload that requires a GPU can be directed to the correct nodes with labels such as:

kubectl label nodes <node-name> hardware-type=gpu

The pod specification would need to target nodes with the ‘hardware-type=gpu’ label accordingly.

Similarly, you may have a database that must run within a specific geographic region due to data sovereignty laws. In this case, you could label nodes based on region and use nodeAffinity to restrict the database pods to those nodes:

kubectl label nodes <node-name> region=eu-central

Conclusion

Node Selectors and Affinity are powerful tools for optimizing pod scheduling in Kubernetes. By understanding and leveraging these features, operators and developers can ensure their applications are resilient, performant, and compliant with organizational and regulatory requirements. As clusters scale, mastering these techniques becomes instrumental in managing containerized workloads effectively.