一、docker使用nvidia GPU
1、nvidia-docker2
nvidia-docker2是對docker的封裝,提供一些必要的組件可以很方便的在容器中用GPU資源執行代碼,nvidia-docker共享了宿主機的CUDA Driver
2、nvidia-container-toolkit
最新版的nvidia-docker就是nvidia-container-toolkit,比nvidia-docker2更加優秀
nvidia-container-toolkit需要主機已安裝當前新版的docker 19.03
使用nvidia-container-toolkit的最大優點:
linux主機不需要安裝cuda和cudnn,僅安裝顯卡驅動即可(tensorflow和nvidia-docker官方均強調這一點)
安裝nvidia-container-toolkit
curl -fsSL https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list sudo apt-get update sudo apt-get install -y --no-install-recommends nvidia-container-toolkit sudo systemctl restart docker
3、CUDA(運算平台)與cudnn(GPU加速庫)
CUDA(ComputeUnified Device Architecture):顯卡廠商NVIDIA推出的運算平台。 CUDA是一種由NVIDIA推出的通用並行計算架構,該架構使GPU能夠解決復雜的計算問題。
4、實操記錄
設置下載源
yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm curl -s -L https://nvidia.github.io/nvidia-docker/centos7/x86_64/nvidia-docker.repo | sudo tee /etc/yum.repos.d/nvidia-docker.repo
補充:有的網址是自動跳轉的。curl使用 -L 參數,curl 就會跳轉到新的網址。-s, --silent Silent mode. Don't output anything靜默不輸出信息
檢測顯卡驅動和型號
$ sudo yum install nvidia-detect $ nvidia-detect -v Probing for supported NVIDIA devices... [10de:1e04] NVIDIA Corporation TU102 [GeForce RTX 2080 Ti] This device requires the current 440.64 NVIDIA driver kmod-nvidia [1af4:1050] Red Hat, Inc. Virtio GPU Linux查看顯卡信息:(ps:若找不到lspci命令,可以安裝 yum install pciutils) lspci | grep -i vga 使用nvidia GPU可以: lspci | grep -i nvidia 查看顯卡驅動 cat /proc/driver/nvidia/version
4、查找可安裝的nvidia docker版本
yum search --showduplicates nvidia-docker yum install -y nvidia-docker2
5、下載對應版本的顯卡驅動
NIVID官網:http://www.geforce.cn/drivers
下載的時候,在手動搜索驅動程序中,大致勾選自己機器的類型,然后查找到跟步驟3檢測到的型號對應的版本進行下載
執行安裝
# 如果無法正常安裝nvidia $ yum -y install gcc $ yum install epel-release -y $ yum install kernel-devel-$(uname -r) kernel-headers-$(uname -r) -y $ chmod 777 NVIDIA-Linux-x86_64-450.66.run $ bash NVIDIA-Linux-x86_64-450.66.run
安裝過程中一些選項:
The distribution-provided pre-install script failed! Are you sure you want to continue?
選擇 yes 繼續。
Would you like to register the kernel module souces with DKMS? This will allow DKMS to automatically build a new module, if you install a different kernel later?
選擇 No 繼續。
問題大概是:Nvidia’s 32-bit compatibility libraries?
選擇 No 繼續。
Would you like to run the nvidia-xconfigutility to automatically update your x configuration so that the NVIDIA x driver will be used when you restart x? Any pre-existing x confile will be backed up.
選擇 Yes 繼續
卸載nvidia-driver
停掉占用
1. bash xxx.run --uninstall 2. 在現實的提示框中選擇 *no* (默認選項),然后回車
nvidia-docker配置如下:
$ cat /etc/docker/daemon.json
{
"registry-mirrors": ["https://xxxxxxx"], #替換成自己的harbor倉庫地址
"live-restore": true,
"default-shm-size": "128M",
"max-concurrent-downloads": 10,
"oom-score-adjust": -1000,
"debug": false,
"exec-opts": ["native.cgroupdriver=cgroupfs"],
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "3"
},
"storage-driver": "overlay2",
"default-runtime": "nvidia",
"storage-opts": [
"overlay2.override_kernel_check=true"
],
"runtimes": {
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
}
}
注意:
如果您有自定義/etc/docker/daemon.json,則nvidia-docker2程序包會覆蓋它,可能導致docker 原有的 volume配置會全部丟失。
安裝nvidia-docker2
下載安裝包
apt download libnvidia-container1 apt download libnvidia-container-tools apt download nvidia-container-runtime-hook apt download nvidia-container-runtime apt download nvidia-docker2
安裝
dpkg -i libnvidia* nvidia*
添加普通用戶至docke組
sudo groupadd docker #添加docker用戶組 sudo gpasswd -a $USER docker #將登陸用戶加入到docker用戶組中 newgrp docker #更新用戶組 docker ps #測試docker命令是否可以使用sudo正常使用
常見報錯:
內核問題

