+44

[K8S basic] Scheduling - Lập lịch trên Kubernetes

Mở đầu

Hello mọi người đã trở lại với series k8s basic. Hôm nay mình sẽ giới thiệu về một chủ đề khá thú vị đó là lập lịch trên k8s.

Khi chúng ta tạo ra một workload (pod/deployment..) thì chúng ta có bao giờ đặt câu hỏi rằng các Pod sinh ra sẽ được chạy trên node nào chưa?

Trong bài viết này mình sẽ giải thích cơ chế lập lịch của k8s và một số ứng dụng của nó. image.png

Các tham số quyết định tới lập lịch (scheduling)

Khi chúng ta tạo ra một workload thành phần kube-scheduler mặc định của k8s sẽ là đối tượng có nhiệm vụ lập lịch thực thi các workload đó trên các node phù hợp.

Vậy làm sao để biết được node nào là phù hợp để chạy một workload, thì chúng ta có thể xem xét các tham số ảnh hưởng tới việc chọn node cho workload như sau: image.png

Về cơ bản nhiệm vụ của kube-scheduler là tìm ra một node phù hợp nhất để thực thi một Pod. Kết quả cuối cùng là sẽ cập nhật tham số "nodeName" trong thông tin của Pod.

Các phương thức mà chúng ta có thể sử dụng khi khai báo Pod (mình nói Pod là vì Deployment hay các workload khác thì cuối cùng cũng sẽ là tạo ra các Pod):

  • static pod: Đây là cách chúng ta có thể chạy một Pod trên một node cụ thể mà ta mong muốn.
  • nodeName: Đây là cách chúng ta gán trực tiếp thông tin của Node mà ta mong muốn sẽ chạy Pod của chúng ta. Bản chất của lập lịch sử dụng các phương thức khác thì cũng đưa về kết quả là gán được một node vào trong tham số nodeName này.
  • nodeSelector: Cách này giúp chúng ta lựa chọn Node cho Pod theo một label cụ thể của Node
  • Taint/Toleration: Đây là một loại đánh dấu đặc biệt dành cho các Node. Nó có tác dụng chỉ cho phép những Pod có đặc tính tương đương (toleration) được chạy trên các Node có taint tương ứng.
  • Affinity: Ý tưởng của nó là cho phép cấu hình Pod trên node với điều kiện node/pod đó có label thỏa mãn một điều kiện cho trước.
  • Resource request/limits: Đây không phải là tham số chúng ta sử dùng để lựa Node cho Pod. Tuy nhiên nó là điều kiện cần để Node được chọn. Node sẽ chỉ thích hợp (eligible) để được chọn nếu phần tài nguyên của nó còn đủ đáp ứng tài nguyên yêu cầu (resource requests) của Pod, ví dụ như CPU/RAM..

Trong bài này chúng ta sẽ cùng đi tìm hiểu một số phương thức cơ bản như static pod, nodeName, nodeSelectortaint/tolerations.

Trong môi trường lab của mình đang cài đặt kubernetest v1.20.7 có 3 master và 3 worker như sau:

[sysadmin@viettq-master1 ~]$ k get nodes -owide
NAME             STATUS   ROLES                  AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION                CONTAINER-RUNTIME
viettq-master1   Ready    control-plane,master   99d   v1.20.7   192.168.10.11   <none>        CentOS Linux 7 (Core)   3.10.0-1160.45.1.el7.x86_64   docker://20.10.10
viettq-master2   Ready    control-plane,master   99d   v1.20.7   192.168.10.12   <none>        CentOS Linux 7 (Core)   3.10.0-1160.45.1.el7.x86_64   docker://20.10.10
viettq-master3   Ready    control-plane,master   99d   v1.20.7   192.168.10.13   <none>        CentOS Linux 7 (Core)   3.10.0-1160.45.1.el7.x86_64   docker://20.10.10
viettq-worker1   Ready    <none>                 99d   v1.20.7   192.168.10.14   <none>        CentOS Linux 7 (Core)   3.10.0-1160.45.1.el7.x86_64   docker://20.10.10
viettq-worker2   Ready    <none>                 99d   v1.20.7   192.168.10.15   <none>        CentOS Linux 7 (Core)   3.10.0-1160.45.1.el7.x86_64   docker://20.10.10
viettq-worker3   Ready    <none>                 99d   v1.20.7   192.168.10.16   <none>        CentOS Linux 7 (Core)   3.10.0-1160.45.1.el7.x86_64   docker://20.10.10
[sysadmin@viettq-master1 ~]$

