Docker сколько оперативной памяти

Обновлено: 03.07.2024

В нашей компании мы часто используем докер, и поэтому решили перевести статью и пролить немного света на тему мониторинга ресурсов.

Мониторинг Docker окружения – непростая задача. Ведь каждый контейнер запускается в одном процессе, имеет свою собственную среду, использует виртуальные сети или имеет различные методы управления системами хранения. Традиционные решения для мониторинга контролируют метрики каждого сервера и приложения, на котором они запущены. Серверы и приложения, работающие на них, как правило, статичны, т.е. работают непрерывно. Docker формирует различные наборы контейнеров и может запускать множество приложений, совместно использующих ресурсы одного или нескольких хостов. Docker под силу запускать тысячи «временных» контейнеров (например, для пакетных заданий), параллельно осуществляя работу набора постоянных служб в штатном режиме.

Традиционные инструменты мониторинга не используются для динамических сред и не подходят для таких задач. С другой стороны, некоторые современные системы мониторинга (например, SPM от Sematex) были созданы для динамических систем и поддерживают средства для мониторинга и отчётов Docker «из коробки». Кроме того, совместное использование ресурсов контейнера предусматривает строгое соблюдение ограничений на использование ресурсов, которое необходимо контролировать. Для того чтобы внести соответствующие изменения в квоты ресурсов, необходимо видеть каких ограничений достиг контейнер сейчас, и какие ошибки это вызывает (может вызвать). Мы рекомендуем использовать оповещения в соответствии с настроенными лимитами. Таким образом вы можете установить лимиты или настроить использование ресурсов ещё до того, как появятся ошибки.

Мониторинг SPM

Контролируйте ресурсы вашей Docker машины

Процессор

Информация об утилизации CPU хостов помогает оптимизировать использование ресурсов Docker машины. Использование CPU может быть уменьшено для того, чтобы избежать ситуации, когда один контейнер занимает все процессорное время, замедляя работу других контейнеров. Уменьшение процессорного времени является хорошим способом обеспечить минимум вычислительных мощностей для всех сервисов — это как старые добрые уровни в Unix/Linux.

Даже когда использование ресурсов оптимизировано, все же могут возникать ситуации, когда загрузка процессора близка к максимуму. Оповещения имеют смысл только в случае, когда утилизация процессора падает (происходит сбой в работе службы) или в течение длительного периода увеличивается до некоторого максимального предела (например, 85%).

Сверхутилизация Docker машины является признаком неисправности.

Недоиспользование Docker машины является признаком того, что вы переплачиваете за неиспользуемые ресурсы.

Память

Для выполнения текущих операций и планирования мощностей необходимо знать общий объем используемой памяти каждым Docker хостом. Динамические кластер-менеджеры, такие как Docker Swarm, используют общий объем памяти, доступный на хосте, а также дополнительную запрашиваемую память для контейнеров, чтобы решить, на каком хосте лучше запустить контейнер. Приложения не получится развернуть, если кластер-менеджер не сможет найти хост с достаточными ресурсами для запуска контейнера. Поэтому важно знать об утилизации памяти хоста и ограничениях памяти контейнеров. Регулировка мощности новых узлов кластера в соответствии с утилизацией приложений Docker может помочь оптимизировать использование ресурсов.

Linux не перерасходует оперативную память. Однако когда буфер кэшируются, памяти не остается, а значит необходимо расширить кластер.

Дисковое пространство

Образы Docker и контейнеры потребляют дополнительное дисковое пространство. Например, образ приложения может включать в себя операционную систему Linux и иметь размер 150-700 Мб в зависимости от размера базового образа и установленных инструментов в контейнере. Постоянные Docker тома также потребляют дисковое пространство на хосте. По нашему опыту, наблюдение за дисковым пространством и применение инструментов очистки диска обязательно при постоянной эксплуатации Docker машины.

Хорошие дети убирают в комнате.

Хорошие Docker OPS убирают со своих дисков неиспользуемые контейнеры и образы.

Использование дискового пространства на Docker хосте

Использование дискового пространства на Docker хосте

Поскольку необходимо всегда иметь запас дискового пространства, важно установить оповещения на использование дискового пространства, чтобы они предупреждали о нехватке места и обеспечили достаточное время для очистки дисков или возможности добавить дополнительный объем. Например, SPM автоматически устанавливает правила уведомления использования дискового пространства и вам не нужно больше помнить об этом.

Общее количество запущенных контейнеров

Текущее и накопленное количество контейнеров является интересной метрикой по многим причинам. Очень удобно во время развертывания и обновлений проверять, что все работает, как и раньше. Когда кластер-менеджеры, такие как Docker Swarm, Mesos, Kubernetes, CoreOS/Fleet, автоматически планируют запуск контейнера на разных компьютерах, используются разные политики планирования. Количество контейнеров, работающих на каждой машине может помочь проверить активированные политики планирования на ней. Гистограмма ниже показывает количество контейнеров на каждой машине и общее число контейнеров, демонстрируя как кластер-менеджер распределил контейнеры по доступным машинам.

Количество контейнеров с течением времени

Контроль нештатного поведения, в отличие от оповещений на основе пороговых значений, позволит «поймать» внезапные миграции контейнера, что может являться предвестником серьёзного сбоя.

Данная метрика использует различные «модели» в зависимости от варианта применения. Например, пакетные операции, выполняющиеся посредством контейнеров, в сравнении с работающей постоянно службой, формируют модель, представляющую собой множество контейнеров. Пакетные операции обычно запускают контейнер по требованию или периодически, и контейнер с этими операциями завершается через относительно короткое время. В таком случае можно было бы увидеть большой разброс в количестве работающих контейнеров, в результате чего метрики контейнеров отобразят пиковые значения. С другой стороны, постоянно работающие сервисы, такие как веб-серверы или базы данных, как правило, функционируют до тех пор, пока они не будут перезапущены во время обновления программного обеспечения. Хотя механизмы масштабирования могут увеличить или уменьшить количество контейнеров в зависимости от нагрузки, трафика и других факторов, при расчете метрики как правило, будет учитываться относительно устойчивый контейнер, так как в таких случаях контейнеры часто добавляются и удаляются более постепенно. Из-за этого нет общего шаблона, который мы могли бы использовать по умолчанию для правил оповещения о количестве запущенных контейнеров в Docker.

Тем не менее, оповещения на основе обнаружения нештатного поведения контейнеров, которые обнаруживают внезапные изменения количества контейнеров в общей сложности (или для определенных хостов) в коротком временном периоде очень удобны в большинстве случаев. Простые предупреждения на основе пороговых значений имеют смысл лишь когда максимальное или минимальное количество запущенных контейнеров известно и, в динамическом окружении, масштабируется вверх и вниз на основе внешних факторов. Но зачастую это не так.

Метрики Docker контейнеров

Метрики контейнеров в основном те же показатели, что и доступные для каждого процесса Linux параметры, но они включают в себя ограничения, установленные с помощью контрольных групп в Docker, такие как ограничение для использования CPU или памяти. Обратите внимание, что сложные решения для мониторинга Docker, такие как SPM, способны агрегировать метрики контейнера на разных уровнях Docker хостов/узлов кластера, названиях или ID образов и название или ID контейнера. Такие возможности позволяют легко отслеживать использование ресурсов хоста, типов приложений (имен образов) или специальных контейнерах. В следующих примерах, приведены параметры которые мы могли использовать для агрегирования данных на различных уровнях.

Используйте современные решения для мониторинга Docker, чтобы получить продольные и поперечные срезы хоста, узла, образа или контейнера.

Одним из самых основных метрик является информация о том, сколько CPU потребляется всеми контейнерами вместе, образами, или отдельными контейнерами. Большим преимуществом использования Docker является возможность ограничить загрузку процессора по контейнерам. Конечно, вы не можете настроить и оптимизировать что-то, если вы не измеряете это, поэтому мониторинг таких ограничений является необходимым условием. Наблюдая за общими метриками загрузки CPU контейнерами, стало понятно, что CPU был утилизирован почти на 100%, поэтому необходимо настроить параметры для совместного использования процессоров в Docker. Обратите внимание, что CPU имеет высокую утилизацию только тогда, когда использование хост-процессора максимизировано. До тех пор, пока у хоста есть запасные мощности CPU, доступные для Docker, он не уменьшит использование процессора для контейнера. Таким образом, повышенная утилизация CPU или нулевая является пиком этой метрики, и как правило, это является хорошим показателем одного или нескольких контейнеров, требующих большей мощности процессора, чем хост может обеспечить.

Использование CPU, контейнер использовал все процессорное время

Использование CPU, контейнер использовал все процессорное время

На следующем снимке показаны контейнеры с квотой 5% CPU, запущенные с помощью команды docker run -cpu-quota=5000 nginx. Отчётливо видно, как утилизация CPU растет, пока не достигнет порога примерно в 5%.

Использование Docker CPU контейнером

Использование CPU контейнером, и утилизация процессорного времени CPU при квоте 5%

Счётчик ошибок памяти Docker показывает, когда контейнеру нужно больше памяти.

Счётчик ошибок памяти Docker показывает, когда контейнеру нужно больше памяти

Пик в счетчике сбоев использования памяти является критическим событием, и оповещения могут помочь найти некорректные настройки ограничений потребления памяти или контейнеры, которые пытаются потреблять больше памяти, чем ожидалось изначально.

Использование памяти контейнером

Каждое приложение потребляет память в своем определенном объёме. Понимание необходимого объема памяти контейнеров приложений крайне важно для обеспечения стабильной работы окружения. Ограничение объема памяти для контейнеров позволяет убедиться, что приложения работают хорошо, без излишнего потребления памяти, которое может повлиять на другие контейнеры, находящиеся на этом же хосте.

  • Мониторинг использования памяти приложения в контейнере.
  • Настройка лимитов памяти по результатам наблюдения.
  • Продолжение мониторинга памяти, счётчика сбоев памяти и событий нехватки памяти.

Если нехватка памяти происходит, то может потребоваться увеличение лимитов памяти контейнера или отладка, чтобы найти причину высокого потребления памяти.

Использование памяти контейнером

Использование памяти контейнером

Swap в контейнерах

Как и в памяти любого другого процесса, память какого-либо контейнера может быть выгружена на диск. Для приложений, таких как Elasticsearch или Solr, часто приходилось находить инструкции, чтобы отключить swap на Linux машине, но при запуске таких приложений на Docker это может быть сделано достаточно просто, необходимо добавить в команду запуска -memory-swap=-1.

Как быстро отключить Swapping в Docker – контейнере?
Используйте команду -memory-swap=-1

Контейнер swap, страницы памяти, и скорость подкачки

Контейнер swap, страницы памяти, и скорость подкачки

Контейнер дискового ввода/вывода

В Docker несколько приложений одновременно могут использовать одни и те же ресурсы. Таким образом, наблюдения за дисковой системой ввода/вывода помогают определить пределы для конкретных приложений и обеспечивают более высокую пропускную способность для критически важных приложений, таких как хранилища данных или веб-серверы. Например, утилизация дисковых операций ввода/вывода для пакетных операций достигает 100%, и в этом случае команда docker run -it -device-write-bps /dev/sda:1mb mybatchjob будет актуальна: она ограничит максимальную скорость записи на диск до 1 МБ/с.

Контейнер дискового ввода/вывода

Контейнер дискового ввода/вывода

Чтобы ограничить контейнер Docker от потребления всего вашего диска необходимо использовать команду -device-write-bps /dev/sda:1mb

