Как хранить данные в Docker volumes

Если полистать какой-нибудь Докер гайдлайн, то скорее всего в нём скажут, что контейнеры должны быть маленькими, с одним процессом, легко удаляемыми и так же легко заменяемыми на более новый. Прекрасная концепция. О чём в гайдлайнах пишут немного реже, так это что же делать с данными внутри таких контейнеров. Я же не могу легко удалить тот же mysql контейнер и заменить его на новый, но пустой внутри. Он ведь мне только ради данных и нужен был.

Но, оказывается, проблема вполне решаема. Docker volumes, которые существовали в Докере с момента сотворения мира, начиная с версии 1.8 получили обновлённый API, и теперь справляются с хранением данных не только технически, но и эстетически прекрасно.

Что такое Docker volumes

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

1. Простое монтирование папки

Это вообще легко. Мы берём любую подходящую хостовую папку и подключаем её в нужное место файловой системы контейнера. Например, если мне нужно иногда обновлять mysql контейнер, или делать бэкап его данных, то просто монтируем папку к /var/lib/mysql, и теперь mysql данные находятся в относительной безопасности хостовой файловой системы:

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

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

Монтирование только для чтения

Иногда такие папки имеет смысл давать только на чтение. Например, для веб-сервера. Это очень легко сделать, добавив :ro в конце пути:

Просмотр уже подключённых папок

Запомнить, что же именно сейчас подключено к тому или иному контейнеру — нереально. Но это и не нужно. Есть команда docker inspect %container%, которая кроме настроек сети, хоста и, собственно, контейнера, выведет и все volumes, которые к нему подключены:

2. Тома с данными (data volumes)

Давайте попробуем ещё одну штуку. Запустим первый пример ещё раз, но в этот раз без -v параметра. Теперь, если мы проверим, подключены ли какие-нибудь volumes к контейнеру, то.. да, там кто-то снова есть:

В этот раз со странным именем, и ещё более странным путём к хостовой папке, но он всё ещё указывает на  /var/lib/mysql. Как же так?

Намёк на причину можно найти в Dockerfile для mysql образа:

VOLUME /var/lib/mysql, оказывается, создаёт новый том, который привязывается к /var/lib/mysql. Тома выглядят и работают практически так же, как и простые примонтированные папки, но есть и несколько отличий.

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

Во-вторых, для каждого тома Docker создаёт папку в хостовой файловой системе вида  /var/lib/docker/volumes/%имя%.

В-третьих, когда контейнер запускается в первый раз, Докер копирует его содержимое из примонтированного пути (например, из /var/lib/mysql) в только что созданный том, и потом уже берёт данные исключительно из последнего. Это может привести к несколько неожиданным эффектам. Например, если я заменю mysql контейнер на новый, созданный из более свежего образа, и в более свежем образе в папке /var/lib/mysql будут обновлённые файлы, то они будут проигнорированы, если том уже был проинициализирован.

Создаём тома из командной строки

Создавать тома можно не только из Dockerfile. Из командной строки это тоже хорошо получается:

Эта команда создаст новый том, подключённый к /data, затем выполнит touch /data/README.md, которая создаст пустой файл внутри неё, и, наконец, контейнер завершится.

В отличие от обычных подключаемых папок, Докер знает, какие тома он когда либо создавал, и через команды docker volume ... может этим знанием поделиться. Например, ls выведет список всех известных томов. В том числе и тот, который мы только что создали:

Кстати, том можно подключить и к «чужим» контейнерам. Зная имя, его можно подключить, например, к случайно гулявшей мимо Убунте:

Классический пример использования такой фичи — создание резервных копий тома. Мы просто запускаем контейнер, который умеет делать бэкапы, подключаем к нему том с исходными данными, затем ещё один, чтобы было куда складывать результат, и всё:

Создаём тома без контейнеров

Чтобы создать том, не обязательно даже создавать контейнер. Можно воспользоваться командой  docker volume create , чтобы подготовить том заранее, и потом уже подключать его по мере надобности. Прелесть этого подхода в том, что вместо нечитабельных 64 символов сгенерированного имени мы можем задать что-то более произносимое:

Создаём тома на внешнем хранилище

volume create может нечто большее, чем просто задавать красивые имена. До сих пор мы создавали тома на локальной машине, что в мире облаков и распределённых приложение — не самый масштабируемый подход. Docker поддерживает целый зоопарк плагинов-драйверов, которые могут отделить том от конкретного хоста, и хранить его в каком-нибудь Azure, DigitalOcean, или просто размазать по облаку.

Установка и конфигурация такого плагина может быть сложноватой, но если уже получилось, то пользоваться им — одно удовольствие. Например, чтобы хранить том в Digital Ocean, нужна всего-лишь такая команда:

3. Тома-контейнеры

Был ещё такой старый паттерн — «контейнеры только с данными». И он делал именно то, как назывался: просто существовал, как правило, выключенный, и единственной целью его жизни было делиться своими томами с другими контейнерами.

Чтобы получить к ним доступ, нужно было указать параметр  --volumes-from, и магия получалась сама собой:

Если честно, то я не вижу никакой выгоды от использования этого подхода, по сравнению с обычными томами данных. Может, до версии 1.8, когда появился нормальный АПИ для томов, том-контейнер был единственным вменяемым решением, то сейчас даже Гугл не уверен, почему они всё ещё существуют.

Итого

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

9 комментариев для “Как хранить данные в Docker volumes

    1. Насколько я помню — нет. Они же привязаны к локальной файловой системе, а swarm даже загруженными образами между хостами делиться не может. Чтобы тома были видны отовсюду, их надо хранить где-нибудь на стороне через volume plugin

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

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

  2. Спасибо большое. Пересмотрел десятку видео но так и не мог понять и тут — 10 минут и все встало на свои места.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *