Как примонтировать папку в docker
Перейти к содержимому

Как примонтировать папку в docker

  • автор:

Управление данными в Docker

К докер контейнерам можно подключать внешние папки. Рассматриваем основные способы.

Время чтения: больше 15 мин

Открыть/закрыть навигацию по статье

  1. Связанные папки (bind mounts)
    1. Как пользоваться
    1. Как пользоваться
    2. Использование драйверов
    3. Резервные копии
    1. Как пользоваться
    1. Игорь Коровченко советует
    1. Опишите жизненный цикл контейнера Docker

    Обновлено 17 мая 2022

    В этой статье мы поговорим про управление данными приложений в Docker. Узнать, что такое Docker, вы сможете из статьи «Что такое Docker». Также вы можете почитать о мультиконтейнерных приложениях и Docker Compose и о том, как устроен Dockerfile.

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

    • связанные папки, примонтированные к контейнеру как внешние диски (bind mounts);
    • тома (volumes);
    • часть оперативной памяти для работы с данными (tmpfs mounts или npipe mounts).

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

    Наглядная схема типов управления данными в Docker:

    Наглядная схема типов управления данными в Docker.

    Рассмотрим каждый тип по отдельности.

    Связанные папки (bind mounts)

    Скопировать ссылку «Связанные папки (bind mounts)» Скопировано

    Связанные папки появились в Docker с самых первых релизов. Это удобный инструмент, но у него есть ограничения. Этот тип управления данными позволяет связать папку на компьютере пользователя (то есть хосте, на котором установлен Docker Engine) и папку в контейнере. Работать в контейнере и на хосте с такой папкой можно одновременно, все изменения будут отображаться и там, и там. Механизм bind mounts подразумевает, что данные могут быть изменены в любое время как из подключённого контейнера, так и непосредственно на хосте.

    При создании связанной папки указывается полный путь к ней на хосте и путь внутри контейнера. Если папка не существует на хосте, Docker может создать её сам.

    Связанные папки используются:

    Когда конфигурационные файлы на хосте и в контейнере одни и те же. Именно этот тип использует сам Docker для автоматического монтирования конфигурации DNS хоста.

    Когда работаем с исходным кодом и артефактами сборок. Можно использовать системы сборки для исходного кода внутри контейнера. Вы меняете код, бандлер, который находится внутри контейнера, это видит, и код попадает в новую сборку. Другой вариант использования — работа с уже собранными бандлами, например, для тестирования или отладки приложений.

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

    Как пользоваться

    Скопировать ссылку «Как пользоваться» Скопировано

    Чтобы связать папку на хосте с папкой внутри контейнера, можно воспользоваться флагами -v или — — mount . $ ( pwd ) в командах ниже означает, что примонтируется текущая папка на хосте.

    Пример с флагом -v :

     docker run -d \ -it \ --name devtest \ -v "$(pwd)"/target:/app \ node:lts docker run -d \ -it \ --name devtest \ -v "$(pwd)"/target:/app \ node:lts     

    Можно задать следующие опции: rprivate , private , rshared , shared , rslave , slave , ro , z и Z .

    Первые шесть параметров позволяют управлять тем, как будут влиять изменения в одной точке монтирования тома на другие точки монтирования. По умолчанию используется rprivate , что означает — никак.

    Последние три параметра могут быть указаны только для флага -v . Значение ro определяет режим только для чтения. Папка на хосте не может быть изменена внутри контейнера. Значение z обозначает, что папка на хосте может быть использована несколькими контейнерами. Значение Z обозначает, что папка используется только одним контейнером. Не указывайте значение Z для системных папок, например, /usr или /home. Это приведёт к тому, что работа операционной системы на хосте будет парализована. Будьте аккуратны!

    Пример с флагом — — mount :

     docker run -d \ -it \ --name devtest \ --mount type=bind,source="$(pwd)"/target,target=/app \ node:lts docker run -d \ -it \ --name devtest \ --mount type=bind,source="$(pwd)"/target,target=/app \ node:lts     

    Ключ bind-propagation

    Для флага — — mount есть ключ bind — propagation , который работает только на Linux (операционные системы контейнера и хоста должны поддерживать этот режим работы).

    Представьте, есть две точки монтирования /mnt1 и /mnt2 , к которым привязана одна и та же папка на хосте. Значения ключа bind — propagation определяют, что произойдёт, если в связанной папке появятся подпапки. Что произойдёт с /mnt2 / sub при монтировании /mnt1 / sub ? Возможны следующие варианты:

    — shared указывает на то, что изменения для точки монтирования /mnt1 / sub будут в точности отражаться в /mnt2 / sub и наоборот;
    — slave указывает на то же, что shared , но только в одном направлении (изменения в первой точке монтирования будут распространяться на вторую, но не наоборот);
    — private указывает, что изменения в первой точке монтирования не будут отображаться во второй, и наоборот;
    — rshared — то же, что shared , распространяет подобное поведение на все реплики точек монтирования;
    — rslave — то же, что slave , распространяет подобное поведение на все реплики точек монтирования;
    — rprivate (значение по умолчанию) — то же, что private , распространяет подобное поведение на все реплики точек монтирования.

     docker run -d \ -it \ --name devtest \ --mount type=bind,source="$(pwd)"/app/src,target=/app \ --mount type=bind,source="$(pwd)"/app/src,target=/app2,readonly,bind-propagation=rslave \ node:lts docker run -d \ -it \ --name devtest \ --mount type=bind,source="$(pwd)"/app/src,target=/app \ --mount type=bind,source="$(pwd)"/app/src,target=/app2,readonly,bind-propagation=rslave \ node:lts     

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

    — приложение app2 может только читать данные из папки на хосте;
    — изменения в первой точке монтирования сразу происходят и во второй, но не наоборот.

    Ключ bind — propagation служит для управления хранилищами на продвинутом уровне и, как правило, нужен в специальных задачах. Об этом механизме вы можете почитать подробнее в официальной документации Linux.

    Флаг — — mount не поддерживает опции для управления метками selinux ( z и Z ).

    Проверьте корректность работы хранилища с помощью команды:

     docker inspect devtest docker inspect devtest     

    В соответствующей секции Mounts вы сможете найти исчерпывающую информацию. Например, если вы находились в папке /tmp/source/target при запуске контейнера, то в этой секции будет указана примерно следующая информация:

     "Mounts": [  "Type": "bind", "Source": "/tmp/source/target", "Destination": "/app", "Mode": "", "RW": true, "Propagation": "rprivate" >], "Mounts": [  "Type": "bind", "Source": "/tmp/source/target", "Destination": "/app", "Mode": "", "RW": true, "Propagation": "rprivate" > ],      

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

     docker container stop devtestdocker container rm devtest docker container stop devtest docker container rm devtest     
    1. Связанными папками нельзя управлять из Docker CLI.
    2. Абсолютные пути на разных компьютерах могут быть разными.
    3. Если в контейнере в примонтированной папке есть содержимое, то оно «перекроет» содержимое связанной папки на все время работы контейнера.
    4. Использовать связанные папки для работы с конфигурационными файлами небезопасно.
    5. Файловая система и структура папок могут сильно отличаться на разных компьютерах.
    6. Правила описания путей к файлам могут отличаться при переходе от одной платформы к другой.
    7. Вы можете столкнуться с ситуацией, когда приложение в контейнере получит доступ к системным папкам или удалит критически важные файлы.

    Тома (volumes)

    Скопировать ссылку «Тома (volumes)» Скопировано

    Тома — это лучший тип управления данных в Docker. Только объекты или службы Docker должны иметь права на изменение данных, расположенных в томах. На хосте данные хранятся в специальных папках, но без доступа администратора к ним не подобраться. В идеологии Docker тома — что-то вроде образа флэш-накопителя или CD/DVD.

    Тома можно размещать не только на хосте. Можно, например, пользоваться облачными платформами для совместной работы с данными или для тестирования приложений. А ещё тома будут работать как с Linux-контейнерами, так и с Windows-контейнерами, поскольку файловая система томов одна и та же.

    Когда том примонтирован к контейнеру, операционная система хоста не имеет к нему доступа. Docker управляет томами отдельно, позволяя подключаться одному или нескольким контейнерам одновременно. Плюсом является и то, что том существует самостоятельно и не зависит от жизненного цикла контейнеров.

    Тома могут быть созданы при сборке контейнера (с помощью Dockerfile или Docker Compose) или вручную с помощью Docker Engine. Тома могут иметь имя, назначенное пользователем (именованные тома, named volumes), а могут быть анонимными с именем, которое Docker устанавливает автоматически (анонимные тома, anonymous volumes).

    Концепция драйвера позволяет преобразовывать данные в томах или влиять на потоки данных между томами и контейнерами. Например, это можно использовать для шифрования. Но чаще с помощью драйверов к контейнеру подключают тома, которые расположены не локально на хосте, а в облаке или на сервере. Это позволяет, не меняя логику работы приложения внутри контейнера, обрабатывать данные, которых на хосте нет.

    Итак, возможности томов:

    — миграция данных и создание резервных копий;
    — управление с помощью Docker CLI или Docker API;
    — тома работают и с Linux-, и с Windows-контейнерами;
    — данные легко и безопасно можно использовать в нескольких контейнерах;
    — существует механизм драйверов, который позволяет хранить данные не только на хосте, но и на сервере или в облаке, шифровать данные в томе или добавлять дополнительную функциональность;
    — новые тома могут создаваться с уже загруженными с помощью контейнера данными;
    — если на хосте установлены Mac или Windows, тома будут быстрее работать с Docker Desktop, чем связанные папки;
    — тома не увеличивают размер контейнера;
    — тома находятся вне жизненного цикла контейнера.

    Когда нам нужно получить доступ к данным из разных контейнеров. Том создаётся в первый раз либо вручную, либо при сборке контейнера. Уничтожается том всегда только с помощью Docker вручную. После остановки контейнера том будет продолжать работать, пока не будет удалён пользователем.

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

    Когда вы хотите хранить данные не только у себя на локальном компьютере, но и на сервере или в облаке.

    Когда нужно создать резервную копию или перенести тома с одного компьютера на другой. Тома хранятся в определённой папке на компьютере. Вы можете просто скопировать её, заархивировать и перенести на другой хост. Примерно так же создаётся и резервная копия.

    Если ваше приложение требует высокой скорости обмена данными на Mac и Windows. Тома сохраняются на виртуальной машине Linux VM, на которой работают и контейнеры, поэтому скорость чтения и записи высокая. Нет лишних накладных расходов на доступ к файловой системе хоста.

    Когда важно, чтобы файловая система имела нативное поведение. Например, база данных должна контролировать кэширование на диске для гарантии выполнения транзакций. Файловые системы на Mac и Windows работают не так, как на Linux. Это может привести к ошибкам работы некоторых приложений.

    Как пользоваться

    Скопировать ссылку «Как пользоваться» Скопировано

    Создать том можно с помощью флагов -v или — — mount при запуске контейнера. Для флага -v можно указать параметр ro , который будет означать использование режима только для чтения. Для флага — — mount есть ключ volume — opt , который устанавливает набор опций, разделённых запятыми. Не забывайте, что значения для этого ключа должны быть экранированы кавычками. Работа с томами такова, что изменения в одной точке монтирования в контейнере не будут отображаться в другой точке монтирования (параметр bind — propagation всегда выставлен в значение rprivate ).

    Подключить том с именем my — vol можно следующим образом.

    С флагом — — mount :

     docker run -d \ --name devtest \ --mount source=my-vol,target=/app \ node:lts docker run -d \ --name devtest \ --mount source=my-vol,target=/app \ node:lts     
     docker run -d \ --name devtest \ -v my-vol:/app \ node:lts docker run -d \ --name devtest \ -v my-vol:/app \ node:lts     

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

     docker inspect devtest docker inspect devtest     

    Чтобы удалить том, необходимо отключить связанный с ним контейнер и удалить сам контейнер:

     docker container stop devtestdocker container rm devtestdocker volume rm my-vol docker container stop devtest docker container rm devtest docker volume rm my-vol     

    Управлять томами можно через Docker API с помощью Docker CLI и Docker Compose.

    Чтобы создать новый том с помощью Docker CLI, используйте команду:

     docker volume create my-vol docker volume create my-vol     

    Получите список томов на хосте:

     docker volume ls docker volume ls      

    Посмотрите информацию о томе:

     docker volume inspect my-vol docker volume inspect my-vol     

    Удалите том командой:

     docker volume rm my-vol docker volume rm my-vol     

    Если том был анонимным, то можно удалить его сразу после завершения работы контейнера. Для этого при запуске контейнера вы можете прописать флаг — — rm . Вместе с удалением контейнера в этом случае удалится и том:

     docker run --rm -v /foo -v awesome:/bar container app docker run --rm -v /foo -v awesome:/bar container app     

    После завершения работы и последующего удаления контейнера анонимный том удалится, а именованный awesome продолжит работать.

    Чтобы удалить все неиспользуемые тома, используйте команду:

     docker volume prune docker volume prune     

    Для того, чтобы подключить том с помощью Dockerfile, необходимо использовать инструкцию VOLUME :

     FROM node:ltsRUN useradd userRUN mkdir /data && touch /data/xRUN chown -R user:user /dataVOLUME /data FROM node:lts RUN useradd user RUN mkdir /data && touch /data/x RUN chown -R user:user /data VOLUME /data      

    Интересно, что вы не сможете внести какие-либо изменения в данные на этапе сборки образа. Следующий Dockerfile правильно работать не будет:

     FROM node:ltsRUN useradd userVOLUME /dataRUN touch /data/xRUN chown -R user:user /data FROM node:lts RUN useradd user VOLUME /data RUN touch /data/x RUN chown -R user:user /data      

    Том будет подключён только после создания образа на этапе запуска контейнера. Возможно, придётся использовать инструкции CMD или ENTRYPOINT . Подробнее описано в статье «Как устроен Dockerfile».

    Запустить том для отдельного контейнера с Docker Compose можно с помощью следующей конфигурации:

     services: frontend: image: node:lts volumes: - myapp:/home/node/appvolumes: myapp: services: frontend: image: node:lts volumes: - myapp:/home/node/app volumes: myapp:      

    Команда docker — compose up поднимет не только сам контейнер frontend , но и создаст том myapp . Если он уже был создан, Docker Compose подключит его к контейнеру, но надо указать это явно с помощью элемента external так:

     services: frontend: image: node:lts volumes: - myapp:/home/node/appvolumes: myapp: external: true services: frontend: image: node:lts volumes: - myapp:/home/node/app volumes: myapp: external: true      

    Подробнее о формате конфигурации Docker Compose можно прочитать в статье о Docker Compose.

    Использование драйверов

    Скопировать ссылку «Использование драйверов» Скопировано

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

    Например, есть два компьютера — хост, на котором установлен Docker и запускаются контейнеры, и файловый сервер, который поставляет данные для них. Контейнеры ничего не знают про эту архитектуру: все запускалось изначально на локальном хосте. Драйвер vieux / sshfs позволяет использовать SSH-соединение для связи с файловым сервером, при этом данные будут представлены в виде тома Docker.

    Для начала необходимо установить соответствующий плагин для Docker Engine:

     docker plugin install --grant-all-permissions vieux/sshfs docker plugin install --grant-all-permissions vieux/sshfs     

    Затем нужно создать том и прописать учётные данные:

     docker volume create --driver vieux/sshfs \ -o sshcmd=test@node2:/home/test \ -o password=testpassword \ sshvolume docker volume create --driver vieux/sshfs \ -o sshcmd=test@node2:/home/test \ -o password=testpassword \ sshvolume     

    Если для связи по SSH между клиентом и сервером уже работают ключи доступа, то пароль можно опустить. Флаг -o указывает на опции, которые могут быть переданы драйверу. Набор доступных опций у каждого драйвера свой.

    Можно создать том и другим способом, при запуске контейнера:

     docker run -d \ --name sshfs-container \ --volume-driver vieux/sshfs \ --mount src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword \ nginx:latest docker run -d \ --name sshfs-container \ --volume-driver vieux/sshfs \ --mount src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword \ nginx:latest     

    Если драйвер требует передачи опций, приходится использовать флаг — — mount .

    Резервные копии

    Скопировать ссылку «Резервные копии» Скопировано

    Для того чтобы создать резервную копию тома, можно использовать механизм контейнеров Docker. Например, вы уже создали контейнер с именем dbstore на базе операционной системы Ubuntu и работаете с данными в томе dbdata . Для этого вы уже выполнили команду и получили доступ к терминалу контейнера:

     docker run -v /dbdata --name dbstore node:lts /bin/bash docker run -v /dbdata --name dbstore node:lts /bin/bash     

    Как создать резервную копию данных в томе? Нужно:

    — запустить новый контейнер и примонтировать том, который используется в контейнере dbstore ;
    — примонтировать папку на хосте, чтобы потом в неё положить резервную копию;
    — зайти внутри контейнера в том, заархивировать данные и положить их в связанную папку.

     docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata     

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

    Допустим, у вас возникла необходимость развернуть данные из сохранённой резервной копии внутри контейнера dbstore2 . Нужно запустить его:

     docker run -v /dbdata --name dbstore2 node:lts /bin/bash docker run -v /dbdata --name dbstore2 node:lts /bin/bash     

    Затем разархивировать данные в том:

     docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1" docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"      

    Хранение в оперативной памяти

    Скопировать ссылку «Хранение в оперативной памяти» Скопировано

    Хранение в оперативной памяти бывает двух типов: tmpfs mounts и npipe mounts.

    Механизм tmpfs mount в операционной системе Linux позволяет выделить часть оперативной памяти хоста для хранения данных. Данные не сохраняются в файловой системе, и получается быстрое хранилище. Примонтированная папка tmpfs работает, пока запущен контейнер, поэтому не стоит использовать этот способ для хранения настроек и результатов работы приложения.

    Для пользователей операционной системы Windows существует ещё один тип управления данными — npipe mount. Этот тип позволяет получить доступ к хосту Docker из контейнера и в основном используется для управления данными с Docker Engine API.

    Используем оперативную память:

    Если вы не хотите оставлять данные после завершения работы приложения.

    Как пользоваться

    Скопировать ссылку «Как пользоваться» Скопировано

    Этот раздел посвящён использованию только на Linux.

    С помощью томов и связанных папок вы можете делиться файлами между хостом и контейнером. После остановки контейнера данные сохраняются. Но если на хосте используется операционная система Linux, то существует и третий тип работы с данными — tmpfs. Это временное файловое хранилище, которое располагается в оперативной памяти, присутствует во многих Unix-подобных системах. Когда вы создаёте контейнер, Docker может создать отдельный слой в оперативной памяти снаружи контейнера для хранения и обработки данных.

    При использовании этого типа работы с данными в Docker есть два ограничения:

    — операционной системой хоста может быть только Linux;
    — данные в tmpfs доступны лишь из одного контейнера.

    tmpfs хорошо работает в случае хранения чувствительной информации: ключей шифрования, паролей, сертификатов доступа и тому подобного.

    Чтобы запустить контейнер с tmpfs, используют команду:

     docker run -d \ -it \ --name tmptest \ --mount type=tmpfs,destination=/app \ node:lts docker run -d \ -it \ --name tmptest \ --mount type=tmpfs,destination=/app \ node:lts     

    С помощью ключа tmpfs — size можно определить максимальный размер хранилища в байтах. По умолчанию он не ограничен. Ключ tmpfs — mode служит для определения уровня доступа в восьмеричном формате. Например, значение по умолчанию 1777 обозначает, что любой пользователь или программа в контейнере имеют неограниченный доступ к данным, которые будут доступны и вне контейнера. Этот параметр работает также, как и для tmpfs в Unix-подобных операционных системах.

    Также есть альтернативная более короткая команда для управления tmpfs mounts:

     docker run -d \ -it \ --name tmptest \ --tmpfs /app \ node:lts docker run -d \ -it \ --name tmptest \ --tmpfs /app \ node:lts     

    Проверьте состояние контейнера, чтобы убедиться, что файловое хранилище создано корректно:

     docker container inspect tmptest docker container inspect tmptest     

    В соответствующей секции будет доступна информация о примонтированной папке:

     "Tmpfs":  "/app": "">, "Tmpfs":  "/app": "" >,      

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

     docker container stop tmptestdocker container rm tmptest docker container stop tmptest docker container rm tmptest     

    На практике

    Скопировать ссылку «На практике» Скопировано

    Игорь Коровченко советует

    Скопировать ссылку «Игорь Коровченко советует» Скопировано

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

    Если папка или том окажутся не пустыми, то при монтировании к контейнеру содержимое будет на время скрыто. Контейнер будет воспринимать эту папку как пустую, данные будут в неё сохраняться и будут доступны с хоста во время работы контейнера. После окончания работы контейнера или после того, как том или папка будут отмонтированы, данные из контейнера будут потеряны, поскольку снова будут доступны те файлы и подпапки, которые были скрыты при монтировании. Это тот же механизм, который Linux будет использовать, когда вы, например, примонтируете USB-накопитель к уже заполненной чем-то папке.

    На собеседовании

    Скопировать ссылку «На собеседовании» Скопировано

    Как работают и создаются тома с bind и mount в Docker

    Создаем volume в Docker используя bind и mount на примерах

    Запуская контейнер Docker нам может понадобится сохранить где-то данные или наоборот добавить их в контейнер. Для реализации этой задачи, в Docker, был создан объект томов и возможность проброса папок. Рассмотрим как это работает на примерах.

    Навигация по посту

    • Когда использовать Docker Volume
    • Типы томов Docker
    • Монтирование через docker run
      • Подключение volume
      • Вложенные тома и папки
      • Просмотр привязанных томов
      • Привязка томов из другого контейнера
      • Драйвера и options
      • Размещение томов в другой директории
      • Использование внешних томов
      • Создание тома в другой директории

      Когда использовать Docker Volume

      Понимание надобности проброса папок и создания томов появляется при первом ознакомлении работы контейнеров в целом.

      Если у вас есть файл «code.py», который подразумевает работу какого-то приложения, вы можете положить его в образ (image), но это создаст некоторые проблемы. Например вам нужно будет выполнять пересоздание образа (build) каждый раз, как «code.py» изменится. Сборка образа может происходить десятки минут. Образ Docker становится read-only после его создания т.е. не рассчитан на изменения.

      Если вы не положили «code.py» в образ, а решили скопировать его внутрь контейнера — это так же создаст проблему. Контейнер является дополнительным слоем/snapshot над выбранным образом и имеет возможность записи. Время жизни контейнера равно времени жизни сервису, который запущен внутри него. Т.е. если у вас будет ошибка в приложении, то вам нужно будет пересоздавать контейнер и копировать файл еще раз. Все еще больше усугубиться, если вы запускаете 10 контейнеров, а вес файлов исчисляется в Гб.

      Слой для записи в Docker

      Похожая проблема есть, если вы хотите получить данные из контейнера (например логи). Вы можете написать скрипт, который будет копировать большой объем файлов с 1 или 100 контейнеров, но этой будет занимать много времени.

      Проброс папок и томов в Docker решает эти проблемы позволяя монтировать директории хоста внутри контейнера либо создавая централизованное хранилище. Таким образом мы получаем следующие преимущества:

      1. Мы не копируем данные, они хранятся в одно месте для всех контейнеров;
      2. Т.к. копирование отнимало время, а сейчас это делать не нужно, контейнеры запускаются быстрее;
      3. У нас появляется больше возможностей для управления данными.

      Типы томов Docker

      Есть два основных способа обмена данными с контейнером, которые часто называют томами:

      • перенаправление какой-то папки или файла с хоста в контейнер (так же называется bind mount);
      • создание специального объекта — volume (так же называется named volume), который имеет больше возможностей управления данными через Docker.

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

      Еще одно, незначительно, отличие это поведение по умолчанию bind mount и volume. Для примера, внутри контейнера, по пути «/usr/share/nginx/html/» лежит файл «index.html». В случае проброса томов в эту папку поведение будет разным:

      • В случае монтирования папки — «index.html», внутри контейнера, будет удален. Это произойдет даже если папка хоста пустая;
      • В случае volume — при первом использовании тома файл «index.html» будет скопирован. При последующих — удален.

      Есть еще один тип томов — tmpfs, который работает только под Linux. Он подразумевает хранение данных в ОЗУ и с ограничением в 1 контейнер.

      Типы томов Docker

      Монтирование через docker run

      Для монтирования данных используются следующие параметры:

      • -v или —volume
      • —mount

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

      Папки и файлы

      Для примера — у меня есть следующая папка на хосте:

      /home/alex/docker_data

      В случае параметра «-v» указывается два пути «откуда:куда». В случае «—mount» это именованные параметры разделенные запятыми. Пример работы обоих:

      -v /home/alex/docker_data:/usr/share/nginx/html # или --mount type=bind,source=/home/alex/docker_data,destination=/usr/share/nginx/html

      В mount мы используем следующие параметры:

      • type — со значением ‘bind’ говорит, что мы монтируем папку или файл;
      • source — источник т.е. папка или файл, который мы хотим подключить к контейнеру;
      • destination — папка или файл внутри контейнера.

      В обоих случаях мы можем монтировать данный доступные только для чтения (read-only) добавив «ro» в конце:

      -v /home/alex/docker_data:/usr/share/nginx/html:ro --mount type=bind,source=/home/alex/docker_data,destination=/usr/share/nginx/html,ro

      Так выглядит запуск контейнера с проброшенной папкой:

      docker run -d --name nginx_vol1 -v /home/alex/docker_data:/usr/share/nginx/html:ro nginx # или docker run -d --name nginx_vol2 --mount type=bind,source=/home/alex/docker_data,destination=/usr/share/nginx/html,ro nginx

      Вы можете проверить работу смонтированной папки создав файл на хосте с последующим выводом внутри контейнера:

      # тестовый файл touch /home/alex/docker_data/testfile # проверяем, что он виден внутри контейнеров docker exec nginx_vol1 ls /usr/share/nginx/html docker exec nginx_vol2 ls /usr/share/nginx/html

      Проверка работы смонтированной папки в Docker

      Подключение volume

      При монтировании тома нужно учитывать следующие моменты:

      • название тома указывается без слешей;
      • если тома с этим названием нет, то он будет создан;
      • в случае с mount, в параметре type, указывается volume.

      При использовании docker run использование томов будет выглядеть так:

      docker run -d --name nginx_vol1 -v docker_volume:/usr/share/nginx/html nginx # или docker run -d --name nginx_vol2 --mount type=volume,source=docker_volume,destination=/usr/share/nginx/html nginx

      Так же как и с папками мы можем добавить «:ro» или «,ro» в конец значения, что бы дать права только на чтение директорий.

      В предыдущем примере один том был подключен к двум контейнерам. Их совместную работу можно проверить создав файл в одном контейнере, а вывести через другой:

      # создаем файл в одном контейнере docker exec nginx_vol1 touch /usr/share/nginx/html/file1 # проверяем файл через другой контейнер docker exec nginx_vol2 ls /usr/share/nginx/html

      Проверка работы смонтированного именного тома в Docker

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

      Вложенные тома и папки

      Вы можете объявлять тома внутри смонтированных папок и наоборот. Это может создавать путаницу, но это требуется в определенных ситуациях. Например некоторые фреймворки используют следующую структуру хранение модулей и приложений:

      • «/usr/src/app» — папка с приложением, которое разрабатывает один или несколько разработчиков;
      • «/usr/src/app/node_modules» — содержит модули, которые компилируются под определенную систему.

      Сложность с «node_modules» в следующем:

      • так как некоторые модули компилируются — они могут быть связаны с конкретной ОС и компилятором. Ошибки, в случае запуска на другой ОС, могут быть непредсказуемы;
      • папка создается долго, имеет большой объем и множество файлов;
      • папка может быть использована несколькими контейнерами.

      Мы можем положить «node_modules» в том, что улучшит организацию. В то же время, папка «app», обновляется через GIT, который редко используется в контейнерах.

      Один из оптимальных способов решения этих проблем является проброс «app» как папки, а «node_modules» как тома. Для начала мы создаем том и устанавливаем в него модули примерно так:

      docker run -v $(pwd)/app/package.json:/usr/src/app/package.json \ -v node_modules:/usr/src/app/node_modules \ node \ npm install

      После того как том создан — мы можем использовать его с нашим приложением:

      docker run -v $(pwd)/app:/usr/src/app \ -v node_modules:/usr/src/app/node_modules \ node

      Просмотр привязанных томов

      Что бы посмотреть тома уже в запущенном или остановленном контейнере — можно использовать команду ‘docker inspect’. В следующем примере будут выведена только часть относящаяся к томам:

      docker inspect nginx_vol2 --format "'>'"

      Просмотр томов, которые использует контейнер в Docker

      Привязка томов из другого контейнера

      С помощью параметра «—volumes-from» мы можем скопировать тома у запущенного или остановившегося тома. В значении мы указываем контейнер:

      # контейнер 1 docker run -v $(pwd)/app:/usr/src/app \ -v node_modules:/usr/src/app/node_modules \ --name node1 \ node # контейнер 2 docker run --volumes-from node1 --name node2 node

      Использование параметра volumes-from в Docker для копирования томов

      Создание volume

      Т.к. volume — это отдельны объект у docker есть команды, с помощью которых можно им управлять:

      • docker volume ls — выведет список томов;
      • docker volume inspect — покажет подробную информацию о томе в т.ч. его расположение на хосте;
      • docker volume create — создание нового тома;
      • docker volume prune — удалит все тома, которые не используются контейнерами;
      • docker volume rm — удалит один том.

      Для примера создадим том, выведем все существующие и посмотрим детальную информацию о нем:

      docker volume create some_nginx docker volume ls docker volume inspect some_nginx 

      Создание тома Docker

      Можно легко не заметить как тома начнут занимать много места на диске. Что бы удалить тома, которые не смонтированы — можно использовать следующую команду:

      docker volume prune

      Удаление томов Docker

      Параметр ‘-f’ сделает то же самое, но без подтверждения.

      Драйвера и options

      В скриншоте выше можно было увидеть значения «Driver: local». Это значение говорит, что вы будете использовать функционал практически идентичным команде «mount» в Linux. Такой «mount» позволяет использовать nfs и cifs директории, а так же многие другие указывая их в опциях (параметры «-o» или «-opt»).

      docker volume create --driver local \ --opt type=nfs \ --opt o=addr=192.168.2.60,rw \ --opt device=:/home/alex \ nfs-volume

      Создание NFS тома в Docker volume

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

      Опции и драйвера напрямую используются редко. Мною лично только через другие приложения. Кроме этого они отличаются от ОС, которые вы используете. В Windows, например, опции не доступны по умолчанию.

      Размещение томов в другой директории

      Есть два способа с помощью которых вы можете изменить местоположение тома.

      В первом случае вы должны указывать местоположение тома при его создании. В примере ниже он будет храниться по пути «/home/alex/somevol»:

      docker volume create --driver local \ --opt type=none \ --opt device=/home/alex/somevol \ --opt o=bind \ home-vol2

      Создание тома в другой директории в Docker

      Пример смонтированного тома:

      Создание тома в другой директории в Docker пример работы

      Второй способ затрагивает не только «volume», но и все данные которые использует docker (образы, сеть, контейнеры и т.д.). Перед тем как начать — нужно остановить сервис docker:

      sudo systemctl stop docker

      После этого мы должны отредактировать или создать файл «daemon.json»:

      vi /etc/docker/daemon.json

      Если у вас этого файла нет или он пустой, то содержимое должно быть следующим:

      Если какие-то данные в этом файле были, то вам нужно добавить запятую и убрать скобки.

      Данные с предыдущей директории так же нужно скопировать в новую директорию (в примере ниже это «/docker_data»):

      sudo rsync -aP /var/lib/docker/ /docker_data

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

      Можно запустить сервис и проверить, что все работает:

      sudo systemctl start docker.service docker volume create vol1 docker inspect vol1

      Изменение директории работы Docker

      Подключение тома и папки через Dockerfile

      При создании образа через Dockerfile у вас так же есть возможность создать том, но не использовать существующий. Смонтировать папку, через Dockerfile, так же нельзя.

      Создание тома будет иметь ряд ограничений:

      1. Вы не сможете указать имя тома или выбрать существующий. Имя будет сгенерировано автоматически;
      2. В любом случае том будет создан во время запуска контейнера т.е. так же как и в случае использования ‘-v’;
      3. Каждое создание контейнера будет создавать новый том.

      Для создания тома есть инструкция VOLUME. Пример синтаксиса:

      FROM nginx # указываем точку монтирования внутри контейнера VOLUME /somedata1

      Примерный результат где запускаются 2 контейнера с одного образа:

      Создание и использование тома в образе Docker

      Аналогичный результат можно получить используя одну из следующих команд:

      docker run -v /somedata1 nginx # или docker run -v $(docker volume create):/somedata1 nginx

      Создание томов без названия в Docker

      Если вы используете инструкцию «VOLUME» и параметр «-v» указывающий на одну и ту же директорию, то «-v» возьмет верх.

      Volume в docker-compose

      Docker-compose позволяет запускать несколько контейнеров используя один файл инструкций. Синтаксис монтирования томов может быть ограниченным и расширенным так же как «-v» и «—mount».

      Для монтирования тома, кроме инструкции в самом контейнере, нужно указать дополнительную инструкцию ‘volumes’ в верхнем уровне. Для папки этого делать не нужно:

      version: "3.8" services: web: image: nginx:alpine volumes: # том - somevol:/app # папка - /home/alex:/app2 # для тома volumes: somevol:

      Том ‘somevol’ может использоваться совместно в нескольких контейнерах.

      Если нам нужно дать права на том или папку, то мы просто добавляем ‘ro’ или ‘rw’ в коней пути:

      . volumes: # том - somevol:/app:ro # папка - /home/alex:/app2:rw . 

      Для монтирования так же есть расширенный синтаксис, похожий на команду mount в docker. Следующий пример аналогичен предыдущем по эффекту:

      version: "3.8" services: web: image: nginx:alpine volumes: # том - type: volume source: somevol target: /app1 # папка - type: bind source: /home/alex target: /app2 volumes: somevol:

      Есть еще инструкции, которые вы можете использовать. Ниже только их часть, но они используются редко:

       volumes: - type: volume source: somevol target: /app1 # папка только для чтения read_only: true # не будет копировать файлы в том, которые уже находятся в контейнере volume: nocopy: true # папка - type: bind source: /home/alex target: /app2 # папка только для чтения read_only: true # создаст папку на хосте если ее нет create_host_path: true 

      Как уже говорилось выше — мы можем использовать один и тот же том в нескольких контейнерах (сервисах). Кроме этого есть инструкция «volumes_from», которая использует тома с указанного контейнера. Ниже оба примера:

      version: "3.8" services: container1: image: nginx:alpine volumes: - somevol:/app1 - /home/alex:/app2 container2: image: nginx:alpine volumes: # тот же том, но в другом контейнере - somevol:/app2 container3: image: nginx:alpine # берем тома из сервиса container1 # с доступностью только на чтение volumes_from: - container1:ro volumes: somevol:

      Ниже результат работы таких инструкций. Как видно у контейнера 1 и контейнера 3 одни и те же тома:

      Копирование и монтирование томов в docker compose

      Если вам нужно удалить тома, которые были использованы или созданы при выполнении «docker compose up», можно добавить параметр «—volumes»:

      docker compose down --volumes

      Удаление контейнеров с томами в docker compose

      По умолчанию, в compose, тома используют приставку с названием проекта в названии. Если название тома «some_vol«, а путь, в котором лежит файл docker-compose.yml следующий «/home/alex/project_name/», то том будет иметь название «project_name_some_vol».

      Использование внешних томов

      Если вам нужно использовать том, который был создан не в текущем файле docker-compose.yml, то вы можете его указать через параметр «external». Автоматический такой том не создается:

      . volumes: somevol: external: true

      Монтирование внешних томов в docker compose

      Создание тома в другой директории

      Через compose мы так же можем указывать драйвера и опции. Так, например, мы создадим тома в другой директории по аналогии с тем, что делали выше:

      . volumes: my_test_volume: driver: local driver_opts: o: bind type: none device: /home/alex/compose_vol1

      Tmpfs

      Еще одним способом монтирования томов является tmpfs. Данные этого тома хранятся в оперативной памяти. При остановке контейнера, в отличие от других томов, данные будут удалены. Эти данные просто не выгружаются из оперативной памяти. Такой тип тома вы можете создать только на одном контейнере и только в Linux.

      Такие типы хранилищ редко используются. Их можно использовать для хранения чувствительных данных (для безопасности) или что бы ускорить работу какого-то приложения, но оба варианта, обычно, реализовываются на стороне приложения.

      Есть два способа создания tmpfs:

      docker run \ --tmpfs /app \ nginx:latest # или docker run -d \ --mount type=tmpfs,destination=/app,tmpfs-size=400,tmpfs-mode=1777 \ nginx:latest

      Удаление файлов в Docker tmpfs

      При использовании параметра «—tmpfs» вы можете указать только директорию, которую планируете использовать.

      При использовании «mount» у вас появляются не обязательные параметры:

      • tmpfs-size — размер в байтах. По умолчанию не ограничен;
      • tmpfs-mode — права на файлы. По умолчанию 1777. Можно не указывать специальные разрешения (т.е. 700, например).

      Через Docker Compose мы так же можем создать и использовать tmpfs:

      volumes: foo: driver: local driver_opts: type: "tmpfs" o: "o=size=100m,uid=1000" device: "tmpfs"

      Монтирование папок в Docker

      Монтирование папок (или файловых систем) означает связывание или подключение файловой системы (папки или директории) с определенным местом в другой файловой системе. Это позволяет сделать содержимое одной папки доступным внутри другой папки или даже в другом месте в системе.

      Что означает монтирование?

      Монтирование папок является важной функцией операционных систем и часто используется в контексте контейнеризации, такой как Docker или виртуализации, чтобы обеспечить доступ к файлам и данным между хостовой машиной и контейнерами или виртуальными машинами.

      Основная идея монтирования папок заключается в том, чтобы разделить область физического хранения (например, диска или файловой системы) на различные логические области для организации и доступа к файлам. Это позволяет контролировать и изменять содержимое файловой системы, не затрагивая другие файловые системы или директории.

      Монтирование папок часто используется для обмена данными между хостовой машиной и контейнерами, обновления кода при разработке приложений, хранения баз данных или настройки конфигурационных файлов.

      Для чего необходимо монтирование папок?

      Монтирование папок в Docker Compose позволяет обмениваться данными между хостовой машиной и контейнерами, что предоставляет несколько преимуществ и возможностей:

      1. Сохранение данных: используя монтирование папок, вы можете сохранять данные, созданные или измененные внутри контейнеров, на хостовой машине. Это важно для сохранения постоянных данных, например баз данных или файловых систем, которые могут быть восстановлены после перезапуска контейнеров.
      2. Обновление кода: если вы разрабатываете приложение и хотите мгновенно видеть изменения кода без необходимости пересборки образа и перезапуска контейнера, монтирование папок является отличным способом. Вы можете монтировать папку с исходным кодом на хостовой машине внутрь контейнера, и при изменении файлов кода на хосте, они автоматически отображаются внутри контейнера без необходимости его перезапуска.
      3. Разделение конфигураций: вы также можете использовать монтирование папок для разделения конфигурационных файлов между хостовой машиной и контейнерами.

      Как монтировать?

      В Docker Compose вы можете монтировать папки хостовой машины в контейнеры с помощью опции `volumes`. Вот пример Rиспользования volumes в файле docker-compose.yml:

      yaml
      version: ‘3’
      services:
      app:
      image: nginx
      volumes:
      — /путь/к/папке/хоста:/путь/к/папке/контейнера

      В приведенном примере `/путь/к/папке/хоста` — это путь к папке на вашей хостовой машине, которую вы хотите монтировать в контейнере. `/путь/к/папке/контейнера` — это путь к папке внутри контейнера, куда вы хотите монтировать папку хоста.

      Также вы можете использовать относительные пути вместо абсолютных, указывая путь относительно местоположения файла `docker-compose.yml`. В этом случае, используйте `./` для указания текущей директории.

      yaml
      version: ‘3’
      services:
      app:
      image: nginx
      volumes:
      — ./папка/хоста:/путь/к/папке/контейнера

      Вы также можете указывать несколько монтированных папок, просто добавляя их в список `volumes`.

      docker и -v: как понять, монтиируется с хоста в контейнер или наоборот?

      Я долго мучался с докером, и понял, что даже после прочтения документации и ответов на частые вопросы не понимаю вот чего. Вот пример команды:

      docker run --name=nginx -d -v ~/nginxlogs:/var/log/nginx -p 5000:80 nginx 

      Про эту команду пишут, что она возьмет логи энджинкса и смонтирует их мне в ~/nginxlogs. Но здесь же нигде не указывается «направление монтирования». С тем же успехом я могу ожидать, что команда возьмёт мою директорию ~/nginxlogs и засунет её в контейнер. Есть ещё команда —mount, которая тоже позволяет указать source, destination — но никак не «направление монтирования» Не могли бы Вы объяснить мне этот момент? То есть, как надо монтировать фолдеры «с хоста в контейнер» и «из контейнера на хост» и какими командами (или ключами) для этого правильно пользоваться?

      Отслеживать
      397 2 2 золотых знака 10 10 серебряных знаков 39 39 бронзовых знаков
      задан 14 сен 2020 в 1:03
      11k 1 1 золотой знак 23 23 серебряных знака 44 44 бронзовых знака

      1 ответ 1

      Сортировка: Сброс на вариант по умолчанию

      В текущем контексте можно считать, что монтирование командами -v и —mount всегда происходит в направлении из docker-хоста в docker-контейнер. Если же ты хочешь расшарить имеющуюся папку из контейнера в хост, то тут нужны уже совсем другие средства. В твоём случаем команда

       docker run --name=nginx -d -v ~/nginxlogs:/var/log/nginx -p 5000:80 nginx 

      будет давать доступ контейнеру к хостовой папке ~/nginxlogs , но под видом /var/log/nginx . Конечно, это упрощённое объяснение. Соответственно, если в твоём образе контейнера уже была папка /var/log/nginx с каким-либо непустым содержимым, то контейнер не увидит это содержимое при запуске в такой конфигурации.

      Более подробно: На самом деле docker сначала создаёт том (volume), который может быть основан на папке хоста, а уже потом монтирует его к контейнеру (или нескольким контейнерам). Отсюда всегда строгое направление монтирования: из хоста в контейнер. docker run -v можно рассматривать как сокращение, т.е. создание тома + запуск контейнера со смонтированным томом в одной команде.

      Если по каким-либо причинам необходимо смонтировать уже имеющиеся файлы/папки из контейнера в хост-систему, что по моему мнению немного извращение, то тут надо использовать уже другие средства, которые на сам docker никак не завязаны. К примеру, монтирование удалённых(сетевых) папок в linux. Возможно есть какие-то плагины docker’а, которые облегчат данную работу, но я тут сказать ничего не могу.

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

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