«1. Обзор
Контейнеры Docker используются для запуска приложений в изолированной среде. По умолчанию все изменения внутри контейнера теряются при остановке контейнера. Если мы хотим сохранить данные между запусками, могут помочь тома Docker и привязки.
В этом руководстве мы узнаем о томах Docker, а также о том, как управлять ими и подключать их к контейнерам.
2. Что такое том?
2.1. Файловая система Docker
Контейнер Docker запускает программный стек, определенный в образе. Изображения состоят из набора слоев, доступных только для чтения, которые работают в файловой системе под названием Union File System. Когда мы запускаем новый контейнер, Docker добавляет слой чтения-записи поверх слоев образа, позволяя контейнеру работать так, как если бы он работал в стандартной файловой системе Linux.
Итак, любое изменение файла внутри контейнера создает рабочую копию в слое чтения-записи. Однако, когда контейнер останавливается или удаляется, этот уровень чтения-записи теряется.
Мы можем попробовать это, выполнив команду, которая записывает, а затем читает файл:
$ docker run bash:latest \
bash -c "echo hello > file.txt && cat file.txt"
Результат:
hello
Но если мы запустим тот же образ только с командой для вывода содержимое файла:
$ docker run bash:latest bash -c "cat file.txt"
cat: can't open 'file.txt': No such file or directory
Второй запуск контейнера выполняется на чистой файловой системе, поэтому файл не найден.
2.2. Bind Mounts
Bind Mounts — это высокопроизводительное подключение контейнера к каталогу на хост-компьютере. Это позволяет хосту совместно использовать свою собственную файловую систему с контейнером, который можно сделать доступным только для чтения или для чтения и записи.
Это позволяет нам использовать контейнер для запуска инструментов, которые мы не хотим устанавливать на наш хост, и при этом работать с файлами нашего хоста. Например, если мы хотим использовать пользовательскую версию bash для определенного скрипта, мы можем выполнить этот скрипт в контейнере bash, смонтированном в наш текущий рабочий каталог:
$ docker run -v $(pwd):/var/opt/project bash:latest \
bash -c "echo Hello > /var/opt/project/file.txt"
Можно использовать параметр —v для всех форм монтирования и указывает, в данном случае, источник на хосте — рабочий каталог в выводе $(pwd) — и целевую точку монтирования в контейнере — /var/opt/ проект.
После запуска этой команды мы найдем файл .txt в рабочем каталоге нашего хост-компьютера. Это простой способ предоставления постоянных файлов между вызовами контейнера Docker, хотя он наиболее полезен, когда контейнер выполняет работу от имени хоста.
Хорошим вариантом использования для этого было бы выполнение различных версий инструментов сборки языка в Docker, чтобы избежать конфликтующих установок на компьютере разработчика.
Следует отметить, что $(pwd -W) иногда требуется в оболочках Windows bash для предоставления рабочего каталога в форме, которую оболочка bash может передать в Docker.
2.3. Тома Docker
При монтировании с привязкой используется файловая система хоста, но тома Docker являются родными для Docker. Данные хранятся где-то в хранилище, подключенном к хосту — часто в локальной файловой системе. Жизненный цикл самого тома длиннее, чем у контейнера, что позволяет ему сохраняться до тех пор, пока необходимость в нем отпадет. Тома могут быть разделены между контейнерами.
В некоторых случаях том находится в форме, которую хост не может использовать напрямую.
3. Управление томами
Docker позволяет нам управлять томами с помощью набора команд docker volume. Мы можем дать тому явное имя (именованные тома) или позволить Docker сгенерировать случайное имя (анонимные тома).
3.1. Создание томов
Мы можем создать том, используя подкоманду create и передав имя в качестве аргумента:
$ docker volume create data_volume
data_volume
Если имя не указано, Docker генерирует случайное имя:
$ docker volume create
d7fb659f9b2f6c6fd7b2c796a47441fa77c8580a080e50fb0b1582c8f602ae2f
3.2. Список томов
Подкоманда ls показывает все тома, известные Docker:
$ docker volume ls
DRIVER VOLUME NAME
local data_volume
local d7fb659f9b2f6c6fd7b2c796a47441fa77c8580a080e50fb0b1582c8f602ae2f
Мы можем фильтровать, используя флаг -f или –filter и передавая параметры ключ=значение для большей точности:
$ docker volume ls -f name=data
DRIVER VOLUME NAME
local data_volume
~~ ~ 3.3. Проверка томов
Чтобы отобразить подробную информацию об одном или нескольких томах, мы используем подкоманду проверки:
$ docker volume inspect ca808e6fd82590dd0858f8f2486d3fa5bdf7523ac61d525319742e892ef56f59
[
{
"CreatedAt": "2020-11-13T17:04:17Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/ca808e6fd82590dd0858f8f2486d3fa5bdf7523ac61d525319742e892ef56f59/_data",
"Name": "ca808e6fd82590dd0858f8f2486d3fa5bdf7523ac61d525319742e892ef56f59",
"Options": null,
"Scope": "local"
}
]
«
«Следует отметить, что драйвер тома описывает, как хост Docker находит том. Тома могут быть на удаленном хранилище через nfs, например. В этом примере том находится в локальном хранилище.
3.4. Удаление томов
$ docker volume rm data_volume
data_volume
Чтобы удалить один или несколько томов по отдельности, мы можем использовать подкоманду rm:
3.5. Удаление томов
$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
data_volume
Мы можем удалить все неиспользуемые тома с помощью подкоманды prune:
4. Запуск контейнера с томом
4.1. Использование -v
$ docker run -v $(pwd):/var/opt/project bash:latest \
bash -c "ls /var/opt/project"
Как мы видели в предыдущем примере, мы можем запустить контейнер с привязкой монтирования, используя опцию -v:
$ docker run -v data-volume:/var/opt/project bash:latest \
bash -c "ls /var/opt/project"
Этот синтаксис также поддерживает монтирование тома:
$ docker run -v data-volume:/var/opt/project bash:latest \
bash -c "echo Baeldung > /var/opt/project/Baeldung.txt"
As наш том пуст, это ничего не перечисляет из точки монтирования. Однако, если бы мы записывали в том во время одного вызова контейнера:
$ docker run -v data-volume:/var/opt/project bash -c "ls /var/opt/project"
Baeldung.txt
Тогда наше последующее использование контейнера с этим смонтированным томом могло бы получить доступ к файлу:
-
Параметр v содержит три компонента, разделенных двоеточием:
Исходный каталог или имя тома Точка монтирования внутри контейнера (необязательно) ro, если монтирование должно быть только для чтения
4.2. Использование опции —mount
$ docker run --mount \
'type=volume,src=data-volume,\
dst=/var/opt/project,volume-driver=local,\
readonly' \
bash -c "ls /var/opt/project"
Мы можем предпочесть использовать более понятную опцию —mount, чтобы указать том, который мы хотим смонтировать:
-
Ввод для —mount представляет собой строку пар ключ-значение, разделенных запятыми. Здесь мы установили:
type — as volume, чтобы указать источник монтирования тома — имя тома, хотя это мог бы быть исходный каталог, если бы мы выполняли привязку mount dst — – в качестве конечной точки монтирования в контейнере Volume-Driver – локальный драйвер в данном случае только для чтения – чтобы сделать это монтирование доступным только для чтения; мы могли бы выбрать rw для чтения/записи
Следует отметить, что приведенная выше команда также создаст том, если он еще не существует.
4.3. Использование —volumes-from для совместного использования томов
Следует отметить, что присоединение тома к контейнеру создает долгосрочную связь между контейнером и томом. Даже когда контейнер вышел, связь все еще существует. Это позволяет нам использовать вышедший контейнер в качестве шаблона для монтирования того же набора томов в новый.
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4920602f8048 bash "docker-entrypoint.s…" 7 minutes ago Exited (0) 7 minutes ago exciting_payne
Допустим, мы запустили наш эхо-скрипт в контейнере с монтированием тома данных. Позже мы могли бы перечислить все контейнеры, которые мы использовали:
$ docker run --volumes-from 4920 \
bash:latest \
bash -c "ls /var/opt/project"
Baeldung.txt
Мы могли бы запустить наш следующий контейнер, скопировав тома, используемые этим: используется для связывания томов между запущенными контейнерами. Jenkins использует его для обмена данными между агентами, работающими как контейнеры Docker.
5. Заключение
В этой статье мы увидели, как Docker обычно создает контейнер со свежей файловой системой, но как связывание монтирования и томов позволяет долгосрочное хранение данных за пределами жизненного цикла контейнера.
Мы увидели, как составлять список томов Docker и управлять ими, а также как подключать тома к работающему контейнеру через командную строку.