Sử dụng static pod

static pod được quản lý trực tiếp bởi service kubelet trên các node mà không cần giám sát bởi API-server. Static Pod luôn được gán vào một Kubelet ở một node cụ thể.

Khi chúng ta tạo ra một static pod thì kubelet cũng sẽ tạo ra một bản sao Pod đó trên kubernetes API server. Điều này để giúp cho việc chúng ta có thể thấy được thông tin của Static Pod thông qua API Server nhưng chúng ta không thể điều khiển được các Pod này (vì nó được quản lý bởi kubelet trên node).

Tên của các Static Pod sẽ có thêm hậu tố là hostname của node mà nó đang chạy theo format: [pod-name]-[node-hostname]

Cách tạo static Pod

Để tạo một static pod trên một node cụ thể thì ta cần tạo file định nghĩa (manifest) cho Pod tại thư mục mặc định /etc/kubernetes/manifests/.

Service kubelet trên node đó sẽ định kỳ scan thư mục này và nếu phát hiện có file manifest mới thì nó sẽ tạo ra Pod từ file manifest đó.

Trong ví dụ này mình sẽ kết nối vào node là viettq-master1 và tạo file manifest mynginx.yaml ở đường dẫn /etc/kubernetes/manifests/ có nội dung như sau:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: mynginx
  name: mynginx
spec:
  containers:
  - image: nginx
    name: mynginx
    resources: {}
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

Lưu ý 1: Chúng ta chỉ cần tạo file manifest thôi, không cần chạy lệnh kubectl apply nhé! Kubelet sẽ tự động scan thư mục này và tạo Pod cho chúng ta!

Kiểm tra kết quả:

[sysadmin@viettq-master1 manifests]$ k get pod mynginx-viettq-master1 -owide
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE             NOMINATED NODE   READINESS GATES
mynginx-viettq-master1   1/1     Running   0          4m13s   10.233.64.4   viettq-master1   <none>           <none>

Mọi người có thể thấy tên Pod lúc này là mynginx-viettq-master1 chính Pod-Name + Hostname của node.

Lưu ý 2: Khi chúng ta tạo các static pod thì nó sẽ không quan tâm tới các giá trị Taint đang có ở node đó.

Sửa/xóa static Pod

Các bạn có thể thấy chúng ta chỉ tạo ra file manifest ở đúng đường dẫn thôi thì kubelet đã tạo Pod cho chúng ta rồi, không cần phải thực hiện tạo bằng kubect apply như mọi khi nữa.

Vậy nếu ta muốn stop/start hay delete static pod thì phải làm thế nào? Câu trả lời đơn giản là trạng thái của static pod chính là trạng thái tồn tại file manfinest của nó trong thư mục chứa static pod.

Do đó:

  • Để stop/delete static pod: Move file manifest của Pod ra khỏi thư mục static pod (/etc/kubernetes/manifests/)
  • Để create/start static pod: Tạo file manifest vào thư mực static pod (/etc/kubernetes/manifests/)

Xem thông tin cấu hình thư mục chứa static pod của kubelet

Để verify chính xác thư mục chứa static pod của kubelet thì các bạn có thể check theo cách sau: Lấy thông tin config của service kubelet:

[sysadmin@viettq-worker1 system]$ sudo cat /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Wants=docker.socket

