成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

探索Kubernetes與AI的結合:PyTorch訓練任務在k8s上調度實踐

人工智能
通過搭建本地的k8s GPU環境,可以方便的進行AI相關的開發和測試,也能充分利用閑置的筆記本GPU性能。利用kueue、karmada、kuberay和ray等框架,讓GPU等異構算力調度在云原生成為可能。

概述

Kubernetes的核心優勢在于其能夠提供一個可擴展、靈活且高度可配置的平臺,使得應用程序的部署、擴展和管理變得前所未有的簡單。通用計算能力方面的應用已經相對成熟,云原生化的應用程序、數據庫和其他服務可以輕松部署在Kubernetes環境中,實現高可用性和彈性。

然而,當涉及到異構計算資源時,情形便開始變得復雜。異構計算資源如GPU、FPGA和NPU,雖然能夠提供巨大的計算優勢,尤其是在處理特定類型的計算密集型任務時,但它們的集成和管理卻不像通用計算資源那樣簡單。由于硬件供應商提供的驅動和管理工具差異較大,Kubernetes在統一調度和編排這些資源方面還存在一些局限性。這不僅影響了資源的利用效率,也給開發者帶來了額外的管理負擔。

下面分享下如何在個人筆記本電腦上完成K8s GPU集群的搭建,并使用kueue、kubeflow、karmada在具有GPU節點的k8s集群上提交pytorch的訓練任務。

k8s支持GPU

  1. kubernetes對于GPU的支持是通過設備插件的方式來實現,需要安裝GPU廠商的設備驅動,通過POD調用GPU能力。
  2. Kind、Minikube、K3d等常用開發環境集群構建工具對于GPU的支持也各不相同,Kind暫不支持GPU,Minikube和K3d支持Linux環境下的NVIDIA的GPU

RTX3060搭建具有GPU的K8s

GPU K8s

先決條件

  • Go 版本 v1.20+
  • kubectl 版本 v1.19+
  • Minikube 版本 v1.24.0+
  • Docker 版本v24.0.6+
  • NVIDIA Driver 最新版本
  • NVIDIA Container Toolkit 最新版本

備注:

  • ubuntu 系統的 RTX3060+顯卡(不能是虛擬機系統,除非你的虛擬機支持pve或則esxi顯卡直通功能), windows的wsl 是不支持的,因為wsl的Linux內核是一個自定義的內核,里面缺失很多內核模塊,導致NVIDIA的驅動調用有問題
  • 需要Github、Google、Docker的代碼和倉庫訪問能力

GPU Docker

完成以上操作后,確認Docker具備GPU的調度能力,可以通過如下的方式來進行驗證

  1. 創建如下的docker compose 文件
services:
  test:
    image: nvidia/cuda:12.3.1-base-ubuntu20.04
    command: nvidia-smi
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
  1. 使用Docker啟動cuda任務
docker compose up
Creating network "gpu_default" with the default driver
Creating gpu_test_1 ... done
Attaching to gpu_test_1    
test_1  | +-----------------------------------------------------------------------------+
test_1  | | NVIDIA-SMI 450.80.02    Driver Version: 450.80.02    CUDA Version: 11.1     |
test_1  | |-------------------------------+----------------------+----------------------+
test_1  | | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
test_1  | | Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
test_1  | |                               |                      |               MIG M. |
test_1  | |===============================+======================+======================|
test_1  | |   0  Tesla T4            On   | 00000000:00:1E.0 Off |                    0 |
test_1  | | N/A   23C    P8     9W /  70W |      0MiB / 15109MiB |      0%      Default |
test_1  | |                               |                      |                  N/A |
test_1  | +-------------------------------+----------------------+----------------------+
test_1  |                                                                                
test_1  | +-----------------------------------------------------------------------------+
test_1  | | Processes:                                                                  |
test_1  | |  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
test_1  | |        ID   ID                                                   Usage      |
test_1  | |=============================================================================|
test_1  | |  No running processes found                                                 |
test_1  | +-----------------------------------------------------------------------------+
gpu_test_1 exited with code 0

GPU Minikube

配置Minikube,啟動kubernetes集群

minikube start --driver docker --container-runtime docker --gpus all

