資源約束

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

許多這些功能需要你的核心支援 Linux capabilities。要檢查支援情況,可以使用 docker info 命令。如果你的核心中停用了某個 capability,你可能會在輸出末尾看到類似以下的警告:

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 主機上配置 swap 時要小心。Swap 比記憶體慢,但可以作為防止系統記憶體耗盡的緩衝區。
  • 考慮將你的容器轉換為服務,並使用服務級約束和節點標籤來確保應用程式僅在具有足夠記憶體的主機上執行。

限制容器對記憶體的訪問

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 時才有意義。使用 swap 允許容器在耗盡所有可用 RAM 後將額外的記憶體需求寫入磁碟。對於經常將記憶體交換到磁碟的應用程式,會產生效能損失。

其設定可能產生複雜的影響:

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

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

  • 如果 --memory-swap 設定為與 --memory 相同的值,且 --memory 設定為正整數,則容器無權訪問 swap。參見 阻止容器使用 swap

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

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

  • 在容器內部,free 等工具報告的是主機的可用 swap,而不是容器內部的可用 swap。不要依賴 free 或類似工具的輸出來確定是否存在 swap。

阻止容器使用 swap

如果 --memory--memory-swap 設定為相同的值,這將阻止容器使用任何 swap。這是因為 --memory-swap 是記憶體和 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=<值>指定容器可以使用多少可用的 CPU 資源。例如,如果主機有兩個 CPU,並且你設定了 --cpus="1.5",則容器最多保證使用一核半 CPU。這等同於設定 --cpu-period="100000"--cpu-quota="150000"
--cpu-period=<值>指定 CPU CFS 排程週期,與 --cpu-quota 一起使用。預設值為 100000 微秒(100 毫秒)。大多數使用者不會更改此預設值。對於大多數用例,--cpus 是一個更方便的替代方法。
--cpu-quota=<值>對容器強制執行 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 守護程式

要使用即時排程程式執行容器,請使用 --cpu-rt-runtime 標誌啟動 Docker 守護程式,該標誌設定為每個執行時週期為即時任務保留的最大微秒數。例如,在預設週期為 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 目錄頁面。

頁面選項