Kubernetes

Kubernetes – ujarzmianie POD-ów

W tym artykule odpowiemy na pytania, w jaki sposób kontrolować to, gdzie zostaną uruchomione POD-y oraz jak działa kube-scheduler? Omówimy także szereg narzędzi służących do ustalania powiązań POD-ów z NODE-ami: labels (etykiety), nodeSelector, affinity oraz oznaczanie „skażonych” (tainted) NODE-ów.

W tym artykule odpowiemy na pytania, w jaki sposób kontrolować to, gdzie zostaną uruchomione POD-y oraz jak działa kube-scheduler. Omówimy także szereg narzędzi służących do ustalania powiązań POD-ów z NODE-ami: labels (etykiety), nodeSelector, affinity oraz oznaczanie „skażonych” (tainted) NODE-ów.

Poniższe przykłady zostały przeprowadzone na laboratorium Kubernetes złożonym z dwóch NODE-ów. Dlatego też zalecamy dostęp do takiego laboratorium. Proces tworzenia takiego klastra testowego opisaliśmy w artykule Połączenie dwóch maszyn EuroLinux 8 w klaster Kubernetes.

kubectl get nodes
NAME          STATUS     ROLES           AGE   VERSION
euro1         Ready      control-plane   35d   v1.24.0
euro2         Ready      <none>          35d   v1.24.0

Kubernetes scheduler

Scheduling to proces, który zarządza uruchamianiem POD-ów na odpowiednio dopasowanych NODE-ach tak, aby proces kubelet mógł je obsłużyć.

Proces schedulera można przedstawić w następujących krokach:

1. Oczekiwanie na pojawienie się nowego POD-a, który nie ma przypisanego NODE-a.
2. Znalezienie najlepszego NODE-a dla każdego wykrytego POD-a.
3. Poinformowanie API o wyborze.

kube-scheduler

kube-scheduler to domyślny scheduler Kubernetes, który działa jako część control-plane. Jest zaprojektowany tak, by umożliwiać użycie innych elementów (scheduling components) napisanych samodzielnie lub przez strony trzecie.

Wybór najlepszego NODE-a dla nowego POD-a wymaga odpowiedniego filtrowania dostępnych NODE-ów. Te NODE-y, które spełniają wymagania schedulingu, nazywa się feasible nodes (NODE-y wykonywalne). Gdy żaden z NODE-ów nie jest odpowiedni, POD pozostaje „nieuruchomiony” (unscheduled) do czasu, gdy scheduler nie znajdzie dla niego odpowiedniego miejsca (NODE). Po wybraniu NODE-ów wykonywalnych, scheduler wykonuje zestaw funkcji punktujących wybrane NODE-y. Następnie, wybiera NODE z najwyższym wynikiem. Ostatnim krokiem jest poinformowanie API serwera o danym wyborze w procesie binding (wiązania).

Przypisanie POD-ów do NODE-ów

Kubernetes umożliwia przypisywanie POD-ów do zdefiniowanych klas NODE-ów. Wszystkie zalecane metody wykorzystują selekcję etykiet (label selector). Najczęściej przypisywanie nie jest konieczne, ponieważ scheduler automatycznie bardzo dobrze przypisuje POD-y do NODE-ów dysponujących odpowiednimi zasobami. Z drugiej strony, łatwo można sobie wyobrazić przypadek, w którym definiowanie dodatkowej klasy NODE-ów może być użyteczne. Przykładowo, gdy potrzebny jest dostęp do „szybkiej” pamięci ssd lub chcemy, by NODE-y należały do jednej „szybkiej” sieci LAN.

NODE labels (etykiety)

Jak wiele innych obiektów Kubernetes, NODE-y posiadają etykiety (labels). Można je przypisywać ręcznie. Ponadto, Kubernetes wprowadza standardowy zbiór etykiet dla wszystkich NODE-ów w klastrze. Warto je poznać na potrzeby troubleshootingu. Jednak nie będziemy tego rozwijać w tym artykule.

Dodawanie etykiet umożliwia uruchamianie POD-ów na wybranej grupie NODE-ów. Ta metoda jest często wykorzystywana do zapewnienia działania aplikacji na specjalnie odizolowanych NODE-ach, spełniających szczególne wymagania bezpieczeństwa. W tym przypadku zalecane jest, by wybrać taki klucz etykiety, którego kubelet nie może zmodyfikować. Uniemożliwi to zmianę etykiety po uzyskaniu dostępu do pojedynczego worker NODE-a. Takimi etykietami można zarządzić jedynie przez master NODE. Można to zrobić w następujących krokach:

  • upewniamy się, że używamy Node authorizera oraz że włączona jest wtyczka rozszerzenia NodeRestriction:
kubectl -n kube-system describe po kube-apiserver-euro1|grep NodeRestriction
      --enable-admission-plugins=NodeRestriction
  • dodajemy etykietę z prefiksem node-restriction.kubernetes.io/ do wybranych NODE-ów:
kubectl label nodes euro2 restriction.kubernetes.io/supersecure=true
  • używamy tych etykiet w polu nodeSelector:
cat << EOF | tee deployment-supersecure.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-supersecure
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
      nodeSelector:
        restriction.kubernetes.io/supersecure: "true"
EOF

Uruchomienie deploymentu:

kubectl apply -f deployment-supersecure.yaml
deployment.apps/deployment-supersecure created

Weryfikacja dystrybucji POD-ów:

kubectl get pods -o wide
NAME                                      READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
deployment-supersecure-5d4ccb7468-gf54d   1/1     Running   0          20s   10.33.0.20   euro1   <none>           <none>
deployment-supersecure-5d4ccb7468-vlncv   1/1     Running   0          20s   10.33.0.18   euro1   <none>           <none>
deployment-supersecure-5d4ccb7468-znpfw   1/1     Running   0          20s   10.33.0.19   euro1   <none>           <none>

Usunięcie przykładowego deploymentu:

kubectl delete deployments.apps deployment-supersecure
deployment.apps "deployment-supersecure" deleted

nodeSelector

Pole nodeSelector można dodać do specyfikacji POD-a. Zawiera ono listę etykiet, które musi posiadać NODE, na którym Kubernetes scheduler może uruchomić POD. NODE musi zawierać wszystkie wybrane etykiety.

Przykład:

Nadanie NODE-om euro1 i euro2 etykiety a=a:

kubectl label nodes euro1 euro2 a=a
node/euro1 labeled
node/euro2 labeled

Nadanie NODE-owi euro2 etykiety b=b:

kubectl label nodes euro2 b=b
node/euro2 labeled

Przerobienie poprzedniego przykładowego deploymentu tak, by wymagał etykiet a=a i b=b:

cat << EOF | tee deployment-ab.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-ab
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
      nodeSelector:
        a: "a"
        b: "b"
EOF

Uruchomienie deploymentu:

kubectl apply -f deployment-ab.yaml
deployment.apps/deployment-ab created

Weryfikacja dystrybucji POD-ów:

kubectl get pods -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
deployment-ab-74d6cc75b9-74qsq   1/1     Running   0          8s    10.33.2.19   euro2   <none>           <none>
deployment-ab-74d6cc75b9-bflh9   1/1     Running   0          8s    10.33.2.18   euro2   <none>           <none>
deployment-ab-74d6cc75b9-gvt7l   1/1     Running   0          8s    10.33.2.20   euro2   <none>           <none>​

Usunięcie starego deploymentu:

kubectl delete deployment deployment-ab
deployment.apps "deployment-ab" deleted

Affinity / anti-affinity

nodeSelector jest uproszczoną metodą przypisywania POD-ów do NODE-ów. Pole affinity (powiązanie) i anti-affinity znacznie rozszerza możliwości pozwiązywania POD-ów z NODE-ami, a także POD-ów z POD-ami.

nodeAffinity

nodeAffinity (powiązanie NODE-ów) działa podobnie do nodeSelector. Wyróżnia się dwa rodzaje nodeAffinity:

  • requiredDuringSchedulingIgnoreDuringExecution – Kubernetes scheduler może uruchomić POD-a tylko wtedy, gdy reguła jest spełniona. Reguła może być określona w bardziej złożony sposób, w porównaniu z nodeSelectorem, gdzie jedyną opcją jest dopasowanie wszystkich etykiet
  • preferredDuringSchedulingIgnoredDuringExecution – Kubernetes spróbuje wybrać NODE-a spełniającego regułę. Jednak jest to tylko preferencja z przypisaną wagą.

IgnoredDuringExecution należy rozumieć, jako – jeśli NODE zmieni etykietę podczas pracy (DuringExecution), nie zakłóci to pracy tego POD-a.