驗證集群的GPU能力

  1. 確認節點具備GPU信息
kubectl describe node minikube
...
Capacity:
  nvidia.com/gpu: 1
...
  1. 測試在集群中執行CUDA
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  restartPolicy: Never
  containers:
    - name: cuda-container
      image: nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda10.2
      resources:
        limits:
          nvidia.com/gpu: 1 # requesting 1 GPU
  tolerations:
  - key: nvidia.com/gpu
    operator: Exists
    effect: NoSchedule
EOF
$ 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

使用kueue提交pytorch訓練任務

kueue簡介

kueue是k8s特別興趣小組(SIG)的一個開源項目,是一個基于 Kubernetes 的任務隊列管理系統,旨在簡化和優化 Kubernetes 中的作業管理。 主要具備以下功能:

  • 作業管理:支持基于優先級的作業隊列,提供不同的隊列策略,如StrictFIFO和BestEffortFIFO。
  • 資源管理:支持資源的公平分享和搶占,以及不同租戶之間的資源管理策略。
  • 動態資源回收:一種釋放資源配額的機制,隨著作業的完成而動態釋放資源。
  • 資源靈活性:在 ClusterQueue 和 Cohort 中支持資源的借用或搶占。
  • 內置集成:內置支持常見的作業類型,如 BatchJob、Kubeflow 訓練作業、RayJob、RayCluster、JobSet 等。
  • 系統監控:內置 Prometheus 指標,用于監控系統狀態。
  • 準入檢查:一種機制,用于影響工作負載是否可以被接受。
  • 高級自動縮放支持:與 cluster-autoscaler 的 provisioningRequest 集成,通過準入檢查進行管理。
  • 順序準入:一種簡單的全或無調度實現。
  • 部分準入:允許作業以較小的并行度運行,基于可用配額。

kueue的架構圖如下:

圖片圖片

通過拓展workload來支持BatchJob、Kubeflow 訓練作業、RayJob、RayCluster、JobSet 等作業任務,通過ClusterQueue來共享LocalQueue資源,任務最終提交到LocalQueue進行調度和執行,而不同的ClusterQueue可以通過Cohort進行資源共享和通信,通過Cohort->ClusterQueue->LocalQueue->Node實現不同層級的資源共享已支持AI、ML等Ray相關的job在k8s集群中調度。 在kueue中區分管理員用戶和普通用戶,管理員用戶負責管理ResourceFlavor、ClusterQueue、LocalQueue等資源,以及管理資源池的配額(quota)。普通用戶負責提批處理任務或者各類的Ray任務。

運行PyTorch訓練任務

安裝kueue

需要k8s 1.22+,使用如下的命令安裝

kubectl apply --server-side -f https://github.com/kubernetes-sigs/kueue/releases/download/v0.6.0/manifests.yaml

配置集群配額

git clone https://github.com/kubernetes-sigs/kueue.git && cd kueue
kubectl apply -f examples/admin/single-clusterqueue-setup.yaml

其實不安裝kueue也是能夠提交Pytorch的訓練任務,因為這個PytorchJob是kubeflow traning-operator的一個CRD,但是安裝kueue的好處是,他可以支持更多任務。

除了kubeflow的任務,還可以支持kuberay的任務,并且它內置了管理員角色,方便對于集群的配置和集群的資源做限額和管理,支持優先級隊列和任務搶占,更好的支持AI、ML等任務的調度和管理。上面安裝的集群配額就是設置任務的限制,避免一些負載過高的任務提交,在任務執行前快速失敗。

安裝kubeflow的training-operator

kubectl apply -k "github.com/kubeflow/training-operator/manifests/overlays/standalone"

運行FashionMNIST的訓練任務

FashionMNIST 數據集是一個用于圖像分類任務的常用數據集,類似于經典的 MNIST 數據集,但是它包含了更加復雜的服裝類別。

  • FashionMNIST 數據集包含了 10 個類別的服裝圖像,每個類別包含了 6,000 張訓練圖像和 1,000 張測試圖像,共計 60,000 張訓練圖像和 10,000 張測試圖像。
  • 每張圖像都是 28x28 像素的灰度圖像,表示了不同類型的服裝,如 T 恤、褲子、襯衫、裙子等。

