Kubernetes Affinity là gì?
Kubernetes scheduler thường sử dụng các rule cơ bản dựa trên sự khả dụng tài nguyên để lập lịch cho các pod trên các node. Tuy nhiên có những lúc ta cần lập lịch một cách phức tạp hơn.
Ví dụ nếu ta muốn lập lịch các pod trên các node cụ thể hoặc nếu ta muốn tránh triển khai một số pod nhất định trên node thì ta cần sử dụng Affinity và Anti-affinity để làm điều này.
Affinity cho phép Kubernetes scheduler lập lịch một pod trên một nhóm các node hoặc lập lịch dựa trên vị trí của các pod khác. Để điều khiển được ví trí lập lịch pod trên một nhóm các node, user cần dùng node affinity. Ngược lại pod affinity và pod anti-affinity cung cấp khả năng điều khiển lập lịch pod dựa trên vị trí các pod khác.
Trong bài viết này ta sẽ tìm hiểu sâu hơn về affinity: tìm hiểu về các phương pháp lập lịch nâng cao, các loại affinity và thử demo một node affinity trong phần hands-on.
Các phương pháp lập lịch nâng cao của Kubernetes
Trước khi tìm hiểu sâu hơn về affinity, ta sẽ nói qua về các kĩ thuật lập lịch pod nhé.
Kĩ thuật | Miêu tả |
Taints and Tolerations | Cho phép 1 node điều khiển những pod nào có thể chạy trên node đó. |
NodeSelector | Gán 1 pod tới node cụ thể sử dụng label. |
Node Affinity | Tương tự NodeSelector nhưng rộng hơn và linh hoạt với các rule Required và Preferred. |
Pod Affinity and Anti-Affinity | Điều khiển vị trí của pod, lập lịch pod dựa trên các affinity và anti-affinity rule. |
Taints and Tolerations
Với Taints, các node sẽ có thể điều khiển vị trí đặt pod. Taints cho phép các node định nghĩa những pod nào có thể được đặt lên nó và những pod nào thì không.
Ví dụ bạn có một node với phần cứng đặc biệt và muốn scheduler chỉ triển khai các pod yêu cầu phần cứng đó lên node đó thì ta có thể dùng Toleration cho Taints của node đáp ứng yêu cầu đó.
Các pod cần phần cứng đặc biệt này cần phải định nghĩa toleration cho Taint trên các node. Với toleration các node sẽ cho phép các pod chạy trên nó.
K8s NodeSelector
NodeSelector là phương pháp đơn giản nhất để lập lịch cho một pod. NodeSelector định nghĩa một label cho một node và gán pod tới node có label tương ứng đã định nghĩa trong pod spec. Tuy nhiên điểm yếu của nó là không linh hoạt bằng các phương pháp lập lịch phức tạp hơn.
Affinity và Anti-Affinity
Node Affinity
Node Affinity dùng các label trên các node và label selector trong pod spec. Các node không có quyền điều khiển vị trí lập lịch. Nếu scheduler sử dụng node affinity rule để lập lịch một pod và rule không còn khả dụng sau đó nữa (ví dụ như có thay đổi về label) thì pod sẽ tiếp tục chạy trên node đó.
Có rất nhiều trường hợp sử dụng node affinity bao gồm lập lịch pod trên một nhóm các node với phần cứng cụ thể và lập lịch pod trên các node trong một availability zone cụ thể trên cloud.
Ví dụ sử dụng Node Affinity
Có 2 loại Node Affinity rule:
Required
Preferred
Required rule là các rule bắt buộc phải đạt được thì mới được phép lập lịch cho pod. Với preferred rule thì scheduler sẽ cố gắng bám sát rule nhất có thể nhưng không chắc chắn các rule đều thỏa mãn.
Pod Affinity và Pod Anti-Affinity
Pod Affinity và Anti-Affinity cho phép tạo các rule điều khiển vị trí lập lịch của pod dựa trên vị trí các pod khác. Một user cần phải label các node và dùng label selector trong pod spec.
Pod Affinity/Anti-Affinity cho phép pod định nghĩa một affinity hoặc anti-affinity cho một nhóm các pod.
Affinity rule hoạt động dựa trên các label. Với affinity rule, scheduler có thể lập lịch pod trên cùng một node với các pod khác nếu label của pod mới đó trùng với label của các pod khác.
Một anti-affinity rule sẽ cung cấp label của pod cho scheduler và scheduler sẽ không lập lịch pod đó trên cùng node với các pod có trùng label đó. Anti-affinity cho phép bạn giữ các pod không chạy trên cùng node với các pod khác. Nó sẽ hữu dụng trong các trường hợp như tránh đặt các pod có khả năng ảnh hưởng tới hiệu năng của một nhóm pod trên cùng 1 node.
Tương tự như node affinity rule, pod affinity rule cũng có Required và Preferred.
Node Affinity hands-on
Giờ hãy cùng demo thử nhé.
Trong demo này ta sẽ thực hiện từng bước:
Label một vài node trong cụm để hiểu cách Node Affinity rule hoạt động.
Tạo một deployment với Node Affinity rule trong pod spec.
Kiểm tra các pod trên các node đã gắn label.
Đầu tiên ta sẽ kiểm tra các node đã có trên cụm:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-node-master-1 Ready controlplane,etcd 723d v1.15.12
k8s-node-master-2 Ready controlplane,etcd 723d v1.15.12
k8s-node-master-3 Ready controlplane,etcd 723d v1.15.12
k8s-node-worker-1 Ready worker 723d v1.15.12
k8s-node-worker-2 Ready worker 723d v1.15.12
k8s-node-worker-3 Ready worker 723d v1.15.12
k8s-node-worker-4 Ready worker 723d v1.15.12
Ta sẽ label 2 node trong demo này:
$ kubectl label node k8s-node-worker-3 app=frontend
$ kubectl label node k8s-node-worker-4 app=frontend
Giờ tạo namespace test cho demo:
$ kubectl create ns test
Tạo deployment với Node Affinity như sau trong pod spec:
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-affinity
namespace: test
spec:
replicas: 4
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
affinity:
Node affinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: app
operator: In
values:
- frontend
Giờ hãy triển khai deployment:
$ kubectl create -f deployment-node-affinity.yml
Kiểm tra trạng thái deployment:
$ kubectl get deploy -n test
Kiểm tra các pod đã tạo:
$ kubectl get pods -n test
NAME READY STATUS RESTARTS AGE
node-affinity-7fd7f8f75f-dng7b 1/1 Running 0 30s
node-affinity-7fd7f8f75f-frsxz 1/1 Running 0 30s
node-affinity-7fd7f8f75f-nn6jl 1/1 Running 0 30s
node-affinity-7fd7f8f75f-wpc66 1/1 Running 0 30s
Để kiểm tra hoạt động của Node Affinity rule, ta kiểm tra danh sách các pod trên các node và xem xem các pod đã được đặt trên các node với label app=frontend chưa:
$ kubectl get pod -o=custom-columns=NODE:.spec.nodeName,NAME:.metadata.name -n test
NODE NAME
k8s-node-worker-4 node-affinity-7fd7f8f75f-dng7b
k8s-node-worker-4 node-affinity-7fd7f8f75f-frsxz
k8s-node-worker-3 node-affinity-7fd7f8f75f-nn6jl
k8s-node-worker-3 node-affinity-7fd7f8f75f-wpc66
Xóa demo để kết thúc:
$ kubectl delete ns test --cascade
Pod Affinity và Anti-Affinity Demo
Giả sử ta có môt Redis cache cho web app và ta cần chạy 3 replicas của Redis nhưng ta cần đảm bảo các replica chạy trên các node khác nhau, lúc này ta có thể sử dụng pod anti-affinity.
Redis deployment với pod anti-affinity rule như dưới:
cat redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
namespace: test
spec:
selector:
matchLabels:
app: store
replicas: 3
template:
metadata:
labels:
app: store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: redis-server
image: redis:3.2-alpine
Triển khai deployment:
kubectl create -f redis.yaml
Kiểm tra trạng thái deployment:
$ kubectl -n test get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
redis-cache 3/3 3 3 11s
Kiểm tra xem các pod có chạy trên các node khác nhau không:
$ kubectl get pod -o=custom-columns=NODE:.spec.nodeName,NAME:.metadata.name -n test
NODE NAME
k8s-node-worker-3 redis-cache-67786cc786-bgxtp
k8s-node-worker-1 redis-cache-67786cc786-btppc
k8s-node-worker-2 redis-cache-67786cc786-dchf7
Giờ giả sử ta có một web server đang chạy và ta cần đảm bảo mỗi web server pod được đặt cùng nơi với các Redis cache pod, nhưng cùng lúc đó phải đảm bảo 2 web server pod không chạy trên cùng 1 node.
Ta có thể tạo deployment như sau:
cat web-server.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
namespace: test
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.12-alpine
Triển khai deployment:
kubectl create -f web-server.yaml
Kiểm tra vị trí các pod:
kubectl get pod -o=custom-columns=NODE:.spec.nodeName,NAME:.metadata.name -n test
NODE NAME
k8s-node-worker-3 redis-cache-67786cc786-bgxtp
k8s-node-worker-1 redis-cache-67786cc786-btppc
k8s-node-worker-2 redis-cache-67786cc786-dchf7
k8s-node-worker-1 web-server-84b9456f9b-dhd9x
k8s-node-worker-3 web-server-84b9456f9b-lf6xs
k8s-node-worker-2 web-server-84b9456f9b-zbjq2
Kết luận
Affinity và anti-affinity cung cấp các phương pháp lập lịch pod trên các node hoặc đăt các pod dựa trên vị trí các pod khác. Bạn có thể sử dụng affinity rule để tối ưu vị trí đặt pod trên các worker node để đáp ứng yêu cầu về hiệu năng, chống chịu lỗi hay các yêu cầu phức tạp về lập lịch.