Taints and tolerations
Lorsqu’un Pod ne peut pas être planifié sur un nœud malgré des ressources disponibles, cela peut être dû à un mécanisme appelé taints et tolerations.
Les taints sont appliquées aux nœuds pour contrôler quels Pods peuvent s’y exécuter. Nous allons détailler dans cet article leur fonctionnement ainsi que les tolerations associées.
Une taint est un mécanisme appliqué à un nœud pour indiquer qu’il ne doit pas accepter certains Pods, sauf si ceux-ci déclarent explicitement une toleration correspondante. Le mot taint en anglais peut être traduit entre autres par infecté. C’est comme si on dit que le nœud est infecté, ce qui repousse les pods d’être schedulés dessus. La seule manière pour un pod d’être schedulé sur un nœud avec une taint, c’est qu’il ait la toleration qui correspond à la taint, donc qu’il supporte cette taint.
Nous allons voir un exemple sur notre cluster l’effet qu’a une taint. Exemple Nous allons mettre une taint sur les noeuds workers de notre cluster :
kubectl taint node k8s-node-1 type=special:NoSchedule
kubectl taint node k8s-node-2 type=special:NoScheduleIci, nous avons dit que les noeuds vont porter une taint avec la clé “type” et la valeur “special”, et que ça va avoir pour effet NoSchedule, c’est-à-dire que ça va empêcher le scheduling des pods, qui ne portent pas la toleration qui correspond, sur ces noeuds. La clé et la valeur sont complètement arbitraires, “type” et “special” ne sont pas des mot-clés.
Une taint est composée de trois éléments, sous la forme :
clé=valeur:effet
L’effet peut être :
NoSchedule: empêche le scheduling de nouveaux Pods qui ne tolèrent pas la taint.PreferNoSchedule: évite le scheduling si possible, mais sans le bloquer strictement.NoExecute: expulse les Pods déjà présents sur le nœud s’ils ne tolèrent pas la taint.
Voici le pod que nous allons lancer, qui n’a donc pas de toleration :
pod-no-toleration.yaml:
apiVersion: v1
kind: Pod
metadata:
labels:
run: podtol
name: podtol
spec:
containers:
- image: busybox:latest
name: podtol
args:
- sleep
- "3600"Nous lançons le pod :
kubectl apply -f pod-no-toleration.yamlQuand on voit l’état du pod, on voit qu’il est en pending, donc pas encore schedulé :
kubectl get pods
NAME READY STATUS RESTARTS AGE
podtol 0/1 Pending 0 10sSi on regarde de plus près ce qui se passe :
kubectl describe pods
…
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 7s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 node(s) had untolerated taint {type: special}. preemption: 0/3 nodes are available: 3 Preemption is not helpful for scheduling..On voit que le scheduler ne trouve pas de noeud à assigner au pod, car tous les noeuds ont une taint : celle qu’on a ajouté aux deux noeuds worker, et celle qui existait déjà sur le control plane.
Pour remédier à ça, il faut donc ajouter la toleration qui va bien au pod.
pod-toleration.yaml:
apiVersion: v1
kind: Pod
metadata:
labels:
run: podtol
name: podtol
spec:
containers:
- image: busybox:latest
name: podtol
args:
- sleep
- "3600"
tolerations:
- key: "type"
operator: "Equal"
value: "special"
effect: "NoSchedule"Dans la section tolerations à la fin de la description du pod, on peut lire qu’on dit que ce pod est capable de tolérer les taints qui ont pour clé “type” qui est égale à la valeur “special” et qui a pour effet NoSchedule.
Si on relance le pod avec la nouvelle toleration et qu’on regarde son état :
kubectl apply -f pod-toleration.yaml
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
podtol 1/1 Running 0 49s 10.244.2.5 k8s-node-2Donc il a bien été schedulé sur un des noeuds worker, même si ce dernier a une taint.
Revenons maintenant à notre Daemonset de l’article dernier, mais d’abord un peu de rangement.
On supprime le pod, et on enlève la taint des noeuds workers (comme pour ajouter mais en mettant un “-” à la fin de la taint) :
kubectl delete -f pod-toleration.yaml
pod "podtol" deleted
kubectl taint node k8s-node-1 type=special:NoSchedule-
node/k8s-node-1 untainted
kubectl taint node k8s-node-2 type=special:NoSchedule-
node/k8s-node-2 untaintedDaemonset
Notre Daemonset la dernière fois ressemblait à ça :
daemonset-no-toleration.yaml:
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
type: daemon
name: daemontest
spec:
selector:
matchLabels:
run: daemon
template:
metadata:
labels:
run: daemon
name: daemonpod
spec:
containers:
- image: busybox:latest
name: daemonpod
args:
- sleep
- "3600"On va le lancer :
kubectl apply -f daemonset-no-toleration.yaml
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
daemontest-hmm8j 1/1 Running 0 61s 10.244.1.4 k8s-node-1
daemontest-nmcjr 1/1 Running 0 64s 10.244.2.3 k8s-node-2Encore une fois, le Daemonset est lancé sur les noeuds worker mais pas sur le noeud de control plane. Or pour les Daemonset, nous avons parfois besoin de les avoir sur tous les noeuds, comme par exemple pour un Daemonset qui récolte les métriques ou les logs sur tous les noeuds.
Nous savons maintenant que ça doit être un souci avec les taints. Cherchons donc la taint qui bloque au niveau du noeud de control plane :
kubectl get nodes k8s-controlplane -o json | jq '.spec.taints' [
{
"effect": "NoSchedule",
"key": "node-role.kubernetes.io/control-plane"
}
]Ici, on voit que le noeud du control plane a une taint qui a pour clé “node-role.kubernetes.io/control-plane” et qui n’a pas de valeur. En fait, les taints n’ont pas besoin de valeur pour être effective. On dit que la taint “node-role.kubernetes.io/control-plane” tout simplement existe.
Nous allons donc ajouter une toleration au Daemonset pour qu’il se schedule sur le pod :
daemonset-toleration.yaml:
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
type: daemon
name: daemontest
spec:
selector:
matchLabels:
run: daemon
template:
metadata:
labels:
run: daemon
name: daemonpod
spec:
containers:
- image: busybox:latest
name: daemonpod
args:
- sleep
- "3600"
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"Ici donc on dit que les pods du Daemonset tolèrent les noeuds qui ont une taint avec la clé “node-role.kubernetes.io/control-plane” et qui ont pour effet NoSchedule.
Quand on lance le Daemonset :
kubectl apply -f daemonset-toleration.yaml
daemonset.apps/daemontest configured
kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE
daemontest-2klsg 1/1 Running 0 3m10s 10.244.0.6 k8s-controlplane
daemontest-hmm8j 1/1 Running 0 3m16s 10.244.1.5 k8s-node-1
daemontest-nmcjr 1/1 Running 0 3m13s 10.244.2.4 k8s-node-2On voit bien que le Daemonset a aussi été schedulé sur le noeud de control plane.
Conclusion
Les taints et tolerations permettent de contrôler précisément quels Pods peuvent être planifiés sur quels nœuds.
Elles sont particulièrement utiles pour réserver certains nœuds à des workloads spécifiques ou pour empêcher l’exécution de Pods non désirés sur des nœuds critiques comme le control-plane.
Ce mécanisme constitue un outil fondamental du scheduler Kubernetes et complète d’autres stratégies de placement telles que les sélecteurs de nœuds et les règles d’affinité.