Сетевые метрики контейнера

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

Мониторинг сетевого трафика

Мониторинг сетевого трафика

Заключение

Теперь у вас есть главные показатели работы контейнеров Docker, на которых стоит сфокусировать внимание. Также не стоит забывать, что проведенный должным образом анализ поможет избежать многих трудностей при внедрении Docker на таких платформах как Docker Swarm, Docker Cloud, Docker Datacenter или любой другой платформе, поддерживающей контейнеры Docker.

Комментарии переводчика

Мы рекомендуем придерживаться следующих пороговых значений для выявления проблем:

При запуске Node.js-приложений в контейнерах Docker традиционные настройки памяти не всегда работают так, как ожидается. Материал, перевод которого мы сегодня публикуем, посвящён поиску ответа на вопрос о том, почему это так. Здесь же будут приведены практические рекомендации по управлению памятью, доступной Node.js-приложениям, работающим в контейнерах.




Обзор рекомендаций

Предположим, Node.js-приложение выполняется в контейнере с установленным лимитом памяти. Если речь идёт о Docker, то для установки этого лимита могла быть использована опция --memory . Нечто подобное возможно и при работе с системами оркестрации контейнеров. В таком случае рекомендуется, при запуске Node.js-приложения, использовать опцию --max-old-space-size . Это позволяет сообщить платформе о том, какой объём памяти ей доступен, а так же учесть то, что этот объём должен быть меньше лимита, заданного на уровне контейнера.

Когда Node.js-приложение выполняется внутри контейнера, задавайте ёмкость доступной ему памяти в соответствии с пиковым значением использования активной памяти приложением. Это делается в том случае, если ограничения памяти контейнера можно настраивать.

Теперь поговорим о проблеме использования памяти в контейнерах подробнее.

Лимит памяти Docker

По умолчанию контейнеры не имеют ограничений по ресурсам и могут использовать столько памяти, сколько позволяет им операционная система. У команды docker run имеются опции командной строки, позволяющие задавать лимиты, касающиеся использования памяти или ресурсов процессора.

Команда запуска контейнера может выглядеть так:


Обратите внимание на следующее:

  • x — это лимит объёма памяти, доступной контейнеру, выраженный в единицах измерения y .
  • y может принимать значение b (байты), k (килобайты), m (мегабайты), g (гигабайты).


Здесь лимит памяти установлен в 1000000 байт.

Для проверки лимита памяти, установленного на уровне контейнера, можно, в контейнере, выполнить следующую команду:


Поговорим о поведении системы при указании с помощью ключа --max-old-space-size лимита памяти Node.js-приложения. При этом данный лимит памяти будет соответствовать лимиту, установленному на уровне контейнера.

То, что в имени ключа называется «old-space», представляет собой один из фрагментов кучи, управляемой V8 (то место, где размещаются «старые» JavaScript-объекты). Этот ключ, если не вдаваться в детали, которых мы коснёмся ниже, контролирует максимальный размер кучи. Подробности о ключах командной строки Node.js можно почитать здесь.

В общем случае, когда приложение пытается использовать больше памяти, чем доступно в контейнере, его работа завершается.

В следующем примере (файл приложения называется test-fatal-error.js ) в массив list , с интервалом в 10 миллисекунд, помещают объекты MyRecord . Это приводит к бесконтрольному росту кучи, имитируя утечку памяти.


Обратите внимание на то, что все примеры программ, которые мы будем тут рассматривать, помещены в образ Docker, который можно загрузить с Docker Hub:


Вы можете воспользоваться этим образом для самостоятельных экспериментов.

Кроме того, можно упаковать приложение в контейнер Docker, собрать образ и запустить его с указанием лимита памяти:


Здесь ravali1906/dockermemory — это имя образа.

Теперь можно запустить приложение, указав лимит памяти для него, превышающий лимит контейнера:


Здесь ключ --max_old_space_size представляет собой лимит памяти, указываемый в мегабайтах. Метод process.memoryUsage() даёт сведения об использовании памяти. Значения показателей выражены в байтах.

Работа приложения в некий момент времени принудительно завершается. Происходит это тогда, когда объём использованной им памяти переходит некую границу. Что это за граница? О каких ограничениях на объём памяти можно говорить?

Ожидаемое поведение приложения, запущенного с ключом --max-old-space-size

По умолчанию максимальный размер кучи в Node.js (вплоть до версии 11.x) составляет 700 Мб на 32-битных платформах, и 1400 Мб на 64-битных. О настройке этих значений можно почитать здесь.

В теории, если установить с помощью ключа --max-old-space-size лимит памяти, превышающий лимит памяти контейнера, можно ожидать, что приложение будет завершено защитным механизмом ядра Linux OOM Killer.

В реальности этого может и не случиться.

Реальное поведение приложения, запущенного с ключом --max-old-space-size

Приложению, сразу после запуска, не выделяется вся память, лимит которой указан с помощью --max-old-space-size . Размер JavaScript-кучи зависит от нужд приложения. О том, какой размер памяти использует приложение, можно судить на основании значения поля heapUsed из объекта, возвращаемого методом process.memoryUsage() . Фактически, речь идёт о памяти, выделенной в куче под объекты.

В результате мы приходим к выводу о том, что приложение будет принудительно завершено в том случае, если размер кучи окажется больше лимита, заданного ключом --memory при запуске контейнера.

Но в реальности этого тоже может не случиться.

При профилировании ресурсоёмких Node.js-приложений, которые запущены в контейнерах с заданным лимитом памяти, можно наблюдать следующие паттерны:

  1. OOM Killer срабатывает гораздо позже того момента, когда значения heapTotal и heapUsed оказываются значительно превышающими ограничения на объём памяти.
  2. OOM Killer никак не реагирует на превышение ограничений.

Объяснение особенностей поведения Node.js-приложений в контейнерах

Контейнер наблюдает за одним важным показателем приложений, которые в нём выполняются. Это — RSS (resident set size). Этот показатель представляет некую часть виртуальной памяти приложения.

Более того, он представляет собой фрагмент памяти, которая выделена приложению.

Но и это ещё не всё. RSS — это часть активной памяти, выделенной приложению.

Активной может быть не вся память, выделенная приложению. Дело в том, что «выделенная память» не обязательно физически выделяется до тех пор, пока процесс не начнёт ей по-настоящему пользоваться. Кроме того, в ответ на запросы на выделение памяти от других процессов, операционная система может сбросить в файл подкачки неактивные фрагменты памяти приложения и передать освободившееся пространство другим процессам. А когда приложению снова понадобятся эти фрагменты памяти, они будут взяты из файла подкачки и возвращены в физическую память.

Показатель RSS указывает на объём активной и доступной приложению памяти в его адресном пространстве. Именно он влияет на принятие решения о принудительном завершении работы приложения.

Доказательства

▍Пример №1. Приложение, которое выделяет память под буфер

В следующем примере, buffer_example.js , показана программа, которая выделяет память под буфер:


Для того чтобы объём памяти, выделяемой программой, превысил бы лимит, заданный при запуске контейнера, сначала запустим контейнер следующей командой:


После этого запустим программу:


Как видно, система не завершила выполнение программы, хотя при этом выделенная программой память и превышает лимит контейнера. Произошло это из-за того, что программа не работает со всей выделенной памятью. Показатель RSS очень мал, он не превышает лимит памяти контейнера.

▍Пример №2. Приложение, заполняющее буфер данными

В следующем примере, buffer_example_fill.js , память не просто выделяется, а ещё и заполняется данными:


После этого запустим приложение:


