Taints and tolerations
Dans l’article précédent, nous avons parlé de Daemonset, et à la fin nous avons vu que les pods du daemonset arrivaient à se scheduler sur les nœuds worker, mais pas sur le control plane. Nous avons dit que c’était un problème lié aux taints du nœud de control plane. Nous allons expliquer dans cet article les notions de taints, et de tolerations qui lui sont associées.
La taint est une sorte de label spécial qui est mis sur certains nœuds pour dire qu’ils ont un caractère spécial ET donc qu’il ne faut pas que des pods puissent se scheduler dessus. 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:NoSchedule
Ici, 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.
Voici le pod que nous allons lancé, 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.yaml
Quand 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 10s
Si 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-2
Donc 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 untainted
Daemonset
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-2
Encore une fois, le daemonset est lancé sur les noeuds worker mais pas sur le noeud de control plane. Hors 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-2
On voit bien que le daemonset a aussi été schedulé sur le noeud de control plane.
Conclusion
Voilà donc pour les taints et tolerations, qui sont très utiles si par exemple on veut avoir une certains nombre de noeuds qui ne sont dédiés qu’à une application spécifique qui aura la bonne toleration, afin d’empêcher les autres applications d’être schedulées sur les mêmes noeuds.
Nous allons voir dans les prochains articles d’autres moyens de jouer avec le scheduling dans kubernetes, avec les notions de node selector et d’affinity en général.