在kueue上提交PyTorchJob類型的任務,為了能夠保存訓練過程中的日志和結果,我們需要使用openebs的hostpath來將訓練過程的數據保存到節點上,因為任務訓練結束后,不能登錄到節點查看。所以創建如下的資源文件

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pytorch-results-pvc
spec:
  storageClassName: openebs-hostpath
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: "kubeflow.org/v1"
kind: PyTorchJob
metadata:
  name: pytorch-simple
  namespace: kubeflow
spec:
  pytorchReplicaSpecs:
    Master:
      replicas: 1
      restartPolicy: OnFailure
      template:
        spec:
          containers:
            - name: pytorch
              image: pytorch-mnist:2.2.1-cuda12.1-cudnn8-runtime
              imagePullPolicy: IfNotPresent
              command:
                - "python3"
                - "/opt/pytorch-mnist/mnist.py"
                - "--epochs=10"
                - "--batch-size"
                - "32"
                - "--test-batch-size"
                - "64"
                - "--lr"
                - "0.01"
                - "--momentum"
                - "0.9"
                - "--log-interval"
                - "10"
                - "--save-model"
                - "--log-path"
                - "/results/master.log"
              volumeMounts:
              - name: result-volume
                mountPath: /results
          volumes:
          - name: result-volume
            persistentVolumeClaim:
              claimName: pytorch-results-pvc
    Worker:
      replicas: 1
      restartPolicy: OnFailure
      template:
        spec:
          containers:
            - name: pytorch
              image: pytorch-mnist:2.2.1-cuda12.1-cudnn8-runtime
              imagePullPolicy: IfNotPresent
              command:
                - "python3"
                - "/opt/pytorch-mnist/mnist.py"
                - "--epochs=10"
                - "--batch-size"
                - "32"
                - "--test-batch-size"
                - "64"
                - "--lr"
                - "0.01"
                - "--momentum"
                - "0.9"
                - "--log-interval"
                - "10"
                - "--save-model"
                - "--log-path"
                - "/results/worker.log"
              volumeMounts:
              - name: result-volume
                mountPath: /results
          volumes:
          - name: result-volume
            persistentVolumeClaim:
              claimName: pytorch-results-pvc

其中pytorch-mnist:v1beta1-45c5727是一個在pytorch上運行CNN訓練任務的代碼,具體的代碼如下:

from __future__ import print_function

import argparse
import logging
import os

from torchvision import datasets, transforms
import torch
import torch.distributed as dist
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

WORLD_SIZE = int(os.environ.get("WORLD_SIZE", 1))


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 20, 5, 1)
        self.conv2 = nn.Conv2d(20, 50, 5, 1)
        self.fc1 = nn.Linear(4*4*50, 500)
        self.fc2 = nn.Linear(500, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2, 2)
        x = x.view(-1, 4*4*50)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)


def train(args, model, device, train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        if batch_idx % args.log_interval == 0:
            msg = "Train Epoch: {} [{}/{} ({:.0f}%)]\tloss={:.4f}".format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item())
            logging.info(msg)
            niter = epoch * len(train_loader) + batch_idx


def test(args, model, device, test_loader, epoch):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            test_loss += F.nll_loss(output, target, reductinotallow="sum").item()  # sum up batch loss
            pred = output.max(1, keepdim=True)[1]  # get the index of the max log-probability
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    logging.info("{{metricName: accuracy, metricValue: {:.4f}}};{{metricName: loss, metricValue: {:.4f}}}\n".format(
        float(correct) / len(test_loader.dataset), test_loss))


def should_distribute():
    return dist.is_available() and WORLD_SIZE > 1


def is_distributed():
    return dist.is_available() and dist.is_initialized()