Как видно, даже теперь приложение не завершается! Почему? Дело в том, что когда объём активной памяти достигает лимита, заданного при запуске контейнера, и при этом в файле подкачки есть место, некоторые из старых страниц памяти процесса перемещаются в файл подкачки. Освобождённая память оказывается доступной тому же самому процессу. По умолчанию Docker выделяет под файл подкачки пространство, равное лимиту памяти, заданному с помощью флага --memory . Учитывая это можно сказать, что у процесса есть 2 Гб памяти — 1 Гб в активной памяти, и 1 Гб — в файле подкачки. То есть, благодаря тому, что приложение может пользоваться своей же памятью, содержимое которой временно перемещается в файл подкачки, размер показателя RSS находится в пределах лимита контейнера. В результате приложение продолжает работать.

▍Пример №3. Приложение, заполняющее буфер данными, выполняющееся в контейнере, в котором файл подкачки не используется

Вот код, с которым мы будем здесь экспериментировать (это — тот же файл buffer_example_fill.js ):


На этот раз запустим контейнер, явным образом настроив особенности работы с файлом подкачки:

Общие рекомендации

Когда Node.js-приложения запускают с ключом --max-old-space-size , значение которого превышает лимит памяти, заданный при запуске контейнера, может показаться, что Node.js «не обращает внимания» на лимит контейнера. Но, как видно из предыдущих примеров, явной причиной подобного поведения является тот факт, что приложение просто не использует весь объём кучи, заданный с помощью флага --max-old-space-size .

Помните о том, что приложение не всегда будет вести себя одинаково в том случае, если оно использует больше памяти, чем доступно в контейнере. Почему? Дело в том, что на активную память процесса (RSS) влияет множество внешних факторов, на которые не может воздействовать само приложение. Они зависят от нагруженности системы и от особенностей окружения. Например — это особенности самого приложения, уровень параллелизма в системе, особенности работы планировщика операционной системы, особенности работы сборщика мусора, и так далее. Кроме того, эти факторы, от запуска к запуску приложения, могут меняться.

Имеют ли контейнеры Docker Windows с Docker Desktop для Windows ограничение памяти по умолчанию? У меня есть приложение, которое аварийно завершает работу, когда я запускаю его в контейнере, но когда я пытался указать параметр --memory 2048mb для команды docker run , оно, кажется, работает нормально. По крайней мере, в сценарии, где он падал раньше. Это создает у меня впечатление, что по умолчанию существует ограничение памяти, но я не смог найти его в документации. Итак, мой вопрос, есть ли предел памяти, и если это где это задокументировано?

И в этом типе изоляции ваш контейнер запускается внутри облегченной виртуальной машины, которая имеет ограничение по умолчанию и составляет 1 ГБ. Поэтому, если вы хотите иметь больше памяти, вы должны использовать параметр -m.

Запустите Docker QuickStart Terminal

Удалить по умолчанию vm:

Воссоздайте vm по умолчанию: в зависимости от ваших требований вы можете изменить значения для различных параметров ниже.

Теперь снова откройте терминал быстрого запуска Docker.

Теперь, когда вы запустите и создадите новый контейнер, у вас будет больше оперативной памяти по умолчанию. Но будьте осторожны, это удалит уже все вытащенные изображения докера.

Я думаю, возможно, у вас слишком много мертвых контейнеров. Может быть, попробуйте docker rm $(docker ps -a -q) удалить все вышедшие контейнеры, затем попробуйте запустить нужный контейнер.

Недавно у нас возникла очень похожая проблема и вопрос, и поэтому мы провели несколько экспериментов с памятью докеров в Windows:

Кажется, это сильно зависит от вашей конфигурации. Если вы запускаете Docker-контейнеры, давайте назовем это режимом hyper-v, ограничение памяти составит около 512 МБ. Вы можете расширить данную память с помощью опции -m для запуска Docker. Назначение 2 гб не было проблемой.

