Kubernetes 教程:在 Containerd 容器中使用 GPU

原文連接:fuckcloudnative.io/posts/add-n…php

前兩天鬧得沸沸揚揚的事件不知道你們有沒有據說,Google 居然將 Docker 踢出了 Kubernetes 的羣聊,不帶它玩了。。。linux

具體的解釋請看《據說 K8s 要甩了 Docker 了》,我這裏簡單描述下,Kubernetes 是經過 CRI 來對接容器運行時的,而 Docker 自己是沒有實現 CRI 的,因此 Kubernetes 內置了一個 「爲 Docker 提供 CRI 支持」 的 dockershim 組件。如今 Kubernetes 宣佈再也不維護這個組件了,大概的意思就是:Docker 雖然好用,但那是對人來講的,Kubernetes 又不是人,不須要那些花裏胡哨的東西!git

Kubernetes 這是話裏有話,說白了就是:我特麼之前爲了兼容你,我集成在我本身這裏,如今我就想本身單純一點,要麼你本身寫 CRI 的接口 要麼就再見。github

衆 YAML 工程師直呼 Containerd 真香!docker

下面進入今天的主題,Kubernetes 具備對機器的資源進行分配和使用的能力,好比能夠指定容器最多使用多少內存以及使用多少 CPU 計算資源。那麼問題來了,通常來講容器就是使用 CPU 和內存資源,那麼對於須要使用顯卡的 Pod,Kubernetes 也可以支持嗎?答案固然是能夠啦!目前 Kubernetes 不只支持容器請求 GPU 資源,還支持請求幾塊顯卡的 GPU 資源,這使得 Kubernetes 在深度學習和區塊鏈等場景下也有了用武之地。api

關於 Kubernetes 集羣中 Docker 如何使用 GPU,Kubernetes 的官方文檔已經說的很清楚了,網上也有鋪天蓋地的博客手把手教你怎麼作。至於以 Containerd 做爲容器運行時的集羣如何使用 GPU,網上還找不到一篇像樣的文檔來告訴你們怎麼作,今天我就來作吃螃蟹的第一人。bash

要想在容器裏使用 GPU,本質上就是咱們要在容器裏能看到而且使用宿主機上的顯卡,全部的步驟都是圍繞這個來作的。固然,本文不會涉及如何安裝 Containerd,也不會涉及如何安裝 Kubernetes,若是這些都搞不定,建議不要往下看。markdown

1. Nvidia 驅動

某些命令以 Ubuntu 做爲示例。 首先宿主機上必現安裝 Nvidia 驅動。這裏推薦從 Nvidia 官網下載腳本安裝,安裝和卸載都比較方便而且適用於任何 Linux 發行版,包括 CentOS,Ubuntu 等。 NVIDIA Telsa GPU 的 Linux 驅動在安裝過程當中須要編譯 kernel module,系統需提早安裝 gcc 和編譯 Linux Kernel Module 所依賴的包,例如 kernel-devel-$(uname -r) 等。架構

  • 安裝 gcc 和 kernel-dev(若是沒有) sudo apt install gcc kernel-dev -yapp

  • 訪問官網下載。

  • 選擇操做系統和安裝包,並單擊【SEARCH】搜尋驅動,選擇要下載的驅動版本

  • 下載對應版本安裝腳本 在宿主機上執行:

    $ wget  https://www.nvidia.com/content/DriverDownload-March2009/confirmation.php?url=/tesla/450.80.02/NVIDIA-Linux-x86_64-450.80.02.run&lang=us&type=Tesla
    複製代碼
  • 安裝 執行腳本安裝:

    $ chmod +x NVIDIA-Linux-x86_64-450.80.02.run && ./NVIDIA-Linux-x86_64-450.80.02.run
    複製代碼
  • 驗證 使用以下命令驗證是否安裝成功 nvidia-smi 若是輸出相似下圖則驅動安裝成功。

2. CUDA 驅動

