戰(zhàn):從運(yùn)行時(shí)配置到CUDA容器遷移)
簡介面向 Jetson Nano 等 ARM64 平臺的 Docker 部署入門文檔適合需要在嵌入式設(shè)備上搭建深度學(xué)習(xí)推理環(huán)境的開發(fā)者。內(nèi)容圍繞 docker 安裝、nvidia-docker 運(yùn)行時(shí)配置與 GPU 調(diào)用展開從 apt 安裝 docker-ce到安裝 nvidia-container-runtime再到修改 daemon.json 將 nvidia 設(shè)為默認(rèn)運(yùn)行時(shí)覆蓋了鏡像構(gòu)建、容器啟動時(shí)映射 /dev/nvhost-* 設(shè)備節(jié)點(diǎn)與 tegra 驅(qū)動目錄等關(guān)鍵環(huán)節(jié)。文檔整理為 1 個(gè) docx 文件共 208KB按安裝與部署順序梳理便于對照操作。已有 157 人瀏覽學(xué)習(xí)相比網(wǎng)上零散的教程這份資料把 Jetson 平臺與 GPU 容器結(jié)合時(shí)容易踩坑的設(shè)備映射、庫路徑掛載等問題集中說明并給出了可執(zhí)行的 Dockerfile 示例對于想快速跑通 nvidia-docker 并調(diào)用 GPU 的開發(fā)者可以節(jié)省不少排查時(shí)間。文檔中 deviceQuery 容器測試與 Dockerfile 基礎(chǔ)鏡像選擇等細(xì)節(jié)對有 CUDA 環(huán)境訴求的用戶也很有參考價(jià)值作者還提供了配套免費(fèi)博文方便進(jìn)一步查閱遷移部署細(xì)節(jié)。1. 在 Jetson Nano 上讓 Docker 調(diào)用 GPU為什么剛裝好的 Docker 跑不了 CUDA 容器很多人拿到 Jetson Nano 的第一反應(yīng)是先裝個(gè) Docker裝完跑 hello-world 一切正常接著docker run --gpus all nvidia/cuda直接報(bào)錯(cuò) could not select device driver。這不是命令寫錯(cuò)了而是 Jetson 的 GPU 訪問鏈路和 PC 完全不一樣Tegra 的驅(qū)動不在鏡像里CUDA 庫在宿主機(jī)的 /usr/lib/aarch64-linux-gnu/tegra 下GPU 設(shè)備則分散在一組 /dev/nvhost-* 節(jié)點(diǎn)里缺一個(gè)容器就拿不到計(jì)算資源。這篇文章把我在 Jetson Nano 上從零走通的完整流程拆開講Docker 安裝、NVIDIA Container Runtime 配置、daemon.json 默認(rèn)運(yùn)行時(shí)、Dockerfile 構(gòu)建、設(shè)備節(jié)點(diǎn)掛載最后是鏡像遷移。每一步都給出能直接復(fù)制的命令坑單獨(dú)放在第五章按現(xiàn)象找原因就行。這篇適合想在自己板子上跑 CUDA 容器、想把整套環(huán)境固化下來遷到別的 Jetson 設(shè)備上的讀者照著做基本一遍過。2. 安裝 Docker 與 NVIDIA Container Runtime從 apt 源到運(yùn)行時(shí)驗(yàn)證Jetson 上的 JetPack 底層就是 UbuntuDocker 本體可以走官方 Ubuntu 安裝流程但有幾個(gè)參數(shù)必須按 ARM 平臺調(diào)整。我按實(shí)際操作的順序?qū)懨恳徊秸f明為什么這么寫遇到 404 或者依賴缺失也知道去哪查。2.1 先裝 Docker 本體apt 源與架構(gòu)參數(shù)一次配好先把基礎(chǔ)工具裝齊然后用官方 GPG 密鑰配置 apt 源最后安裝 docker-ce 三件套。如果你之前用 apt 裝過 docker 或 containerd先sudo apt remove docker docker-engine docker.io containerd runc清干凈并把 /var/lib/docker 改名備份避免升級殘留沖突這是從舊系統(tǒng)帶著配置過來最容易翻車的地方。sudo apt-get update sudo apt-get install -y \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo \ deb [archarm64 signed-by/usr/share/keyrings/docker-archive-keyring.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第一組 apt-get install 裝的是 apt 走 HTTPS 和校驗(yàn) GPG 所需的前置工具。curl 那行把 Docker 的公鑰下載下來用 gpg --dearmor 轉(zhuǎn)成二進(jìn)制放到 /usr/share/keyrings 下這樣 apt 校驗(yàn)源簽名時(shí)不會彈 warning。注意輸出文件名是 docker-archive-keyring.gpg后面 apt 源里 signed-by 必須指向同一個(gè)文件路徑寫錯(cuò)會直接導(dǎo)致后續(xù) update 失敗。echo 那行寫 apt 源archarm64是本次安裝最容易錯(cuò)的地方寫成 amd64 會在 apt update 時(shí)提示架構(gòu)不匹配什么都不寫雖然也能裝上但 apt 會拿錯(cuò)架構(gòu)的包列表。$(lsb_release -cs)自動取系統(tǒng)代號JetPack 4.x 對應(yīng) Ubuntu 18.04bionicJetPack 5.x 對應(yīng) 20.04focal寫成變量后換系統(tǒng)版本不用改配置。裝完確認(rèn)兩件事docker --version sudo systemctl status docker如果 docker 命令存在但服務(wù)沒起來先看journalctl -u docker再繼續(xù)常見原因是 daemon.json 寫錯(cuò)導(dǎo)致 dockerd 拒絕啟動或者和已存在的 containerd 版本沖突。順手把當(dāng)前用戶加進(jìn) docker 組sudo usermod -aG docker $USER之后不用每條命令都帶 sudo。這里比 PC 上省心的點(diǎn)在于Jetson 走的是 Linux 原生 Docker 加 NVIDIA Container Runtime 的路線不會遇到 Docker Desktop 在 PC 上常見的 virtualization support not detected 這類虛擬化層問題少了一層排查。2.2 安裝 nvidia-container-toolkit 與 runtime順序反了會白折騰x86 上很多人直接apt install nvidia-docker2就完事Jetson 不行。Tegra 平臺 GPU 驅(qū)動不是通過 PCIe 暴露的nvidia-docker2 的 hook 在 Jetson 上找不到硬件必須改用 nvidia-container-runtime 加 nvidia-container-toolkit 這套組合。執(zhí)行curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - distribution$(. /etc/os-release;echo $ID$VERSION_ID) echo $distribution 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 nvidia-container-toolkit sudo apt-get install -y nvidia-container-runtime sudo systemctl restart dockerdistribution$(...echo $ID$VERSION_ID)這行是關(guān)鍵它會拼出 ubuntu18.04 或 ubuntu20.04 這樣的字符串下一行 curl 用這個(gè)字符串拼倉庫路徑。如果這里輸出是空的檢查 /etc/os-release 是否存在部分 JetPack 精簡版系統(tǒng)會缺這個(gè)文件手工填 distributionubuntu18.04 也能走通。兩個(gè) apt install 的順序建議固定toolkit 里包含 nvidia-container-cliruntime 依賴它做設(shè)備注入。先裝 runtime 再裝 toolkit 也能裝但 toolkit 裝完會覆蓋更新 runtime 的配置等于白折騰一遍。裝完不重啟 Dockerruntime 不會生效后面docker info里看不到 nvidia。我見過不少人卡在這一步誤以為沒裝成功反復(fù)重裝了好幾輪。如果倉庫 404可以單獨(dú)下載 nvidia-container-runtime-hook 的 deb 包手工裝wget https://nvidia.github.io/nvidia-container-runtime/ubuntu14.04/amd64/./nvidia-container-runtime-hook_1.4.0-1_amd64.deb sudo dpkg -i nvidia-container-runtime-hook_1.4.0-1_amd64.deb sudo apt-get -f install注意這里包的發(fā)布路徑是上游倉庫的遺留命名里面帶了 ubuntu14.04 和 amd64實(shí)際上是個(gè)通用 hook 包arm64 的 Jetson 系統(tǒng)裝了也能正常用。dpkg 報(bào)依賴缺失就執(zhí)行sudo apt-get -f install補(bǔ)齊再繼續(xù)。2.3 配置前的基線驗(yàn)證docker info 與 deviceQuery 的預(yù)期輸出現(xiàn)在先別急著改配置文件跑兩條命令記錄未配置狀態(tài)后面才有對比docker info | grep -i runtime docker run -it --rm jitteam/devicequery ./deviceQuery第一條命令未配置時(shí)通常只顯示 runc如果顯示 runc 和 nvidia 都出現(xiàn)說明系統(tǒng)里已經(jīng)存在舊配置第 3 章的改動會被舊配置影響。第二條命令用 Jetson 社區(qū)常用的 jitteam/devicequery 鏡像里面打包了 NVIDIA 官方 deviceQuery 程序的交叉編譯產(chǎn)物專門用來驗(yàn)證容器能否訪問 GPU。關(guān)鍵點(diǎn)第一次跑理想輸出是ResultFAIL。這不是失敗而是基線——Docker 默認(rèn)用 runc 啟動容器容器里沒有任何 GPU 資源deviceQuery 探測不到設(shè)備自然返回 FAIL??吹?FAIL 不需要重裝驅(qū)動只要容器能正常啟動、能看到 CUDA 相關(guān)日志就說明 Docker 本體沒問題。更穩(wěn)妥的驗(yàn)證方式是同時(shí)看容器的 exit code如果 docker run 直接報(bào)錯(cuò)退出說明容器沒起來問題在 Docker 側(cè)如果容器起來了、輸出了 FAIL問題只在 GPU 資源分配這是第 3 章要解決的事。提示日志里出現(xiàn) could not select device driver說明 dockerd 的 runtimes 列表里還沒有 nvidia這是配置問題不是驅(qū)動問題別卸載重裝。3. 把 nvidia 設(shè)為默認(rèn)運(yùn)行時(shí)daemon.json 兩個(gè)字段與三次確認(rèn)裝完 runtime 只完成了一半。Docker 默認(rèn)還是用 runc 啟動所有容器必須顯式告訴 dockerd nvidia 這個(gè)運(yùn)行時(shí)怎么調(diào)、要不要作為默認(rèn)。這一步不配置GPU 容器永遠(yuǎn)跑不起來而且報(bào)錯(cuò)非常誤導(dǎo)人。3.1 daemon.json 字段解讀default-runtime 與 runtimes 缺一不可修改 /etc/docker/daemon.json內(nèi)容如下{ default-runtime: nvidia, runtimes: { nvidia: { path: nvidia-container-runtime, runtimeArgs: [] } } }兩個(gè)字段各管一件事。runtimes是注冊表告訴 dockerd 存在一個(gè)名為 nvidia 的運(yùn)行時(shí)它的可執(zhí)行文件是 nvidia-container-runtimeruntimeArgs 留空數(shù)組表示不需要額外參數(shù)。default-runtime是全局開關(guān)把 nvidia 設(shè)為默認(rèn)后以后所有 docker run 啟動的容器都自動走 nvidia-container-runtime不用每條命令都帶--runtime nvidia。我每次改這個(gè)文件前都會先備份sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bakdaemon.json 本身是可選的如果之前不存在直接新建。JSON 里不能有注釋不能有尾逗號鍵名不能寫錯(cuò)。最容易犯的低級錯(cuò)誤是把 default-runtime 寫進(jìn) runtimes 里面或者把 runtimes 寫成單數(shù) runtime這兩處寫錯(cuò) dockerd 都會啟動失敗錯(cuò)誤信息卻只是 generic 的 failed to start daemon不仔細(xì)看 journalctl 根本定位不到。改完后先做一次語法校驗(yàn)再重啟sudo dockerd --config-file /etc/docker/daemon.json這條命令會前臺啟動 dockerd如果 JSON 格式有問題會直接打印出具體的解析錯(cuò)誤行號。確認(rèn)沒有報(bào)錯(cuò)后 CtrlC 退出再執(zhí)行sudo systemctl restart docker。我習(xí)慣把這條驗(yàn)證寫進(jìn)操作清單它能把配置錯(cuò)誤和系統(tǒng)問題在 10 秒內(nèi)分開。3.2 改完配置的完整驗(yàn)證路徑重啟、grep、再跑一次 deviceQuery重啟后按順序做三次確認(rèn)缺一次都可能在后面埋雷。sudo systemctl restart docker docker info | grep -i runtime正常情況下輸出包含以下兩行Runtimes: nvidia runc Default Runtime: nvidia如果 Runtimes 里有 nvidia 但 Default Runtime 不是 nvidia說明 default-runtime 字段沒寫對或者拼寫和 runtimes 里的 key 不一致。如果 Runtimes 里壓根沒有 nvidia說明 daemon.json 沒被加載檢查文件路徑和權(quán)限/etc/docker/daemon.json 需要 root 可讀一般 644 權(quán)限沒問題。確認(rèn)注冊成功后再跑一次 deviceQuery這次顯式指定運(yùn)行時(shí)docker run -it --runtime nvidia jitteam/devicequery ./deviceQuery這次結(jié)果應(yīng)該是ResultPASS設(shè)備名稱顯示 Jetson 的 GPU 型號。跑完 PASS 后再用默認(rèn)運(yùn)行時(shí)跑一次不帶 --runtime 參數(shù)的版本確認(rèn) default-runtime 確實(shí)生效。兩次都 PASSGPU 容器鏈路才算真正打通可以進(jìn)入第 4 章構(gòu)建自己的鏡像。3.3 為什么設(shè)成默認(rèn)運(yùn)行時(shí)省掉的不僅是命令參數(shù)有人會問不設(shè) default-runtime每次手動加--runtime nvidia不也一樣表面看一樣實(shí)際有兩個(gè)隱患。第一個(gè)是環(huán)境變量丟失。nvidia-container-runtime 在啟動容器時(shí)會根據(jù)宿主機(jī)注入 CUDA 相關(guān)環(huán)境變量比如 CUDA_VERSION、NVIDIA_VISIBLE_DEVICES。如果忘記帶--runtime nvidia容器里即便有 CUDA 庫也識別不到設(shè)備報(bào)錯(cuò)還特別隱蔽程序可能直接段錯(cuò)誤。第二個(gè)是編排工具的兼容性。docker-compose 對 runtime 字段的支持在部分版本上不完整compose 文件里沒寫 runtime 時(shí)容器就會走默認(rèn)的 runcGPU 訪問靜默失敗。設(shè)成默認(rèn) runtime 之后所有容器不管怎么起都走同一個(gè)運(yùn)行時(shí)少踩很多坑。但默認(rèn)運(yùn)行時(shí)不是萬能的它有明確的邊界daemon.json 只解決用哪個(gè)運(yùn)行時(shí)啟動容器設(shè)備節(jié)點(diǎn)和 tegra 庫的掛載仍然要自己在 docker run 或 compose 里寫。換句話說第 3 章解決的是運(yùn)行時(shí)選擇第 4 章構(gòu)建鏡像第 5 章的設(shè)備掛載解決的是容器里能看到哪些 GPU 資源三件事分開理解排查時(shí)才不會互相繞。4. 用 Dockerfile 構(gòu)建可用的 CUDA 鏡像基礎(chǔ)鏡像、依賴與 build 參數(shù)運(yùn)行時(shí)配好了接下來要做的不是直接拉現(xiàn)成的 CUDA 鏡像而是構(gòu)建一個(gè)符合 Jetson Tegra 環(huán)境的容器。很多 PC 上的 CUDA 鏡像在 Jetson 上跑不起來因?yàn)樗鼈儧]有針對 Tegra 的庫路徑做處理。這一章從 Dockerfile 逐行拆解到 build 命令把每個(gè)依賴為什么裝說清楚。4.1 Dockerfile 逐段拆解基礎(chǔ)鏡像、時(shí)區(qū)與依賴創(chuàng)建一個(gè)工作目錄并新建 Dockerfilemkdir docker-test cd docker-test vi DockerfileDockerfile 內(nèi)容如下我按構(gòu)建順序逐段說明FROM arm64v8/ubuntu:16.04 ENV LD_LIBRARY/usr/lib/aarch64-linux-gnu/tegra RUN mkdir /cudaSamples COPY deviceQuery /cudaSamples/ ENV LANG C.UTF-8 RUN apt-get update -y apt-get upgrade -y RUN apt-get install -y tzdata ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN apt-get -y install vim RUN apt-get install -y libsqlite3-dev RUN apt-get -y install gcc RUN apt-get install libssl-dev -y RUN apt-get install make RUN apt-get -y install zlib* RUN apt-get -y install wget基礎(chǔ)鏡像選 arm64v8/ubuntu:16.04這是 JetPack 4.x 時(shí)代最常見的系統(tǒng)基座Tegra 的庫文件路徑按 aarch64 結(jié)構(gòu)組織。ENV LD_LIBRARY這一行原文里寫的是 LD_LIBRARY實(shí)際作用等價(jià)于后續(xù)容器內(nèi)的庫搜索路徑前綴建議寫成ENV LD_LIBRARY_PATH/usr/lib/aarch64-linux-gnu/tegra否則運(yùn)行 deviceQuery 時(shí)動態(tài)鏈接器找不到 libcuda.so。COPY deviceQuery 這行要求構(gòu)建目錄里必須存在一個(gè)名為 deviceQuery 的可執(zhí)行文件它是從 jitteam/devicequery 鏡像里拷出來的或者你在宿主機(jī)交叉編譯得到的 arm64 版本。這個(gè)文件缺失時(shí) docker build 會直接報(bào) COPY failed: file not found。后面幾行按需安裝tzdata 配合ln -sf把時(shí)區(qū)固定到上海避免后續(xù) Python 或 OpenCV 運(yùn)行時(shí)報(bào) timezone 警告libsqlite3-dev 是編譯 Python 時(shí) sqlite3 模塊的依賴gcc 和 make 是源碼編譯必需libssl-dev 解決 Python 編譯時(shí) No module named _ssl 的問題zlib* 是很多第三方庫編譯時(shí)的通用依賴。這里有個(gè)優(yōu)化點(diǎn)可以把多個(gè) RUN 合并成一個(gè) RUN用 連接減少鏡像層數(shù)。我一般會把依賴安裝合并成一條只有補(bǔ)充工具單獨(dú)留一層便于后續(xù)修改。上面的 Dockerfile 保持了原始風(fēng)格的拆分便于你看清每個(gè)包的作用實(shí)際構(gòu)建時(shí)可以按我的習(xí)慣合并。4.2 docker build 構(gòu)建上下文目錄與鏡像命名Dockerfile 寫好后執(zhí)行構(gòu)建。這一步的關(guān)鍵不在命令本身而在你執(zhí)行命令時(shí)所在的目錄docker build -t nano_env_img .命令最后的點(diǎn)代表構(gòu)建上下文目錄docker 會把當(dāng)前目錄整個(gè)打包發(fā)給 dockerd。所以執(zhí)行這行命令前必須確認(rèn)已經(jīng)在 docker-test 目錄里而且 deviceQuery 文件就在這個(gè)目錄下否則 COPY 指令找不到源文件。鏡像名 nano_env_img 可以任意取我習(xí)慣帶項(xiàng)目名和日期比如 jp46-ai-env-0215方便后面遷移時(shí)辨認(rèn)。構(gòu)建過程中如果 apt-get 走到一半卡住多數(shù)是網(wǎng)絡(luò)波動直接重新跑一次 docker build 即可Docker 會復(fù)用已經(jīng)完成的層不會從頭再來這個(gè)特性在慢速網(wǎng)絡(luò)下特別好用。構(gòu)建完成后確認(rèn)鏡像存在docker images | grep nano_env_img docker run -it nano_env_img /bin/bash能進(jìn)入容器說明基礎(chǔ)構(gòu)建通過。這一步先不掛設(shè)備節(jié)點(diǎn)因?yàn)榻酉聛淼谖逭虏胖v運(yùn)行容器的完整參數(shù)。4.3 準(zhǔn)備運(yùn)行容器所需的路徑清單運(yùn)行 GPU 容器前先確認(rèn)宿主機(jī)的設(shè)備節(jié)點(diǎn)和庫目錄都存在順手檢查一下別等 docker run 報(bào)錯(cuò)了再排查ls -l /dev/nvhost-* /dev/nvmap ls -l /usr/lib/aarch64-linux-gnu/tegra | head如果設(shè)備節(jié)點(diǎn)不存在說明 JetPack 的 BSP 驅(qū)動沒加載完全后續(xù)掛載無從談起如果 tegra 目錄為空CUDA 運(yùn)行庫缺失需要重新刷 JetPack 或者在宿主機(jī)裝對應(yīng)的 CUDA-arm 包。這里提一下 arm 版 CUDA 工具鏈官方有單獨(dú)的 aarch64 發(fā)布通道Jetson 上的 CUDA 版本必須和 JetPack SDK 版本對齊不能用 x86 的 CUDA 安裝包替換。這兩個(gè)檢查都通過后第 5 章給出的運(yùn)行命令才能真正生效。5. 避坑Jetson Nano GPU 容器最常見的 5 個(gè)翻車現(xiàn)場這一章是把前面每一處的踩坑記錄集中起來。每條按現(xiàn)象、原因、解決展開你遇到哪個(gè)就直接定位到哪條。5.1 現(xiàn)象daemon.json 改完Docker 服務(wù)起不來sudo systemctl restart docker后systemctl 提示 faileddocker info直接報(bào) Cannot connect to the Docker daemon。查 journalctl -u docker 會看到一段 dockerd 啟動失敗的日志但錯(cuò)誤內(nèi)容很泛看不出具體原因。原因基本就兩個(gè)daemon.json 的 JSON 語法錯(cuò)誤或者鍵名寫錯(cuò)。最常見的是把 default-runtime 寫進(jìn)了 runtimes 對象內(nèi)部或者 runtimes 誤寫成 runtime導(dǎo)致默認(rèn)字段解析不到。解決方法是先把配置備份然后用sudo dockerd --config-file /etc/docker/daemon.json前臺啟動錯(cuò)誤信息會精確到第幾行第幾個(gè)字符。修好后再用 systemctl 啟動并重新執(zhí)行docker info | grep -i runtime確認(rèn) Default Runtime 字段出現(xiàn)。5.2 現(xiàn)象容器能起來但 deviceQuery 報(bào)找不到 libcuda 或 tegra 相關(guān)庫容器能進(jìn)入 shell但執(zhí)行 ./deviceQuery 時(shí)提示error while loading shared libraries: libcuda.so.1: cannot open shared object file。原因是在 Jetson 上GPU 運(yùn)行庫不在鏡像里而在宿主機(jī)的 /usr/lib/aarch64-linux-gnu/tegra 目錄下。單靠 nvidia-container-runtime 不會自動掛載這個(gè)目錄必須手動通過 -v 參數(shù)映射到容器內(nèi)相同路徑。解決方式是在 docker run 里加上-v /usr/lib/aarch64-linux-gnu/tegra:/usr/lib/aarch64-linux-gnu/tegra并在 Dockerfile 里顯式設(shè)置ENV LD_LIBRARY_PATH/usr/lib/aarch64-linux-gnu/tegra。只掛載目錄但沒設(shè)置環(huán)境變量還是可能因?yàn)?link 解析不到而失敗兩個(gè)動作一起做才穩(wěn)。5.3 現(xiàn)象設(shè)備能識別但 deviceQuery 最終 ResultFAIL容器能啟動deviceQuery 也能找到顯卡型號但結(jié)果始終是 FAIL或者報(bào) CUDA-capable device 數(shù)量為 0。原因是在容器啟動時(shí)沒有把 GPU 設(shè)備節(jié)點(diǎn)傳進(jìn)去。CUDA 在 Jetson 上訪問 GPU 不依賴 /dev/nvidia0而是依賴一組 Tegra 專屬節(jié)點(diǎn)/dev/nvhost-ctrl、/dev/nvhost-ctrl-gpu、/dev/nvhost-prof-gpu、/dev/nvmap、/dev/nvhost-gpu、/dev/nvhost-as-gpu。這些節(jié)點(diǎn)分別負(fù)責(zé) GPU 控制、profiling、內(nèi)存映射等漏傳任何一個(gè)都可能導(dǎo)致初始化失敗。解決方式是把六個(gè)節(jié)點(diǎn)全部用 --device 參數(shù)加進(jìn) docker run 命令。注意有些教程只傳 nvhost-gpu 和 nvmap結(jié)果 container 能跑起來但 CUDA 的 context 創(chuàng)建失敗因?yàn)槿绷?nvhost-ctrl-gpu。完整的六個(gè)我列在第六章的運(yùn)行命令里直接復(fù)制即可。5.4 現(xiàn)象容器內(nèi)編譯 Python報(bào) No module named _ssl在容器內(nèi)源碼編譯 Python 3.6.5make 順利通過但運(yùn)行 python 后在 import ssl 時(shí)報(bào) No module named _sslpip 也完全沒法用因?yàn)?pip 依賴 ssl 模塊。原因是編譯 Python 前沒有安裝 OpenSSL 的開發(fā)頭文件configure 階段檢測不到 libssl于是生成的 Python 里沒有 _ssl 模塊。如果你沒有在 Dockerfile 里安裝 libssl-dev直接 ./configure make默認(rèn)情況下 _ssl 是缺失的。解決方式是在編譯前先apt-get install libssl-dev -yconfigure 時(shí)帶--with-ssl然后再 make install。如果已經(jīng)編譯完成需要清理后重來在源碼目錄執(zhí)行make clean補(bǔ)齊依賴后重新 configure 和 make。5.5 現(xiàn)象docker build 慢到懷疑人生中途還報(bào)網(wǎng)絡(luò)錯(cuò)誤在 Jetson Nano 上構(gòu)建鏡像apt-get update 經(jīng)??ㄔ谀硞€(gè)奇怪的 1%然后超時(shí)或者 docker build 反復(fù)提示 unable to resolve host構(gòu)建進(jìn)程斷掉。原因有兩方面一是小內(nèi)存設(shè)備上 dockerd 和 build 進(jìn)程資源競爭嚴(yán)重二是鏡像里的 apt 源在板子網(wǎng)絡(luò)環(huán)境下延遲很高。另外如果你在容器里不斷執(zhí)行單個(gè) RUN apt-get 裝包每一層都要重新解析依賴慢上加慢。解決方式是把多個(gè) apt-get install 合并成一條 RUN減少層數(shù)和解析次數(shù)如果網(wǎng)絡(luò)確實(shí)不穩(wěn)定適當(dāng)增加 apt 的重試參數(shù)RUN apt-get update -y \ apt-get install -y --no-install-recommends \ libsqlite3-dev gcc libssl-dev make zlib1g-dev wget \ rm -rf /var/lib/apt/lists/*--no-install-recommends 能少裝很多無關(guān)推薦包顯著縮短構(gòu)建時(shí)間。rm -rf 清理 apt 緩存把鏡像層體積壓下來。這些改動不影響功能但對 Jetson Nano 這種小內(nèi)存設(shè)備的構(gòu)建體驗(yàn)提升非常大。6. 保存鏡像并遷移到另一臺 Jetsondocker commit 后的三個(gè)檢查點(diǎn)環(huán)境配置完畢最劃算的收尾方式是把容器固化成鏡像然后遷移到其他 Jetson 設(shè)備上復(fù)用。我的做法是先把容器內(nèi)環(huán)境調(diào)通再執(zhí)行提交。docker commit nano_test nano_env_img:v1 docker save -o nano_env_img_v1.tar nano_env_img:v1第一條命令把當(dāng)前運(yùn)行的容器 nano_test 保存成新鏡像 nano_env_img:v1里面對應(yīng)的是你在容器內(nèi)做的所有環(huán)境修改包括手動編譯的 Python、安裝的依賴庫。第二條命令把鏡像導(dǎo)出成 tar 文件拷貝到目標(biāo) Jetson 設(shè)備后用docker load -i nano_env_img_v1.tar導(dǎo)入。之所以用 docker save 而不是 docker export是保留鏡像的層結(jié)構(gòu)和構(gòu)建歷史后續(xù)還能基于它繼續(xù)打補(bǔ)丁docker export 打出來的是扁平文件系統(tǒng)鏡像元數(shù)據(jù)會丟掉。鏡像遷移到新設(shè)備后有三件事必須重新檢查少一件都可能白拷一次第一新設(shè)備的 daemon.json 是否也配置了 nvidia 默認(rèn)運(yùn)行時(shí)用docker info | grep -i Runtime確認(rèn) Default Runtime 為 nvidia第二新設(shè)備的/usr/lib/aarch64-linux-gnu/tegra 目錄是否存在不同 JetPack 版本的 tegra 庫版本可能不同如果新設(shè)備 JetPack 比構(gòu)建時(shí)的版本新建議在容器里重新跑一次 deviceQuery 驗(yàn)證第三運(yùn)行命令里的六個(gè) --device 節(jié)點(diǎn)和 -v 掛載一個(gè)都不能少。最終運(yùn)行命令我固定寫成這樣docker run -it \ --device/dev/nvhost-ctrl \ --device/dev/nvhost-ctrl-gpu \ --device/dev/nvhost-prof-gpu \ --device/dev/nvmap \ --device/dev/nvhost-gpu \ --device/dev/nvhost-as-gpu \ -v /usr/lib/aarch64-linux-gnu/tegra:/usr/lib/aarch64-linux-gnu/tegra \ --name nano_test nano_env_img:v1每條 --device 對應(yīng)一個(gè)具體的 Tegra 硬件資源-v 把宿主機(jī)驅(qū)動庫映射進(jìn)容器。遷移后如果結(jié)果從 PASS 變成 FAIL優(yōu)先比對這三項(xiàng)而不是重新裝 runtime。從那以后我每次換設(shè)備部署 Jetson 容器都會強(qiáng)制走一遍這套檢查流程docker info 看 runtime、ls tegra 目錄看驅(qū)動、跑 deviceQuery 看結(jié)果三個(gè)檢查做完才敢把環(huán)境交付出去。希望這份經(jīng)驗(yàn)?zāi)軒湍闵僮邘滋藦澛贰1疚倪€有配套的精品資源點(diǎn)擊獲取