ZaLinux.ru

Как передавать данные между контейнерами Docker

Введение

Docker – это популярный инструмент «контейнаризации», используемый для предоставления программным приложением файловой системы, которая содержит всё необходимое для запуска. Использование контейнеров Docker гарантирует, что программное обеспечение будет вести себя одинаково не зависимо от того, где оно было развёрнуто, поскольку среда выполнения остаётся всегда совместимой.

В целом, контейнеры Docker эфемерны, работают ровно столько, сколько нужно для выполнения введённой команды. Тем не менее, иногда нужно, чтобы приложения имели общий доступ к данным или сохраняли данные после удаления контейнера. Базы данных, сгенерированный пользователями контент для веб-сайта и файлы журналов – это всего лишь несколько примеров данных, которые непрактично или невозможно включить в образ Docker’а, но к которым у приложений должен быть доступ. Постоянный доступ к данным обеспечивается томами Docker’а.

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

1 — Создание независимого тома

Представленная в выпуске Docker 1.9 команда docker volume create позвооляет создавать том без относительно к какому-либо определённому контейнеру. Мы будем использовать эту команду для добавления тома с названного DataVolume1:

docker volume create --name DataVolume1

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

Вывод

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

Вывод

 [
    {
        "Name": "DataVolume1",
        "Driver": "local",
        "Mountpoint": "/var/lib/docker/volumes/datavolume1/_data",
        "Labels": null,
        "Scope": "local"
    }
]

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

Далее давайте запустим новый контейнер и присоединим DataVolume1:

docker run --rm -ti -v DataVolume1:/datavolume1 ubuntu
cat /datavolume1/Example1.txt

Вывод

Example1

Давайте выйдем из контейнера.

exit

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

2 — Создание тома, который сохраняется при удалении контейнера

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

Мы будем использовать команду docker run для создания нового контейнера на основе базового образа ubuntu. Ключ -t предоставит нам терминал, а -i позволит нам взаимодействовать с ним. Для ясности мы будем использовать --name чтобы идентифицировать контейнер. Затем мы добавим -v для создания нового тома. Мы назовём его DataVolume2. Мы будем использовать двоеточие для отделения имени от пути, куда том должен быть смонтирован в контейнере. Наконец мы укажем базовой образ Ubuntu и будем полагаться на стандартную команду образа Ubuntu в файле Docker, которая перекинет нас в оболочку bash.

docker run -ti --name=Container2 -v DataVolume2:/datavolume2 ubuntu

Помните: флаг -v очень гибкий. Он может привязать или назвать том небольшим изменением синтаксиса. Если первый аргумент начинается с / или ~/ вы создаёте привязку. Удалите их и вы назовёте том. Например:

  • -v /путь:/путь/в/контейнере монтирует директорию хоста /путь в /путь/в/контейнере
  • -v путь:/путь/в/контейнере создаёт том по названию пути без отношения к хосту.

Для дополнительной информации о привязке (bindmounting) директории из хоста, смотрите «Как передавать данные между контейнером Docker и хостом».

Пока мы там, запишем какие-нибудь данные на том:

echo "Example2" > /datavolume2/Example2.txt
cat /datavolume2/Example2.txt

Вывод

Example2

Выходим из контейнера:

exit

Когда мы перезапустим его, том будет смонтирован автоматически.

docker start -ai Container2

Давайте проверим, что том был действительно был смонтирован и наши данные ещё на месте:

cat /datavolume2/Example2.txt

Вывод

Example2

Наконец, давайте выйдем и сделаем очистку.

exit

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

docker volume rm DataVolume2

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

Вывод

Error response from daemon: Unable to remove volume,
volume still in use: remove DataVolume2: volume is in use -
[719f98391ecf1d6f0f053ffea1bbd84cd2dc9cf6d31d5a4f348c60d98392804c]

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

docker rm 719f98391ecf1d6f0f053ffea1bbd84cd2dc9cf6d31d5a4f348c60d98392804c

Вывод

719f98391ecf1d6f0f053ffea1bbd84cd2dc9cf6d31d5a4f348c60d98392804c

