Одна из самых часто обсуждаемых вопросов у пользователей Docker Desktop - обмен файлами между системами (file sharing). Как посмотреть исходный код внутри моего контейнера? В чем различие между томом (volume) и привязкой монтирования (bind mount)? Почему file sharing работает медленнее, чем на Linux, и как это ускорить?
Примечание: здесь приведен перевод статьи [1].
[Bind mount и volume]
Предположим, что вы запустили контейнер Ubuntu командой docker run -it ubuntu bash. Вы наверняка быстро обнаружили, что: (1) у контейнера своя собственная файловая система, основанная на файловой системе в образе Ubuntu; (2) вы можете создавать, удалять и изменять файлы, но все эти изменения остаются локальными для контейнера и теряются, когда контейнер удален; (3) у контейнера нет доступа к каким-либо файлам на вашем компьютере хоста (наример на ноутбуке, где установлен docker, под которым запущена в docker операционная система Ubuntu).
Из-за этого встают вполне естественные вопросы: как увидеть в контейнере другие файлы? Как контейнеру записать куда-нибудь данные, которые можно прочитать позже, или возможно использовать в других контейнерах? Это как раз то, где вступают в действие такие сущности, как bind mount и volume. Обе эти возможности используют флаг -v команды docker run, чтобы указать некоторые файлы для совместного использования с контейнером.
Bind mount. Первая опция, которую многие используют, это bind mount, где часть от вашей файловой системы становится общей с контейнером. Например:
docker run -it -v /users/stephen:/my_files ubuntu bash
В этом примере файлы в /users/stephen будут доступны в каталоге /my_files контейнера, и вы можете обращаться к ним на чтение и запись. Это очень просто и удобно но если вы используете Docker Desktop и именованный том (named volume), то можете получить лучшую производительность.
Named volume. Вторая опция это именованный том (named volume), это файловая система, управляемая Docker. Вы можете создать named volume командой наподобие docker volume create new_vol, и затем сделать этот том общим, снова используя флаг -v, например:
docker run -it -v new_vol:/my_files ubuntu bash
Созданные таким способом тома останутся, даже когда контейнер удален, и они могут быть сделаны общими для других контейнеров. Вы также можете просматривать содержимое этих томов из оболочки Docker Desktop, используя закладку Volumes.
[Вопросы производительности]
Чтобы понимать отличие параметров производительности между двумя этими опциями, давайте сначала кратко поговорим о том, как работает Docker Desktop. Многие представляют себе, что Docker Desktop - это просто пользовательский интерфейс поверх некоторых инструментов с открытым исходным кодом, но это лишь малая часть того, что есть на самом деле. Docker Desktop это среда разработки и запуска Linux-контейнеров на хосте, т. е. на вашей машине с операционной системой Mac или Windows, с бесшовной интеграцией с хостом таким образом, чтобы гостевые опеционные системы (т. е. те, что работаю в контейнерах) работали естественным для себя образом. Docker Desktop делает это, настраивая Linux VM (виртуальную пашину Linux, или опционально окружение WSL 2 на Windows [2]), в которой можно запустить движок Docker и контейнеры, и затем передавать данные CLI-команд, сети и файлов между хостом и VM.
К сожалению, из-за внутренней организации подсистемы виртуализации всегда существуют неизбежные небольшие накладные расходы при пересечении границы между хостом и виртуальной машиной. Эти расходы невелики, но в системе разработки, где имеется большое дерево исходного кода, когда происходит множество операций чтения и записи (что часто имеет место при кросс-компиляции Linux для встраиваемых систем), эти расходы складываются, что может заметно влиять на производительность. И поскольку Docker Desktop хорош в том, чтобы скрыть лежащую в его основе виртуальную машину (VM), для его пользователей не всегда очевидно, почему падает производительность его контейнеров. На Linux у контейнера есть прямой доступ к bind-mounted файловой системе, а поскольку реализация Docker Desktop на Mac и Windows работает для пользователей прозрачно, то пользователи интуитивно ожидают получить такую же производительность, как на Docker в среде Linux.
Именованные тома (named volumes) не страдают от той же проблемы, потому что они создаются внутри собственной файловой системы VM, так что они работают так же быстро, как и на машине Linux. На WSL 2 операционная система Windows управляет общими файлами (file sharing) вместо управления Docker, но вступают в действие те же вопросы производительности: файлы, смонтированные из файловой системы Windows, могут быть медленными, именованные тома быстрыми, но в этом случае есть другой вариант: файлы, сохраненные в файловой системе Linux, также находятся внутри WSL VM, поэтому они также работают быстро.
[Практические советы]
Описанные выше соображения дают нам понимание, что следует сделать в плане оптимизации производительности. Сначала попробуйте bind mount, это довольно удобно, и возможно вполне будет соответствовать вашим требованиям. Но если производительность становится проблемой, то проверьте:
(1) Вы действительно сделали общими только то, что действительно необходимо, и не более. (2) Подумайте, как оганизовать обмен файлами каким-то другим способом, без bind mount.
У вас есть несколько вариантов хранить файлы внутри VM, включая named volume, файлы Linux в WSL, и в файловой системе контейнера: какой из вариантов использовать, зависит от конкретной ситуации. Например:
• Исходный код, который вы активно редактируете на машине хоста, вероятно лучше всего расшарить через bind mount. • Большие, статические деревья зависимостей или библиотеки можно переместить в именованный том (named volume), или WSL, или даже записать в образ контейнера. • Базы данных болше подходят для хранения в named volume или WSL. • Директории кэша и больших файлов должны быть помещены в named volume или WSL (если вам нужно сохранить их после остановки контейнера), или должны находиться в файловой системе контейнера (если они могут исчезать вместе с контейнером). • Файлы, которые не нужны контейнеру, вообще не должны быть общими. В частности, не расшаривайте всю домашнюю директорию. Часто пользователи неразумно поступают подобным образом, чтобы у них был доступ к любым файлам, но в отличие от docker Linux это не "бесплатно".
Остается вариант, когда реально нужно применить bind mount для большой директории или для большго трафика - сторонние решения кэширования/синхронизации, например Mutagen или docker-sync. Они фактически копируют ваши файлы внутри VM для ускорения доступа на чтение/запись, и обрабатывают синхронизацию (в одном или обоих направлениях) между копией и хостом. Но это предполагает внедрение дополнительного компонента для управления, так что named volume по-прежнему предпочтительно использовать везде, где это возможно.
[Дополнительные замечания]
На протяжении многих лет мы использовали различные реализации совместного использования файлов между системами (Samba и gRPC FUSE на Windows Hyper-V; osxfs и gRPC FUSE на Mac; Windows с использованием 9P на WSL 2). Со временем мы добились некоторых улучшений в плане производительности, но ни одно из них не может достичь собственной производительности системы (native performance). Есть некоторые надежды на новую перспективную реализацию файлообмена на основе virtiofs. Это новая технология, специально разработанная для шаринга файлов между хостом и VM. Она способна существенно повысить производительность, потому что используется тот факт, что VM и хост работают на одной машине, а не в сети.
Мы уже выпустили превью технологии virtiofs для Docker Desktop for Mac, которую вы можете получить из нашей public roadmap [3] (это требует 12.2), и также планируется использовать это для разрабатываемого Docker Desktop for Linux. Вероятно эта технология также сделает несколько быстрее работу bind mount (хотя все еще рекомендуется использовать named volume или собственную файловоую систему контейнера для подходящих случаев).
Если вы намерены глубже ознакомиться с этими темами, то есть смысл обратиться к материалам публикации Jacob Howard, см. [4]. Это масса информации и практических советов, поместившихся в видеоролик из 26 минут.
[Ссылки]
1. File Sharing with Docker Desktop site:docker.com. 2. Запуск Linux на Windows помощью WSL. 3. [Docker Desktop] Improve Mac File system performance site:github.com. 4. A Pragmatic Tour of Docker Filesystems site:xenoscopic.com. 5. Шаринг локальных фалов хоста для контейнеров. |