К сожалению, это совершенно другое для контейнеров Windows Server. Там начальный лимит памяти составляет 1 ГБ, и вы можете уменьшить его с помощью опции -m. Мы не нашли способ увеличить память для этих контейнеров.

Как вы видите свой режим / уровень изоляции:

hyperv - режим hyper-v; процесс - сервер Windows

Некоторые дополнительные примечания: очень трудно найти команду, которая выводит доступную память в контейнер. Мы создали наш собственный тест производительности для надежных результатов.

У меня на хосте 32 ГБ ОЗУ, но я вижу только 1 ГБ ОЗУ для контейнеров Windows:

То же ограничение на изображения, сделанные из:

  • Microsoft / windowsservercore
  • Microsoft / nanoserver

Я думаю, что это происходит из слоя Hyper-V в режиме --isolation=hyperv , где контейнер - это своего рода легкая виртуальная машина.

Вы можете проверить режим изоляции, используемый для существующего контейнера, с помощью команды docker inspect .

Если используется контейнер Linux

Для меня в Windows 10 с использованием Docker Desktop я не мог заставить работать опции --memory= и --cpus= . Вот что работает:


Docker великолепен, и в этом нет сомнений. Пару лет назад он представил новый способ сборки, доставки и запуска любых рабочих нагрузок, демократизируя использование контейнеров и чрезвычайно упрощая управление их жизненным циклом.

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

Если мы не очищали локальную машину в течение некоторого времени, то результаты этой команды могут удивить:


Пример потребления памяти Docker в файловой системе хоста

Эта команда показывает использование диска Docker в нескольких категориях:

  • Образы (Images): размер образов, извлеченных из реестра и созданных локально.
  • Контейнеры (Containers): дисковое пространство, занимаемое слоями чтения-записи каждого из контейнеров, работающих в системе.
  • Локальные тома (Local Volumes): в случае, если хранение осуществляется на хосте, но вне файловой системы контейнера.
  • Кэш сборки (Build Cache): кэш, сгенерированный процессом сборки образа (касается BuildKit в Docker 18.09).

Из вывод а выше видно, что большое количество дискового пространства можно восстановить. Поскольку оно не используется Docker, его можно вернуть хост-машине.

Использование диска контейнерами

Каждый раз, когда создается контейнер, в папке /var/lib/docker на хост-машине появляется несколько папок и файлов. Среди них:

  • Папка /var/lib/docker/containers/ID (ID — уникальный идентификатор контейнера). Если контейнер использует драйвер логгирования по умолчанию, все логи будут сохранены в файле JSON внутри нее. Создание слишком большого количества логов может повлиять на файловую систему хост-машины.
  • Папка в /var/lib/docker/overlay2 , содержащая слой чтения-записи контейнера (overlay2 является предпочтительным драйвером хранилища в большинстве дистрибутивов Linux). Если контейнер сохраняет данные в своей собственной файловой системе, они будут храниться в /var/lib/docker/overlay2 на хост-машине.

Представим, что у нас есть совершенно новая система, в которой только что был установлен Docker.

Во-первых, запустим контейнер NGINX:

Снова запустив команду df , мы увидим:

  • один образ размером 126 Мбайт. Его загрузил NGINX: 1.16, когда мы запустили контейнер;
  • контейнер www, запускающийся из образа NGINX.

Поскольку контейнер запущен, а образ в данный момент используется, освобождаемого пространства пока нет. Так как его размер (2B) незначителен, и поэтому его нелегко отследить в файловой системе, создадим пустой файл размером 100 Мбайт в файловой системе контейнера. Для этого мы используем удобную команду dd из контейнера www.

Этот файл создается в слое чтения-записи, связанном с этим контейнером. Если мы снова проверим вывод команды df , то увидим, что он теперь занимает некоторое дополнительное дисковое пространство.

Где находится этот файл на хосте? Проверим:

Не вдаваясь глубоко в детали — этот файл был создан на слое чтения-записи контейнера, который управляется драйвером overlay2. Если мы остановим контейнер, используемое им дисковое пространство станет пригодным для восстановления. Посмотрим:

Как можно восстановить это пространство? Путем удаления контейнера, что приведет к удалению связанного с ним слоя чтения-записи.

Следующие команды позволяют нам удалить все остановленные контейнеры сразу и освободить место на диске, которое они используют:

Из вывода видно, что больше нет места, используемого контейнерами, и, поскольку образ больше не используется (контейнер не работает), пространство, которое он использует в файловой системе хоста, может быть восстановлено:

Примечание: если образ используется хотя бы одним контейнером, занимаемое им дисковое пространство не может быть освобождено.

Подкоманда prune , которую мы применяли выше, удаляет остановленные контейнеры. Если нам нужно удалить все — и запущенные, и остановленные, мы можем использовать одну из следующих команд (обе эквивалентны):

Примечание: во многих случаях стоит использовать флаг --rm при запуске контейнера, чтобы он автоматически удалялся при остановке процесса PID 1, тем самым немедленно освобождая неиспользуемое пространство.

Использование диска образами

Существует несколько видов образов, которые не видны напрямую конечному пользователю:

  • Промежуточные — те, что ссылаются на другие (дочерние) и не могут быть удалены.
  • Висящие — это те, на которые больше нет ссылок. Они занимают некоторое место на диске и могут быть удалены.

Следующие команды отображают висящие образы в системе:

Чтобы удалить их, можно пойти долгим путем:

$ docker image rm $(docker image ls -f dangling=true -q)

Или использовать подкоманду prune :

В случае, если нужно удалить все образы сразу (а не только висящие), можно запустить следующую команду. Однако это не позволит удалить те, которые используются контейнером в данный момент:

Использование диска томами

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

Допустим, мы запускаем контейнер на основе MongoDB, а затем используем его для тестирования бэкапа, который мы сделали ранее (он доступен локально в файле bck.json):

Данные в файле бэкапа будут храниться на хосте в папке /var/lib/docker/volumes . Почему эти данные не сохраняются в слое контейнера? Причина в том, что в Dockerfile образа mongo расположение /data/db (где mongo хранит свои данные по умолчанию) определяется как том.


Извлечение Dockerfile, используемого для сборки образа контейнера MongoDB

Примечание: многие образы, часто связанные с приложениями с сохранением состояния, определяют тома для управления данными за пределами слоя контейнера.

Окончив тестирование бэкапа, мы останавливаем или удаляем контейнер. Однако том не удаляется, если мы не сделаем этого явно — он остается, потребляя дисковое пространство. Тогда мы можем пойти долгим путем:

Или использовать prune :

Использование диска кэшем сборки

В релизе Docker 18.09 представлены усовершенствования процесса сборки с помощью BuildKit. Использование этого инструмента позволяет повысить производительность, управление хранилищем, функциональность и безопасность. Мы не будем подробно его описывать, а просто посмотрим, как его включить и как он влияет на использование диска.

Рассмотрим следующее фиктивное приложение Node.Js и связанный с ним Dockerfile :

Dockerfile определяет, как построить образ из приведенного выше кода:

Создадим образ как обычно и без включенного BuildKit:

При проверке использования диска мы увидим только базовый ( node:13-alpine был загружен в начале сборки) и конечный образ сборки (app: 1.0):

Теперь соберем версию образа 2.0 с BuildKit. Нужно просто установить DOCKER_BUILDKIT в 1:

При повторной проверке использования диска видим, что был создан кэш сборки (Build Cache):

Для его удаления можно выполнить:

Очистка всего и сразу

В приведенных выше примерах каждая из команд контейнера, образа и тома предоставляет подкоманду prune для освобождения дискового пространства. Она доступна на системном уровне Docker, поэтому удаляет все сразу:

Выполнение этой команды время от времени для очистки диска — хорошая привычка.

Читайте также: