境準備:Docker與GPU聯(lián)動實戰(zhàn)指南)
算力調度平臺這個系列寫到第二篇環(huán)境準備是繞不開的第一道坎。上篇聊了整體架構和資源抽象的思路對平臺要解決什么問題、怎么把GPU資源池化和切分已經有了大方向。這篇接著往下推進把最底層的環(huán)境底座搭起來Docker和GPU聯(lián)動。如果你是從這一篇開始看的也不影響環(huán)境準備這層是獨立的照著做就行。為什么把環(huán)境準備單獨拎出來寫一篇因為算力調度平臺后續(xù)所有東西——K8s調度、GPU顯存隔離、任務編排、鏡像分發(fā)——都建立在一套能穩(wěn)定運行的容器加GPU環(huán)境上。環(huán)境沒搭好后面每一步都會遇到莫名其妙的坑。比如容器里跑深度學習任務時提示CUDA不可用或者Docker Desktop一啟動就報虛擬化錯誤又或者宿主機nvidia-smi正常但容器里死活看不到GPU這些問題的根源十有八九都出在環(huán)境準備階段。這篇會把Docker安裝、GPU驅動、NVIDIA Container Toolkit、容器內GPU驗證這幾個環(huán)節(jié)完整過一遍附帶我實際部署中踩過的坑和排查方法盡量讓你照著做一遍就能把環(huán)境跑通。1. 環(huán)境準備這件事先想清楚再動手1.1 算力調度平臺為什么非要Docker加GPU這套組合先明確一個問題算力調度平臺要調度的是什么表面上是任務和容器本質上調度的是計算資源重點是GPU。深度學習的訓練和推理、大模型的微調、科學計算這些場景對GPU的依賴遠超CPU。所以一個調度平臺要真正落地它的底層必須有能力回答三個問題某個任務能用哪張GPU卡、能用多大顯存、任務跑起來容器內部能不能實際訪問到GPU的計算能力。Docker在這里扮演的角色是標準化封裝和隔離。每個任務打包成一個鏡像環(huán)境依賴、CUDA版本、Python環(huán)境全部固化在鏡像里任務發(fā)到哪臺機器跑起來效果都一樣。GPU則通過驅動和運行時暴露給容器讓容器內的CUDA應用可以直通宿主機顯卡。這套組合的核心價值在于環(huán)境隔離做到了資源復用了任務還保持了可遷移性。如果你不是這次做調度平臺只是單純想在自己機器上跑深度學習這套環(huán)境準備同樣是通用的。本地開發(fā)、模型微調、論文復現(xiàn)都需要先過這一關。所以這篇內容不管你是要搭一個大平臺還是只想把單機GPU環(huán)境搞好都有直接參考價值。1.2 方案選型為什么是Docker而不是裸機或虛擬機聊一下我在環(huán)境選型上的思考過程。最樸素的做法是在裸機上裝驅動、裝CUDA、裝Python環(huán)境然后直接跑訓練腳本。這在單機單卡、沒有多人協(xié)作的情況下沒問題但一旦任務變多、環(huán)境需求沖突比如一個任務要CUDA 11.8另一個要CUDA 12.1裸機方案就亂了。你總不能每切換一次任務就重裝一遍環(huán)境這在工程上是不可接受的。虛擬機方案可以隔離環(huán)境但有兩個硬傷一是虛擬化層對GPU的透傳配置復雜虛擬機和宿主機之間的性能損耗在計算密集型任務里不可忽略二是虛擬機動輒幾十GB鏡像起停時間以分鐘計在調度場景下完全跟不上節(jié)奏。Docker正好落在中間進程級隔離啟動秒級完成鏡像分層復用GPU直通時不引入明顯的性能損耗。對于算力調度平臺這種需要頻繁創(chuàng)建、銷毀任務運行環(huán)境的場景容器是當前性價比最高的方案。這也是為什么當下主流AI基礎設施——K8s、各種訓練平臺、推理服務框架——幾乎全部以容器為底座。選Docker不是跟風是這個場景下的工程理性。1.3 前置檢查清單動手前先花十分鐘確認這些事無論在Linux還是Windows上做環(huán)境準備有幾項前置條件必須先確認不然后面報錯會報得你懷疑人生。操作系統(tǒng)版本Docker和GPU驅動對系統(tǒng)都有版本要求Ubuntu 22.04 LTS是目前兼容性最穩(wěn)妥的選擇。Windows的話建議Windows 11并打開WSL2功能。內核版本Linux下Docker對內核有最低版本要求64位系統(tǒng)、內核版本建議不低于5.x。太老的內核建議先升級。虛擬化支持Windows上跑Docker Desktop必須開啟CPU虛擬化也就是BIOS里的Intel VT-x或AMD-V。這個不開Docker Desktop根本起不來。GPU與驅動版本先確認你的顯卡型號和NVIDIA驅動版本支持情況。老顯卡可能要裝特定版本驅動新顯卡太新反而可能遇到驅動還沒跟上的問題這在部署時經常遇到。磁盤空間AI相關的鏡像和容器動輒幾個GB到十幾個GB建議單獨規(guī)劃一個容量充足的數(shù)據(jù)盤。我經歷過/var/lib/docker所在分區(qū)被打滿導致所有容器異常退出的事故所以現(xiàn)在一律先把data-root指到大磁盤上。這些檢查項看著瑣碎但每一件都在實際排障中救過我的命。花十分鐘確認比出問題后再排查兩小時劃算得多。2. Docker環(huán)境安裝與基礎配置2.1 Linux宿主機安裝Docker以Ubuntu為例生產環(huán)境我強烈建議直接用Linux裸機或Linux虛擬機作為宿主機。這是算力調度平臺真正要跑起來的地方Windows只適合做開發(fā)調試。以Ubuntu 22.04為例安裝Docker的過程可以固化成一個標準操作sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg lsb-release sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin幾個容易被坑的點第一不要用系統(tǒng)自帶源里的docker.io包版本老舊后續(xù)容器運行時兼容性差。第二安裝的是docker-ce社區(qū)版這是最主流的選擇不要用早期的docker-engine。第三docker-compose-plugin順手裝上后面編排多容器任務會用到。安裝完成后的標準操作是把當前用戶加入docker組避免每次執(zhí)行docker命令都要sudo。注意這個操作有安全邊界docker組內的用戶等價于擁有宿主機root權限所以在多人共享的機器上要謹慎不能為了省事無腦把所有人加進去。sudo usermod -aG docker $USER newgrp docker然后啟動并設置開機自啟sudo systemctl enable docker sudo systemctl start docker2.2 配置daemon.json存儲、網絡與日志裝好Docker只是第一步真正讓它在生產環(huán)境里穩(wěn)如老狗靠的是/etc/docker/daemon.json的配置。我常用的一個基礎配置長這樣{ data-root: /data/docker, log-driver: json-file, log-opts: { max-size: 200m, max-file: 5 }, registry-mirrors: [], exec-opts: [native.cgroupdriversystemd] }>sudo systemctl daemon-reload sudo systemctl restart docker2.3 Windows場景下的Docker Desktop與WSL2開發(fā)調試階段很多同事習慣在Windows上工作。Docker Desktop現(xiàn)在的主流形態(tài)是WSL2后端其實就是讓Docker跑在一個輕量級虛擬機里。這個模式下有兩個前置條件必須滿足BIOS開啟虛擬化并且Windows里裝好WSL2。檢查虛擬化是否開啟任務管理器性能標簽頁里能看到虛擬化狀態(tài)。如果顯示已啟用繼續(xù)裝WSLwsl --install wsl --set-default-version 2Docker Desktop安裝包下載安裝后在Settings里把Use the WSL 2 based engine勾上。如果這里啟動報錯virtualization support wasnt detected先回BIOS確認VT-x/AMD-V是否打開再確認Windows功能里的Virtual Machine Platform和Windows Hypervisor Platform這兩個組件是否啟用。Windows上用Docker Desktop要注意文件掛載的性能問題??缥募到y(tǒng)掛載在WSL2里I/O性能較弱編譯類、大量小文件讀寫的任務會明顯卡頓。一個折中做法是把代碼放到WSL2的Linux文件系統(tǒng)里而不是放在Windows盤符下掛載進容器。2.4 安裝后第一件事跑通一個容器環(huán)境裝好后先用一個最小的容器驗證Docker本身是否正常docker run --rm hello-world看到Hello from Docker!就說明Docker守護進程、鏡像拉取、容器啟停這條鏈路都通了。接著跑一個交互式容器驗證基本的終端和網絡能力docker run -it --rm ubuntu:22.04 bash apt-get update這兩個小測試的必要性在于把基礎鏈路和網絡源的問題提前暴露掉避免后面GPU環(huán)境準備時把基礎問題和GPU問題混在一起排查。我見過有人在一個網絡都沒通的Docker環(huán)境里折騰GPU排查了半天最后發(fā)現(xiàn)是鏡像根本拉不動白白浪費了兩小時。3. GPU支持打通驅動、運行時與容器3.1 宿主機GPU驅動安裝與驗證Docker就緒后開始處理GPU。第一步確保宿主機本身能識別GPU。Linux上安裝NVIDIA驅動有兩類主流方式用apt安裝發(fā)行版?zhèn)}庫里的驅動包或者用NVIDIA官網的runfile安裝腳本。apt方式在Ubuntu上很省事sudo apt-get update sudo ubuntu-drivers devices sudo apt-get install -y nvidia-driver-550ubuntu-drivers devices會列出當前機器推薦的驅動版本照著裝就行。裝完重啟執(zhí)行nvidia-smi能看到顯卡型號、驅動版本、顯存信息說明驅動工作正常。這里我提一個反直覺的細節(jié)nvidia-smi里的CUDA Version不是系統(tǒng)里安裝的CUDA工具包版本而是當前驅動支持的最高CUDA運行時版本。應用實際使用的CUDA版本由容器或應用自身的CUDA庫決定只要不超過這個上限即可。很多人把這兩者混為一談導致后面裝CUDA時選錯版本。踩坑提示Ubuntu從22.04開始默認啟用Secure Boot如果BIOS里沒關安裝NVIDIA驅動后模塊可能無法加載。表現(xiàn)是nvidia-smi提示找不到命令或報NVIDIA-SMI has failed because it couldnt communicate with the NVIDIA driver。解決辦法有兩種Secure Boot關閉后重裝驅動或者給DKMS模塊做MOK簽名。命令行簽名流程比較繁瑣環(huán)境準備階段我直接建議在測試機上關閉Secure Boot生產環(huán)境則要提前規(guī)劃好簽名流程。3.2 安裝NVIDIA Container Toolkit宿主機能看到GPU不等于容器里能看到GPU。中間缺的關鍵組件叫NVIDIA Container Toolkit它負責在Docker容器啟動時把GPU設備、驅動庫和nvidia-smi工具注入到容器的運行環(huán)境里。這里解釋一下原理Docker本身不認識GPU硬件。沒有Toolkit時即使宿主機的/dev/nvidia0設備節(jié)點就在那里容器默認也訪問不到。NVIDIA Container Toolkit做的事是在容器創(chuàng)建時通過prestart hook動態(tài)向容器配置注入GPU設備和驅動庫讓容器內的CUDA應用能夠和宿主機驅動通信。執(zhí)行安裝curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed s#deb https://#deb [signed-by/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update sudo apt-get install -y nvidia-container-toolkit然后配置Docker的運行時sudo nvidia-ctk runtime configure --runtimedocker sudo systemctl restart dockernvidia-ctk runtime configure這個命令會把NVIDIA Container Runtime注冊到Docker的運行時配置里。重啟Docker后docker info里應該能看到Runtimes: nvidia這一項。這一步做完Docker才真正具備了把GPU暴露給容器的能力。3.3 容器內nvidia-smi驗證驗證是最激動人心的一步。拉一個帶CUDA的基礎鏡像從容器里執(zhí)行nvidia-smidocker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi如果你看到宿主機里那張熟悉的信息表出現(xiàn)在容器內說明GPU打通了。注意一個細節(jié)容器內nvidia-smi顯示的Driver Version和宿主機的驅動版本通常是一致的。因為容器里的驅動庫直接借用宿主機驅動容器內并沒有真正的驅動內核模塊。這是正常設計不是配置錯了。再驗證一下容器能否真正執(zhí)行CUDA計算。用一個帶CUDA runtime的鏡像在容器內跑一段GPU運算docker run --rm --gpus all nvidia/cuda:12.4.0-runtime-ubuntu22.04 bash -c cat /usr/local/cuda/version.json如果只是想確認設備可用可以寫一段小的CUDA樣例程序或者直接驗證PyTorch。但是要注意整個CUDA鏡像體積不小動輒兩三個GB。如果只為了驗證環(huán)境用nvidia/cuda:12.4.0-base-ubuntu22.04就夠了。3.4 PyTorch GPU環(huán)境實測GPU環(huán)境和Docker打通后再上一個大名鼎鼎的PyTorch做最終驗證。這一步模擬的是真實的深度學習運行環(huán)境。首先拉取官方PyTorch鏡像docker run -it --rm --gpus all -v /workspace:/workspace pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime bash容器內執(zhí)行python -c import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.cuda.device_count()); print(torch.cuda.get_device_name(0))如果輸出True和你的顯卡型號算力調度平臺的單機GPU環(huán)境就算真正落定了。這一步能跑通說明驅動、Container Toolkit、Docker、CUDA運行庫之間的所有鏈路都是通的。這里有一個常見問題在容器內用pip安裝PyTorch時默認安裝的是CPU版導致運行torch.cuda.is_available()返回False。這是因為PyTorch從2.0版本開始默認pip索引里的包不一定帶CUDA支持。解決辦法是顯式安裝帶CUDA索引的版本。這一點在多臺機器上是很容易踩的坑。4. 環(huán)境準備階段的典型問題與排查實錄4.1 容器里看不到GPU八成是Toolkit的問題癥狀宿主機nvidia-smi正常但容器里執(zhí)行nvidia-smi提示command not found或者運行帶--gpus all命令時報錯。排查順序按概率排列第一確認docker info里有沒有顯示Runtimes: nvidia沒有就說明Container Toolkit沒配置好重新執(zhí)行nvidia-ctk runtime configure并重啟Docker。第二確認容器命令里加了--gpus all參數(shù)老版本Docker如果沒有這個參數(shù)在run時也看不到GPU。第三確認驅動和Toolkit版本兼容性。第四檢查/dev/nvidia*設備節(jié)點是否存在如果設備節(jié)點異常多半是驅動模塊加載有問題。按這個順序排查絕大多數(shù)情況都能定位。4.2 Docker Desktop啟動失敗的虛擬化問題Windows場景下Docker Desktop最常見的啟動失敗報錯是virtualization support wasnt detected。這個報錯說白了就是WSL2依賴的虛擬化能力沒有就緒。排查從三個層面展開BIOS里開啟Intel VT-x或AMD-VWindows功能里啟用Virtual Machine Platform和Windows Hypervisor Platform如果Hyper-V和第三方的沙盒軟件沖突比如舊版VMware、部分安卓模擬器也可能導致虛擬化檢測失敗這種情況下關閉沖突軟件的虛擬化獨占功能即可。還有一類特殊情況Windows系統(tǒng)更新補丁導致WSL2內核組件損壞。表現(xiàn)是wsl --status異常。用管理員執(zhí)行wsl --update可以修復。4.3 驅動版本、CUDA版本與鏡像版本的匹配關系三者的關系比很多人想象的寬松。NVIDIA驅動具備向后兼容性新的驅動可以運行舊版的CUDA但舊驅動跑不了新版CUDA。所以宿主機驅動的原則是選新不選舊而應用鏡像里的CUDA版本按任務需求來。舉個例子宿主機安裝Driver 550CUDA 12.1、12.4這些鏡像都能跑反過來宿主機驅動是470CUDA 12.4的應用鏡像大概率就會報CUDA init失敗。所以環(huán)境準備階段我建議直接安裝當前官網主流的穩(wěn)定版驅動給后續(xù)應用鏡像留足向上兼容的空間。關于CUDA鏡像標簽的命名需要提一句nvidia/cuda鏡像分為base、runtime、devel三個層級。base只包含最基礎的CUDA庫runtime加了運行時devel才有完整的開發(fā)工具鏈。調度普通推理任務用runtime就夠訓練任務需要編譯算子時選devel。4.4 網絡不通、鏡像拉取慢的處理思路Docker環(huán)境搭建后下一個常見痛點是網絡。鏡像拉取超時、下載到一半中斷單憑這個原因就能讓環(huán)境準備卡住一整天。首要對策是前面提到的registry-mirrors配置。多填幾個備選鏡像源。第二個對策是給容器配置HTTP代理環(huán)境變量這個適合公司網絡需要通過代理訪問外網的情況。還有一類隱蔽問題是容器內的DNS解析異常表現(xiàn)是容器內apt-get update失敗但宿主機網絡正常。這種問題可以在daemon.json里配dns: [8.8.8.8, 114.114.114.114]解決。另外容器網絡和宿主機網段沖突也會引發(fā)詭異現(xiàn)象。Docker默認bridge網絡的網段是172.17.0.0/16如果宿主機所在的內網正好在同一個網段容器訪問內網服務就會出現(xiàn)路由異常。處理方式是修改daemon.json里的bip參數(shù)把這個網段改掉比如bip: 10.66.0.1/24。4.5 附環(huán)境準備快速排查表癥狀大概率原因快速處理容器內執(zhí)行nvidia-smi: command not foundContainer Toolkit未安裝或未注冊運行時安裝Toolkit并執(zhí)行nvidia-ctk runtime configure --runtimedockerDocker Desktop啟動提示virtualization support未檢測到BIOS未開虛擬化WSL2組件缺失開啟VT-x/AMD-Vwsl --update宿主機nvidia-smi失敗驅動未加載或Secure Boot阻擋驅動模塊重啟檢查dmesg容器內CUDA應用報版本不匹配驅動版本過老或應用對應CUDA版本過高升級宿主機驅動torch.cuda.is_available()返回False裝了CPU版PyTorch用官方鏡像或指定cu121/cu124索引重新安裝鏡像拉取超時或中斷鏡像源不穩(wěn)定或網絡問題配置registry-mirrors必要時配置代理容器內訪問內網服務不通Docker網段和宿主機局域網沖突修改daemon.json的bip參數(shù)調整網段日志寫滿磁盤導致容器異常日志無限增長data-root空間耗盡配置log-opts限制日志大小將data-root遷移到大分區(qū)5. 環(huán)境準備完成后下一步往哪走環(huán)境準備這層踩實之后算力調度平臺才算真正有了地基?;仡欉@篇文章的內容核心其實就三件事Docker正常跑、GPU驅動正常加載、Container Toolkit把GPU暴露給容器。三步走完單機上的容器化GPU環(huán)境就具備了。這個過程中有幾個我特別想再強調的體會。第一環(huán)境準備不要趕進度每一層都要驗證通過再往下走。宿主機驅動沒驗證就直接裝Toolkit出問題了根本不知道是驅動還是Toolkit的問題。第二Docker的daemon.json要早規(guī)劃磁盤、日志、網段這些都是運行時才暴雷的問題與其等生產環(huán)境出故障再去救火不如現(xiàn)在就把參數(shù)定好。第三日志和監(jiān)控從一開始就要留好后續(xù)做算力調度時每個節(jié)點的GPU利用率、顯存占用、任務狀態(tài)都要有數(shù)據(jù)可查。接下來這個系列要處理的問題會更復雜多節(jié)點下怎么統(tǒng)一管理GPU資源任務調度怎么做顯存怎么隔離鏡像倉庫怎么搭建。但這些都是在當前這套Docker加GPU環(huán)境上繼續(xù)疊加。環(huán)境穩(wěn)了后面的事才有討論的前提。下一篇我會寫多機環(huán)境下GPU資源的管理和任務調度的初步方案那是算力調度平臺真正進入到核心邏輯的部分。恰好我手里還有一臺沒裝驅動的備用機正好用來驗證多節(jié)點方案到時候實測數(shù)據(jù)直接寫到下篇里。