docker 容器访问 GPU 资源使用指南

概述

nvidia-dockernvidia-container-runtime 是用于在 NVIDIA GPU 上运行 Docker 容器的两个相关工具。它们的作用是提供 Docker 容器与 GPU 加速硬件的集成支持,使容器中的应用程序能够充分利用 GPU 资源。

  • nvidia-docker

    为了提高 Nvidia GPU 在 docker 中的易用性, Nvidia 通过对原生 docker 的封装提供了 nvidia-docker 工具

    nvidia-docker 是一个 Docker 插件,用于在 Docker 容器中启用 NVIDIA GPU 支持。

    该工具提供了一个命令行界面,允许在运行容器时通过简单的命令来指定容器是否应该访问主机上的 NVIDIA GPU 资源。

    当在容器中运行需要 GPU 加速的应用程序时,可以使用 nvidia-docker 来确保容器能够访问 GPU。

    # 示例命令
    nvidia-docker run -it --rm nvidia/cuda:11.0-base nvidia-smi
    

    上述命令使用 nvidia-docker 在容器中运行 NVIDIA 的 CUDA 基础镜像,并在容器中执行 nvidia-smi 命令以查看 GPU 信息

  • nvidia-container-runtime

    nvidia-container-runtime 是 NVIDIA 的容器运行时,它与 Docker 和其他容器运行时(如 containerd)集成,以便容器可以透明地访问 NVIDIA GPU 资源。

    与 nvidia-docker 不同,nvidia-container-runtime 不是 Docker 插件,而是一种更通用的容器运行时,可与多个容器管理工具集成。

    nvidia-dockernvidia-container-runtime 都是用于使 Docker 容器能够访问 NVIDIA GPU 资源的工具。可以根据自己的需求选择其中一个来配置容器以利用 GPU 加速。

    需要注意的是,最新的 NVIDIA Docker 支持通常建议使用 nvidia-container-runtime,因为它提供了更灵活和通用的 GPU 支持,而不仅仅是为 Docker 定制的解决方案。

    # 示例命令
    docker run --runtime=nvidia -it --rm nvidia/cuda:11.0-base nvidia-smi
    
    • 上述命令使用 Docker 运行容器,通过 --runtime=nvidia 参数指定使用 nvidia-container-runtime 运行时,并在容器中执行 nvidia-smi 命令。
  • 原生 docker 通过设备挂载和磁盘挂载的方式支持访问 GPU 资源

    docker 本身并不原生支持 GPU,但使用 docker 的现有功能可以对 GPU 的使用进行支持

    # 示例命令
    docker run \
    --device /dev/nvidia0:/dev/nvidia0 \
    --device /dev/nvidiactl:/dev/nvidiactl \
    --device /dev/nvidia-uvm:/dev/nvidia-uvm \
    -v /usr/local/nvidia:/usr/local/nvidia \
    -it --privileged nvidia/cuda
    
    • 通过 --device 来指定挂载的 GPU 设备,通过 -v 来将宿主机上的 nvidia gpu 的命令行工具和相关的依赖库挂载到容器。这样,在容器中就可以看到和使用宿主机上的 GPU 设备了。

    注意:这种方式对于 GPU 的可用性(哪些 GPU 是空闲的等)需要人为的判断,效率很低

nvidia-docker 详解

结构图及各组件说明

nvidia-docker 对于使用 GPU 资源的 docker 容器支持的层次关系:

在这里插入图片描述

Nvidia-docker的原理图以及各个部分的作用解析:

在这里插入图片描述

  • **libnvidia-container:**提供了一个库和简单的 CLI 工具,以实现在容器当中支持使用 GPU 设备的目标。

  • nvidia-container-toolkit:

    是一个实现了 runC 的 prestart hook 接口的脚本,该脚本在 runC 创建一个容器之后,启动该容器之前调用,其主要作用就是修改与容器相关联的 config.json,注入一些在容器中使用 NVIDIA GPU 设备所需要的一些信息(比如:需要挂载哪些 GPU 设备到容器当中)。

  • nvidia-container-runtime:

    主要用于将容器 runC spec 作为输入,然后将 nvidia-container-toolkit 脚本作为一个 prestart hook 加入到 runC spec 中,再用修改后的 runC spec 调 runc 的 Exec 接口运行容器。

    所以在容器启动之前会调用 pre-start hook(nvidia-container-toolkit),这个 hook 会通过 nvidia-container-cli 文件,来调用libnvidia-container 库,最终映射挂载宿主机上的 GPU 设备、nvid 驱动的 so 文件、可执行文件到容器内部。

    nvidia-container-runtime 才是真正的核心部分,它在原有的 docker 容器运行时 runc 的基础上增加一个 prestart hook,用于调用 libnvidia-container 库

    在这里插入图片描述

  • RunC

    RunC 是一个轻量级的工具,它是用来运行容器的,只用来做这一件事。

    可以认为它就是个命令行小工具,可以不用通过 docker 引擎,直接运行容器。也是 docker 默认的容器运行方式。

    事实上,runC 是标准化的产物,它根据 OCI 标准来创建和运行容器。而 OCI(Open Container Initiative)组织,旨在围绕容器格式和运行时制定一个开放的工业化标准。

    直接使用 RunC 的命令行即可以完成创建一个容器,并提供了简单的交互能力。

