資源限制
依預設,容器沒有資源限制,可以根據主機核心排程器的允許使用任意量的指定資源。Docker 提供了方法來控制容器可以使用多少記憶體或 CPU,透過設定 `docker run` 命令的執行時組態旗標。本節詳細說明了何時應設定此類限制以及設定它們可能造成的影響。
這些功能中有許多需要您的核心支援 Linux 功能(capabilities)。要檢查支援情況,您可以使用 `docker info` 命令。如果您的核心中某項功能被禁用,您可能會在輸出結尾看到類似以下的警告:
WARNING: No swap limit support
請查閱您的作業系統文件以啟用這些功能。另請參閱 Docker 引擎疑難排解指南 以獲取更多資訊。
記憶體
了解記憶體不足的風險
不應允許執行中的容器消耗過多的主機記憶體,這一點很重要。在 Linux 主機上,如果核心偵測到沒有足夠的記憶體來執行重要的系統功能,它會拋出 `OOME`(或稱為 `記憶體不足例外`),並開始終止程序以釋放記憶體。任何程序都可能被終止,包括 Docker 和其他重要應用程式。如果錯誤的程序被終止,這可能會導致整個系統癱瘓。
Docker 嘗試透過調整 Docker 守護程式的 OOM 優先級來減輕這些風險,使其比系統上的其他程序更不容易被終止。容器的 OOM 優先級未經調整。這使得個別容器比 Docker 守護程式或其他系統程序更有可能被終止。您不應嘗試透過在守護程式或容器上手動將 `--oom-score-adj` 設定為極端負數,或在容器上設定 `--oom-kill-disable` 來規避這些保護措施。
有關 Linux 核心 OOM 管理的更多資訊,請參閱 記憶體不足管理。
您可以透過以下方式減輕因 OOME 導致的系統不穩定風險:
- 在將應用程式投入生產環境之前,進行測試以了解其記憶體需求。
- 確保您的應用程式僅在具備足夠資源的主機上運行。
- 限制容器可以使用的記憶體量,如下所述。
- 在 Docker 主機上設定交換空間(swap)時請務必留意。交換空間比記憶體慢,但可以作為應對系統記憶體不足的緩衝。
- 考慮將您的容器轉換為一個 服務,並使用服務層級約束和節點標籤來確保應用程式僅在具有足夠記憶體的主機上運行
限制容器對記憶體的存取
Docker 可以實施硬性或軟性記憶體限制。
- 硬性限制讓容器使用的記憶體量不超過一個固定值。
- 軟性限制允許容器使用所需的記憶體量,除非滿足特定條件,例如核心偵測到主機記憶體不足或資源爭用。
這些選項中的一些在單獨使用或多個選項同時設定時,會產生不同的效果。
這些選項大多接受一個正整數,後接 `b`、`k`、`m`、`g` 字尾,分別表示位元組(bytes)、千位元組(kilobytes)、百萬位元組(megabytes)或十億位元組(gigabytes)。
| 選項 | 描述 |
|---|---|
-m 或 `--memory=` | 容器可以使用的最大記憶體量。如果您設定此選項,允許的最小值為 `6m` (6 百萬位元組)。也就是說,您必須將值設定為至少 6 百萬位元組。 |
--memory-swap* | 此容器允許交換(swap)到磁碟的記憶體量。請參閱 `--memory-swap` 詳細資訊。 |
--memory-swappiness | 依預設,主機核心可以將容器使用的匿名頁面(anonymous pages)的一部分交換出去。您可以將 `--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 終止器(OOM killer)。如果未設定 `-m` 旗標,主機可能會記憶體不足,核心可能需要終止主機系統的程序以釋放記憶體。 |
有關 cgroups 和一般記憶體的更多資訊,請參閱 記憶體資源控制器 的文件。
--memory-swap 詳細資訊
--memory-swap 是一個修飾旗標,只有在 `--memory` 也設定時才有意義。使用交換空間允許容器在其所有可用記憶體耗盡時,將多餘的記憶體需求寫入磁碟。頻繁將記憶體交換到磁碟的應用程式會受到效能損失。
其設定可能產生複雜的效果:
如果 `--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 會關閉匿名頁面(anonymous page)交換功能。
- 設定為 100 會將所有匿名頁面設定為可交換。
- 依預設,如果您沒有設定 `--memory-swappiness`,則該值會從主機繼承。
--kernel-memory 詳細資訊
核心記憶體限制是根據分配給容器的總記憶體來表達的。請考慮以下情境:
- **無限記憶體,無限核心記憶體**:這是預設行為。
- **無限記憶體,有限核心記憶體**:當所有 cgroup 所需的記憶體量大於主機上實際存在的記憶體量時,此設定適用。您可以將核心記憶體配置為永遠不超過主機上可用的記憶體量,而需要更多記憶體的容器則需要等待。
- **有限記憶體,無限核心記憶體**:整體記憶體是有限的,但核心記憶體不受限制。
- **有限記憶體,有限核心記憶體**:限制使用者和核心記憶體對於偵錯記憶體相關問題很有用。如果容器使用了意外量的任何一種記憶體,它會記憶體不足,但不會影響其他容器或主機。在此設定下,如果核心記憶體限制低於使用者記憶體限制,則核心記憶體不足會導致容器發生 OOM 錯誤。如果核心記憶體限制高於使用者記憶體限制,則核心限制不會導致容器發生 OOM。
當您啟用核心記憶體限制時,主機會追蹤每個程序的「高水位線」統計數據,以便您可以追蹤哪些程序(在此情況下為容器)正在使用過多的記憶體。這可以透過在主機上查看 `/proc/
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 配額。在被節流(throttled)之前,容器在每個 `--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 存取權。 |
如果您有一個 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 單位檔案(unit file)。例如,請參閱有關如何使用 systemd 單位檔案 為守護程式設定代理的說明。
設定個別容器
當您使用 `docker run` 啟動容器時,可以傳遞多個旗標來控制容器的 CPU 優先級。請查閱您的作業系統文件或 `ulimit` 命令,以獲取有關適當值的資訊。
| 選項 | 描述 |
|---|---|
--cap-add=sys_nice | 授予容器 `CAP_SYS_NICE` 功能,這允許容器提高程序 `nice` 值,設定即時排程策略,設定 CPU 親和性(affinity),以及其他操作。 |
--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 以供使用
當您啟動容器以存取 GPU 資源時,請包含 `--gpus` 旗標。指定要使用的 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 目錄頁面。