CUDA(Compute Unified Device Architecture)是顯卡廠商 NVIDIA 推出的運算平臺。CUDA™ 是一種由 NVIDIA 推出的通用並行計算架構,該架構使 GPU 可以解決複雜的計算問題。它包含了 CUDA 指令集架構(ISA)以及 GPU 內部的並行計算引擎。 這裏安裝的方式和顯卡驅動安裝相似。

  • 訪問官網下載

  • 下載對應版本以下圖

  • 配置環境變量

    $ echo 'export PATH=/usr/local/cuda/bin:$PATH' | sudo tee /etc/profile.d/cuda.sh
    $ source /etc/profile
    複製代碼

    3. nvidia-container-runtime

    nvidia-container-runtime 是在 runc 基礎上多實現了 nvidia-container-runime-hook(如今叫 nvidia-container-toolkit),該 hook 是在容器啓動後(Namespace已建立完成),容器自定義命令(Entrypoint)啓動前執行。當檢測到 NVIDIA_VISIBLE_DEVICES 環境變量時,會調用 libnvidia-container 掛載 GPU Device 和 CUDA Driver。若是沒有檢測到 NVIDIA_VISIBLE_DEVICES 就會執行默認的 runc。

    下面分兩步安裝:

    先設置 repository 和 GPG key:

    $ curl -s -L https://nvidia.github.io/nvidia-container-runtime/gpgkey | sudo apt-key add -
    
    $ curl -s -L https://nvidia.github.io/nvidia-container-runtime/$(. /etc/os-release;echo $ID$VERSION_ID)/nvidia-container-runtime.list | sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list
    複製代碼

    安裝:

    $ apt install nvidia-container-runtime -y
    複製代碼

    配置 Containerd 使用 Nvidia container runtime

    若是 /etc/containerd 目錄不存在,就先建立它:

    $ mkdir /etc/containerd
    複製代碼

    生成默認配置:

    $ containerd config default > /etc/containerd/config.toml
    複製代碼

    Kubernetes 使用設備插件(Device Plugins) 來容許 Pod 訪問相似 GPU 這類特殊的硬件功能特性,但前提是默認的 OCI runtime 必須改爲 nvidia-container-runtime,須要修改的內容以下:

    /etc/containerd/config.toml

    ...
        [plugins."io.containerd.grpc.v1.cri".containerd]
          snapshotter = "overlayfs"
          default_runtime_name = "runc"
          no_pivot = false
    ...
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
            [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
              runtime_type = "io.containerd.runtime.v1.linux" # 將此處 runtime_type 的值改爲 io.containerd.runtime.v1.linux
    ...
      [plugins."io.containerd.runtime.v1.linux"]
        shim = "containerd-shim"
        runtime = "nvidia-container-runtime" # 將此處 runtime 的值改爲 nvidia-container-runtime
    ...
    複製代碼

    重啓 containerd 服務:

    $ systemctl restart containerd
    複製代碼

    4. 部署 NVIDIA GPU 設備插件

    一條命令解決戰鬥:

    $ kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.7.1/nvidia-device-plugin.yml
    複製代碼

    查看日誌:

    $ kubectl -n kube-system logs nvidia-device-plugin-daemonset-xxx
    2020/12/04 06:30:28 Loading NVML
    2020/12/04 06:30:28 Starting FS watcher.
    2020/12/04 06:30:28 Starting OS watcher.
    2020/12/04 06:30:28 Retreiving plugins.
    2020/12/04 06:30:28 Starting GRPC server for 'nvidia.com/gpu'
    2020/12/04 06:30:28 Starting to serve 'nvidia.com/gpu' on /var/lib/kubelet/device-plugins/nvidia-gpu.sock
    2020/12/04 06:30:28 Registered device plugin for 'nvidia.com/gpu' with Kubelet
    複製代碼

    能夠看到設備插件部署成功了。在 Node 上面能夠看到設備插件目錄下的 socket:

    $ ll /var/lib/kubelet/device-plugins/
    total 12
    drwxr-xr-x 2 root root 4096 Dec  4 01:30 ./
    drwxr-xr-x 8 root root 4096 Dec  3 05:05 ../
    -rw-r--r-- 1 root root    0 Dec  4 01:11 DEPRECATION
    -rw------- 1 root root 3804 Dec  4 01:30 kubelet_internal_checkpoint
    srwxr-xr-x 1 root root    0 Dec  4 01:11 kubelet.sock=
    srwxr-xr-x 1 root root    0 Dec  4 01:11 kubevirt-kvm.sock=
    srwxr-xr-x 1 root root    0 Dec  4 01:11 kubevirt-tun.sock=
    srwxr-xr-x 1 root root    0 Dec  4 01:11 kubevirt-vhost-net.sock=
    srwxr-xr-x 1 root root    0 Dec  4 01:30 nvidia-gpu.sock=
    複製代碼

    5. 測試 GPU

    首先測試本地命令行工具 ctr,這個應該沒啥問題:

    $ ctr images pull docker.io/nvidia/cuda:9.0-base
    
    $ ctr run --rm -t --gpus 0 docker.io/nvidia/cuda:9.0-base nvidia-smi nvidia-smi
    Fri Dec  4 07:01:38 2020
    +-----------------------------------------------------------------------------+
    | NVIDIA-SMI 440.95.01    Driver Version: 440.95.01    CUDA Version: 10.2     |
    |-------------------------------+----------------------+----------------------+
    | GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
    | Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
    |===============================+======================+======================|
    |   0  GeForce RTX 208...  Off  | 00000000:A1:00.0 Off |                  N/A |
    | 30%   33C    P8     9W / 250W |      0MiB / 11019MiB |      0%      Default |
    +-------------------------------+----------------------+----------------------+
    
    +-----------------------------------------------------------------------------+
    | Processes:                                                       GPU Memory |
    |  GPU       PID   Type   Process name                             Usage      |
    |=============================================================================|
    |  No running processes found                                                 |
    +-----------------------------------------------------------------------------+
    複製代碼

    最後進入終極測試:在 Pod 中測試 GPU 可用性。先建立部署清單:

    gpu-pod.yaml

    apiVersion: v1
    kind: Pod
    metadata:
      name: cuda-vector-add
    spec:
      restartPolicy: OnFailure
      containers:
        - name: cuda-vector-add
          image: "k8s.gcr.io/cuda-vector-add:v0.1"
          resources:
            limits:
              nvidia.com/gpu: 1
    複製代碼

    執行 kubectl apply -f ./gpu-pod.yaml 建立 Pod。使用 kubectl get pod 能夠看到該 Pod 已經啓動成功:

    $ kubectl get pod
    NAME                              READY   STATUS      RESTARTS   AGE
    cuda-vector-add                   0/1     Completed   0          3s
    複製代碼

    查看 Pod 日誌:

    $ kubectl logs cuda-vector-add
    [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
    複製代碼

    能夠看到成功運行。這也說明 Kubernetes 完成了對 GPU 資源的調用。須要注意的是,目前 Kubernetes 只支持卡級別的調度,而且顯卡資源是獨佔,沒法在多個容器之間分享。

參考資料

相關文章
相關標籤/搜索