解決:
$ yum update $ yum -y install gcc $ yum install epel-release -y $ yum install kernel-devel-$(uname -r) kernel-headers-$(uname -r) -y
通信失敗報錯
NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
卸載
卸載cuda-toolkit cd /usr/local/cuda-8.0/bin/ ./uninstall_*** 卸載顯卡驅動 假如安裝的是NVIDIA-Linux-x86-340.96.run 則運行如下命令:sh NVIDIA-Linux-x86-270.41.19.run --uninstall
解決:
sudo yum install dkms ls /usr/src | grep nvidia sudo dkms install -m nvidia -v 418.87.00
禁用Nouveau
ERROR: The Nouveau kernel driver is currently in use by your system.
解決:
關閉Nouveau: 把驅動加入黑名單中: 在/etc/modprobe.d/blacklist.conf 加入 blacklist nouveau 使用 dracut重新建立 initramfs image file : * 備份 the initramfs file $ sudo mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak * 重新建立 the initramfs file $ sudo dracut -v /boot/initramfs-$(uname -r).img $(uname -r) $ lsmod | grep nouveau 檢查nouveau沒有被加載
報錯Failed to run /sbin/dkms build -m nvidia -v
ERROR: Failed to run /sbin/dkms build -m nvidia -v 450.66 -k 3.10.0-1160.24.1.el7.x86_64: Error! echo
Your kernel headers for kernel 3.10.0-1160.24.1.el7.x86_64 cannot be found at
/lib/modules/3.10.0-1160.24.1.el7.x86_64/build or /lib/modules/3.10.0-1160.24.1.el7.x86_64/source.
解決:
yum install kernel-devel kernel-doc kernel-headers gcc\* glibc\* glibc-\* 如果再提示錯誤“NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver” 需要運行 nvidia-uninstall
GPU占用
An NVIDIA kernel module 'nvidia-drm' appears to already be loaded in your kernel. This may be because it is in use (for example, by an X server, a CUDA program, or the NVIDIA Persistence Daemon), but this may also happen if your kernel was configured without support for module unloading. Please be sure to exit any programs that may be using the GPU(s) before attempting to upgrade your driver. If no GPU-based programs are running, you know that your kernel supports module unloading, and you still receive this message, then an error may have occured that has corrupted an NVIDIA kernel module's usage count, for which the simplest remedy is to reboot your computer.
解決:解決方式:關閉所有裝置,並停止載入NVIDIA驅動程序(會 kill docker)
sudo systemctl isolate multi-user.target sudo modprobe -r nvidia-drm
二、nvidia-smi命令
nvidia 的系統管理界面 (nvidia-smi),可以收集各種級別的信息,查看顯存使用情況。此外, 可以啟用和禁用 GPU 配置選項 (如 ECC 內存功能)。
1、nvidia-smi
當多塊GPU壞掉一塊 ,沒法確認時,同時開啟風扇吹熱風,沒風的就是壞的。
顯存占用和GPU占用是兩個不一樣的東西,顯卡是由GPU和顯存等組成的,顯存和GPU的關系有點類似於內存和CPU的關系。
2. nvidia-smi vgpu
查看當前vGPU的狀態信息:
3. nvidia-smi vgpu -p 循環顯示虛擬桌面中應用程序對GPU資源的占用情況
4. nvidia-smi -q 查看當前所有GPU的信息,也可以通過參數i指定具體的GPU。
比如nvidia-smi-q -i 0 代表我們查看服務器上第一塊GPU的信息。
通過nvidia-smi -q 我們可以獲取以下有用的信息:
GPU的SN號、VBIOS、PN號等信息:
GPU的總線、PCI-E總線倍速、風扇轉速等信息:
GPU的顯存、BAR1、所有資源利用率、ECC模式等信息:
nvidia-smi -q |grep "GPU Link" -A6
NVLink目前更主要的還是大大提升了GPU間通信的帶寬。
5、nvidia-smi -L 命令:列出所有可用的 NVIDIA 設備
6、nvidia-smi topo --matrix 命令:查看系統拓撲
7、nvidia-smi topo -mp
8、查詢GPU的版本等格式化輸出
nvidia-smi --query-gpu=driver_version,name,memory.total,memory.used --format=csv driver_version, name, memory.total [MiB], memory.used [MiB] 450.66, GeForce RTX 2080 Ti, 11019 MiB, 0 MiB 450.66, GeForce RTX 2080 Ti, 11019 MiB, 0 MiB
三、shell監控GPU腳本
monitor.sh
GPU跨平台通用監控腳本
功能: Useage: monitor.sh fast|mem|gpu|temp|all|[pathToLog sleepTimeNum]
注意: ./monitor.sh fast速度最快
#!/bin/bash
#. /etc/profile
#. ~/.bash_profile
#. ~/.bashrc
# 判斷nvidia-smi命令是否存在
/usr/bin/nvidia-smi > /dev/null
if [ $? -eq 0 ]
then
echo 'nvidia-smi check pass' `date`
else
echo 'nvidia-smi not exists'
exit 1
fi
# 獲取GPU Count
function get_gpu_list()
{
count=`nvidia-smi -L|wc -l`
echo $count
}
#獲取GPU id對應uuid
function get_uuid()
{
uuid=`nvidia-smi -q -i $1|grep 'UUID'|awk '{print $4}'`
echo $uuid
}
#獲取顯存使用率
function get_memory_usage()
{
usage=`nvidia-smi -q -d MEMORY -i $1|grep -E 'Total|Used'|head -2|awk '{print $3}'|xargs echo|awk '{print $2/$1}'`
echo $usage
}
#獲取內存詳細信息
function get_memory_detail()
{
detail=`nvidia-smi -q -d MEMORY -i $1|grep -E 'Total|Used|Free'|head -3|awk '{print $3}'|xargs echo`
echo $detail
}
#獲取GPU使用率
function get_volatile_gpu()
{
vol=`nvidia-smi -q -d UTILIZATION -i $1 |grep -A 5 "GPU Utilization"|tail -1|awk '{print $3}'`
echo $vol
}
#獲取GPU Current 溫度
function get_temperature()
{
temp=`nvidia-smi -q -d Temperature -i $1|grep 'GPU Current'|awk '{print $5}'`
echo $temp
}
#獲取Pod_id
function get_pod_id()
{
echo `hostname`
}
#數據output
#output $1 $2 $3 $4 $5
#$1 字段名 $2 pod_id $3 gpu編號 $4 uuid $5 監控值
function output()
{
echo $1"{podid=\""$2"\",gpu=\""$3"\",uuid=\""$4"\"}" $5
}
#輸出mem prometheus格式數據
#dcgm_mem_usage{pod_id="localhost"}
function mem_prm()
{
for((i=0;i<`get_gpu_list`;i++))
do
name="dcgm_mem_usage"
pod_id=`get_pod_id`
uuid=`get_uuid $i`
value=`get_memory_usage $i`
output $name $pod_id $i $uuid $value
done
}
#輸出mem detail prometheus格式數據
#dcgm_mem_detail{pod_id="localhost"}
function mem_detail_prm()
{
for((i=0;i<`get_gpu_list`;i++))
do
pod_id=`get_pod_id`
uuid=`get_uuid $i`
value=`get_memory_detail $i`
output "dcgm_fb_total" $pod_id $i $uuid `echo $value|awk '{print $1}'`
output "dcgm_fb_used" $pod_id $i $uuid `echo $value|awk '{print $2}'`
output "dcgm_fb_free" $pod_id $i $uuid `echo $value|awk '{print $3}'`
done
}
#輸出gpu prometheus格式數據
#dcgm_gpu_utilization{...}
function gpu_prm()
{
for((i=0;i<`get_gpu_list`;i++))
do
name="dcgm_gpu_utilization"
pod_id=`get_pod_id`
uuid=`get_uuid $i`
value=`get_volatile_gpu $i`
output $name $pod_id $i $uuid $value
done
}
#輸出溫度 prometheus格式數據
#dcgm_temp{...}
function temp_prm()
{
for((i=0;i<`get_gpu_list`;i++))
do
name="dcgm_temp"
pod_id=`get_pod_id`
uuid=`get_uuid $i`
value=`get_temperature $i`
output $name $pod_id $i $uuid $value
done
}
function allinone()
{
mem_prm
mem_detail_prm
gpu_prm
temp_prm
}
#快速獲取
function fast()
{
nvidia-smi -q > /tmp/1
num=0
count=0
uuid=''
first=0
for i in `cat /tmp/1|grep -E 'Minor Number|UUID|GPU Current Temp|Gpu|Total|Used|Free'|cut -d ':' -f2|awk '{print $1}'`
do
if [ $num -eq 0 ];then
uuid=$i
elif [ $num -eq 1 ];then
count=$i
elif [ $num -eq 2 ];then
if [ $first -lt 13 ];then
echo '# HELP dcgm_fb_total Framebuffer memory total (in MiB).'
echo '# TYPE dcgm_fb_total gauge'
fi
output 'dcgm_fb_total' ${HOSTNAME} $count $uuid $i
elif [ $num -eq 3 ];then
if [ $first -lt 13 ];then
echo '# HELP dcgm_fb_used Framebuffer memory used (in MiB).'
echo '# TYPE dcgm_fb_used gauge'
fi
output 'dcgm_fb_used' ${HOSTNAME} $count $uuid $i
elif [ $num -eq 4 ];then
if [ $first -lt 13 ];then
echo '# HELP dcgm_fb_free Framebuffer memory free (in MiB).'
echo '# TYPE dcgm_fb_free gauge'
fi
output 'dcgm_fb_free' ${HOSTNAME} $count $uuid $i
elif [ $num -eq 8 ];then
if [ $first -lt 13 ];then
echo '# HELP dcgm_gpu_utilization GPU utilization (in %).'
echo '# TYPE dcgm_gpu_utilization gauge'
fi
output 'dcgm_gpu_utilization' ${HOSTNAME} $count $uuid $i
elif [ $num -eq 13 ];then
if [ $first -le 13 ];then
echo '# HELP dcgm_gpu_temp GPU temperature (in C).'
echo '# TYPE dcgm_gpu_temp gauge'
fi
output 'dcgm_gpu_temp' ${HOSTNAME} $count $uuid $i
fi
if [ $num -eq 13 ];then
num=0
else
((num++))
fi
((first++))
done
}
case $1 in
"help")
echo 'Useage: monitor.sh fast|mem|gpu|temp|all|[pathToLog sleepTimeNum]'
;;
"mem")
mem_prm
mem_detail_prm
;;
"gpu")
gpu_prm
;;
"temp")
temp_prm
;;
"fast")
fast
;;
"all")
allinone
;;
"onebyone")
if [ ! -n "$1" ];then
if [ ! -d "/run/prometheus" ];then
mkdir -p /run/prometheus
fi
while true;do allinone > /run/prometheus/`hostname`_dcgm.prom;sleep 15;done
else
if [ ! -n "$2" ];then
while true;do allinone > $1;sleep 15;done
else
while true;do allinone > $1;sleep $2;done
fi
fi
;;
*)
if [ ! -n "$1" ];then
if [ ! -d "/run/prometheus" ];then
mkdir -p /run/prometheus
fi
while true;do fast > /run/prometheus/`hostname`_dcgm.prom;sleep 15;done
else
if [ ! -n "$2" ];then
while true;do fast > $1;sleep 15;done
else
while true;do fast > $1;sleep $2;done
fi
fi
;;
esac
四、docker的存儲驅動類型及其作用
https://docs.docker.com/storage/storagedriver/select-storage-driver/ 官網說明
1、理解的前提:
(1)鏡像是分層的文件系統(分層鏡像機制),不可寫,容器才是可寫入數據的。
(2)容器 = 鏡像 + 讀寫層
(3)多個容器可以共用一個images,利用寫時復制(CoW)機制。
CoW就是copy-on-write,表示只在需要寫時才去復制,這個是針對已有文件的修改場景。比如基於一個image啟動多個Container,如果為每個Container都去分配一個image一樣的文件系統,那么將會占用大量的磁盤空間。而CoW技術可以讓所有的容器共享image的文件系統,所有數據都從image中讀取,只有當要對文件進行寫操作時,才從image里把要寫的文件復制到自己的文件系統進行修改。所以無論有多少個容器共享同一個image,所做的寫操作都是對從image中復制到自己的文件系統中的復本上進行,並不會修改image的源文件,且多個容器操作同一個文件,會在每個容器的文件系統里生成一個復本,每個容器修改的都是自己的復本,相互隔離,相互不影響。使用CoW可以有效的提高磁盤的利用率。
(4)存儲驅動是干嘛的:每個容器都有一個本地存儲空間,用於保存層疊的鏡像層以及掛載的容器文件系統。默認情況下,容器的所有讀寫操作都發生在其鏡像層上或掛載的文件系統中,所以存儲是每個容器的性能和穩定性不可或缺的一個環節。本地存儲是通過存儲驅動(Storage Driver)進行管理的,有時候也被稱為 Graph Driver。docker 存儲驅動的職責就是將鏡像層和可寫容器層管理起來.不同的驅動實現管理的方式也不一致。實現容器與鏡像管理的兩個關鍵技術就是分層鏡像機制和copy-on-write (寫時復制)。
2、docker支持的幾種存儲驅動
Docker支持以下存儲驅動程序:
(1)overlay2 :當前所有受支持的Linux發行版的首選存儲驅動程序,不需要任何額外的配置,已經並入內核主線。
overlayFS 是Linux內核3.18后支持的,也是一種Union FS,和AUFS的多層不同的是Overlay只有兩層:一個upper文件系統和一個lower文件系統,分別代表
Docker的鏡像層和容器層。當需要修改一個文件時,使用CoW將文件從只讀的lower復制到可寫的upper進行修改,結果也保存在upper層。在Docker中,底下的只
讀層就是image,可寫層就是Container。目前最新的OverlayFS為Overlay2。