容器创建过程

  • 创建一个正常容器(不支持 GPU)的流程:

    docker --> dockerd --> containerd–> containerd-shim -->runc --> container-process
    

    docker 客户端将创建容器的请求发送给 dockerd,当 dockerd 收到请求任务之后将请求发送给 containerd,containerd 经过查看校验启动 containerd-shim 或者自己来启动容器进程。

  • 创建一个支持 GPU 的容器的流程:

    docker–> dockerd --> containerd --> containerd-shim–> nvidia-container-runtime --> nvidia-container-runtime-hook --> libnvidia-container --> runc – > container-process
    

    基本流程和不支持 GPU 的容器差不多,只是把 docker 默认的运行时替换成了 NVIDIA 封装的 nvidia-container-runtime

    这样当 nvidia-container-runtime 创建容器时,先执行 nvidia-container-runtime-hook,这个 hook 去检查容器是否需要使用GPU(通过环境变 NVIDIA_VISIBLE_DEVICES 来判断)。如果需要则调用 libnvidia-container 来暴露 GPU 给容器使用。否则走默认的 runc 逻辑。

NVIDIA Docker 整体工作架构

软硬件基础

  1. 硬件,服务器上安装了英伟达 GPU

  2. 宿主机,安装了操作系统和 Cuda Driver,以及 Docker 引擎

  3. 容器,包含容器 OS 用户空间,Cuda Toolkit,以及用户应用程序

注意:

  • 宿主机上需要安装 cuda driver,容器内需要安装 cuda toolkit。容器内无需安装 cuda driver
  • NVIDIA 提供了一些官方镜像,其中已经安装好了 cuda toolkit,但还是需要在宿主机安装 cuda driver。

API 结构

NVIDIA 提供了三层 API:

  • CUDA Driver API

    GPU 设备的抽象层,通过提供一系列接口来操作 GPU 设备,性能最好,但编程难度高,一般不会使用该方式开发应用程序

  • CUDA Runtime API:

    对 CUDA Driver API 进行了一定的封装,调用该类 API 可简化编程过程,降低开发难度

  • CUDA Libraries:

    是对 CUDA Runtime API 更高一层的封装,通常是一些成熟的高效函数库,开发者也可以自己封装一些函数库便于使用

CUDA 结构图如下:

在这里插入图片描述

  • CUDA 调用关系:

    应用程序可调用 CUDA Libraries 或者 CUDA Runtime API 来实现功能,当调用 CUDA Libraries 时,CUDA Libraries 会调用相应的 CUDA Runtime API,CUDA Runtime API 再调用 CUDA Driver API,CUDA Driver API 再操作 GPU 设备。

CUDA 的容器化

目标:让应用程序可以在容器内调用 CUDA API 来操作 GPU

因此需要实现:

  1. 在容器内应用程序可调用 CUDA Runtime API 和 CUDA Libraries

  2. 在容器内能使用 CUDA Driver 相关库。因为 CUDA Runtime API 其实就是 CUDA Driver API 的封装,底层还是要调用到 CUDA Driver API

  3. 在容器内可操作 GPU 设备

因此容器中访问 GPU 资源过程为:

  • 要在容器内操作 GPU 设备,需要将 GPU 设备挂载到容器里

    • Docker 可通过 --device 挂载需要操作的设备,或者直接使用特权模式(不推荐)。

    • NVIDIA Docker 是通过注入一个 prestart 的 hook 到容器中,在容器自定义命令启动前就将GPU设备挂载到容器中。

      至于要挂载哪些GPU,可通过 NVIDIA_VISIBLE_DEVICES 环境变量控制。

  • 挂载 GPU 设备到容器后,还要在容器内可调用 CUDA API

    • CUDA Runtime API 和 CUDA Libraries 通常跟应用程序一起打包到镜像里

    • CUDA Driver API 是在宿主机里,需要将其挂载到容器里才能被使用。

      NVIDIA Docker 挂载 CUDA Driver 库文件到容器的方式和挂载 GPU 设备一样,都是在 runtime hook 里实现的。

注意:

  • 该方案也有一些问题,即容器内的 CUDA Runtime 同宿主机的 CUDA driver 可能存在版本不兼容的问题。

    CUDA Libraries 和 CUDA Runtime API 是和应用程序一起打包到镜像中的,而 Driver 库是在创建容器时从宿主机挂载到容器中的,需要保证 CUDA Driver 的版本不低于 CUDA Runtime 版本。