Przykład konfiguracji POD-a powiązanego z NODE-em o etykiecie a=a i preferencją wobec NODE-ów o etykietach b=b (waga minimalna równa 1) i node-role.kubernetes.io/control-plane= (waga maksymalna równa 100):

cat <<EOF | tee node-affinity-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: a
            operator: In
            values:
            - a
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: b
            operator: In
            values:
            - b
      - weight: 100
        preference:
          matchExpressions:
          - key: node-role.kubernetes.io/control-plane
            operator: Exists

  containers:
  - image: nginx
    name: node-affinity
EOF

Uruchomienie POD-a:

kubectl apply -f node-affinity-pod.yaml

Wynik komendy kubectl get pods -o wide powinien wyglądać następująco:

NAME                READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
node-affinity-pod   1/1     Running   0          19s   10.33.0.23   euro1   <none>           <none>

POD można usunąć komendą:

kubectl delete pod node-affinity-pod

W kolejnych przykładach skonfigurujemy deployment oparty na analogicznych POD-ach.

Konfiguracja deploymentu złożonego z 4 POD-ów analogicznych do POD-a z powyższego przykładu:

cat << EOF | tee node-affinity-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-affinity-deployment
  labels:
    app: nginx
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: a
                operator: In
                values:
                - a
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: b
                operator: In
                values:
                - b
          - weight: 100
            preference:
              matchExpressions:
              - key: node-role.kubernetes.io/control-plane
                operator: Exists

      containers:
      - name: node-affinity-deployment
        image: nginx
EOF

Uruchomienie deploymentu:

kubectl apply -f node-affinity-deployment.yaml

Sprawdzenie dystrybucji POD-ów między NODE-ami:

kubectl get pods -o wide
NAME                                     READY   STATUS    RESTARTS       AGE   IP           NODE    NOMINATED NODE   READINESS GATES
node-affinity-deployment-bbc88d9-25qr2   1/1     Running   1 (2m2s ago)   14m   10.33.0.30   euro1   <none>           <none>
node-affinity-deployment-bbc88d9-947x5   1/1     Running   1 (2m2s ago)   14m   10.33.0.28   euro1   <none>           <none>
node-affinity-deployment-bbc88d9-pg4zz   1/1     Running   1 (2m2s ago)   14m   10.33.0.33   euro1   <none>           <none>
node-affinity-deployment-bbc88d9-s9vsf   1/1     Running   1 (2m2s ago)   14m   10.33.0.32   euro1   <none>           <none>

Wszystkie POD-y zostały uruchomione na control-plane NODE ze względu na wyższą wagę preferencji tego NODE-a.

W kolejnym przykładzie ustawimy dwie równe wagi preferencji.

cat << EOF | tee node-affinity-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-affinity-deployment
  labels:
    app: nginx
spec:
  replicas: 4
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: a
                operator: In
                values:
                - a
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: b
                operator: In
                values:
                - b
          - weight: 1
            preference:
              matchExpressions:
              - key: node-role.kubernetes.io/control-plane
                operator: Exists

      containers:
      - name: node-affinity-deployment
        image: nginx
EOF

Wdrożenie zmian w konfiguracji:

kubectl apply -f node-affinity-deployment.yaml

Weryfikacja dystrybucji POD-ów:

kubectl get pods -o wide
NAME                                       READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
node-affinity-deployment-dc8df7bbf-nlkcz   1/1     Running   0          72s   10.33.2.24   euro2   <none>           <none>
node-affinity-deployment-dc8df7bbf-nq88k   1/1     Running   0          72s   10.33.0.34   euro1   <none>           <none>
node-affinity-deployment-dc8df7bbf-nxt85   1/1     Running   0          70s   10.33.2.25   euro2   <none>           <none>
node-affinity-deployment-dc8df7bbf-rpt5k   1/1     Running   0          69s   10.33.0.35   euro1   <none>           <none>

POD-y są rozłożone równomiernie pomiędzy NODE-y. euro1 ma przypisaną etykietę node-role.kubernetes.io/control-plane o wadze 1. euro2 ma również przypisaną etykietę b=b o wadze 1.

Co się wydarzy, jeżeli usuniemy etykietę a=a z NODE-a euro1 i etykietę b=b z NODE-a euro2?

