資源限制

預設情況下,容器沒有資源限制,可以使用主機核心排程器允許的任意數量的給定資源。Docker 提供了控制容器可以使用多少記憶體或 CPU 的方法,透過設定 docker run 命令的執行時配置標誌。本節詳細介紹了何時應設定此類限制以及設定它們可能帶來的影響。

其中許多功能需要您的核心支援 Linux 功能。要檢查是否支援,您可以使用 docker info 命令。如果您的核心中停用了某個功能,您可能會在輸出末尾看到類似以下的警告

WARNING: No swap limit support

請查閱您作業系統的文件以啟用它們。另請參閱 Docker Engine 故障排除指南 以獲取更多資訊。

記憶體

瞭解記憶體耗盡的風險

重要的是不要允許正在執行的容器消耗過多主機的記憶體。在 Linux 主機上,如果核心檢測到沒有足夠的記憶體來執行重要的系統功能,它會丟擲 OOME,即 Out Of Memory Exception(記憶體不足異常),並開始終止程序以釋放記憶體。任何程序都可能被終止,包括 Docker 和其他重要應用程式。如果錯誤的程序被終止,這可能會有效地使整個系統癱瘓。

Docker 試圖透過調整 Docker 守護程序的 OOM 優先順序來降低這些風險,使其比系統上的其他程序更不容易被殺死。容器的 OOM 優先順序沒有被調整。這使得單個容器比 Docker 守護程序或其他系統程序更有可能被殺死。您不應該試圖透過在守護程序或容器上手動將 --oom-score-adj 設定為一個極端的負數,或者在容器上設定 --oom-kill-disable 來規避這些保護措施。

有關 Linux 核心 OOM 管理的更多資訊,請參閱 記憶體不足管理

您可以透過以下方式降低由於 OOME 導致的系統不穩定性風險:

  • 在將應用程式投入生產之前,進行測試以瞭解其記憶體需求。
  • 確保您的應用程式僅在具有足夠資源的主機上執行。
  • 如下所述,限制您的容器可以使用的記憶體量。
  • 在 Docker 主機上配置交換空間時要小心。交換空間比記憶體慢,但可以為防止系統記憶體耗盡提供一個緩衝。
  • 考慮將您的容器轉換為服務,並使用服務級別的約束和節點標籤來確保應用程式僅在具有足夠記憶體的主機上執行。

限制容器對記憶體的訪問

Docker 可以強制執行硬性或軟性記憶體限制。

  • 硬性限制讓容器使用的記憶體不超過固定數量。
  • 軟性限制允許容器根據需要使用盡可能多的記憶體,除非滿足某些條件,例如核心檢測到主機記憶體不足或出現爭用。

其中一些選項單獨使用或多個選項同時設定時會有不同的效果。

這些選項中的大多數接受一個正整數,後跟 bkmg 字尾,以表示位元組、千位元組、兆位元組或吉位元組。

選項描述
-m--memory=容器可以使用的最大記憶體量。如果設定此選項,允許的最小值為 6m(6 兆位元組)。也就是說,您必須將該值設定為至少 6 兆位元組。
--memory-swap*此容器允許交換到磁碟的記憶體量。請參閱 --memory-swap 詳情
--memory-swappiness預設情況下,主機核心可以換出容器使用的匿名頁面的一個百分比。您可以將 --memory-swappiness 設定為 0 到 100 之間的值,以調整此百分比。請參閱 --memory-swappiness 詳情
--memory-reservation允許您指定一個小於 --memory 的軟性限制,當 Docker 檢測到主機上的爭用或記憶體不足時啟用。如果您使用 --memory-reservation,它必須設定得低於 --memory 才能生效。因為它是一個軟性限制,所以它不保證容器不會超過該限制。
--kernel-memory容器可以使用的最大核心記憶體量。允許的最小值為 6m。因為核心記憶體不能被換出,一個核心記憶體不足的容器可能會阻塞主機資源,這會對主機和其他容器產生副作用。請參閱 --kernel-memory 詳情
--oom-kill-disable預設情況下,如果發生記憶體不足 (OOM) 錯誤,核心會殺死容器中的程序。要更改此行為,請使用 --oom-kill-disable 選項。僅在您也設定了 -m/--memory 選項的容器上停用 OOM killer。如果未設定 -m 標誌,主機可能會耗盡記憶體,核心可能需要殺死主機的系統程序以釋放記憶體。

