Очаквано време за четене: 16 минути

докер

По подразбиране контейнерът няма ограничения за ресурси и може да използва толкова от даден ресурс, колкото позволява планиращото ядро ​​на хоста. Docker предоставя начини за контролиране на това колко памет или CPU може да използва контейнер, като задава флагове за конфигуриране по време на изпълнение на командата за изпълнение на docker. Този раздел предоставя подробности за това кога трябва да зададете такива ограничения и възможните последици от тяхното определяне.

Много от тези функции изискват вашето ядро ​​да поддържа възможности на Linux. За да проверите за поддръжка, можете да използвате командата докер информация. Ако дадена възможност е деактивирана във вашето ядро, може да видите предупреждение в края на изхода, както следва:

Консултирайте се с документацията на вашата операционна система, за да ги активирате. Научете повече.

Памет

Разберете рисковете от изчерпване на паметта

Важно е да не позволявате на работещ контейнер да консумира твърде много памет на хост машината. На Linux хостове, ако ядрото открие, че няма достатъчно памет за изпълнение на важни системни функции, то изхвърля OOME или Out Of Memory Exception и започва да убива процеси, за да освободи паметта. Всеки процес подлежи на убиване, включително Docker и други важни приложения. Това може ефективно да срине цялата система, ако бъде убит грешен процес.

Docker се опитва да смекчи тези рискове, като коригира приоритета на OOM в демона на Docker, така че да е по-малко вероятно да бъде убит от други процеси в системата. Приоритетът на OOM за контейнери не е коригиран. Това прави по-вероятно да бъде убит отделен контейнер, отколкото да бъде убит демонът на Docker или други системни процеси. Не бива да се опитвате да заобикаляте тези предпазни мерки, като ръчно задавате --oom-score-adj на крайно отрицателно число на демона или контейнер или като задавате --oom-kill-disable на контейнер.

За повече информация относно управлението на OOM на ядрото на Linux, вижте Управление извън паметта.

Можете да смекчите риска от нестабилност на системата поради OOME чрез:

  • Извършете тестове, за да разберете изискванията за паметта на вашето приложение, преди да го пуснете в производство.
  • Уверете се, че вашето приложение се изпълнява само на хостове с подходящи ресурси.
  • Ограничете размера на паметта, която контейнерът ви може да използва, както е описано по-долу.
  • Бъдете внимателни, когато конфигурирате суап на вашите хостове на Docker. Размяната е по-бавна и по-малко ефективна от паметта, но може да осигури буфер срещу изчерпване на системната памет.
  • Помислете за преобразуване на вашия контейнер в услуга и използване на ограничения на ниво услуга и етикети на възли, за да сте сигурни, че приложението работи само на хостове с достатъчно памет

Ограничете достъпа на контейнера до паметта

Docker може да налага твърди ограничения на паметта, които позволяват на контейнера да използва не повече от дадено количество потребителска или системна памет, или меки ограничения, които позволяват на контейнера да използва толкова памет, колкото е необходимо, освен ако не са изпълнени определени условия, като например ядрото открива недостатъчна памет или спорове на хост машината. Някои от тези опции имат различни ефекти, когато се използват самостоятелно или когато са зададени повече от една опция.

Повечето от тези опции вземат положително цяло число, последвано от суфикс b, k, m, g, за да посочат байтове, килобайтове, мегабайти или гигабайта.

За повече информация относно cgroups и паметта като цяло вижте документацията за Memory Resource Controller.

--подробности за размяна на памет

--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, ако в хост контейнера е конфигурирана swap памет. Например, ако --memory = "300m" и --memory-swap не е зададено, контейнерът може да използва общо 600m памет и суап.

Ако --memory-swap е изрично зададено на -1, контейнерът има право да използва неограничен суап, до сумата, налична в хост системата.