def main():
    # Training settings
    parser = argparse.ArgumentParser(descriptinotallow="PyTorch MNIST Example")
    parser.add_argument("--batch-size", type=int, default=64, metavar="N",
                        help="input batch size for training (default: 64)")
    parser.add_argument("--test-batch-size", type=int, default=1000, metavar="N",
                        help="input batch size for testing (default: 1000)")
    parser.add_argument("--epochs", type=int, default=10, metavar="N",
                        help="number of epochs to train (default: 10)")
    parser.add_argument("--lr", type=float, default=0.01, metavar="LR",
                        help="learning rate (default: 0.01)")
    parser.add_argument("--momentum", type=float, default=0.5, metavar="M",
                        help="SGD momentum (default: 0.5)")
    parser.add_argument("--no-cuda", actinotallow="store_true", default=False,
                        help="disables CUDA training")
    parser.add_argument("--seed", type=int, default=1, metavar="S",
                        help="random seed (default: 1)")
    parser.add_argument("--log-interval", type=int, default=10, metavar="N",
                        help="how many batches to wait before logging training status")
    parser.add_argument("--log-path", type=str, default="",
                        help="Path to save logs. Print to StdOut if log-path is not set")
    parser.add_argument("--save-model", actinotallow="store_true", default=False,
                        help="For Saving the current Model")

    if dist.is_available():
        parser.add_argument("--backend", type=str, help="Distributed backend",
                            choices=[dist.Backend.GLOO, dist.Backend.NCCL, dist.Backend.MPI],
                            default=dist.Backend.GLOO)
    args = parser.parse_args()

    # Use this format (%Y-%m-%dT%H:%M:%SZ) to record timestamp of the metrics.
    # If log_path is empty print log to StdOut, otherwise print log to the file.
    if args.log_path == "":
        logging.basicConfig(
            format="%(asctime)s %(levelname)-8s %(message)s",
            datefmt="%Y-%m-%dT%H:%M:%SZ",
            level=logging.DEBUG)
    else:
        logging.basicConfig(
            format="%(asctime)s %(levelname)-8s %(message)s",
            datefmt="%Y-%m-%dT%H:%M:%SZ",
            level=logging.DEBUG,
            filename=args.log_path)

    use_cuda = not args.no_cuda and torch.cuda.is_available()
    if use_cuda:
        print("Using CUDA")

    torch.manual_seed(args.seed)

    device = torch.device("cuda" if use_cuda else "cpu")

    if should_distribute():
        print("Using distributed PyTorch with {} backend".format(args.backend))
        dist.init_process_group(backend=args.backend)

    kwargs = {"num_workers": 1, "pin_memory": True} if use_cuda else {}

    train_loader = torch.utils.data.DataLoader(
        datasets.FashionMNIST("./data",
                              train=True,
                              download=True,
                              transform=transforms.Compose([
                                  transforms.ToTensor()
                              ])),
        batch_size=args.batch_size, shuffle=True, **kwargs)

    test_loader = torch.utils.data.DataLoader(
        datasets.FashionMNIST("./data",
                              train=False,
                              transform=transforms.Compose([
                                  transforms.ToTensor()
                              ])),
        batch_size=args.test_batch_size, shuffle=False, **kwargs)

    model = Net().to(device)

    if is_distributed():
        Distributor = nn.parallel.DistributedDataParallel if use_cuda \
            else nn.parallel.DistributedDataParallelCPU
        model = Distributor(model)

    optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)

    for epoch in range(1, args.epochs + 1):
        train(args, model, device, train_loader, optimizer, epoch)
        test(args, model, device, test_loader, epoch)

    if (args.save_model):
        torch.save(model.state_dict(), "mnist_cnn.pt")


if __name__ == "__main__":
    main()

將訓練任務提交到k8s集群

kubectl apply -f sample-pytorchjob.yaml

提交成功后會出現兩個訓練任務,分別是master和worker的訓練任務,如下:

?  ~ kubectl get po
NAME                      READY   STATUS    RESTARTS   AGE
pytorch-simple-master-0   1/1     Running   0          5m5s
pytorch-simple-worker-0   1/1     Running   0          5m5s

再查看宿主機的顯卡運行情況,發現能夠明顯聽到集群散熱的聲音,運行nvida-smi可以看到有兩個Python任務在執行,等待執行完后,會生成模型文件mnist_cnn.pt。

?  ~ nvidia-smi
Mon Mar  4 10:18:39 2024
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.161.07             Driver Version: 535.161.07   CUDA Version: 12.2     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce RTX 3060 ...    Off | 00000000:01:00.0 Off |                  N/A |
| N/A   39C    P0              24W /  80W |    753MiB /  6144MiB |      1%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|    0   N/A  N/A      1674      G   /usr/lib/xorg/Xorg                          219MiB |
|    0   N/A  N/A      1961      G   /usr/bin/gnome-shell                         47MiB |
|    0   N/A  N/A      3151      G   gnome-control-center                          2MiB |
|    0   N/A  N/A      4177      G   ...irefox/3836/usr/lib/firefox/firefox      149MiB |
|    0   N/A  N/A     14476      C   python3                                     148MiB |
|    0   N/A  N/A     14998      C   python3                                     170MiB |
+---------------------------------------------------------------------------------------+

在提交和執行任務的時候,要注意cuda的版本和pytorch的版本要保持對應,官方demo中的dockerfile是這樣的

FROM pytorch/pytorch:1.0-cuda10.0-cudnn7-runtime

ADD examples/v1beta1/pytorch-mnist /opt/pytorch-mnist
WORKDIR /opt/pytorch-mnist

# Add folder for the logs.
RUN mkdir /katib

RUN chgrp -R 0 /opt/pytorch-mnist \
  && chmod -R g+rwX /opt/pytorch-mnist \
  && chgrp -R 0 /katib \
  && chmod -R g+rwX /katib

ENTRYPOINT ["python3", "/opt/pytorch-mnist/mnist.py"]

這個要求你要使用pytorch1.0和cuda10的版本進行訓練,而我們實際的使用的cuda12,所以直接用這個基礎鏡像去構建是不行,任務會一致處于運行中,永遠結束不了,為了能夠避免每次重復下載mnist的數據集,我們需要提前下載然后將數據集打包到容器里面,所以修改后的Dockerfile如下:

FROM pytorch/pytorch:2.2.1-cuda12.1-cudnn8-runtime

ADD . /opt/pytorch-mnist
WORKDIR /opt/pytorch-mnist

# Add folder for the logs.
RUN mkdir /katib

RUN chgrp -R 0 /opt/pytorch-mnist \
  && chmod -R g+rwX /opt/pytorch-mnist \
  && chgrp -R 0 /katib \
  && chmod -R g+rwX /katib

ENTRYPOINT ["python3", "/opt/pytorch-mnist/mnist.py"]

使用最終的訓練結束后mnist_cnn.pt模型文件,進行模型預測和測試得到的結果如下:{metricName: accuracy, metricValue: 0.9039};{metricName: loss, metricValue: 0.2756}, 即這個模型的準確性為90.39%,模型損失值為0.2756,說明我們訓練的模型在FashionMNIST 數據集上表現良好,在訓練過程中epoch參數比較重要,它代表訓練的輪次,過小會出現效果不好,過大會出現過擬合問題,在測試的時候我們可以適當調整這個參數來控制模型訓練運行的時間。 

通過kueue通過webhook的方式對于的進行AI、ML等GPU任務進行準入控制和資源限制,提供租戶隔離的概念,為k8s對于GPU的支持提供了根據豐富的場景。如果筆記本的顯卡能力夠強,可以將chatglm等開源的大模型部署到k8s集群中,從而搭建自己個人離線專屬的大模型服務。

karmada多集群提交pytorch訓練任務

創建多集群k8s

在多集群的管控上,我們可以使用karamda來實現管理,其中member2作為控制面主集群,member3、member4作為子集群。在完成minikube的nvidia的GPU配置后,使用如下的命令創建3個集群。

