M l ops Kubernetes Docker

Tìm hiểu về Kubernetes và áp dụng vào bài toán AI - Phần 1: Kubernetes Pod

Tìm hiểu về Kubernetes và áp dụng vào bài toán AI - Phần 1: Kubernetes Pod

Trong các bài viết trước, mình đã giới thiệu về Docker, sử dụng kết hợp với Nginx, uWSGI, Flask để deploy model trong môi trường production. Nhìn chung mà nói, cách kết hợp 4 dịch vụ này đủ để áp ứng cho hầu hết các bài toán AI, ngoại trừ vấn đề cấu hình tương đối phức tạp và khó triển khai trên cloud (thực tế là AWS và GCP đề không hỗ trợ cách này, nếu muốn chúng ta vẫn phải cấu hình bằng tay như dưới local).

Gần đây, Kubernetes nổi lên như là một xu hướng mới, đáp ứng đầy đủ các yêu cầu của việc triển khai model trong môi trường production. Hơn thế nữa, việc cấu hình rất đơn giản và được hỗ trợ bởi các ông lớn cloud (AWS và GCP đều có dịch vụ Kubernetes). Trong loạt bài tiếp theo, mình sẽ cùng mọi người tìm hiểu về hot trend này và cách thức sử dụng nó để deploy các AI model của chúng ta nhé!

1. Kubernetes là gì?

Theo định nghĩa từ trang chủ của Kubernetes thì:

Kubernetes, also known as K8s, is an open-source system for automating deployment, scaling, and management of containerized applications.

Phần tử hạt nhân của Kubernetes chính là các Containers (Docker Container). Nói theo một cách khác, Kubernetes giúp chúng ta:

  • Manage containers: Quản lý đồng thời nhiều docker containers của cùng một ứng dụng hoặc thậm chí là nhiều ứng dụng khác nhau.
  • Manage lifecycle: Quản lý toàn bộ lifecycle của các containers, từ lúc được tạo ra đến khi bị xóa bỏ.
  • Hardware optimization: Tối ưu hóa, tối đa hóa khả năng của phần cứng thiết bị.
  • Schedule: Lập lịch khi nào cần bật/tắt containers.
  • Load balancer: Phân tải xử lý đều cho các containers.
  • Backup: Khi một container chết, sẽ có một container khác đươc tạo để thay thế.
  • Scalling: Dễ dàng scale các containers up/down theo một trong 2 chế độ: tự động hoặc bằng tay.
  • Monitor system: Dễ dàng giám sát hoạt động của toàn bộ hệ thống.

Việc cấu hình cho Kubernetes khá đơn giản, tất cả chỉ thông qua một file cấu hình duy nhất.

Trong tiếng Hy Lạp, Kubernetes có nghĩa là người chỉ huy hay thuyền trưởng.

Tất cả những đặc điểm trên đều phù hợp với giải pháp mà chúng ta tìm kiếm để đưa AI model vào môi trường production. Tất nhiên là phạm vi ứng dụng của Kubernetes còn rộng lớn hơn rất nhiều, nhưng trong lĩnh vực làm việc và nghiên cứu của mình, mình chỉ tập trung tìm hiểu và sử dụng Kubernetes cho các bài toán về AI.

2. Kiến trúc và thành phần của Kubernetes

Kubernetes bao gòm các Nodes, được chia thành 2 loại: Master Node và Worker Nodes. Master Nodes chịu trách nhiệm quản lý các Worker Nodes, trong khi các Worker Nodes làm nhiệm vụ thực hiện các công việc tính toán, … Mỗi Worker Node lại được chia nhỏ thành các Pods, và trong mỗi Pod chính là các Containers.

Phần còn lại của bài hôm nay, mình sẽ cùng các bạn tìm hiểu về Pod. Các bài tiếp theo, chúng ta sẽ làm việc với Job, CronJob, Deployment, Service.

3. Kubernetes Pod

3.1 Kubernetes Pod là gì?

Theo định nghĩa, Pod là đối tượng nhỏ nhất có khả năng triển khai trong kiến trúc của Kubernetes, tức là bạn có thể tạo, sử dụng, hay xóa Pod. Có thể coi Pod chính là đại diện của một ứng dụng (instance application) chạy trong Kubernetes.

Như đã nói ở phần 2, mỗi Pod chứa một hoặc nhiều Containers để thực hiện một công việc (Job) nào đó. Các Containers trong cùng Pod nằm trong cùng một mạng local và chia sẻ tài nguyên sử dụng với nhau. Chính vì thế mà chúng dễ dàng giao tiếp và làm việc với nhau.

Vì Pod là Single Instance của ứng dụng chạy trong Kebernetes, số lượng Pod được tạo ra hay xóa đi một cách tự động (Load Balancing & Failure Recovery), tùy theo tải mà ứng dụng phải phục vụ.

3.2 Tạo Pod từ Docker Image có sẵn