Вътре в контейнера инструменти като безплатни отчитат наличния суап на хоста, а не това, което е налично в контейнера. Не разчитайте на резултатите от безплатни или подобни инструменти, за да определите дали е налице суап.

Предотвратете контейнера да използва суап

Ако --memory и --memory-swap са зададени на една и съща стойност, това пречи на контейнерите да използват какъвто и да е суап. Това е така, защото --memory-swap е количеството комбинирана памет и суап, което може да се използва, докато --memory е само количеството физическа памет, което може да се използва.

--подробности за паметта

  • Стойност 0 изключва анонимната размяна на страници.
  • Стойност 100 задава всички анонимни страници като сменяеми.
  • По подразбиране, ако не зададете --memory-swappiness, стойността се наследява от хост машината.

--подробности за паметта на ядрото

Ограниченията на паметта на ядрото се изразяват по отношение на общата памет, разпределена за контейнер. Обмислете следните сценарии:

  • Неограничена памет, неограничена памет на ядрото: Това е поведението по подразбиране.
  • Неограничена памет, ограничена памет на ядрото: Това е подходящо, когато количеството памет, необходимо на всички cgroups, е по-голямо от количеството памет, което действително съществува на хост машината. Можете да конфигурирате паметта на ядрото така, че никога да не преглежда наличното на хост машината, а контейнерите, които се нуждаят от повече памет, трябва да го изчакат.
  • Ограничена памет, неограничена памет на ядрото: Общата памет е ограничена, но паметта на ядрото не е.
  • Ограничена памет, ограничена памет на ядрото: Ограничаването както на потребителска, така и на ядрена памет може да бъде полезно за отстраняване на грешки, свързани с паметта. Ако контейнер използва неочаквано количество памет от всеки тип, паметта му свършва, без да се засягат други контейнери или хост машината. В рамките на тази настройка, ако ограничението на паметта на ядрото е по-ниско от ограничението на паметта на потребителя, изчерпването на паметта на ядрото води до грешка в OOM на контейнера. Ако ограничението на паметта на ядрото е по-високо от ограничението на паметта на потребителя, ограничението на ядрото не води до това, че контейнерът изпитва OOM.

Когато включите каквито и да е ограничения на паметта на ядрото, хост машината проследява статистически данни за „висока водна маркировка“ за всеки процес, така че можете да проследявате кои процеси (в този случай контейнери) използват излишна памет. Това може да се види за всеки процес чрез преглед/proc /

/ състояние на хост машината.

По подразбиране достъпът на всеки контейнер до процесора на процесора на хост машината е неограничен. Можете да зададете различни ограничения, за да ограничите достъпа на даден контейнер до процесорните цикли на хост машината. Повечето потребители използват и конфигурират CFS планирателя по подразбиране. Можете също така да конфигурирате планировчика в реално време.

Конфигурирайте CFS планирателя по подразбиране

CFS е плановият процесор на ядрото на Linux за нормални Linux процеси. Няколко флагове за изпълнение ви позволяват да конфигурирате размера на достъпа до ресурсите на процесора, който вашият контейнер има. Когато използвате тези настройки, Docker променя настройките за cgroup на контейнера на хост машината.

Ако имате 1 процесор, всяка от следните команди гарантира на контейнера най-много 50% от процесора всяка секунда.

Което е еквивалентно на ръчно определяне на --cpu-period и --cpu-quota;

Конфигурирайте планировчика в реално време

Можете да конфигурирате контейнера си да използва график в реално време за задачи, които не могат да използват CFS планировчика. Трябва да се уверите, че ядрото на хост машината е конфигурирано правилно, преди да можете да конфигурирате демона на Docker или да конфигурирате отделни контейнери.

Планирането и приоритизирането на процесора са усъвършенствани функции на ниво ядро. Повечето потребители не трябва да променят тези стойности по подразбиране. Неправилното задаване на тези стойности може да доведе до нестабилна или неизползваема система на вашия хост.

