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ậtMiêu tả
Taints and TolerationsCho phép 1 node điều khiển những pod nào có thể chạy trên node đó.
NodeSelectorGán 1 pod tới node cụ thể sử dụng label.
Node AffinityTươ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.

An example of Kubernetes pod scheduling using Node Affinity.

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.

An example of Kubernetes pod scheduling using Pod Affinity.

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.

An example of Kubernetes pod scheduling using Pod Anti-Affinity.

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.