Удаление контейнера не возымеет эффекта над томом. Выведя список томов командой docker volume ls, мы сможем увидеть, что он ещё присутствует в системе.

docker volume ls

Вывод

DRIVER              VOLUME NAME
local               DataVolume2

И мы можем использовать docker volume rm для его удаления.

docker volume rm DataVolume2

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

3 — Создание тома из существующей директории с данными

Обычно, создание независимого тома командой docker volume create и создание тома во время создания контейнера являются эквивалентными, за одним исключением. Если мы создаём том в то же время, что мы создаём  контейнер и мы указываем путь до директории, которые содержит данные в базовом образе, эти данные будут скопированы в том.

В качестве примера мы создадим контейнер и добавим в том данные из директории /var, которая содержит файлы в базовом образе:

docker run -ti --rm -v DataVolume3:/var ubuntu

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

docker run --rm -v DataVolume3:/datavolume3 ubuntu ls DataVolume3

DataVolume3 имеет копию содержимого директории образа /var:

Вывод

backups
cache
lib
local
lock
log
mail
opt
run
spool
tmp

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

4 — Общие данные между несколькими контейнерами Docker

До сих пор мы прикрепляли том к одному контейнеру за раз. Но часто мы хотим несколько контейнеров присоединить к одному тому данных. Это довольно просто сделать, но есть одно важное предостережение: на данный момент Docker не обрабатывает блокировку файлов. Если вам нужно, чтобы несколько контейнеров записывали на том, запущенные в этих контейнерах приложения должны быть сконструированы для записи в общие хранилища данных, чтобы предотвратить повреждение данных.

Создадим Container4 и DataVolume4

Используйте docker run для создания нового контейнера с именем named 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

Вывод

This file is shared between containers

Давайте из второго контейнера добавим к нему какой-нибудь текст:

echo "Both containers can write to DataVolume4" >> /datavolume4/Example4.txt

Наконец мы выходим из контейнера:

exit

Далее мы проверим, что наши данные ещё находятся в контейнере Container4.

Просмотр сделанных изменений в Container5

Давайте проверим изменения, которые были записаны на том данных контейнером Container5, сделаем это перезапустив Container4:

docker start -ai Container4
cat /datavolume4/Example4.txt
cat /DataVolume4/Example4.txt

Вывод

This file is shared between containers
Both containers can write to DataVolume4

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

exit

Ещё раз, Docker не работает с каким-либо блокировщиком файлов, поэтому приложения должны позаботиться о блокировки файлов. Возможно смонтировать том Docker только для чтения, чтобы убедиться, что не случиться случайного повреждение данных. Когда нужно, чтобы контейнер имел только доступ для чтения, добавьте :ro. Взглянем, как это работает.

Запуск Container 6 и монтирование тома только для чтения

Когда том был смонтирован в контейнер, вместо его размонтирования, как бы мы сделали с типичной файловой системой Linux, мы создадим новый контейнер и смонтируем тем образом, каким хотим, если нужно, удалим предыдущий контейнер. Чтобы сделать том только для чтения добавьте в конец имени контейнера :ro

docker run -ti --name=Container6 --volumes-from Container4:ro ubuntu

Проверим доступ только для чтения попытавшись удалить наш файл примера:

rm /datavolume4/Example4.txt

Вывод

rm: cannot remove '/datavolume4/Example4.txt': Read-only file system

Наконец, выйдем из контейнера и очистим наши тестовые контейнеры и тома:

exit

Сделано, давайте удалим наши контейнеры и тома:

docker rm Container4 Container5 Container6
docker volume rm DataVolume4

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

Заключение

В этой инструкции мы создали том данных, который позволял постоянное хранение данных после удаления контейнера. Мы расшарили том с данными между контейнерами, помня при этом, что приложения должны уметь обрабатывать блокировку файлов для предотвращения повреждения данных. Наконец, мы показали, как смонтировать том в режиме только для чтения. Если вам интересно узнать о расшаривании данных между контейнерами и хостовой системой, посмотрите «Как передавать данные между контейнером Docker и хостом».

Рекомендуемые статьи:

Оставить комментарий

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