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:
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.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:
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:
$ 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.
docker tag docker-model-batch-infer:latest tiensu/ml-model-batch-refer:latest
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