kubectl label nodes euro1 a- ; kubectl label nodes euro2 b-
node/euro1 unlabeled
node/euro2 unlabeled
kubectl get pods -o wide
NAME                                       READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
node-affinity-deployment-dc8df7bbf-nlkcz   1/1     Running   0          32m   10.33.2.24   euro2   <none>           <none>
node-affinity-deployment-dc8df7bbf-nq88k   1/1     Running   0          32m   10.33.0.34   euro1   <none>           <none>
node-affinity-deployment-dc8df7bbf-nxt85   1/1     Running   0          32m   10.33.2.25   euro2   <none>           <none>
node-affinity-deployment-dc8df7bbf-rpt5k   1/1     Running   0          32m   10.33.0.35   euro1   <none>           <none>

Nic się nie zmieniło. Usunięcie etykiet nie wpłynęło na dystrybucję uruchomionych już POD-ów. Natomiast po restarcie wszystkie POD-y trafią na euro2, ponieważ tylko ten POD ma przypisaną etykietę a=a.

kubectl rollout restart deployment node-affinity-deployment
deployment.apps/node-affinity-deployment restarted
kubectl get pods -o wide
NAME                                       READY   STATUS    RESTARTS   AGE    IP           NODE    NOMINATED NODE   READINESS GATES
node-affinity-deployment-979965644-45kch   1/1     Running   0          114s   10.33.2.27   euro2   <none>           <none>
node-affinity-deployment-979965644-88gng   1/1     Running   0          110s   10.33.2.28   euro2   <none>           <none>
node-affinity-deployment-979965644-gjlwv   1/1     Running   0          114s   10.33.2.26   euro2   <none>           <none>
node-affinity-deployment-979965644-mnl6s   1/1     Running   0          109s   10.33.2.29   euro2   <none>           <none>

Można usunąć przykładowy deployment za pomocą komendy:

kubectl delete deployments node-affinity-deployment
deployment.apps "node-affinity-deployment" deleted
Inter-pod affinity / anti-affinity

Zasada powiązywania (inter-pod affinity) lub izolowania (anti-affinity) przyjmuje taką formę:

Ten POD powinien (lub nie powinien) działać na X pod warunkiem, że na X działają już POD-y spełniające regułę Y. X stanowi topologię oznaczoną kluczem topologyKey. Y stanowi label selector rule z opcjonalną listą namespace’ów.

Wyróżnia się dwa rodzaje (podobnie jak przy nodeAffinity):

  • requiredDuringSchedulingIgnoreDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution

Na potrzeby demonstracji skonfigurujemy dwa POD-y.

cat << EOF | tee examplePODs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: euro1-pod
  labels:
    nr: "1"
spec:
  nodeSelector:
    kubernetes.io/hostname: euro1
  containers:
  - image: nginx
    name: nginx
---
apiVersion: v1
kind: Pod
metadata:
  name: euro2-pod
  labels:
    nr: "2"
spec:
  nodeSelector:
    kubernetes.io/hostname: euro2
  containers:
  - image: nginx
    name: nginx
EOF

Uruchomienie POD-ów:

kubectl apply -f examplePODs.yaml
pod/euro1-pod created
pod/euro2-pod created
kubectl get pods -o wide --show-labels
NAME        READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES   LABELS
euro1-pod   1/1     Running   0          18m   10.33.0.36   euro1   <none>           <none>            nr=1
euro2-pod   1/1     Running   0          18m   10.33.2.30   euro2   <none>           <none>            nr=2

W kolejnym etapie demonstracji uruchomimy 2 POD-y powiązane odpowiednio z POD-ami euro1-pod i euro2-pod za pomocą etykiety nr.

cat << EOF | tee podAffinity-pods.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nr1-pod
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: nr
            operator: In
            values:
            - "1"
        topologyKey: kubernetes.io/hostname
  containers:
  - name: nginx
    image: nginx
---
apiVersion: v1
kind: Pod
metadata:
  name: nr2-pod
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: nr
            operator: In
            values:
            - "2"
        topologyKey: kubernetes.io/hostname
  containers:
  - name: nginx
    image: nginx
EOF

Uruchomienie i weryfikacja dystrybucji POD-ów:

kubectl apply -f podAffinity-pods.yaml
pod/nr1-pod created
pod/nr2-pod created
kubectl get pods -o wide --show-labels
NAME        READY   STATUS    RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES   LABELS
euro1-pod   1/1     Running   0          70m     10.33.0.36   euro1   <none>           <none>            nr=1
euro2-pod   1/1     Running   0          70m     10.33.2.30   euro2   <none>           <none>            nr=2
nr1-pod     1/1     Running   0          2m20s   10.33.0.37   euro1   <none>           <none>            <none>
nr2-pod     1/1     Running   0          2m20s   10.33.2.31   euro2   <none>           <none>            <none>​