Để làm việc được với Pod, trước tiên cần phải cài đặt kubectl theo hướng dẫn trên trang chủ của Kubernetes tại đây hoặc tại đây

Cách dễ nhất để tạo và triển khai Kubernetes là sử dụng config file. File này sẽ chỉ định đối tượng được tạo là gì, các metadata gắn với đối tượng đó, tài nguyên cần thiết là bao nhiêu, …

Dưới đây là template của config file (pod_public.yaml) để tạo một Pod:

apiVersion: v1
kind: Pod
metadata:
  name: python3-pod
  labels:
    app: python3
spec:
  containers:
  - name: python3-container
    image: python:3.6
    command: ['python3', '-c', 'print("Hello, World!")']
  restartPolicy: Never

File config này bao gồm những thông tin sau:

  • apiVersion: Phiên bản của Kubernetes API đang sử dụng.
  • kind: Loại tài nguyên (đối tượng) của Kubernetes được tạo ra: Pod, Job, Development, … Ở đây là Pod object.
  • metadata: Là một tập hợp các labels và các thuộc tính của model mà người phát triển có thể thêm vào tùy ý giống như phiên bản, độ chính xác, thuật toán, …
  • spec: Bao gồm thông tin của các Containers chạy bên trong Pod: tên, docker image, command. Như trong cấu hình hiện tại thì chỉ có 1 Container.
  • restartPolicy: Cho phép Container có restart hay không khi nó bị lỗi. Giá trị never ở đây tức là không cho phép restart.

Thực hiên lệnh sau để tạo Pod:

$ kubectl create -f pod_public.yaml
pod "python3-pod" created

Kiểm tra trạng thái của pod vừa tạo:

$ kubectl get pods
NAME        READY     STATUS      RESTARTS   AGE
python3-pod   0/1       Completed   0          3s

Xem log của pod vừa tạo:

$ kubectl logs python3-pod
Hello, World!

Xóa pod vừa tạo:

$ kubectl delete -f pod_public.yaml
pod "python3-pod" deleted

3.3 Tạo Pod từ Docker Image tự tạo

Mình sẽ sử dụng Docker Image đã tạo từ bài này để đưa vào Pod.

Trước tiên, bạn hãy login vào Docker Hub để tạo một Repository. Giả sử mình tạo Repository tên là ml-model-batch-infer.

ở máy local, thực hiện các bước sau để đưa Docker Image đã tạo lên Repository:

  • Login vào Docker Hub
$ docker login -u tiensu

Trong đó, tiensu là tên đăng nhập của mình, bạn hãy thay bằng tên đăng nhập của bạn. Nhập mật khẩu khi được hỏi.

  • Gán Tag cho Docker Image theo tên mới trên Repository
docker tag docker-model-batch-infer:latest tiensu/ml-model-batch-refer:latest
  • Push Docker Image đã gắn Tag lên Repository
docker push tiensu/ml-model-batch-infer:latest

Output:

The push refers to repository [docker.io/tiensu/ml-model-batch-infer]
2a0a8f09fca2: Pushed 
ea3e588d9e9f: Pushed 
2b8e8179f02d: Pushed 
254c54a05297: Pushed 
bdeb303132f3: Pushed 
5f70bf18a086: Mounted from jupyter/scipy-notebook 
6f5a41ae77fd: Mounted from jupyter/scipy-notebook 
5a1b9a3f9355: Mounted from jupyter/scipy-notebook 
b1d7816bac14: Mounted from jupyter/scipy-notebook 
c91fed2d1998: Mounted from jupyter/scipy-notebook 
cc70098d00e3: Mounted from jupyter/scipy-notebook 
88727e93cbac: Mounted from jupyter/scipy-notebook 
cadaf24035f3: Mounted from jupyter/scipy-notebook 
8f170f4774e3: Mounted from jupyter/scipy-notebook 
33bd52db887f: Mounted from jupyter/scipy-notebook 
21e5dd010f50: Mounted from jupyter/scipy-notebook 
ea370ab22368: Mounted from jupyter/scipy-notebook 
421d1408f872: Mounted from jupyter/scipy-notebook 
18fd1ca0de51: Mounted from jupyter/scipy-notebook 
8f01aab6d756: Mounted from jupyter/scipy-notebook 
e18a1c4e1d31: Mounted from jupyter/scipy-notebook 
8552f27c3cd8: Mounted from jupyter/scipy-notebook 
1a4c57efcc23: Mounted from jupyter/scipy-notebook 
94b8fe888eac: Mounted from jupyter/scipy-notebook 
02473afd360b: Mounted from jupyter/scipy-notebook 
dbf2c0f42a39: Mounted from jupyter/scipy-notebook 
9f32931c9d28: Mounted from jupyter/scipy-notebook 
latest: digest: sha256:2552cb24c104d9b4fe3a43cc952371a7a1b0cce84e1c95821622b4fe508a6877 size: 6786

Để tạo Pod với Docker Image này, cập nhật lại file config (đổi tên thành pod_custom.yaml) của Pod như sau:

apiVersion: v1
kind: Pod
metadata:
  name: pod-ml-model-batch-infer
  labels:
    app: python3
spec:
  containers:
  - name: container-ml-model-batch-infer
    image: tiensu/ml-model-batch-infer:latest
    command:  ['python3', 'batch_inference.py']
  restartPolicy: Never

Chạy lệnh sau để tạo Pod:

$ kubectl create -f pod_custom.yaml
pod/pod-ml-model-batch-infer created

Kiểm tra trạng thái của Pod vừa tạo:

$ kubectl get pods
NAME                       READY   STATUS      RESTARTS   AGE
command-demo               0/1     Completed   0          113m
pod-ml-model-batch-infer   0/1     Completed   0          5m28s
python3-pod                0/1     Completed   0          90m

Chú ý là Docker Image của chúng ta được tải về trên Worker Node. Bạn có thể kiểm tra trên đó bằng lệnh $ docker ps.

Chúng ta có thể xem miêu tả chi tiết quá trình tạo Pod như sau:

$ kubectl describe pod pod-ml-model-batch-infer
Name:         pod-ml-model-batch-infer
Namespace:    default
Priority:     0
Node:         duynm-vostro-3670/10.1.34.169
Start Time:   Wed, 27 Jan 2021 16:44:15 +0700
Labels:       app=python3
Annotations:  cni.projectcalico.org/podIP: 
              cni.projectcalico.org/podIPs: 
Status:       Succeeded
IP:           192.168.24.198
IPs:
  IP:  192.168.24.198
Containers:
  container-ml-model-batch-infer:
    Container ID:  docker://535749cae10e6dd605030b6d84ba978cc245bfd44bb6981d3307a3ffa8a5bf94
    Image:         tiensu/ml-model-batch-infer:latest
    Image ID:      docker-pullable://tiensu/ml-model-batch-infer@sha256:2552cb24c104d9b4fe3a43cc952371a7a1b0cce84e1c95821622b4fe508a6877
    Port:          <none>
    Host Port:     <none>
    Command:
      python3
      batch_inference.py
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Wed, 27 Jan 2021 16:44:24 +0700
      Finished:     Wed, 27 Jan 2021 16:44:24 +0700
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-gmswp (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             False 
  ContainersReady   False 
  PodScheduled      True 
Volumes:
  default-token-gmswp:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-gmswp
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  4m18s  default-scheduler  Successfully assigned default/pod-ml-model-batch-infer to duynm-vostro-3670
  Normal  Pulling    4m15s  kubelet            Pulling image "tiensu/ml-model-batch-infer:latest"
  Normal  Pulled     4m11s  kubelet            Successfully pulled image "tiensu/ml-model-batch-infer:latest" in 4.353859959s
  Normal  Created    4m9s   kubelet            Created container container-ml-model-batch-infer
  Normal  Started    4m9s   kubelet            Started container container-ml-model-batch-infer

Cuối cùng, hãy xem log tạo ra khi thực hiên Inference:

$ kubectl logs pod-ml-model-batch-infer
Running inference...
Loading data...
Loading model from: /code/model/clf.joblib
Scoring observations...
[15.32448686 27.68741572 24.20025598 31.94786177 10.42732759 34.12058193
 22.05210667 11.58265489 13.1649368  42.84036647 33.03218733 15.77635169
 23.93521876 19.91587166 25.43466604 20.55132127 13.65254047 47.47279364
 17.58214889 21.51806638 22.57388848 16.97645106 16.25503893 20.57862843
 14.57438158 11.81385445 24.78353556 37.65978361 30.18436261 19.67895051
 23.22841646 24.94197905 18.65459129 30.19731636  8.9560549  13.8130382
 14.23277857 17.3840622  19.83840166 24.91315811 20.44991809 15.32433651
 25.8157052  16.47533793 19.2214524  19.87110293 21.47113681 21.56443118
 24.64517965 22.43665872 22.18289286]

7. Kết luận

Mặc dù Pod là đối tượng quan trọng, không thể thiếu trong bất kỳ kiến trúc Kubernetes nào nhưng các Best Practice đều không khuyến khích việc sử dung nó một cách trực tiếp, mà nên được triển khai cùng với các đối tượng khác ở mức cao hơn của Kubernetes để quản lý nó, như Job chẳng hạn. Job sẽ tạo ra một hoặc nhiều Pods, và khi một Pod bị chết thì Pod khác sẽ được bật lên để sẵn sàng thay thế cho nó.

Chúng ta sẽ tìm hiểu vấn đề này trong bài viết tiếp theo. Mời các bạn đón đọc!

Source code của bài này các bạn tham khảo tại đây.

8. Tham khảo