有關 cgroups 和記憶體的更多資訊,請參閱 記憶體資源控制器 的文件。

--memory-swap 詳情

--memory-swap 是一個修飾符標誌,僅當 --memory 也被設定時才有意義。使用交換空間允許容器在耗盡所有可用 RAM 時將超出的記憶體需求寫入磁碟。對於頻繁將記憶體交換到磁碟的應用程式,會存在效能損失。

它的設定可能會產生複雜的效果:

  • 如果 --memory-swap 設定為一個正整數,則 --memory--memory-swap 都必須設定。--memory-swap 表示可以使用的記憶體和交換空間的總量,而 --memory 控制非交換記憶體的使用量。因此,如果 --memory="300m"--memory-swap="1g",容器可以使用 300m 的記憶體和 700m (1g - 300m) 的交換空間。

  • 如果 --memory-swap 設定為 0,該設定將被忽略,並且該值被視為未設定。

  • 如果 --memory-swap 的值與 --memory 相同,且 --memory 設定為一個正整數,則**容器無法訪問交換空間**。請參閱防止容器使用交換空間

  • 如果 --memory-swap 未設定,而 --memory 已設定,則如果主機容器配置了交換記憶體,容器可以使用的交換空間量與 --memory 設定相同。例如,如果 --memory="300m"--memory-swap 未設定,容器總共可以使用 600m 的記憶體和交換空間。

  • 如果 --memory-swap 明確設定為 -1,則允許容器使用無限的交換空間,最高可達主機系統上可用的量。

  • 在容器內部,像 free 這樣的工具報告的是主機的可用交換空間,而不是容器內部可用的。不要依賴 free 或類似工具的輸出來判斷是否存在交換空間。

防止容器使用交換空間

如果 --memory--memory-swap 設定為相同的值,這將阻止容器使用任何交換空間。這是因為 --memory-swap 是可以使用的記憶體和交換空間的總和,而 --memory 只是可以使用的物理記憶體量。

--memory-swappiness 詳情

  • 值為 0 會關閉匿名頁交換。
  • 值為 100 將所有匿名頁設定為可交換。
  • 預設情況下,如果您不設定 --memory-swappiness,該值會從主機繼承。

--kernel-memory 詳情

核心記憶體限制以分配給容器的總記憶體來表示。考慮以下場景:

  • **無限記憶體,無限核心記憶體**:這是預設行為。
  • **無限記憶體,有限核心記憶體**:當所有 cgroups 需要的記憶體總量大於主機上實際存在的記憶體量時,這種設定是合適的。您可以配置核心記憶體永遠不超過主機上可用的量,需要更多記憶體的容器需要等待。
  • **有限記憶體,無限核心記憶體**:總記憶體是有限的,但核心記憶體不是。
  • **有限記憶體,有限核心記憶體**:限制使用者和核心記憶體對於除錯記憶體相關問題很有用。如果一個容器使用了超出預期的任何一種記憶體,它會在不影響其他容器或主機的情況下耗盡記憶體。在此設定下,如果核心記憶體限制低於使用者記憶體限制,核心記憶體耗盡會導致容器遇到 OOM 錯誤。如果核心記憶體限制高於使用者記憶體限制,核心限制不會導致容器遇到 OOM。

當您啟用核心記憶體限制時,主機會按程序跟蹤“高水位”統計資料,這樣您就可以跟蹤哪些程序(在這種情況下是容器)正在使用過多的記憶體。這可以透過在主機上檢視 /proc/<PID>/status 來按程序檢視。