(2)aufs是更早版本的首選存儲驅動程序。
AUFS(AnotherUnionFS)是一種Union FS,是文件級的存儲驅動。AUFS是一個能透明覆蓋一個或多個現有文件系統的層狀文件系統,把多層合並成文件系統的單層表示。簡單來說就是支持將不同目錄掛載到同一個虛擬文件系統下的文件系統。這種文件系統可以一層一層地疊加修改文件。無論底下有多少層都是只讀的,只有最上層的文件系統是可寫的。當需要修改一個文件時,AUFS創建該文件的一個副本,使用CoW將文件從只讀層復制到可寫層進行修改,結果也保存在可寫層。在Docker中,底下的只讀層就是image,可寫層就是Container。

(3)常用存儲驅動對比
| 存儲驅動 | 特點 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| AUFS | 聯合文件系統、未並入內核主線、文件級存儲 | 作為docker的第一個存儲驅動,已經有很長的歷史,比較穩定,且在大量的生產中實踐過,有較強的社區支持 | 有多層,在做寫時復制操作時,如果文件比較大且存在比較低的層,可能會慢一些 | 大並發但少IO的場景 |
| overlayFS | 聯合文件系統、並入內核主線、文件級存儲 | 只有兩層 | 不管修改的內容大小都會復制整個文件,對大文件進行修改顯示要比小文件消耗更多的時間 | 大並發但少IO的場景 |
| Devicemapper | 並入內核主線、塊級存儲 | 塊級無論是大文件還是小文件都只復制需要修改的塊,並不是整個文件 | 不支持共享存儲,當有多個容器讀同一個文件時,需要生成多個復本,在很多容器啟停的情況下可能會導致磁盤溢出 | 適合io密集的場景 |
| Btrfs | 並入linux內核、文件級存儲 | 可以像devicemapper一樣直接操作底層設備,支持動態添加設備 | 不支持共享存儲,當有多個容器讀同一個文件時,需要生成多個復本 | 不適合在高密度容器的paas平台上使用 |
| ZFS | 把所有設備集中到一個存儲池中來進行管理 | 支持多個容器共享一個緩存塊,適合內存大的環境 | COW使用碎片化問題更加嚴重,文件在硬盤上的物理地址會變的不再連續,順序讀會變的性能比較差 | 適合paas和高密度的場景 |
AUFS VS OverlayFS
AUFS和Overlay都是聯合文件系統,但AUFS有多層,而Overlay只有兩層,所以在做寫時復制操作時,如果文件比較大且存在比較低的層,則AUSF可能會慢一些。而且Overlay並入了linux kernel mainline,AUFS沒有。目前AUFS已基本被淘汰
OverlayFS VS Device mapper
OverlayFS是文件級存儲,Device mapper是塊級存儲,當文件特別大而修改的內容很小,Overlay不管修改的內容大小都會復制整個文件,對大文件進行修改顯示要比小文件要消耗更多的時間,而塊級無論是大文件還是小文件都只復制需要修改的塊,並不是整個文件,在這種場景下,顯然device mapper要快一些。因為塊級的是直接訪問邏輯盤,適合IO密集的場景。而對於程序內部復雜,大並發但少IO的場景,Overlay的性能相對要強一些。
注:修改驅動之前做好數據備份!!!!!
雲原生實驗室 (cnblogs.com) Kubernetes 教程:在 Containerd 容器中使用 GPU
https://nvidia.github.io/nvidia-docker/ nvidia-docker的部署
https://fanfuhan.github.io/2019/11/22/docker_based_use/ 配合上一個地址使用
https://gitlab.com/nvidia
https://github.com/NVIDIA/gpu-operator
https://github.com/zhebrak/nvidia_smi_exporter
https://github.com/mindprince/nvidia_gpu_prometheus_exporter gpu監控
Installation Guide — NVIDIA Cloud Native Technologies documentation
Index of /compute/cuda/repos/debian10/x86_64 (nvidia.com)
https://mp.weixin.qq.com/s/smLeFV5NOHspoW4wWqWvNQ
