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ó.
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:
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
, nodeSelector
và taint/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.
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ố nodeSelector
là size: 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.
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-worker1
và viettq-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=ssd
là viettq-worker1
và viettq-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.
Trong ví dụ ở hình trên, 2 node viettq-worker1
và viettq-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-worker1
và viettq-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-worker1
và viettq-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.