CPU

預設情況下,每個容器對主機 CPU 週期的訪問是無限的。您可以設定各種約束來限制給定容器對主機 CPU 週期的訪問。大多數使用者使用和配置預設的 CFS 排程器。您也可以配置即時排程器

配置預設 CFS 排程器

CFS 是 Linux 核心中用於普通 Linux 程序的 CPU 排程器。幾個執行時標誌讓您配置容器對 CPU 資源的訪問量。當您使用這些設定時,Docker 會修改主機上容器 cgroup 的設定。

選項描述
--cpus=<value>指定一個容器可以使用多少可用 CPU 資源。例如,如果主機有兩個 CPU,並且您設定了 --cpus="1.5",則容器最多保證可以使用一個半 CPU。這相當於設定 --cpu-period="100000"--cpu-quota="150000"
--cpu-period=<value>指定 CPU CFS 排程器週期,與 --cpu-quota 一起使用。預設為 100000 微秒(100 毫秒)。大多數使用者不會更改此預設值。對於大多數用例,--cpus 是一個更方便的替代方案。
--cpu-quota=<value>對容器施加 CPU CFS 配額。在被節流之前,容器在每個 --cpu-period 期間被限制的微秒數。因此,它充當了有效的上限。對於大多數用例,--cpus 是一個更方便的替代方案。
--cpuset-cpus限制容器可以使用的特定 CPU 或核心。如果您的 CPU 不止一個,這是一個逗號分隔的列表或連字元分隔的範圍,表示容器可以使用的 CPU。第一個 CPU 編號為 0。一個有效的值可能是 0-3(使用第一、二、三、四個 CPU)或 1,3(使用第二和第四個 CPU)。
--cpu-shares將此標誌設定為大於或小於預設值 1024 的值,以增加或減少容器的權重,並使其能夠訪問更多或更少比例的主機 CPU 週期。這僅在 CPU 週期受限時強制執行。當有足夠的 CPU 週期可用時,所有容器都會根據需要使用盡可能多的 CPU。這樣,這是一個軟性限制。--cpu-shares 不會阻止容器在 Swarm 模式下被排程。它會為可用的 CPU 週期優先分配容器的 CPU 資源。它不保證或保留任何特定的 CPU 訪問許可權。

如果您有 1 個 CPU,以下每個命令都保證容器每秒最多佔用 50% 的 CPU。

$ docker run -it --cpus=".5" ubuntu /bin/bash

這等同於手動指定 --cpu-period--cpu-quota

$ docker run -it --cpu-period=100000 --cpu-quota=50000 ubuntu /bin/bash

配置即時排程器

您可以將容器配置為使用即時排程器,用於那些不能使用 CFS 排程器的任務。您需要確保主機核心已正確配置,然後才能配置 Docker 守護程序配置單個容器

警告

CPU 排程和優先順序是高階的核心級功能。大多數使用者不需要更改這些值的預設設定。不正確地設定這些值可能會導致您的主機系統變得不穩定或無法使用。

配置主機的核心

透過執行 zcat /proc/config.gz | grep CONFIG_RT_GROUP_SCHED 或檢查檔案 /sys/fs/cgroup/cpu.rt_runtime_us 是否存在,來驗證 Linux 核心中是否啟用了 CONFIG_RT_GROUP_SCHED。有關配置核心即時排程器的指導,請查閱您作業系統的文件。

配置 Docker 守護程序

要使用即時排程器執行容器,請在執行 Docker 守護程序時,將 --cpu-rt-runtime 標誌設定為每個執行時週期為即時任務保留的最大微秒數。例如,對於預設的 1000000 微秒(1 秒)週期,設定 --cpu-rt-runtime=950000 可確保使用即時排程器的容器在每個 1000000 微秒的週期內可以執行 950000 微秒,為非即時任務留下至少 50000 微秒。要在使用 systemd 的系統上使此配置永久生效,請為 docker 服務建立一個 systemd 單元檔案。例如,請參閱關於如何使用 systemd 單元檔案 配置守護程序以使用代理的說明。