Конфигурирайте ядрото на хост машината

Проверете дали CONFIG_RT_GROUP_SCHED е активиран в ядрото на Linux, като стартирате zcat /proc/config.gz | grep CONFIG_RT_GROUP_SCHED или чрез проверка за съществуването на файла /sys/fs/cgroup/cpu.rt_runtime_us. За насоки относно конфигурирането на планиращото ядро ​​в реално време, вижте документацията за вашата операционна система.

Конфигурирайте демона на Docker

За да стартирате контейнери, използвайки планиращото устройство в реално време, изпълнете демона на Docker с флаг --cpu-rt-runtime, зададен на максималния брой микросекунди, запазени за задачи в реално време за период на изпълнение. Например с период по подразбиране от 1000000 микросекунди (1 секунда), настройката --cpu-rt-runtime = 950000 гарантира, че контейнерите, използващи планиращия реално време, могат да работят 950000 микросекунди на всеки 1000000 микросекунди, оставяйки на разположение поне 50000 микросекунди за задачи в реално време. За да направите тази конфигурация постоянна за системи, които използват systemd, вижте Контрол и конфигуриране на Docker с systemd.

Конфигурирайте отделни контейнери

Можете да предадете няколко флага, за да контролирате приоритета на процесора на контейнера, когато стартирате контейнера с помощта на докер изпълнение. Консултирайте се с документацията на вашата операционна система или командата ulimit за информация относно подходящите стойности.

Опция Описание
--cap-add = sys_nice Предоставя на контейнера възможността CAP_SYS_NICE, която позволява на контейнера да повишава процеса на приятни стойности, да задава политики за планиране в реално време, да задава афинитет на процесора и други операции.
--cpu-rt-runtime = Максималният брой микросекунди, които контейнерът може да изпълнява с приоритет в реално време в рамките на периода на планиране на демона на Docker в реално време. Трябва ви и флагът --cap-add = sys_nice.
--ulimit rtprio = Максималният приоритет в реално време, разрешен за контейнера. Трябва ви и флагът --cap-add = sys_nice.

Следващият пример команда задава всеки от тези три флага на контейнер debian: jessie.

Ако ядрото или демонът на Docker не са конфигурирани правилно, възниква грешка.

Достъп до графичен процесор NVIDIA

Предпоставки

Посетете официалната страница за драйвери на NVIDIA, за да изтеглите и инсталирате правилните драйвери. Рестартирайте системата си, след като го направите.

Проверете дали вашият графичен процесор работи и е достъпен.

Инсталирайте nvidia-container-runtime

Следвайте инструкциите на (https://nvidia.github.io/nvidia-container-runtime/) и след това изпълнете тази команда:

Уверете се, че nvidia-container-runtime-hook е достъпен от $ PATH .

Рестартирайте демона на Docker.

Излагайте графични процесори за използване

Включете флага --gpus, когато стартирате контейнер за достъп до GPU ресурси. Посочете колко графични процесора да използвате. Например:

Излага всички налични графични процесори и връща резултат, подобен на следния:

Използвайте опцията за устройство, за да посочите графични процесори. Например:

Излага конкретния графичен процесор.

Излага първия и третия графичен процесор.

Графичните процесори на NVIDIA могат да бъдат достъпни само от системи с един двигател.

Задайте възможности на NVIDIA

Можете да зададете възможности ръчно. Например на Ubuntu можете да изпълните следното:

Това дава възможност за помощния драйвер, който добавя инструмента nvidia-smi към контейнера.

Възможностите, както и други конфигурации могат да бъдат зададени в изображения чрез променливи на околната среда. Повече информация за валидни променливи можете да намерите на страницата на nvidia-container-runtime GitHub. Тези променливи могат да бъдат зададени в Dockerfile.

Можете също така да използвате CUDA изображения, които автоматично задават тези променливи. Вижте страницата GitHub за изображения на CUDA за повече информация.