[Service]
User=root
EnvironmentFile=-/etc/kubernetes/kubelet.env
ExecStart=/usr/local/bin/kubelet \
                $KUBE_LOGTOSTDERR \
                $KUBE_LOG_LEVEL \
                $KUBELET_API_SERVER \
                $KUBELET_ADDRESS \
                $KUBELET_PORT \
                $KUBELET_HOSTNAME \
                $KUBELET_ARGS \
                $DOCKER_SOCKET \
                $KUBELET_NETWORK_PLUGIN \
                $KUBELET_VOLUME_PLUGIN \
                $KUBELET_CLOUDPROVIDER
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target

Như vậy kubelet được chạy với các tham số từ các biến môi trường được lưu ở file /etc/kubernetes/kubelet.env.

Kiểm tra các biến môi trường:

[sysadmin@viettq-worker1 system]$ sudo cat /etc/kubernetes/kubelet.env |grep config
KUBELET_ARGS="--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf \
--config=/etc/kubernetes/kubelet-config.yaml \
--kubeconfig=/etc/kubernetes/kubelet.conf \

Ta sẽ xem config của kubelet ở file /etc/kubernetes/kubelet-config.yaml

[sysadmin@viettq-worker1 system]$ sudo cat /etc/kubernetes/kubelet-config.yaml |grep static
staticPodPath: /etc/kubernetes/manifests

Mọi người có thể thấy tham số staticPodPath chính là tham số khai báo thư mục chứa các static pod của node chạy kubelet này.

Sử dụng nodeName

Tham số nodeName có thể được sử dụng trong khai báo Pod để chỉ định một node cụ thể mà ta muốn dùng để chạy Pod này. Ví dụ mình muốn chạy một pod nginx trên node có tên viettq-worker1 thì sẽ thực hiện tạo file manifest pod-nodename.yaml như sau:

apiVersion: v1
kind: Pod
metadata:  
  labels:
    app: be
  name: pod-nodename
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: mynginx
    resources: {}
  nodeName: viettq-worker1
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

Sau đó apply file này vào hệ thống để tạo Pod:

[sysadmin@vtq-cicd scheduling]$ k apply -f pod-nodename.yaml
pod/pod-nodename created
[sysadmin@vtq-cicd scheduling]$ k get pod pod-nodename -owide
NAME           READY   STATUS    RESTARTS   AGE   IP              NODE             NOMINATED NODE   READINESS GATES
pod-nodename   1/1     Running   0          8s    10.233.69.104   viettq-worker1   <none>           <none>

Như vậy Pod mới tạo ra đã được allocate vào node viettq-worker1 đúng như ta mong muốn.

Để tiếp tục làm rõ ta sẽ thử tạo ra một deploy để sinh ra nhiều Pod có cùng cấu hình nodeName xem sao. Ta sẽ tạo file deployment-nodename.yaml có nội dung như sau:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: deployment-nodename
  name: deployment-nodename
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deployment-nodename
  strategy: {}
  template:
    metadata:
      labels:
        app: deployment-nodename
    spec:
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: nginx
        resources: {}
      nodeName: viettq-worker1

Sau đó ta apply file này vào hệ thống và kiểm tra:

[sysadmin@vtq-cicd scheduling]$ k apply -f deployment-nodename.yaml
deployment.apps/deployment-nodename created

[sysadmin@vtq-cicd scheduling]$ k get pods -l "app=deployment-nodename" -owide
NAME                                   READY   STATUS    RESTARTS   AGE   IP              NODE             NOMINATED NODE   READINESS GATES
deployment-nodename-84d4fdb94d-glb9r   1/1     Running   0          73m   10.233.69.105   viettq-worker1   <none>           <none>
[sysadmin@vtq-cicd scheduling]$

Bây giờ dù ta có scale số lượng Pod của deployment này là bao nhiêu thì toàn bộ Pod sinh ra bởi deployment sẽ đều có tham số nodeName: viettq-worker1 do đó sẽ đều được tạo trên node này. Ta sẽ kiểm chứng bằng cách scale số Pod của deployment thành 10:

[sysadmin@vtq-cicd scheduling]$ k scale deployment deployment-nodename --replicas=10
deployment.apps/deployment-nodename scaled
[sysadmin@vtq-cicd scheduling]$ k get pods -l "app=deployment-nodename" -owide
NAME                                   READY   STATUS    RESTARTS   AGE   IP              NODE             NOMINATED NODE   READINESS GATES
deployment-nodename-84d4fdb94d-2qjfb   1/1     Running   0          11s   10.233.69.106   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-8rv24   1/1     Running   0          11s   10.233.69.109   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-cfbnd   1/1     Running   0          11s   10.233.69.111   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-glb9r   1/1     Running   0          75m   10.233.69.105   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-hhjgd   1/1     Running   0          11s   10.233.69.112   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-j5282   1/1     Running   0          11s   10.233.69.114   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-l72jn   1/1     Running   0          11s   10.233.69.113   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-nv9gb   1/1     Running   0          11s   10.233.69.107   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-qn8xf   1/1     Running   0          11s   10.233.69.110   viettq-worker1   <none>           <none>
deployment-nodename-84d4fdb94d-t4rqt   1/1     Running   0          11s   10.233.69.108   viettq-worker1   <none>           <none>

Sử dụng nodeSelector

Ý tưởng của việc sử dụng tham số nodeSelector này đó là chúng ta mong muốn cấu hình các Pod chỉ chạy trên một tập các node có lable nhất định.

Ví dụ trên hệ thống của chúng ta có 3 worker node mà được gán nhãn như sau:

[sysadmin@vtq-cicd scheduling]$ k get nodes --show-labels
NAME             STATUS   ROLES                  AGE   VERSION   LABELS
viettq-worker1   Ready    <none>                 99d   v1.20.7   disktype=ssd,kubernetes.io/hostname=viettq-worker1,size=small
viettq-worker2   Ready    <none>                 99d   v1.20.7   disktype=ssd,kubernetes.io/hostname=viettq-worker2,size=medium
viettq-worker3   Ready    <none>                 99d   v1.20.7   disktype=hdd,kubernetes.io/hostname=viettq-worker3,size=large

Các bạn có thể gán nhãn (label) cho node bằng cú pháp sau:

kubectl label nodes [node-name] [key]=[value]

Và ta có một Pod cần chạy trên một node có cấu hình lớn (tương ứng nhãn size=large) thì ta sẽ nghĩ tới ý tưởng sử dụng tham số nodeSelector. image.png Ta sẽ định nghĩa Pod bằng file manifest pod-nodeselector.yaml có nội dung như sau:

apiVersion: v1
kind: Pod
metadata:  
  labels:
    app: using-nodeselector
  name: pod-nodeselector
spec:
  nodeSelector:
    size: large
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: mynginx
    resources: {}  
  dnsPolicy: ClusterFirst
  restartPolicy: Always

Apply file manifest trên vào hệ thống và kiểm tra:

[sysadmin@vtq-cicd scheduling]$ k apply -f pod-nodeselector.yaml
pod/pod-nodeselector created
[sysadmin@vtq-cicd scheduling]$ k get pods -l "app=using-nodeselector" -owide
NAME               READY   STATUS    RESTARTS   AGE   IP              NODE             NOMINATED NODE   READINESS GATES
pod-nodeselector   1/1     Running   0          14s   10.233.67.121   viettq-worker3   <none>           <none>
[sysadmin@vtq-cicd scheduling]$ k get node -l "size=large"
NAME             STATUS   ROLES    AGE   VERSION
viettq-worker3   Ready    <none>   99d   v1.20.7

Như vậy Pod mà chúng ta định nghĩa đã được allocate vào node viettq-worker3 vì node này có gán label thỏa mãn điều kiện của tham số nodeSelectorsize: large.

