iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0

前言

我們常常需要在客戶現場的資料系統中,處理一些少見而詭異的事情。安排在這系列的第一篇,是我們維護這些大大小小系統以來,最詭異的事件。

在客戶的現場,有一座很特別的服務,是 Cloudera 多年前開發出來,為資料科學工作所量身打造的工作平台,也是 Cloudera ML (現改名為 Cloudera AI) 的前身。

它的底層內含了一個調校過的 Docker daemon 與微型 Kubernetes,用來提供許多的使用者 (多以資料科學家為主),可以方便、靈活地執行自己的程式,並存取 Hadoop 系統裡的資料。

這樣的系統與設計目的,首先讓人想到的,就是訓練模型!因此,支援 GPU 自然不在話下,但既然如此,又為何需要呼求支援?

症狀

由於客戶需要特定版號的 Python 與 CUDA ,我們便幫客戶建立了一個客製化的 ML Runtime 映像檔 (以下稱 image)。打包完後,帶來客戶的環境部署。

部署上版後,在容器內測試是否有抓到 NVIDIA 的 GPU,結果很令人意外地,竟然不是抓不到 GPU,而是抓不到 Driver 路徑的訊息:

要確認 NVIDIA GPU 是否有抓到,最基本的指令,是執行 nvidia-smi 。然而,在啟動後的容器 (以下稱 container) ,輸入 nvidia-smi 時,卻出現 libnvidia-ml.so 找不到的問題!

經交叉測試後,我們發現一件很有趣的事。在這個平台上,容器映像檔有兩代,舊的那一代我們稱 legacy,新的一代稱 ML Runtime (聽這名字,就知道它是為了與 Cloudera ML 對接而設計的),舊的 (legacy) 在啟動 container 後,執行 nvidai-smi 可獲得正確的結果,但新的 (ML Runtime) 卻出現上述的問題。

病因

我們詳細調查後,該問題不只存在於我們客製化過的 image,也存在於 Cloudera 原廠的 ML Runtime image 中,意即只要啟動原廠 ML Runtime 的 container,都會發生這樣的問題。

由於容器啟動的機制綁定在這座服務的底層環境中,我們無法深入調查,但合理推斷 (通靈),應該是因為 CDSW 的 docker 與 kuberntes 版本太舊的緣故,導致不相容於新一點的 base image (基於 Ubuntu 20.04 的 ML Runtime)。

既然無法處理底層,我們便轉往另一個方向,確認 Host 端的 driver 是否有整合進 container 的執行環境。

要調查 Linux 的 driver,主要的做法是前往 /usr/lib/x86_64-linux-gnu 這個目錄,查找剛剛我們看到的那個檔案在不在。

然而我們透過與 legacy 的 container 比對結果,發現很奇妙的是,驅動程式原名為 libnvidia-ml.so.<驅動程式版本> ,在 legacy 的 container 環境下,它自動建了 libnvidia-ml.so.1 這個檔案出來。

反而較新的 ML Runtime 的容器裡,並沒有這個 libnvidia-ml.so.1 檔,且 libnvidia-ml.so 所相依的 libcuda.so 也沒有結尾 .1 的 alias。

因此,我們判斷這應該是平台底層的 BUG,在 ML runtime 的 container 啟動時,平台沒有正確地為驅動程式建立 alias,使得 nvidia-smi 指令找不到檔案。

處方

在我們為客戶客製化的 image 中,我們引用了 NVIDIA 的 CUDA Image 的包法,其中也包含 entrypoint.d 的機制。

entrypoint.d 是在設計 Docker image 的一個技巧,通常會用一個叫 entrypoint.d 的目錄,將 container 啟動時要執行的腳本都放進去,並用數字編號的方式來安排執行的次序。

我們便在 entrypoint.d 目錄中,找到驅動程式檢測的腳本,加入一段用來建立 alias 的做法:


#!/bin/bash

# Copyright (c) 2017-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.

# Check if libcuda.so.1 -- the CUDA driver -- is present in the ld.so cache or in LD_LIBRARY_PATH
_LIBCUDA_FROM_LD_CACHE=$(ldconfig -p | grep libcuda.so.1)
_LIBCUDA_FROM_LD_LIBRARY_PATH=$( ( IFS=: ; for i in ${LD_LIBRARY_PATH}; do ls $i/libcuda.so.1 2>/dev/null | grep -v compat; done) )
_LIBCUDA_FOUND="${_LIBCUDA_FROM_LD_CACHE}${_LIBCUDA_FROM_LD_LIBRARY_PATH}"

# Check if /dev/nvidiactl (like on Linux) or /dev/dxg (like on WSL2) or /dev/nvgpu (like on Tegra) is present
_DRIVER_FOUND=$(ls /dev/nvidiactl /dev/dxg /dev/nvgpu 2>/dev/null)

# If either is not true, then GPU functionality won't be usable.
if [[ -z "${_LIBCUDA_FOUND}" || -z "${_DRIVER_FOUND}" ]]; then
	echo
	echo "WARNING: The NVIDIA Driver was not detected. GPU functionality will not be available."
	echo " Use the NVIDIA Container Toolkit to start this container with GPU support; see"
	echo " https://docs.nvidia.com/datacenter/cloud-native/ ."
	export NVIDIA_CPU_ONLY=1
else # ==== 此段是我們加入的 ====
	echo "Detected drivers and preparing symlinks."
	ln -sfv /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.<driver ver> /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1
	ln -sfv /usr/lib/x86_64-linux-gnu/libcuda.so.<driver ver> /usr/lib/x86_64-linux-gnu/libcuda.so.1
fi

如此一來,ML runtime 的 container 啟動後,會自動建立驅動程式的 alias,也就能正常地使用 GPU 了!


上一篇
[ Day 23 ] 資料專案管理:在資料有價的時代,程式功能驗證的必勝法則
下一篇
[ Day 25 ] Spark 死掉了怎麼辦
系列文
資料專案修羅場,30天手把手教你暗黑求生術!!!30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言