Возможно вам нужна инструкция, как обмениваться данными между Docker и хостом.
Docker — популярный инструмент контейнеризации, используемый для обеспечения программных приложений файловой системой, содержащей все необходимое для их запуска. Использование контейнеров Docker гарантирует, что программное обеспечение будет вести себя одинаково независимо от того, где оно развернуто, поскольку его среда выполнения согласована.
В общем, контейнеры Docker недолговечны и работают ровно столько, сколько требуется для выполнения команды, выданной в контейнере. Однако иногда приложениям необходимо предоставлять общий доступ к данным или сохранять данные после удаления контейнера. Базы данных, пользовательский контент для веб-сайта и файлы журналов — это всего лишь несколько примеров данных, которые непрактично или невозможно включить в образ Docker, но к которым приложениям необходим доступ. Постоянный доступ к данным обеспечивается с помощью томов Docker.
Тома Docker могут быть созданы и присоединены той же командой, которая создает контейнер, или они могут быть созданы независимо от каких-либо контейнеров и присоединены позже. В этой статье вы рассмотрите четыре различных способа обмена данными между контейнерами.
Предварительные требования
Чтобы следовать этой статье, вам понадобится сервер Ubuntu 22.04 со следующим:
- Пользователь, не являющийся пользователем root, с правами sudo;
- Установленный Docker.
Примечание: Хотя в Предварительных требованиях приведены инструкции по установке Docker в Ubuntu 22.04, docker
команды для томов данных Docker в этой статье должны работать в других операционных системах, пока установлен Docker и пользователь sudo добавлен в docker
группу.
Создание независимого тома
Представленная в выпуске Docker команда docker volume create
позволяет создавать тома, не привязывая их к какому-либо конкретному контейнеру. Вы будете использовать эту команду для добавления тома с именем DataVolume1
:
docker volume create --name DataVolume1
Отображается имя, указывающее на то, что команда выполнена успешно:
Output
DataVolume1
Чтобы использовать том, вы создадите новый контейнер из образа Ubuntu, используя --rm
флаг для автоматического удаления его при выходе. Вы также будете использовать -v
для монтирования нового тома. -v
требуется имя тома, двоеточие, затем абсолютный путь к тому месту, где том должен появиться внутри контейнера. Если каталоги в пути не существуют как часть образа, они будут созданы при выполнении команды. Если они существуют, подключенный том скроет существующее содержимое:
docker run -ti --rm -v DataVolume1:/datavolume1 ubuntu
Находясь в контейнере, запишите некоторые данные на том:
echo "Example1" > /datavolume1/Example1.txt
Поскольку вы использовали флаг --rm
, ваш контейнер будет автоматически удален при выходе. Однако ваш том по-прежнему будет доступен.
exit
Вы можете убедиться, что том присутствует в вашей системе с помощью docker volume inspect
:
docker volume inspect DataVolume1
Output[
{
"CreatedAt": "2018-07-11T16:57:54Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/DataVolume1/_data",
"Name": "DataVolume1",
"Options": {},
"Scope": "local"
}
]
Примечание: вы даже можете просмотреть данные на хосте по пути, указанному как Mountpoint
. Однако вам не следует изменять это, поскольку это может привести к повреждению данных, если приложения или контейнеры не будут знать об изменениях.
Далее запустите новый контейнер и прикрепите DataVolume1
:
docker run --rm -ti -v DataVolume1:/datavolume1 ubuntu
Проверьте содержимое:
cat /datavolume1/Example1.txt
Output
Example1
Выйдите из контейнера:
exit
В этом примере вы создали том, прикрепили его к контейнеру и проверили его сохраняемость.
Создание тома, который сохраняется при удалении контейнера
В следующем примере вы создадите том одновременно с контейнером, удалите контейнер, затем присоедините том к новому контейнеру.
Вы будете использовать docker run
команду для создания нового контейнера с использованием базового образа Ubuntu. -t
предоставит нам терминал и -i
позволит нам взаимодействовать с ним. Для наглядности вы будете использовать --name
для идентификации контейнера.
Флаг -v
позволит нам создать новый том, который вы вызовете DataVolume2
. Вы будете использовать двоеточие, чтобы отделить это имя от пути, по которому том должен быть смонтирован в контейнере. Наконец, вы укажете базовый образ Ubuntu и будете полагаться на команду по умолчанию в файле Docker базового образа Ubuntu, bash
чтобы перенести нас в оболочку:
docker run -ti --name=Container2 -v DataVolume2:/datavolume2 ubuntu
Примечание: -v
Флаг очень гибкий. Он может привязывать или присваивать имя тому с небольшой корректировкой синтаксиса. Если первый аргумент начинается с /
или ~/
, вы создаете bindmount . Удалите это, и вы присвоите тому имя. Например:
-v /path:/path/in/container
монтирует каталог хоста,/path
на/path/in/container
-v path:/path/in/container
создает том с именемpath
, не имеющий отношения к хосту.
Подробнее о привязке каталога к хосту см. в разделе Как обмениваться данными между контейнером Docker и хостом
Находясь в контейнере, вы будете записывать некоторые данные на том:
echo "Example2" > /datavolume2/Example2.txt
cat /datavolume2/Example2.txt
Output
Example2
Выйдите из контейнера:
exit
При перезапуске контейнера том подключится автоматически:
docker start -ai Container2
Убедитесь, что том действительно смонтирован и ваши данные все еще на месте:
cat /datavolume2/Example2.txt
Output
Example2
Наконец, завершите работу и очистите:
exit
Docker не позволит нам удалить том, если на него ссылается контейнер. Чтобы посмотреть, что произойдет, попробуйте:
docker volume rm DataVolume2
В сообщении сообщается, что том все еще используется, и предоставляется расширенная версия идентификатора контейнера:
OutputError response from daemon: unable to remove volume: remove DataVolume2: volume is in use - [d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63]
Вы можете использовать идентификатор в приведенном выше сообщении об ошибке, чтобы удалить контейнер:
docker rm d0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63
Outputd0d2233b668eddad4986313c7a4a1bc0d2edaf0c7e1c02a6a6256de27db17a63
Удаление контейнера не повлияет на объем. Вы можете увидеть, что он все еще присутствует в системе, перечислив тома с помощью docker volume ls
:
docker volume ls
OutputDRIVER VOLUME NAME
local DataVolume2
И вы можете использовать docker volume rm
для его удаления:
docker volume rm DataVolume2
В этом примере вы создали пустой том данных одновременно с созданием контейнера. В следующем примере вы увидите, что происходит, когда вы создаете том с каталогом контейнера, который уже содержит данные.
Создание тома из существующего каталога с данными
Как правило, независимое создание тома с помощью docker volume create
и создание тома при создании контейнера эквивалентны, за одним исключением. Если вы создаете том одновременно с созданием контейнера и указываете путь к каталогу, который содержит данные в базовом образе, эти данные будут скопированы в том.
В качестве примера вы создадите контейнер и добавите объем данных в /var
, каталог, который содержит данные в базовом образе:
docker run -ti --rm -v DataVolume3:/var ubuntu
Все содержимое из /var
каталога базового образа копируется в том, и вы можете смонтировать этот том в новом контейнере.
Выйдите из текущего контейнера:
exit
На этот раз вместо того, чтобы полагаться на bash
команду базового образа по умолчанию, вы выполните свою собственную ls
команду, которая покажет содержимое тома без входа в оболочку:
docker run --rm -v DataVolume3:/datavolume3 ubuntu ls datavolume3
В каталоге datavolume3
теперь есть копия содержимого /var
каталога базового образа:
Outputbackups
cache
lib
local
lock
log
mail
opt
run
spool
tmp
Маловероятно, что вы захотите монтировать /var/
таким образом, но это может быть полезно, если вы создали свой собственный образ и хотите простой способ сохранения данных. В следующем примере вы продемонстрируете, как можно совместно использовать том между несколькими контейнерами.
Обмен данными между несколькими контейнерами Docker
До сих пор вы подключали тома к одному контейнеру за раз. Часто требуется подключить несколько контейнеров к одному тому данных. Выполнить это относительно просто, но есть одно важное предостережение: на данный момент Docker не обрабатывает блокировку файлов. Если вам нужно, чтобы несколько контейнеров выполняли запись на один том, приложения, работающие в этих контейнерах, должны быть сконструированы так, чтобы выполнять запись в общие хранилища данных, чтобы предотвратить повреждение данных.
Создайте Container4 и DataVolume4
Используйте docker run
для создания нового контейнера с именем Container4
с прикрепленным объемом данных:
docker run -ti --name=Container4 -v DataVolume4:/datavolume4 ubuntu
Далее вы создадите файл и добавите текст:
echo "This file is shared between containers" > /datavolume4/Example4.txt
Затем вы выйдете из контейнера:
exit
Это возвращает нас в командную строку хоста, где вы создадите новый контейнер, из которого будет монтироваться объем данных Container4
.
Создайте Container5 и смонтируйте тома из Container4
Вы собираетесь создавать Container5
и монтировать тома из Container4
:
docker run -ti --name=Container5 --volumes-from Container4 ubuntu
Проверьте сохраняемость данных:
cat /datavolume4/Example4.txt
OutputThis file is shared between containers
Теперь добавьте немного текста из Container5
:
echo "Both containers can write to DataVolume4" >> /datavolume4/Example4.txt
Наконец, вы выйдете из контейнера:
exit
Далее вы убедитесь, что ваши данные все еще присутствуют в Container4
.
Просмотреть изменения, внесенные в Container5
Теперь проверьте, нет ли изменений, которые были записаны в том данных с помощью Container5
перезапуска Container4
:
docker start -ai Container4
Проверьте наличие изменений:
cat /datavolume4/Example4.txt
OutputThis file is shared between containers
Both containers can write to DataVolume4
Теперь, когда вы убедились, что оба контейнера могут выполнять чтение и запись из тома данных, вы выйдете из контейнера:
exit
Опять же, Docker не обрабатывает блокировку файлов, поэтому приложения должны сами отвечать за блокировку файлов. Можно подключить том Docker как доступный только для чтения, чтобы гарантировать, что повреждение данных не произойдет случайно, когда контейнеру требуется доступ только для чтения, добавив :ro
. Теперь вы посмотрите, как это работает.
Запустите контейнер 6 и смонтируйте том только для чтения
После того, как том смонтирован в контейнере, вместо того, чтобы размонтировать его, как вы бы сделали с обычной файловой системой Linux, вы можете вместо этого создать новый контейнер, смонтированный так, как вы хотите, и, при необходимости, удалить предыдущий контейнер. Чтобы сделать том доступным только для чтения, вы добавляете :ro
в конец имени контейнера:
docker run -ti --name=Container6 --volumes-from Container4:ro ubuntu
Вы проверите статус «только для чтения», попытавшись удалить файл примера:
rm /datavolume4/Example4.txt
Outputrm: cannot remove '/datavolume4/Example4.txt': Read-only file system
Наконец, вы выйдете из контейнера и очистите свои тестовые контейнеры и тома:
exit
Теперь, когда вы закончили, очистите свои контейнеры и тома:
docker rm Container4 Container5 Container6
docker volume rm DataVolume4
В этом примере вы показали, как обмениваться данными между двумя контейнерами с помощью тома данных и как подключить том данных как доступный только для чтения.
Заключение
В этом руководстве вы создали том данных, который позволил данным сохраняться после удаления контейнера. Вы разделили тома данных между контейнерами, с оговоркой, что приложения должны быть спроектированы так, чтобы обрабатывать блокировку файлов для предотвращения повреждения данных. Наконец, вы показали, как подключить общий том в режиме только для чтения. Если вам интересно узнать об обмене данными между контейнерами и хост-системой, см. Как обмениваться данными между контейнером Docker и хостом.