Vẫn 3 node ở trên các bạn để ý có 2 node có nhãn disktype=ssd và 1 node có nhãn disktype=hdd. Bây giờ mình một triển khai một ứng dụng (dưới dạng deployment) mà các Pod của deployment này cần phải được chạy trên các node có SSD (xác định bởi label disktype=ssd) thì ta sẽ tiếp tục sử dụng nodeSelector để giải quyết bài toán trên. image.png

Ta sẽ tạo một file manifest có tên deployment-nodeselector.yaml có nội dung như sau:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: deployment-nodeselector
  name: deployment-nodeselector
spec:
  replicas: 3
  selector:
    matchLabels:
      app: deployment-nodeselector
  strategy: {}
  template:
    metadata:
      labels:
        app: deployment-nodeselector
    spec:
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: nginx
        resources: {}
      nodeSelector:
        disktype: ssd

Ta apply file manifest này vào hệ thống và kiểm tra kết quả:

[sysadmin@vtq-cicd scheduling]$ k get pods -l "app=deployment-nodeselector" -owide
NAME                                     READY   STATUS    RESTARTS   AGE   IP             NODE             NOMINATED NODE   READINESS GATES
deployment-nodeselector-858d7565-4cghn   1/1     Running   0          74s   10.233.68.66   viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-fx9nn   1/1     Running   0          5s    10.233.68.68   viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-hm6d6   1/1     Running   0          5s    10.233.68.67   viettq-worker2   <none>           <none>
[sysadmin@vtq-cicd scheduling]$ k get node -l "disktype=ssd" --show-labels
NAME             STATUS   ROLES                  AGE   VERSION   LABELS
viettq-worker1   Ready    <none>                 99d   v1.20.7   disktype=ssd,kubernetes.io/hostname=viettq-worker1,size=small
viettq-worker2   Ready    <none>                 99d   v1.20.7   disktype=ssd,kubernetes.io/hostname=viettq-worker2,size=medium

Như vậy thì có 3 Pod của deployment này được tạo ra và đều chạy trên node viettq-worker2, lý do là vì node này có label thỏa mãn điều kiện nodeSelector của Pod (disktype=ssd).

Lưu ý rằng chỉ có 2 node viettq-worker1viettq-worker2 thỏa mãn điều kiện nodeSelector của deployment trên. Nên dù ta có scale deployment này thành 10 hay 100 Pod thì nó sẽ vẫn chỉ chạy trên 2 node trên (nếu có đủ tài nguyên).

Ta sẽ scale deployment thành 20 Pod để kiểm chứng:

[sysadmin@vtq-cicd scheduling]$ k scale deployment deployment-nodeselector --replicas=20
deployment.apps/deployment-nodeselector scaled
[sysadmin@vtq-cicd scheduling]$ k get pods -l "app=deployment-nodeselector" -owide
NAME                                     READY   STATUS              RESTARTS   AGE     IP              NODE             NOMINATED NODE   READINESS GATES
deployment-nodeselector-858d7565-2pfs2   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-2r8m9   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-4cghn   1/1     Running             0          6m8s    10.233.68.66    viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-4fggr   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-85mjb   1/1     Running             0          8s      10.233.69.116   viettq-worker1   <none>           <none>
deployment-nodeselector-858d7565-96phf   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-bwcqw   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-cw5xx   1/1     Running             0          8s      10.233.69.115   viettq-worker1   <none>           <none>
deployment-nodeselector-858d7565-dppg7   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-f8t97   1/1     Running             0          8s      10.233.69.117   viettq-worker1   <none>           <none>
deployment-nodeselector-858d7565-fx9nn   1/1     Running             0          4m59s   10.233.68.68    viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-mdxmr   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-pt6r2   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-qvfc8   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-tkjtw   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-vtmbt   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-xdqxp   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-xns46   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-zmwvf   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
deployment-nodeselector-858d7565-znwz7   0/1     ContainerCreating   0          8s      <none>          viettq-worker2   <none>           <none>
[sysadmin@vtq-cicd scheduling]$