NVIDIA Docker 2.0(nvidia-container-runtime)

实现机制

  • nvidia-docker2.0 是一个简单的包,它主要通过修改 docker 的配置文件 /etc/docker/daemon.json,将默认的 Runtime 修改为 nvidia-container-runtime,可实现将 GPU 设备,CUDA Driver 库挂载到容器中。

    cat /etc/docker/daemon.json 
    {"default-runtime": "nvidia","runtimes": {"nvidia": {"path": "/usr/bin/nvidia-container-runtime","runtimeArgs": []}}
    }
    

Debug日志

修改 nvidia runc 的默认配置文件 /etc/nvidia-container-runtime/config.toml ,打开 hook 的 debug 日志选项,可以看到宿主机挂载 nvidia 驱动、设备到容器内部的详细过程。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/492296.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序 --- 小程序基础知识

小程序基础知识 1. 认识什么是小程序 什么是微信小程序 微信小程序是一种运行在微信内部的 轻量级 应用程序。 在使用小程序时 不需要下载安装,用户 扫一扫 或 搜一下 即可打开应用。它也体现了 “用完即走” 的理念,用户不用关心安装太多应用的问题…

【python】0、超详细介绍:json、http

文章目录 一、json二、http2.1 json 读取 request 序列化 三、基本类型3.1 decimal 四、图像4.1 颜色格式转换 一、json import json f open(data.json) # open json file data json.load(f) # 读出 json object for i in data[emp_details]: # 取出一级属性 emp_details, …

springboot219基于SpringBoot的网络海鲜市场系统的设计与实现

网络海鲜市场系统的设计与实现 摘 要 计算机网络发展到现在已经好几十年了,在理论上面已经有了很丰富的基础,并且在现实生活中也到处都在使用,可以说,经过几十年的发展,互联网技术已经把地域信息的隔阂给消除了&…

SQLServer sp_help使用方法

给大家介绍一个在SQLServer中非常实用的一个存储过程,在SQL Server中,sp_help 存储过程用于显示有关数据库对象(如表、视图或存储过程)的信息。它提供有关指定对象的结构和属性的详细信息。使用 sp_help 的语法如下: …

Python3.7.1标准安装

Python3.7.1标准安装 官网下载 官网地址:https://www.python.org/downloads/ 下载3.7.1 下载64/32bitwindows安装文件,下图x86-64是64bit,x86是32bit 双击exe文件安装 第一个界面如下图选择 第二个界面默认选择,然后点击Nex…

【数据结构】周末作业

1.new(struct list_head*)malloc(sizeof(struct list_head*)); if(newNULL) { printf("失败\n"); return; } new->nextprev->next; prev->nextnew; return; 2.struct list_head* pprev->next; prev->nextp->next; p->next->prevpr…

多输入回归预测|WOA-CNN|鲸鱼算法优化的卷积神经网络回归预测(Matlab)

目录 一、程序及算法内容介绍: 基本内容: 亮点与优势: 二、实际运行效果: 三、部分程序: 四、完整程序数据下载: 一、程序及算法内容介绍: 基本内容: 本代码基于Matalb平台编译…

Vi/Vim 使用小窍门,如何消除搜索后的关键字高亮

Vim/Vi 基本上是 *nix 世界最受欢迎的编辑器了,不知道为什么,一直以来觉得和 Emacs 比起来,Vim 更加有亲和力。用起来很舒服。 今天就记录一个困扰了我很久的问题。 大家应该都知道,在 Vi 里面如果要搜索某个关键字,…

外贸精英催单秘籍:突破观望犹豫,抓住订单黄金时机!

年底将至,对于外贸人来说,这是一个重要的订单冲刺时机。在这个关键时刻,如何向国外客户催单成为一项关键任务。本文将分享催单的技巧,并附带销冠年底工作安排计划。同时,我们将引入Focussend,一款具有邮件自…

使用CE查找共享代码的多种方法

一般在游戏中,我们会有这样的定义Player和怪物NPC: // 基类 Character class Character { protected:std::string name;int64_t id;int32_t hp;int32_t mp;int32_t level;public:Character(const int64_t name) : id(id) {}virtual void attack() {…

Amazon Generative AI | 基于 Amazon 扩散模型原理的代码实践之采样篇

以前通过论文介绍 Amazon 生成式 AI 和大语言模型(LLMs)的主要原理之外,在代码实践环节主要还是局限于是引入预训练模型、在预训练模型基础上做微调、使用 API 等等。很多开发人员觉得还不过瘾,希望内容可以更加深入。因此&#x…

LeetCode234.回文链表

题目 给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。 示例 输入:head [1,2,2,1] 输出:true思路 找到链表的中间节点:可以使用快慢指针…