Хранение данных в Docker
Важная характеристика Docker-контейнеров — эфемерность. В любой момент контейнер может рестартовать: завершиться и вновь запуститься из образа. При этом все накопленные в нём данные будут потеряны. Но как в таком случае запускать в Docker приложения, которые должны сохранять информацию о своём состоянии? Для этого есть несколько инструментов.
В этой статье рассмотрим docker volumes, bind mount и tmpfs, дадим советы по их использованию, проведём небольшую практику.
Особенности работы контейнеров
Прежде чем перейти к способам хранения данных, вспомним устройство контейнеров. Это поможет лучше понять основную тему.
Контейнер создаётся из образа, в котором есть всё для начала его работы. Но там не хранится и тем более не изменяется ничего важного. В любой момент приложение в контейнере может быть завершено, а контейнер уничтожен, и это нормально. Контейнер отработал — выкидываем его и собираем новый. Если пользователь загрузил в приложение картинку, то при замене контейнера она удалится.
На схеме показано устройство контейнера, запущенного из образа Ubuntu 15.04. Контейнер состоит из пяти слоёв: четыре из них принадлежат образу, и лишь один — самому контейнеру. Слои образа доступны только для чтения, слой контейнера — для чтения и для записи. Если при работе приложения какие-то данные будут изменяться, они попадут в слой контейнера. Но при уничтожении контейнера слой будет безвозвратно потерян, и все данные вместе с ним.
В идеальном мире Docker используют только для запуска stateless-приложений, которые не читают и не сохраняют данные о своём состоянии и готовы в любой момент завершиться. Однако в реальности большинство программ относятся к категории stateful, то есть требуют сохранения данных между перезапусками.
Поэтому нужны способы сделать так, чтобы важные изменяемые данные не зависели от эфемерности контейнеров и, как бонус, были доступными сразу из нескольких мест.
В Docker есть несколько способов хранения данных. Наиболее распространенные:
Особые типы хранения:
На схеме показаны самые популярные типы хранения данных для Linux: в памяти (tmpfs), в файловой системе хоста (bind mount), в томе Docker (docker volumes). Разберём каждый вариант.
Тома (docker volumes)
Тома — рекомендуемый разработчиками Docker способ хранения данных. В Linux тома находятся по умолчанию в /var/lib/docker/volumes/. Другие программы не должны получать к ним доступ напрямую, только через контейнер.
Тома создаются и управляются средствами Docker: командой docker volume create, через указание тома при создании контейнера в Dockerfile или docker-compose.yml.
В контейнере том видно как обычный каталог, который мы определяем в Dockerfile. Тома могут быть с именами или без — безымянным томам Docker сам присвоит имя.
Один том может быть примонтирован одновременно в несколько контейнеров. Когда никто не использует том, он не удаляется, а продолжает существовать. Команда для удаления томов: docker volume prune.
Можно выбрать специальный драйвер для тома и хранить данные не на хосте, а на удалённом сервере или в облаке.
Для чего стоит использовать тома в Docker:
Монтирование каталога с хоста (bind mount)
Это более простая концепция: файл или каталог с хоста просто монтируется в контейнер.
Используется, когда нужно пробросить в контейнер конфигурационные файлы с хоста. Например, именно так в контейнерах реализуется DNS: с хоста монтируется файл /etc/resolv.conf.
Другое очевидное применение — в разработке. Код находится на хосте (вашем ноутбуке), но исполняется в контейнере. Вы меняете код и сразу видите результат. Это возможно, так как процессы хоста и контейнера одновременно имеют доступ к одним и тем же данным.
Особенности bind mount:
Когда использовать тома, а когда монтирование с хоста
| Volume | Bind mount |
|---|---|
| Просто расшарить данные между контейнерами. | Пробросить конфигурацию с хоста в контейнер. |
| У хоста нет нужной структуры каталогов. | Расшарить исходники и/или уже собранные приложения. |
| Данные лучше хранить не локально (а в облаке, например). | Есть стабильная структура каталогов и файлов, которую нужно расшарить между контейнерами. |
Монтирование tmpfs
Tmpfs — временное файловое хранилище. Это некая специально отведённая область в оперативной памяти компьютера. Из определения выходит, что tmpfs — не лучшее хранилище для важных данных. Так оно и есть: при остановке или перезапуске контейнера сохранённые в tmpfs данные будут навсегда потеряны.
На самом деле tmpfs нужно не для сохранения данных, а для безопасности, полученные в ходе работы приложения чувствительные данные безвозвратно исчезнут после завершения работы контейнера. Бонусом использования будет высокая скорость доступа к информации.
Такое хранилище может одновременно работать только с одним контейнером и доступно только в Linux.
Общие советы по использованию томов
Монтирование в непустые директории
Если вы монтируете пустой том в каталог контейнера, где уже есть файлы, то эти файлы не удалятся, а будут скопированы в том. Этим можно пользоваться, когда нужно скопировать данные из одного контейнера в другой.
Если вы монтируете непустой том или каталог с хоста в контейнер, где уже есть файлы, то эти файлы тоже не удалятся, а просто будут скрыты. Видно будет только то, что есть в томе или каталоге на хосте. Похоже на простое монтирование в Linux.
Монтирование служебных файлов
С хоста можно монтировать любые файлы, в том числе служебные. Например, сокет docker. В результате получится docker-in-docker: один контейнер запустится внутри другого. UPD: (*это не совсем так. mwizard в комментариях пояснил, что в таком случае родительский docker запустит sibling-контейнер). Выглядит как бред, но в некоторых случаях бывает оправдано. Например, при настройке CI/CD.
Монтирование /var/lib/docker
Разработчики Docker говорят, что не стоит монтировать с хоста каталог /var/lib/docker, так как могут возникнуть проблемы. Однако есть некоторые программы, для запуска которых это необходимо.
Ключ командной строки для Docker при работе с томами.
Для volume или bind mount:
Команды для управления томами в интерфейсе CLI Docker:
Создадим тестовый том:
Вот он появился в списке:
Команда inspect выдаст примерно такой список информации в json:
Попробуем использовать созданный том, запустим с ним контейнер:
После самоуничтожения контейнера запустим другой и подключим к нему тот же том. Проверяем, что в нашем файле:
То же самое, отлично.
Теперь примонтируем каталог с хоста:
Docker не любит относительные пути, лучше указывайте абсолютные!
Теперь попробуем совместить оба типа томов сразу:
Отлично! А если нам нужно передать ровно те же тома другому контейнеру?
Вы можете заметить некий лаг в обновлении данных между контейнерами, это зависит от используемого Docker драйвера файловой системы.
Создавать том заранее необязательно, всё сработает в момент запуска docker run:
Посмотрим теперь на список томов:
Ещё немного усложним команду запуска, создадим анонимный том:
Такой том самоуничтожится после выхода из контейнера, так как мы указали ключ –rm.
Если этого не сделать, давайте проверим что будет:
Хозяйке на заметку: тома (как образы и контейнеры) ограничены значением настройки dm.basesize, которая устанавливается на уровне настроек демона Docker. Как правило, что-то около 10Gb. Это значение можно изменить вручную, но потребуется перезапуск демона Docker.
При запуске демона с ключом это выглядит так:
Однажды увеличив значение, его уже нельзя просто так уменьшить. При запуске Docker выдаст ошибку.
Если вам нужно вручную очистить содержимое всех томов, придётся удалять каталог, предварительно остановив демон:
Если вам интересно узнать подробнее о работе с данными в Docker и других возможностях технологии, приглашаем на двухдневный онлайн-интенсив в феврале. Будет много практики.
Автор статьи: Александр Швалов, практикующий инженер Southbridge, Certified Kubernetes Administrator, автор и разработчик курсов Слёрм.
Mount bind что это
С выходом релиза 2.4 Linux появилась возможность использования filesystem с новыми свойствами, таких как Reiserfs, XFS, GFS и других. Эти filesystems еще не достаточно опробованы и имеются вопросы, что именно они могут делать, насколько они хороши и насколько оправдано их использование в промышленной Linux среде. В этой статье Daniel дает обзор tmpfs, файловой системы, основанной на VM, и описывает новые, ставшими доступными с переходом на ядро 2.4 возможностями «bind»-mounting abilities.
Презентация tmpfs.
Tmpfs и VM
Это не блочное устройство.
Теперь о другом интересном свойстве tmpfs filesystem. В отличие от большинства «нормальных» файловых систем (например, ext3, ext2, XFS, JFS, ReiserFS) tmpfs не является «надстройкой» над блочным устройством. Поскольку tmpfs напрямую «встроена» в VM, ее можно монтировать сразу после создания командой:
Преимущества tmpfs.
Вы вероятно уже задавались вопросом, а какого размера файловую систему мы подмонтировали к /mnt/tmpfs в примере выше? Ответ неожиданный (особенно, если имели дело только с disk-based файловыми системами). /mnt/tmpfs первоначально имеет очень маленький размер, но, по мере копирования и создания файлов драйвер tmpfs ассигнует у VM дополнительную память, динамически увеличивая емкость. Справедливо и обратное, при удалении файлов из /mnt/tmpfs драйвер отдает освобождаемую память операционной системе. Теперь ясно (память достаточно ценный ресурс и ее «никогда не бывает много»), большой плюс tmpfs в том, что используется ровно столько памяти, сколько требуется. См. Resources.
Использование tmpfs.
Уход от low VM conditions
А чего это мы все говорим о достоинствах? Фактом является то, что tmpfs динамически растет и уменьшается. Поэтому естественен провокационный вопрос. А что случится, если tmpfs filesystem разрастется так, что поглотит всю виртуальную память? Скажем так, приемлемое решение еще не найдено. С ядром 2.4.4, увы, произошло бы зависание. С ядром 2.4.6, подсистема VM имеет некоторую защиту, и авария не произойдет. Когда 2.4.6 почувствует точку, за которой ассигнование дополнительной памяти проблематично, вы просто не сможете ничего более записать в tmpfs filesystem. Кроме того, произойдут некоторые другие вещи. Сначала процессы в системе не смогут ассигновать дополнительную память; внешне система станет очень вялой. У суперпользователя есть время, чтобы предпринять шаги для выхода из low-VM condition.
Решение для Low VM
К счастью, tmpfs позволяет указать максимальный размер filesystem при ее монтировании или перемонтировании. Фактически, с ядром 2.4.6 и util-linux-2.11g, такие параметры можно установить только при монтировании, но не перемонтировании (в следующих версиях ядер это может быть уже решено). Установка оптимального лимита на размер tmpfs зависит от ресурсов и режима использования Linux box; идея в том, чтобы предотвратить возможность со стороны tmpfs filesystem истощения ресурсов виртуальной памяти и предотвратить low-VM conditions, о чем говорилось ранее. Хороший способ найти приемлемый tmpfs upper-bound состоит в использовании top монитора для наблюдения за swap в момент пиковых нагрузок. Установите tmpfs upper-bound немного меньше, чем сумма свободной swap и RAM при пиковой нагрузке.
Создать tmpfs filesystem с лимитом на максимальный размер достаточно просто. Например:
В этом примере монтирование новой tmpfs происходит не к точке /mnt/tmpfs, а к специально созданному /dev/shm. Это каталог, который является «official» mountpoint для tmpfs. Если вы используете devfs, этот каталог будет создан автоматически.
Для автоматического монтирования при загрузке системы допустимо сделать запись в файле /etc/fstab. Например:
Монтирование поверх занятой mountpoints
При использовании ядер 2.2 любая попытка монтирования к уже используемой mountpoint приводила к ошибке. После переписи кода ядра повторное монтирование к занятой точке перестало быть проблемой. Такой пример: при загрузке системы монтируется «реальный» раздел диска к точке /tmp. Принимается оперативное решение использовать tmpfs. В старое время потребовалось бы размонтировать /tmp и повторно смонтировать tmpfs в /tmp:
Однако не всегда это возможно. Если есть процессы с открытыми в /tmp файлами будет выдана следующая ошибка:
На последних 2.4 ядрах можно перемонтировать /tmp filesystem без получения ошибки «device is busy»:
Единственной командой ваша новая tmpfs filesystem монтируется к /tmp поверх ранее смонтированного partition. При этом все новые файлы будут открываться на tmpfs, а процессы, которые имели открытые файлы на «оригинальной» filesystem, так и будут продолжать работать с ними! Если размонтировать tmpfs-based /tmp, «оригинальная» /tmp появится, как и прежде. Фактически, можно монтировать любое число файловых систем на одну mountpoint, и mountpoint будет действовать подобно стеку.
Bind mounts
Используя bind mount, мы можем монтировать всю или только часть уже смонтированной filesystem к другой точке и иметь filesystem, доступную от обеих mountpoints одновременно! Например, можно использовать bind mounts для монтирования root filesystem к /home/drobbins/nifty:
Теперь, если зайти в /home/drobbins/nifty, вы увидите вашу root filesystem (/home/drobbins/nifty/etc, /home/drobbins/nifty/opt и т.д.). Если модифицируется файл на root filesystem, все изменения будут видны и в /home/drobbins/nifty. Так происходит потому, что это одни и те же разделы диска, просто ядро отображает filesystem в двух разных mountpoints. Обратите внимание, когда происходит монтирование файловой системы к новой точке через bind-mounted, все файловые системы, которые были примонтированы к «оригинальной», в новой позиции отображены не будут. Другими словами, если /usr создан на отдельном partition, после выполнения bind-mounted подкаталог /home/drobbins/nifty/usr окажется пустым. Потребуется дополнительное bind mount, чтобы просмотреть содержимое /usr в /home/drobbins/nifty/usr:
Bind mounting для части файловой системы.
Bind mounting делает возможными еще более «тонкие» вещи. Например, вы монтируете tmpfs к /dev/shm, его «традиционной» точке, но одновременно хотите использовать tmpfs для /tmp. Вместо монтирования еще одной tmpfs к /tmp (что возможно), вы решаете share новый /tmp с /dev/shm. Но, bind mount /dev/shm к /tmp нужно сделать так, чтобы каталоги из /dev/shm не были видны в /tmp. Как это сделать? Пример:
В этом примере сначала создается каталог /dev/shm/tmp и назначаются права доступа 1777 (обычные для /tmp). Далее можно монтировать только отдельный /dev/shm/tmp. После этого файл /tmp/foo будет дополнительно виден как /dev/shm/tmp/foo, но файл /dev/shm/bar в каталоге /tmp отображен не будет.
Как следует из примера, bind mounts очень сильное средство и может помочь в проектировании файловой системы сложной архитектуры. Следующая статья будет посвящена devfs; а пока можно посмотреть следующие ресурсы.
Resources
About the author
Команда mount в Linux или все о монтировании разделов, дисков, образов ISO и SMB ресурсов.
Если Вам нужно подключить/примонтировать жесткий диск с файловой системой NTFS или ext2, ext3 к компьютеру на базе операционной системы Linux, то Вы читаете правильную статью.
Зачем делать это руками, если современные desktop-системы Линукс делают автоматически?
Есть отдельные случаи когда система Linux не может автоматически примонтировать/подключить диск в силу каких-то логических сбоев диска, вирусов, которыми заражены NTFS/FAT разделах или из-за еще чего-то аномального. Для этого настоящие системные администраторы делают это руками. И делают это командой mount.
Содержание
Команда mount в линуксе является очень гибким инструментом в руках системного администратора. С помощью команды mount можно подключить сетевой диск, раздел жесткого диска или USB-накопитель.
Данная статья не является полным, исчерпывающим описанием команды mount (полное описание команды mount можно найти выполнив в консоли команду man mount), но стремиться к этому. Статья по описанию команды mount постоянно дорабатывается и видоизменяется. Все пожелания по статье можете оставлять в комментариях.
Устройства, которые в данный момент подключены к компьютеру, можно посмотреть набрав в консоли:
Эта команда показывает все устройства, которые подключенны. Они могут быть не примонтированы, но подключены. На экране Вы увидите примерно это:
Диск /dev/sda: 40.0 ГБ, 40020664320 байт
255 heads, 63 sectors/track, 4865 cylinders
Units = цилиндры of 16065 * 512 = 8225280 bytes
Disk identifier: 0x815aa99a
| Устр-во | Загр | Начало | Конец | Блоки | Id | Система |
| /dev/sda1* | 1 | 1824 | 14651248+ | 83 | Linux | |
| /dev/sda2 | 4742 | 4865 | 996030 | 82 | Linux | своп / Solaris |
| /dev/sda3 | 1825 | 4741 | 23430802+ | 83 | Linux |