docker network create --driver=bridge --subnet=xxx.xxx.xxx.0/24 --ip-range=xxx.xxx.xxx.0/24 minikube-net
minikube start --driver docker --cpus max --memory max --container-runtime docker --gpus all --network minikube-net --subnet='xxx.xxx.xxx.xxx' --mount-string="/run/udev:/run/udev" --mount -p member2 --static-ip='xxx.xxx.xxx.xxx'
minikube start --driver docker --cpus max --memory max --container-runtime docker --gpus all --network minikube-net --subnet='xxx.xxx.xxx.xxx' --mount-string="/run/udev:/run/udev" --mount -p member3 --static-ip='xxx.xxx.xxx.xxx'
minikube start --driver docker --cpus max --memory max --container-runtime docker --gpus all --network minikube-net --subnet='xxx.xxx.xxx.xxx' --mount-string="/run/udev:/run/udev" --mount -p member4 --static-ip='xxx.xxx.xxx.xxx'
?  ~ minikube profile list
|---------|-----------|---------|-----------------|------|---------|---------|-------|--------|
| Profile | VM Driver | Runtime |      IP         | Port | Version | Status  | Nodes | Active |
|---------|-----------|---------|-----------------|------|---------|---------|-------|--------|
| member2 | docker    | docker  | xxx.xxx.xxx.xxx | 8443 | v1.28.3 | Running |     1 |        |
| member3 | docker    | docker  | xxx.xxx.xxx.xxx | 8443 | v1.28.3 | Running |     1 |        |
| member4 | docker    | docker  | xxx.xxx.xxx.xxx | 8443 | v1.28.3 | Running |     1 |        |
|---------|-----------|---------|-----------------|------|---------|---------|-------|--------|

在3個集群分別安裝Training Operator、karmada,并且需要在karmada的控制面安裝Training Operator,這樣才能在控制面提交pytorchjob的任務。由于同一個pytorch任務分布在不同的集群在服務發現和master、worker交互通信會存在困難,所以我們這邊只演示將同一個pytorch任務提交到同一個集群,通過kosmos的控制面實現將多個pytorch任務調度到不同的集群完成訓練。 在karmada的控制面上創建訓練任務

apiVersion: "kubeflow.org/v1"
kind: PyTorchJob
metadata:
  name: pytorch-simple
  namespace: kubeflow
spec:
  pytorchReplicaSpecs:
    Master:
      replicas: 1
      restartPolicy: OnFailure
      template:
        spec:
          containers:
            - name: pytorch
              image: pytorch-mnist:2.2.1-cuda12.1-cudnn8-runtime
              imagePullPolicy: IfNotPresent
              command:
                - "python3"
                - "/opt/pytorch-mnist/mnist.py"
                - "--epochs=30"
                - "--batch-size"
                - "32"
                - "--test-batch-size"
                - "64"
                - "--lr"
                - "0.01"
                - "--momentum"
                - "0.9"
                - "--log-interval"
                - "10"
                - "--save-model"
                - "--log-path"
                - "/opt/pytorch-mnist/master.log"
    Worker:
      replicas: 1
      restartPolicy: OnFailure
      template:
        spec:
          containers:
            - name: pytorch
              image: pytorch-mnist:2.2.1-cuda12.1-cudnn8-runtime
              imagePullPolicy: IfNotPresent
              command:
                - "python3"
                - "/opt/pytorch-mnist/mnist.py"
                - "--epochs=30"
                - "--batch-size"
                - "32"
                - "--test-batch-size"
                - "64"
                - "--lr"
                - "0.01"
                - "--momentum"
                - "0.9"
                - "--log-interval"
                - "10"
                - "--save-model"
                - "--log-path"
                - "/opt/pytorch-mnist/worker.log"

在karmada的控制面上創建傳播策略

apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
  name: pytorchjob-propagation
  namespace: kubeflow
spec:
  resourceSelectors:
    - apiVersion: kubeflow.org/v1
      kind: PyTorchJob
      name: pytorch-simple
      namespace: kubeflow
  placement:
    clusterAffinity:
      clusterNames:
        - member3
        - member4
    replicaScheduling:
      replicaDivisionPreference: Weighted
      replicaSchedulingType: Divided
      weightPreference:
        staticWeightList:
          - targetCluster:
              clusterNames:
                - member3
            weight: 1
          - targetCluster:
              clusterNames:
                - member4
            weight: 1

然后我們就可以看到這個訓練任務成功的提交到member3和member4的子集群上執行任務

?  pytorch kubectl karmada  --kubeconfig ~/karmada-apiserver.config  get po -n kubeflow
NAME                                 CLUSTER   READY   STATUS      RESTARTS   AGE
pytorch-simple-master-0              member3   0/1     Completed   0          7m51s
pytorch-simple-worker-0              member3   0/1     Completed   0          7m51s
training-operator-64c768746c-gvf9n   member3   1/1     Running     0          165m
pytorch-simple-master-0              member4   0/1     Completed   0          7m51s
pytorch-simple-worker-0              member4   0/1     Completed   0          7m51s
training-operator-64c768746c-nrkdv   member4   1/1     Running     0          168m

