計算任務容器化
Docker 是目前業內主流的容器技術,在各個領域得到廣泛的應用。容器的概念具有新穎的設計思想,非常方便開發、測試以及發布產品,并能保證在這幾個環節中軟件運行環境的一致性。Docker 和虛擬機是不一樣的,兩者差異很大,虛擬機需要大量的資源來模擬硬件,運行操作系統,而 Docker 幾乎不會有計算性能的損失,輕量級是其顯著特點,所以得到廣泛的應用。
容器本質上是宿主機上的一個進程,一般來說,容器技術主要包括 cgroups (control groups) 和 namespace 這兩個內核特性,namespace 實現資源隔離,cgroups 實現資源限制。除此之外,在容器鏡像制作時,利用UnionFS 實現 Copy on Write 的 Volume 文件系統。
Namespace 的作用就是隔離,是 Linux 在內核級別的隔離技術,從內核 2.4.19 開始逐漸引入并完善。它可以讓進程擁有自己獨立的進程號,網絡,文件系統(類似 chroot )等,不同 namespace 下的進程之間相互不可見。主要包括以下六類 namespace:
Mount namespace系統內核版本 >2.4.19
實現文件系統掛載
每個容器內有獨立的文件系統層次結構
UTS namespace
系統內核版本 >2.6.19
每個容器有獨立的 hostname 和 domain
name
IPC namespace
系統內核版本 >2.6.19
每個容器內有獨立的 System V IPC 和
POSIX 消息隊列系統
PID namespace
系統內核版本 >2.6.24
每個 PID namespace 中的進程有獨立的 PID,容器中的每個進程有兩個PID:容器中的 PID 和 host 上的 PID
Network namespace
系統內核版本 >2.6.29
每個容器用有獨立的網絡設備,IP 地址、
IP 路由表、端口號等
User namespace
系統內核版本 >3.8
容器中進程的用戶和組 ID 和 host 上不
同,每個容器有不同的用戶和組 、ID;
host 上的非 root 可以成為
usernamespace 中的root
容器通過 namespace 實現資源的隔離,每個容器可以有上述六種獨立的 namespace,有了資源隔離之后,如果不能對各個 namespace 下進程的資源使用做限制的話,那么 namespace 隔離也沒有意義了,所以在容器中,我們利用 cgroups 實現對進程以及其子進程的資源限制。
Cgroups 顧名思義就是把進程放到一個組里統一加以控制。根據官方的定義:
cgroups 是 Linux 內核提供的一種機制,這種機制可以根據特定的行為,把一系列系統任務及其子任務整合(或分隔)到按資源劃分等級的不同組內,從而為系統資源管理提供一個統一的框架。
通俗的來說,cgroups 可以限制、記錄、隔離進程組所使用的物理資源(包括:CPU、memory、IO 等)。需要說明是,我們可以通過本地的文件系統來管理 cgroups 配置,就像修改 /sys 目錄下的文件內容一樣,來類似修改cgroups配置。而且,在 docker引擎這一層已經幫我們屏蔽了底層的細節,我們可以通過 docker 命令很方便的配置 cgroups 相關的參數。
針對CPU資源,cgroups 提供了三種限制CPU資源的方式:cpuset、cpuquota 和 cpushares。針對內存資源,提供包括物理內存和swap兩塊的限制。另外,blkio子系統的功能提供對塊設備讀寫的速率限制。由于篇幅限制,關于cgroups 的介紹不詳細展開。
除了上述 namespace 和 cgroups 實現資源隔離和限制之外,容器利用 UnionFS 提供容器鏡像 (images) 的基礎文件系統。UnionFS 文件系統是分層的,當我們需要修改一個文件的內容的時候,會將這個文件拷貝一份新的放到最上層的目錄上然后修改文件,實際上,下層的目錄并沒有改動。這也是容器鏡像文件系統的一個核心特性,基于此特性:
實現容器鏡像的分層復用和共享,在基礎鏡像的基礎上構建新鏡像,而不需要從零開始構建,容器鏡像也有自己的標準規范(https://github.com/opencontainers/image-spec),多鏡像之間可以復用共享,還可以通過 DockerHub 等網站共享與分發鏡像。
由于 Copy on Write 特性,在使用時需要注意避免不當的構建方式,導致鏡像的 size 過大。
容器鏡像中只包含必要的運行環境(依賴的庫等),,同一節點上的各個容器共享 kernel,相比與虛擬機的重量級封裝,鏡像的 size 可以做的很小。
容器的核心特點是輕量級的資源隔離、限制和封裝,極大的簡化了程序運行時的環境依賴,同時還能解決多版本共存運行的問題,計算程序和運行節點之間獨立,為進一步的多節點間的自動調度奠定了基礎。下圖展示的是容器中計算程序封裝的邏輯圖。其最大好處是整體運行環境的封裝,在此邏輯下,用戶也會逐步從傳統的 make build 生成可執行文件過渡到利用 docker build 制作應用鏡像,容器的使用也會越來越廣泛。
總結一下,使用容器技術作為計算程序運行環境封裝,帶來諸多優勢:
一致的運行環境,解決計算框架環境依賴、安裝配置繁瑣的問題;
多個計算框架可以共存運行,同一個計算框架的多版本也可以共存運行;
計算節點與運行環境獨立,這是實現多節點計算自動調度的基礎;
一次構建,隨時隨地多次運行;
基于上述容器技術帶來的優勢,目前主流的 AI 計算框架都支持以容器的方式運行,也逐漸成為社區的標準運行方式,TensorFlow 官方也在 Dockerhub 上直接提供 TensorFlow 各個版本的相關鏡像下載。其他 AI 計算框架也以提供 Dockerfile 的形式或者直接提供 Docker images 供用戶使用。同時,用戶也可以根據自己的計算需求或者以自己的應用算法為基礎,創建和維護專用鏡像文件。
GPU計算任務容器化
AI 算法目前使用 GPU 提供計算加速,所以,容器方式運行的 AI 計算框架需要同時支持 CPU 和 GPU 兩種運行方式。CPU 版的運行和常規容器運行方式無異,GPU 版的運行方式略微復雜,為此 Nvidia 官方推出 Nvidia-docker(https://github.com/NVIDIA/nvidia-docker)工具簡化 GPU 版的運行方式和流程。在介紹 nvidia-docker 工具之前,我們先看下 GPU 容器的架構邏輯圖,如下所示,從下往上分為:
硬件層:在每個 GPU 計算節點上配置一個或者多個 GPU 硬件資源。
軟件層:每個計算節點安裝操作系統和相應的 GPU drivers。
Docker:每個計算節點安裝 Docker engine 服務。
容器:容器中包含計算相關的運行環境和 CUDA 庫,并且將 host 上的 GPU 設備和驅動動態庫映射到容器中。
用戶可以通過 docker 命令 --device 和 --volume 等參數實現 GPU 設備和動態庫映射,但是,使用起來不方便,而 nvidia-docker 的功能就是簡化 GPU 容器創建和運行,其是一個 wrapper 的 Bash 腳本,封裝了 docker 的命令參數。從下面腳本中可以看出,當用戶使用 nvidia-docker 執行 run 和 create 這兩個子命令時,nvidia-docker 做了兩個改變:
切換runtime 為 nvidia runtime
如果用戶定義了 NV_GPU 環境變量,則將 NV_GPU 環境變量的信息傳給環境變量 NVIDIA_VISIBLE_DEVICES
基于以上分析,我們可以得到兩個結論:
nvidia-docker 在除 run 和 create 之外的其他子命令,和普通的 docker 并無太大區別。所以,我們沒法在使用其他 docker 子命令時(比如 docker build),執行需要使用 GPU 資源的相關代碼。
nvidia-docker 實現簡化 GPU container 運行,實際上是通過 nvidia runtime 實現,并利用 runtime 的環境變量 NVIDIA_VISIBLE_DEVICES 控制容器中能夠使用的 GPU 數量。
NVIDIA_VISIBLE_DEVICES 默認值是 all,其含義是 GPU Container 可使用計算節點上的所有 GPU 資源,用戶利用 NV_GPU 控制可用 GPU 資源數量,而 nvidia-docker 腳本再將 NV_GPU 傳遞給 NVIDIA_VISIBLE_DEVICES。我們可以通過兩種方式設置 NV_GPU:
指定 GPU index,此 index 和 nvidia-smi 顯示的 GPU index 一致
指定 GPU 的 UUID
如下面所示:
# Running nvidia-docker isolating specific GPUs by index
NV_GPU='0,1' nvidia-docker
# Running nvidia-docker isolating specific GPUs by UUID
NV_GPU='GPU-836c0c09,GPU-b78a60a' nvidia-docker
下圖展示的是 nvidia-docker2 的工作邏輯圖,GPU 相關的核心工作是由 nvidia-container-runtime 實現的。nvidia container runtime 是 Nvidia 定義的一種 GPU 運行環境 (https://github.com/NVIDIA/nvidia-container-runtime),基于對 runc (https://github.com/opencontainers/runc)運行環境的一種修改版本,符合 openContainer 的 OCI 標準定義。
為了提供一定的靈活性,nvidia-container-runtime 定義了一些 GPU 相關的環境變量。比如,控制 GPU 使用數目的 NVIDIA_VISIBLE_DEVICES。下面所示的是兩個常用的環境變量示例。詳細的環境變量參見:
https://github.com/NVIDIA/nvidia-container-runtime#environment-variables-oci-spec
NVIDIA_VISIBLE_DEVICES:控制在容器中使用的 GPU 數目,可選項:
0,1,2, GPU-fef8089b …: GPU UUID 或者 GPU 索引列表
all: 默認值,在容器中可以使用節點上的所有 GPU 資源
none: 只加載 GPU 驅動,但 GPU 設備不可使用
void or empty or unset: 等同于常規的 runc
NVIDIA_DRIVER_CAPABILITIES:控制容器如何掛載 GPU 驅動和附屬文件,可選項:
compute,video,graphics,utility …: 驅動功能列表,對于 deep learning 需要開啟 compute
compute: CUDA and OpenCL 計算應用
compat32: 兼容 32位計算應用
graphics: OpenGL and Vulkan 圖形應用
utility: nvidia-smi and NVML 等命令行工具使用
video: 視頻編解碼
all: 開啟驅動所有功能
empty or unset: 空或者不設置,默認值為:utility
我們根據nvidia-container-runtime Github 上的代碼,具體分析其是如何工作的。首先,我們看一下 nvidia-container-runtime 如何編譯出來的,通過下面節選的關鍵代碼,nvidia-container-runtime 是在 runc 代碼的基礎上,打上一個 patch,然后編譯,并重命名為 nvidia-container-runtime。
此 patch 加了一個 prestart Hook,在容器創建時,執行程序 nvidia-container-runtime-hook。除此改動之外,nvidia-container-runtime 其余相關的功能依賴 runc 實現。nvidia-container-runtime-hook 是一個 go 語言實現的可執行程序,主要功能是讀取當前容器的配置信息 config.json,和 runtime 配置文件 /etc/nvidia-container-runtime/config.toml,并處理 runtime 定義的環境變量。
nvidia-container-runtime-hook 的上述功能是通過調用 nvidia-container-cli 實現 (https://github.com/NVIDIA/libnvidia-container),nvidia-container-cli 也是 Nvidia 官方維護的一個 GPU 工具,其主要功能包括:
加載 GPU 驅動運行動態庫
掛載 GPU 設備文件
配置參數
更新系統動態庫緩存
下圖展示的是 nvidia-docker2 調用邏輯與環境變量傳遞圖,從 nvidia-docker2 開始,容器最終的 GPU 配置由 nvidia-container-cli 執行。圖中右側是用戶可以設置的環境變量傳遞圖,從 nvidia-docker2 層級的 NV_GPU 到最終轉化為 nvidia-container-cli 命令的 --compute --device 等參數。
總結
本篇針對如何簡單有效的實現在單機上使用 GPU 和 TensorFlow 等 AI 計算框架,深入介紹任務容器化的基本思路和概念,詳細分析了 nvidia-docker2 的工作流程和實現邏輯。借助 nvidia-docker2,在單機上運行 AI 計算任務更加方便,解決了安裝配置框架以及多版本共存運行的問題,也避免了配置使用 GPU 的繁瑣工作量。
本篇為 AI 計算系列:從單機到集群(上),那么后續篇 —— 從單機到集群(中)繼續為大家帶來 AI 計算系列的分享,將在本篇介紹單機 AI 計算的基礎上,實現從單機到集群的跨越,重點介紹如何構建面向 AI 計算的 GPU 集群,分析最新 AI 計算集群的 GPU 資源調度邏輯,幫助大家更進一步理解如何實現多機 GPU 資源的自動分配和管理。
-
硬件
+關注
關注
11文章
3260瀏覽量
66132 -
軟件
+關注
關注
69文章
4799瀏覽量
87178 -
人工智能
+關注
關注
1791文章
46896瀏覽量
237670
發布評論請先 登錄
相關推薦
評論