配置單個容器

當您使用 docker run 啟動容器時,可以傳遞幾個標誌來控制容器的 CPU 優先順序。請查閱您作業系統的文件或 ulimit 命令以獲取有關適當值的資訊。

選項描述
--cap-add=sys_nice授予容器 CAP_SYS_NICE 能力,這允許容器提高程序的 nice 值、設定即時排程策略、設定 CPU 親和性以及其他操作。
--cpu-rt-runtime=<value>在 Docker 守護程序的即時排程器週期內,容器可以以即時優先順序執行的最大微秒數。您還需要 --cap-add=sys_nice 標誌。
--ulimit rtprio=<value>允許容器使用的最大即時優先順序。您還需要 --cap-add=sys_nice 標誌。

以下示例命令在 debian:jessie 容器上設定了這三個標誌。

$ docker run -it \
    --cpu-rt-runtime=950000 \
    --ulimit rtprio=99 \
    --cap-add=sys_nice \
    debian:jessie

如果核心或 Docker 守護程序配置不正確,將會發生錯誤。

GPU

訪問 NVIDIA GPU

先決條件

訪問官方 NVIDIA 驅動程式頁面 下載並安裝合適的驅動程式。完成後請重新啟動您的系統。

驗證您的 GPU 是否正在執行且可訪問。

安裝 nvidia-container-toolkit

請遵循官方 NVIDIA Container Toolkit 安裝說明

開放 GPU 以供使用

在啟動容器時包含 --gpus 標誌以訪問 GPU 資源。指定要使用的 GPU 數量。例如:

$ docker run -it --rm --gpus all ubuntu nvidia-smi

暴露所有可用的 GPU 並返回類似於以下內容的結果:

+-------------------------------------------------------------------------------+
| NVIDIA-SMI 384.130            	Driver Version: 384.130               	|
|-------------------------------+----------------------+------------------------+
| GPU  Name 	   Persistence-M| Bus-Id    	Disp.A | Volatile Uncorr. ECC   |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M.   |
|===============================+======================+========================|
|   0  GRID K520       	Off  | 00000000:00:03.0 Off |                  N/A      |
| N/A   36C	P0    39W / 125W |  	0MiB /  4036MiB |      0%  	Default |
+-------------------------------+----------------------+------------------------+
+-------------------------------------------------------------------------------+
| Processes:                                                       GPU Memory   |
|  GPU   	PID   Type   Process name                         	Usage  	|
|===============================================================================|
|  No running processes found                                                   |
+-------------------------------------------------------------------------------+

使用 device 選項來指定 GPU。例如:

$ docker run -it --rm --gpus device=GPU-3a23c669-1f69-c64e-cf85-44e9b07e7a2a ubuntu nvidia-smi

暴露那個特定的 GPU。

$ docker run -it --rm --gpus '"device=0,2"' ubuntu nvidia-smi

暴露第一個和第三個 GPU。

注意

NVIDIA GPU 只能由執行單個引擎的系統訪問。

設定 NVIDIA 功能

您可以手動設定功能。例如,在 Ubuntu 上,您可以執行以下命令:

$ docker run --gpus 'all,capabilities=utility' --rm ubuntu nvidia-smi

這將啟用 utility 驅動程式功能,該功能會將 nvidia-smi 工具新增到容器中。

功能以及其他配置可以透過環境變數在映象中設定。有關有效變數的更多資訊,請參見 nvidia-container-toolkit 文件。這些變數可以在 Dockerfile 中設定。

您也可以使用 CUDA 映象,它們會自動設定這些變數。請參閱官方 CUDA 映象 NGC 目錄頁面。