總結

通過搭建本地的k8s GPU環境,可以方便的進行AI相關的開發和測試,也能充分利用閑置的筆記本GPU性能。利用kueue、karmada、kuberay和ray等框架,讓GPU等異構算力調度在云原生成為可能。目前只是在單k8s集群完成訓練任務的提交和運行,在實際AI、ML或者大模型的訓練其實更加復雜,組網和技術架構也需要進行精心的設計。要實現千卡、萬卡的在k8s集群的訓練和推理解決包括但不僅限于

  • 網絡通信性能:傳統的數據中心網絡一般是10Gbps,這個在大模型訓練和推理中是捉襟見肘的,所以需要構建RDMA網絡(Remote Direct Memory Access)
  • GPU調度和配置:多云多集群場景下,如何進行GPU的調度和管理
  • 監控和調試:如何進行有效地監控和調試訓練任務,以及對異常情況進行處理和服務恢復

參考資料

1. [Go](https://go.dev/)

2. [Docker](https://docker.com)

3. [minikube](https://minikube.sigs.k8s.io/docs/tutorials/nvidia/)

4. [nvidia](https://docs.nvidia.com/datacenter/tesla/tesla-installation-notes/index.html)

5. [kubernetes](https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/)

6. [kueue](https://github.com/kubernetes-sigs/kueue)

7. [kubeflow](https://github.com/kubeflow/training-operator)

8. [ray](https://github.com/ray-project/ray)

9. [kuberay](https://github.com/ray-project/kuberay)

10. [karmada](https://github.com/karmada-io/karmada)

11. [kind](https://kind.sigs.k8s.io/)

12. [k3s](https://github.com/k3s-io/k3s)

13. [k3d](https://github.com/k3d-io/k3d)

責任編輯:武曉燕 來源: 暢聊云原生
相關推薦

2023-09-07 08:58:36

K8s多集群

2023-11-06 07:16:22

WasmK8s模塊

2021-11-29 08:48:00

K8S KubernetesAirflow

2024-03-01 19:59:17

2022-09-05 08:26:29

Kubernetes標簽

2021-06-09 13:22:54

WSLWindowsLinux

2024-06-26 00:22:35

2020-12-28 09:56:45

K8sDevOps容器技術

2025-07-02 03:00:00

2025-03-26 00:00:00

k8m工具Kubernete

2025-04-16 03:25:00

2022-04-02 09:57:51

技術京東實踐

2022-04-22 13:32:01

K8s容器引擎架構

2023-11-07 08:23:05

2023-12-25 07:35:40

數據集成FlinkK8s

2023-09-24 22:47:42

Kubernetes親和性

2023-12-01 15:46:01

Kubernetes容器

2023-09-11 14:21:00

2023-11-24 17:51:18

Kubernetes云原生

2025-02-18 00:00:00

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品福利视频 | 中文字幕一区二区三区乱码图片 | 欧美一二三四成人免费视频 | 一区二区在线不卡 | 欧美久久久久久久 | 欧美激情 一区 | 日本不卡高字幕在线2019 | 欧美精品一二区 | 一区二区三区四区在线视频 | 四虎影音| 亚洲三区在线观看 | 亚洲精品影院 | 99精品电影| 亚洲福利| 亚洲第一视频网站 | 黄色网址大全在线观看 | 自拍偷拍3p | 久久蜜桃av一区二区天堂 | 国产一级免费视频 | 国产亚洲精品综合一区 | 日本精品视频一区二区 | 一区二区国产精品 | 国产在线精品一区二区 | 色视频网站在线观看 | 国产成人99久久亚洲综合精品 | 欧美精品一区在线 | 91一区二区在线观看 | 密色视频| 精品国产免费人成在线观看 | 久久精品青青大伊人av | 97精品国产 | 三级视频网站 | 国产成人精品一区二三区在线观看 | 日本免费视频 | 久久久91精品国产一区二区精品 | 理论片午午伦夜理片影院 | 国产精品视频观看 | 国产中文区二幕区2012 | 久久久成 | 一级看片免费视频囗交动图 | 色综合一区二区 |