Kết quả là toàn bộ 20 Pod này đều chỉ chạy trên 2 node có nhãn disktype=ssdviettq-worker1viettq-worker2.

Có một lưu ý ở đây là khi chúng ta sử dụng nodeSelector để lựa chọn node phù hợp cho Pod thì ta chỉ có thể lựa chọn theo một label duy nhất.

Trong ví dụ trên có trường hợp ta muốn lựa chọn phải chạy Pod trên các node có cấu hình lớn (theo label size=large). Tuy nhiên với bài toàn ngược lại ta muốn chạy Pod trên các node có cấu hình "không lớn", tương ứng với các node có label là size=small hoặc size=medium thì sao? Sử dụng nodeSelector sẽ không giải quyết được bài toán này cho chúng ta.

Lúc này chúng ta sẽ cần sử dụng tới nodeAffinity và mình sẽ giới thiệu ở phần sau.

Taint & Toleration

Taint là một loại nhãn đặc biệt dùng để đánh dấu node có những đặc trưng nào đó, xác định bởi một bộ key-value và một action khi Pod không thỏa mãn điều kiện key-value đó.

Hay hiểu đơn giản hơn thì việc gán Taint vào một node giống như việc chúng ta khóa một node bằng một cái ổ khóa (coi taint là ổ khóa), và ổ khóa này đặc trưng bởi một label key=value và một action kèm theo (gọi là effect nếu không có chìa khóa.

Action này có thể là:

  • NoSchedule ==> Không có chìa khóa thì sẽ không được lên lịch chạy trên node này.
  • NoExecute ==> Không có chìa khóa thì sẽ có thể được lên lịch trên node này nhưng Pod sẽ không được thực thi.

Có khóa thì phải có chìa. Để mở khóa (Taint) cho phép Pod có thể được chạy trên Node có Taint, thì Pod đó phải khai báo Tolerations (hiểu là chìa khóa) đặc trưng với một label key=value và một Action kèm theo tương tự như với Taint.

Lưu ý: Một node có thể có nhiều Taint, khi đó Pod được gán vào Node nếu có tất cả các Toleration tương ứng với các Taint của node.

image.png

Trong ví dụ ở hình trên, 2 node viettq-worker1viettq-worker2 đều đang có gán 1 Taint là app=db:NoSchedule.

Pod1 có khai báo Tolerations có key-value (app=db) tương ứng với Taint khai báo trên 2 node do đó có thể được chạy trên 2 node viettq-worker1viettq-worker2. Với node thứ 3 không có Taint thì mặc nhiên Pod1 cũng có thể được lập lịch chạy trên node này. Do đó Pod1 có thể được chạy trên cả 3 node.

Pod2 không có khai báo Tolerations do đó nó không thể được lập lịch chạy trên các node có Taint. Do đó nó chỉ có thể được chạy trên node không có taint còn lại là viettq-worker3.

Cú pháp lênh với taint:

#Gán taint vào node:
kubectl taint node [node-name] [taint-key]=[taint-value]:[effect]

#Bỏ gán taint vào node:
kubectl taint node [node-name] [taint-key]=[taint-value]:[effect]-

#Kiểm tra một node có Taint hay không:
kubectl describe node [node-name] |grep Taint

Bây giờ chúng ta sẽ cũng thực hành bằng cách gán Taint cho node trước:

[sysadmin@vtq-cicd scheduling]$ k taint node viettq-worker1 app=db:NoSchedule
node/viettq-worker1 tainted
[sysadmin@vtq-cicd scheduling]$ k taint node viettq-worker2 app=db:NoSchedule
node/viettq-worker2 tainted
[sysadmin@vtq-cicd scheduling]$ k describe node viettq-worker1 |grep Taint
Taints:             app=db:NoSchedule
[sysadmin@vtq-cicd scheduling]$ k describe node viettq-worker2 |grep Taint
Taints:             app=db:NoSchedule

Ta sẽ gán thêm một cái label taint=yes để đánh dấu node nào có Taint như sau:

[sysadmin@vtq-cicd scheduling]$ k label nodes viettq-worker1 taint=exists
node/viettq-worker1 labeled
[sysadmin@vtq-cicd scheduling]$ k label nodes viettq-worker2 taint=exists
node/viettq-worker2 labeled
[sysadmin@vtq-cicd scheduling]$ k get node -l "taint=exists"
NAME             STATUS   ROLES    AGE   VERSION
viettq-worker1   Ready    <none>   99d   v1.20.7
viettq-worker2   Ready    <none>   99d   v1.20.7

Trước tên ta sẽ tạo một file manifest pod-without-toleration.yaml để định nghĩa Pod không có Toleration và cấu hình nodeSelector để cho Pod này cố tình chạy trên node có Taint sử dụng nodeSelector theo giá trị taint-yes:

apiVersion: v1
kind: Pod
metadata:
  name: pod-without-toleration
  labels:
    type: without-toleration
spec:
  nodeSelector:
    taint: exists
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

Chúng ta apply file trên vào và kiểm tra kết quả:

[sysadmin@vtq-cicd scheduling]$ k apply -f pod-without-toleration.yaml
pod/pod-without-toleration created
[sysadmin@vtq-cicd scheduling]$ k get pods pod-without-toleration
NAME                     READY   STATUS    RESTARTS   AGE
pod-without-toleration   0/1     Pending   0          5s
[sysadmin@vtq-cicd scheduling]$ k describe pod pod-without-toleration |tail -5
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  13s   default-scheduler  0/6 nodes are available: 1 node(s) didn't match Pod's node affinity, 2 node(s) had taint {app: db}, that the pod didn't tolerate, 3 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.
  Warning  FailedScheduling  13s   default-scheduler  0/6 nodes are available: 1 node(s) didn't match Pod's node affinity, 2 node(s) had taint {app: db}, that the pod didn't tolerate, 3 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.

Như vậy Pod này ở trạng thái Pending vì nó đang cố gắng lựa chọn 2 node viettq-worker1viettq-worker2 để chạy theo cấu hình nodeSelector tuy nhiên 2 node này đều đang có Taint mà Pod không khai báo Toleration tương ứng. Do đó Pod sẽ không thể được allocate vào bất cứ node nào trong 2 node này dẫn tới nó sẽ ở trạng thái Pending.

Để tạo Pod mới có thể chạy được trên Node có Taint thì ta phải khai báo tham số tolerations tương ứng với Taint của node. Ta sẽ tạo file manifest pod-with-toleration.yaml để định nghĩa Pod mới có toleration như sau:

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-toleration
  labels:
    type: with-toleration
spec:
  nodeSelector:
    taint: exists
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "app"
    operator: "Equal"
    value: "db"
    effect: "NoSchedule"

Sau đó apply vào hệ thống và kiểm tra:

[sysadmin@vtq-cicd scheduling]$ k apply -f pod-with-toleration.yaml
pod/pod-with-toleration created
[sysadmin@vtq-cicd scheduling]$ k get pods pod-with-toleration -owide
NAME                  READY   STATUS    RESTARTS   AGE   IP              NODE             NOMINATED NODE   READINESS GATES
pod-with-toleration   1/1     Running   0          7s    10.233.69.118   viettq-worker1   <none>           <none>

Như vậy là Pod mới này đã chạy được cả trên Node có gán taint.

Tổng kết:

Khi bạn gán trực tiếp tham số nodeName cho Pod thì k8s sẽ không quan tâm node đó có đang có taint hay không mà sẽ gán ngay và luôn node đó cho Pod

Khi sử dụng nodeSelector thì các bạn cần lưu ý các node được chọn (theo rule của selector) nếu có taint thì phải khai báo tolerations tương ứng cho Pod.

Trên đây là một số phương thức cơ bản để cấu hình phần lập lịch thực thi workload. Trong phần sau mình sẽ tiếp tục giới thiệu một số kỹ thuật phức tạp hơn một chút liên quan tới Affinity.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí