# Sử dụng Node GPU

Bạn có thể sử dụng các máy chủ đám mây GPU làm các node cluster trong UK8s như sau:
- [Tạo một Cluster](#create-a-cluster)
- [Thêm Node](#add-node-nodes)
- [Thêm Host hiện có](#add-existing-host)

## Hướng dẫn về Image
Khi sử dụng các dòng máy chủ đám mây có **card đồ họa hiệu năng chi phí cao** (chẳng hạn như card đồ họa hiệu năng chi phí cao 3, card đồ họa hiệu năng chi phí cao 5, card đồ họa hiệu năng chi phí cao 6) làm node trong cluster UK8s, bạn cần sử dụng image tiêu chuẩn `Ubuntu 20.04 top-end`.
- Card đồ họa hiệu năng chi phí cao hỗ trợ các phân vùng sẵn sàng (availability zones)
  - North China(Wlcb)A
  - East China(Shanghai2)B
  - Beijing 2B

| Card đồ họa | Image | Phiên bản Driver | Phiên bản CUDA |
| ------------------------------- | ------------------ | -------------- | ------------ |
| Card đồ họa hiệu năng chi phí cao (Hiệu năng chi phí cao 3, Hiệu năng chi phí cao 5, Hiệu năng chi phí cao 6) | Ubuntu 20.04 High Value | 	550.120 | 12.4 |
| Card đồ họa không thuộc dòng hiệu năng chi phí cao (chẳng hạn như T4, V100S, P40, v.v.) | Ubuntu 20.04 | 550.90.12 | 12.4 |
| Card đồ họa không thuộc dòng hiệu năng chi phí cao (chẳng hạn như T4, V100S, P40, v.v.) | Centos 7.6 | 450.80.02 | 11.0 |

## Tạo một Cluster
Khi tạo cluster, trong phần cấu hình Node, chọn loại máy là "GPU Type G", sau đó chọn loại card GPU và cấu hình cụ thể.

Lưu ý: Nếu bạn chọn card đồ họa hiệu năng chi phí cao, bạn cần sử dụng image tiêu chuẩn `Ubuntu 20.04 high cost performance` trong phần image của node.

## Thêm Node
Khi thêm một node Node vào cluster hiện có, chọn loại máy là "GPU Type G", sau đó chọn loại card GPU và cấu hình cụ thể.

## Thêm Host hiện có
Thêm máy chủ đám mây GPU đã tạo vào cluster hiện có và chọn image node phù hợp.


## Hướng dẫn sử dụng
1. Theo mặc định, các container không chia sẻ GPU. Mỗi container có thể yêu cầu một hoặc nhiều GPU. Không thể yêu cầu một phần nhỏ của GPU.
2. Node Master của cluster hiện không hỗ trợ các dòng máy GPU.
3. Image tiêu chuẩn do UK8s cung cấp đã được cài đặt sẵn driver nvidia. Ngoài ra, thành phần `nvidia-device-plugin` được cài đặt theo mặc định trong cluster. Sau khi tài nguyên GPU được thêm vào cluster, chúng có thể tự động được nhận diện và đăng ký.
4. Cách xác minh việc sử dụng bình thường của node GPU:
    1. Kiểm tra xem node có tài nguyên `nvidia.com/gpu` hay không.

    2. Chạy ví dụ sau để yêu cầu GPU NVIDIA bằng cách sử dụng loại tài nguyên `nvidia.com/gpu` và kiểm tra xem kết quả log có chính xác hay không.
    ```bash
    $ cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: gpu-pod
    spec:
      restartPolicy: Never
      containers:
        - name: cuda-container
          image: uhub.cloud.vnpt.vn/uk8s/cuda-sample:vectoradd-cuda10.2
          resources:
            limits:
              nvidia.com/gpu: 1 # yêu cầu 1 GPU
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule
    EOF
    ```
    ```bash
    $ kubectl logs gpu-pod
    [Vector addition of 50000 elements]
    Copy input data from the host memory to the CUDA device
    CUDA kernel launch with 196 blocks of 256 threads
    Copy output data from the CUDA device to the host memory
    Test PASSED
    Done
    ```

5. Truyền trực tiếp (Passthrough) tệp NCCL TOPO từ máy chủ đám mây GPU vào Pod

Nếu thử nghiệm hiệu năng NCCL trong pod GPU không đạt kỳ vọng, hãy cân nhắc truyền trực tiếp tệp topology.xml từ máy ảo vào pod. Các thao tác cụ thể như sau:

> Điều kiện tiên quyết: Node được trang bị 8 GPU hiệu năng chi phí cao (ví dụ: GPU hiệu năng chi phí cao 6, GPU hiệu năng chi phí cao 6 Pro, A800, v.v.).

Bước 1: Xác minh sự tồn tại của tệp Topology

Kiểm tra xem tệp virtualTopology.xml có tồn tại trong đường dẫn /var/run/nvidia-topologyd/ trên node GPU hay không:

- Nếu tồn tại, chuyển sang Bước 2.
- Nếu không tồn tại, hãy liên hệ với bộ phận hỗ trợ kỹ thuật để nhận tệp. Sao chép tệp vào đường dẫn /var/run/nvidia-topologyd/virtualTopology.xml trên node GPU, sau đó chuyển sang Bước 2.

Bước 2: Thêm cấu hình vào tệp YAML của Pod GPU

Thêm nội dung sau vào tệp gpu-pod.yaml:

```bash
 containers:
 volumeMounts:
 - mountPath: /var/run/nvidia-topologyd
   name: topologyd
   readOnly: true
 volumes:
 - name: topologyd
   hostPath:
     path: /var/run/nvidia-topologyd
     type: Directory

```

## Nâng cấp Plugin

Nâng cấp nvidia-device-plugin lên phiên bản mới nhất để giải quyết các vấn đề không ổn định của node GPU.

#### Phương pháp nâng cấp

- Phương pháp 1: Sử dụng lệnh kubectl set image để thay đổi phiên bản image của nvidia-device-plugin-daemonset thành v0.14.1:

```bash
$ kubectl set image daemonset nvidia-device-plugin-daemonset -n kube-system nvidia-device-plugin-ctr=uhub.service.ucloud.cn/uk8s/nvidia-k8s-device-plugin:v0.14.1
daemonset.apps/nvidia-device-plugin-daemonset image updated
```

- Phương pháp 2: Sửa đổi tệp yaml của nvidia-device-plugin-daemonset:

1. Thực thi lệnh:

```bash
$ kubectl edit daemonset nvidia-device-plugin-daemonset -n kube-system
```

2. Định vị trường spec.template.spec.containers.image trong cấu hình để xem thông tin image hiện tại:

```bash
- image: uhub.service.ucloud.cn/uk8s/nvidia-k8s-device-plugin:1.0.0-beta4
```

3. Cập nhật image thành uhub.service.ucloud.cn/uk8s/nvidia-k8s-device-plugin:v0.14.1 và lưu các thay đổi.

## Cơ chế Ghim nhân (Core Pinning) trên PHost

Các máy chủ PHost hiện hỗ trợ ghim nhân theo mặc định, giúp cải thiện hiệu suất GPU trong một số kịch bản nhất định.

Để kích hoạt tính năng ghim nhân, hãy xóa tệp /var/lib/kubelet/cpu_manager_state trên node PHost và đảm bảo Kubelet được cấu hình với các tham số sau (tham khảo tài liệu chính thức về Topology Manager Policy và CPU Manager Policy):

```
-cpu-manager-policy=static 
--topology-manager-policy=best-effort 
```

Kiểm tra xem node đã được cấu hình các tham số ghim nhân hay chưa bằng cách chạy lệnh:

```
ps -aux|grep kubelet|grep topology-manager-policy 

```

#### Xác minh ghim nhân thành công

1. Chuẩn bị trước khi tạo Pod thử nghiệm

- Đảm bảo rằng số lượng CPU, Bộ nhớ (Memory) và GPU trong phần limits và requests phải nhất quán, và giá trị CPU phải là số nguyên.
- Đặt spec.nodeName thành địa chỉ IP của PHost để đảm bảo pod được lập lịch chạy trên node này.

Bây giờ chúng ta hãy tạo một Pod

```
apiVersion: v1
kind: Pod
metadata:
  name: dcgmproftester
spec:
  nodeName: "10.60.159.170" # Thay thế bằng IP của node UPHost
  restartPolicy: OnFailure
  containers:
  - name: dcgmproftester12-1
    image: uhub.service.ucloud.cn/uk8s/dcgm:3.3.0
    command: ["/usr/bin/dcgmproftester12"]
    args: ["--no-dcgm-validation", "-t 1004", "-d 3600"] # -d chỉ định thời gian chạy tính bằng giây
    resources: # Sửa đổi các giá trị dựa trên cấu hình máy thực tế
      limits: ## Giữ limits giống hệt requests
        nvidia.com/gpu: 1
        memory: 10Gi 
        cpu: 10
      requests:
        nvidia.com/gpu: 1
        memory: 10Gi
        cpu: 10
    securityContext:
      capabilities:
        add: ["SYS_ADMIN"]
```


2. Sau khi trạng thái của Pod chuyển sang Running, hãy đăng nhập vào node bare metal qua ssh.

3. Nhập lệnh crictl ps để lấy danh sách các container trên node hiện tại. Tìm ID của container vừa tạo dựa trên thời gian tạo hoặc tên container. Sau đó nhập lệnh crictl inspect <container ID> | grep pid để lấy pid của tiến trình.

4. Nhập lệnh taskset -c -p <pid> để lấy thông tin về độ tương thích CPU (CPU affinity):

![img](https://cdn.udelivrs.com/2025/06/8bbdcb5e468d2f6968f97f58aef57741_1751002132630.png)<br>

Chúng ta có thể thấy phạm vi CPU affinity của Pod là 1-5, 65-69 thay vì toàn bộ danh sách CPU, chứng minh rằng việc ghim CPU đã thành công.

5. Bây giờ, nhập lệnh nvidia-smi để lấy thông tin GPU và kiểm tra GPU affinity:

![img](https://cdn.udelivrs.com/2025/06/abc8be6772242d3236060bc42e0e579a_1751002132629.png)<br>

Trong hộp Process ở hình trên, nó ghi lại GPU nào đang chạy. Trong hình, GPU2 đang hoạt động, chứng minh việc liên kết GPU đã thành công.

6. Sử dụng lệnh nvidia-smi topo -m để kiểm tra xem GPU và CPU có nằm trên cùng một node NUMA hay không:

![img](https://cdn.udelivrs.com/2025/06/df5d475ede0ef8315d6fdf167da20cfe_1751002132626.png)<br>

Chúng ta có thể thấy CPU Affinity của GPU3 là 0-7,64-71. Ở bước 4, danh sách CPU Affinity của tiến trình thu được là 1-5,65-69. Điều này chứng minh rằng GPU và CPU tương ứng với cùng một node NUMA.

7. Hướng dẫn bổ sung cho chiến lược Best-effort:

Khi sử dụng chiến lược best-effort, cần phải hiểu trước cấu hình của máy bare metal để xác định các thiết lập tham số cho Pod khi yêu cầu CPU và GPU. Ví dụ, một máy chủ đám mây bare metal có cấu hình như sau:

| CPU  | GPU  | Bộ nhớ (Không ảnh hưởng đến ghim nhân) | Số lượng Node NUMA |
| ---- | ---- | ------------------------------------- | -------------------- |
| 128  | 8    | 1024                                  | 8                    | 

> Các tham số CPU và NUMA có thể được lấy bằng lệnh lscpu. Các tham số GPU có thể được lấy bằng lệnh nvidia-smi topo -m.

Thông qua lệnh lscpu, chúng ta có thể biết mối quan hệ giữa các node NUMA và các nhân CPU:

```
...
NUMA node0 CPU(s):               0-7,64-71
NUMA node1 CPU(s):               8-15,72-79
NUMA node2 CPU(s):               16-23,80-87
NUMA node3 CPU(s):               24-31,88-95
NUMA node4 CPU(s):               32-39,96-103
NUMA node5 CPU(s):               40-47,104-111
NUMA node6 CPU(s):               48-55,112-119
NUMA node7 CPU(s):               56-63,120-127
...
```

Chúng ta có thể tìm hiểu mối quan hệ giữa các node NUMA và các GPU thông qua lệnh nvidia-smi topo -m:

![img](https://cdn.udelivrs.com/2025/06/df5d475ede0ef8315d6fdf167da20cfe_1751004291867.png)<br>

Có thể thấy rằng mỗi node NUMA chứa 16 nhân CPU và 1 GPU. Để đảm bảo cả CPU và GPU đều tương thích (affined) với cùng một node NUMA, cấu hình của chúng ta cần đảm bảo rằng số lượng node NUMA tương thích bởi GPU phải bằng với số lượng node NUMA tương thích bởi CPU; nếu không, có thể dẫn đến các node tương thích không nhất quán. Dưới đây là các tình huống có thể đạt được sự tương thích (affinity):

| GPU  | CPU                                   |
| ---- | ------------------------------------- |
| 1    | 1 ~ 16                                |
| n    | > 16 * (n - 1) và ≤ 16 * n | 

> Ở đây, các giá trị 16 và 1 được lấy bằng cách kiểm tra cấu hình bằng phương pháp được mô tả ở trên. Cấu hình của các máy khác nhau là khác nhau, vì vậy bạn cần tự kiểm tra.

Ngoại trừ các tỷ lệ GPU/CPU nêu trên, các trường hợp khác không thể đạt được sự tương thích NUMA (NUMA affinity).

Để biết thêm chi tiết, vui lòng tham khảo tài liệu chính thức.