Analogicznie do nodeAffinity można definiować podAffinity jako preferencję z przypisaniem wagi (preferredDuringSchedulingIgnoredDuringExecution). Dodatkowo można definiować podAntiAffinity (anty-powiązanie), czyli wybrać, z którymi POD-ami konfigurowane POD-y nie powinny lub nie mogą być uruchamiane w tej samej topologii.

Ważne jest, by wybrany topologyKey był konsekwentnie zdefiniowany dla każdego z NODE-ów. Podany w przykładzie kubernetes.io/hostname, jest zdefiniowany automatycznie. Można też wybrać inny klucz topologii, np. topologyKey: miasto. W tym przypadku należy dopilnować, żeby każdy z NODE-ów miał przypisaną tę etykietę (np. za pomocą komendy kubectl label nodes NODE miasto=Krakow).

Można usunąć POD-y wykorzystywane do demonstracji:

kubectl delete pods euro1-pod euro2-pod nr1-pod nr2-pod

Taints and Tolerations (skazy i tolerancje)

Taints (skazy) stanowią przeciwieństwo nodeAffinity. NODE oznaczony skazą (taint), nie może być wybrany przez scheduler dla POD-a, który nie ma zdefiniowanej tolerancji na skazę.

Dodanie Taint (skazy) za pomocą kubectl taint:

kubectl taint nodes euro1 node-role.kubernetes.io/control-plane:NoSchedule
node/euro1 tainted

Najczęściej control-plane NODE ma dodaną tę skazę domyślnie.

Skazy mogą być też definiowane jako pary KEY=VALUE. Przykład:

kubectl taint nodes euro2 skaza=tragiczna:NoSchedule

Usuwanie skaz (operator -):

kubectl taint nodes euro2 skaza=tragiczna:NoSchedule-

Po wykonaniu powyższych kroków, próba uruchomienia standardowego deploymentu nginx powinna przebiec następująco:

kubectl create deployment nginx --image nginx --replicas 3
deployment.apps/nginx created
kubectl get pods -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
nginx-8f458dc5b-jd56c   1/1     Running   0          68s   10.33.2.36   euro2   <none>           <none>
nginx-8f458dc5b-p72s6   1/1     Running   0          68s   10.33.2.37   euro2   <none>           <none>
nginx-8f458dc5b-tr5jn   1/1     Running   0          68s   10.33.2.38   euro2   <none>           <none>

Wszystkie POD-y „wylądowały” na euro2. NODE euro1 jest skażony.

Taints mogą tworzyć następujące efekty:

  • NoSchedule – Kubernetes scheduler nie uruchomi nowych POD-ów bez wyszczególnionej tolerancji
  • PreferNoSchedule – preferencja niekorzystania z NODE-a. Jeżeli inne NODE-y nie będą feasible, scheduler uruchomi POD na oznaczonym NODE-dzie
  • NoExecute – wszystkie POD-y niemające tolerancji na tę skazę, zostaną wyłączone.

Następny przykład dotyczy różnic w zastosowaniu taint NoSchedule i NoExecute:

kubectl taint nodes euro2 skaza:NoSchedule
node/euro2 tainted
kubectl get pods -o wide
NAME                    READY   STATUS    RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES
nginx-8f458dc5b-jd56c   1/1     Running   0          7m28s   10.33.2.36   euro2   <none>           <none>
nginx-8f458dc5b-p72s6   1/1     Running   0          7m28s   10.33.2.37   euro2   <none>           <none>
nginx-8f458dc5b-tr5jn   1/1     Running   0          7m28s   10.33.2.38   euro2   <none>           <none>

POD-y dalej pracują na skażonym NODE-dzie, ale nowe POD-y nie mogą zostać uruchomione przez scheduler. Wszystkie NODE-y są „skażone”.

kubectl create deployment nginx2 --image nginx
deployment.apps/nginx2 created
kubectl get pods -o wide
NAME                      READY   STATUS    RESTARTS   AGE    IP           NODE     NOMINATED NODE   READINESS GATES
nginx-8f458dc5b-jd56c     1/1     Running   0          11m    10.33.2.36   euro2    <none>           <none>
nginx-8f458dc5b-p72s6     1/1     Running   0          11m    10.33.2.37   euro2    <none>           <none>
nginx-8f458dc5b-tr5jn     1/1     Running   0          11m    10.33.2.38   euro2    <none>           <none>
nginx2-7cc8cd4598-tpp2s   0/1     Pending   0          3s     <none>       <none>   <none>           <none>

Następnie dodajemy skazę NoExecute:

kubectl taint nodes euro2 handsUP:NoExecute
node/euro2 tainted
kubectl get pods -o wide
NAME                      READY   STATUS    RESTARTS   AGE     IP       NODE     NOMINATED NODE   READINESS GATES
nginx-8f458dc5b-29h9v     0/1     Pending   0          48s     <none>   <none>   <none>           <none>
nginx-8f458dc5b-2bj96     0/1     Pending   0          48s     <none>   <none>   <none>           <none>
nginx-8f458dc5b-twf8z     0/1     Pending   0          48s     <none>   <none>   <none>           <none>
nginx2-7cc8cd4598-tpp2s   0/1     Pending   0          8m20s   <none>   <none>   <none>           <none>

POD-y działające na euro2 zostały zatrzymane.

Tolerancja (toleration) jest właściwością POD-a, która umożliwia uruchomienie go pomimo tego, że NODE jest „skażony” (taint). Tolerancję definiuje PodSpec. Przykład:

cat << EOF | tee toleration.yaml
apiVersion: v1
kind: Pod
metadata:
  name: tolerancyjny-pod
spec:
  containers:
  - name: nginx
    image: nginx
  tolerations:
  - key: "skaza"
    operator: "Exists"
    effect: "NoSchedule"
  - key: "handsUP"
    operator: "Exists"
    effect: "NoExecute"
EOF

Uruchomienie POD-a:

kubectl apply -f toleration.yaml
pod/tolerancyjny-pod created

Zweryfikowanie stanu POD-a:

kubectl get pods -o wide
NAME                      READY   STATUS    RESTARTS   AGE    IP           NODE     NOMINATED NODE   READINESS GATES
nginx-8f458dc5b-29h9v     0/1     Pending   0          5m3s   <none>       <none>   <none>           <none>
nginx-8f458dc5b-2bj96     0/1     Pending   0          5m3s   <none>       <none>   <none>           <none>
nginx-8f458dc5b-twf8z     0/1     Pending   0          5m3s   <none>       <none>   <none>           <none>
nginx2-7cc8cd4598-98h5r   0/1     Pending   0          5m3s   <none>       <none>   <none>           <none>
tolerancyjny-pod          1/1     Running   0          68s    10.33.2.46   euro2    <none>           <none>

POD został uruchomiony na skażonym hościeeuro2.

Usunięcie „skaz” z euro2 umożliwi ponowne uruchomienie wstrzymanych POD-ów.

kubectl taint nodes euro2 skaza:NoSchedule-
node/euro2 untainted
kubectl taint nodes euro2 handsUP:NoExecute-
node/euro2 untainted
kubectl get pods -o wide
NAME                      READY   STATUS    RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES
nginx-7588f7b96-45f9m     1/1     Running   0          11m     10.33.2.50   euro2   <none>           <none>
nginx-7588f7b96-f6745     1/1     Running   0          11m     10.33.2.47   euro2   <none>           <none>
nginx-7588f7b96-tws56     1/1     Running   0          11m     10.33.2.48   euro2   <none>           <none>
nginx2-7cc8cd4598-98h5r   1/1     Running   0          11m     10.33.2.49   euro2   <none>           <none>
tolerancyjny-pod          1/1     Running   0          7m46s   10.33.2.46   euro2   <none>           <none>

Podsumowanie

W tym artykule przedstawiliśmy najważniejsze mechanizmy Kubernetes umożliwiające zarządzanie POD-ami w klastrze. Pojęcia takie jak nodeSelector, affinity i taints wykorzystaliśmy w nieskomplikowanych przykładach na klastrze złożonym tylko z 2 NODE-ów. Wyjaśniliśmy również, czym zajmuje się kluczowy komponent Kubernetes – scheduler.

Autorzy

Artykuły na blogu są pisane przez osoby z zespołu EuroLinux. 80% treści zawdzięczamy naszym developerom, pozostałą część przygotowuje dział sprzedaży lub marketingu. Dokładamy starań, żeby treści były jak najlepsze merytorycznie i językowo, ale nie jesteśmy nieomylni. Jeśli zauważysz coś wartego poprawienia lub wyjaśnienia, będziemy wdzięczni za wiadomość.