Что произошло, когда я одновременно использовал том хоста (bind mounts) и именованный том (один тип тома, управляемого Docker)? - PullRequest
0 голосов
/ 19 февраля 2019

Я прочитал некоторые документы, прежде чем задать этот вопрос:

И меня смущают следующие две части:

  1. bind mounts: Монтирование в непустую директорию на контейнере
  2. именованный том: Заполнение тома с использованием контейнера

Их поведение совершенно противоположное:

Для монтирования на связке:

При подключении-подключении к непустому каталогу в контейнере существующее содержимое каталога скрывается подключением-подключением.

Для именованных томов и анонимных томов:

Если вы запускаете контейнер, который создает новый том, как указано выше, и у контейнера есть файлы или каталоги в каталоге для монтирования (например, / app / вышеуказанный), содержимое каталога копируется в том,Затем контейнер монтирует и использует том, а другие контейнеры, использующие том, также имеют доступ к предварительно заполненному контенту.

Мой вопрос: если я использую команду VOLUME в dockerfile длясоздать именованный том анонимные тома и в то же время использовать привязку для монтирования несуществующего пути в контейнер, что происходит за кулисами?

Например:

# in docker file
VOLUME /path/in/container

# when run container
docker run -v /not-exist-dir/in/host:/path/in/container  ... /< image >

У меня есть какой-то тест, и результат:

  1. не было создано анонимных томов в /var/lib/docker/volumes/
  2. создан not-there-dirна хосте, и теперь он не пустой, я думаю, что Заполнение тома с помощью контейнера имеет эффект.

Итак,

  1. почемунет анонимных томов, созданных, как ожидалось?
  2. почему существующее содержимое каталога (на стороне контейнера) не скрыто монтированием bind?

что произошло за этим сценарием?


Позвольте мне привести более конкретный пример:

Вот файл Docker образа openfrontier / gerrit , в конце файла Docker мы видим команду VOLUME:

VOLUME $GERRIT_SITE

, которая будетна самом деле создайте анонимные тома на хосте и смонтируйте его в /var/gerrit/review_site (значение GERRIT_SITE) в контейнере, когда любой контейнер создается из этого образа, например:

docker run -dit --name gerrit  openfrontier/gerrit:2.15.3

, после этого я вижуанонимные тома под /var/lib/docker/volumes/, и я могу использовать docker volume ls, чтобы увидеть его имя be4538dbf3a51da463391c6eca9714fb6dd0c11379f1e2918f74c33d56633f00, а также с помощью команды docker inspect gerrit, мы можем увидеть:

"Mounts": [
            {
                "Type": "volume",
                "Name": "be4538dbf3a51da463391c6eca9714fb6dd0c11379f1e2918f74c33d56633f00",
                "Source": "/var/lib/docker/volumes/be4538dbf3a51da463391c6eca9714fb6dd0c11379f1e2918f74c33d56633f00/_data",
                "Destination": "/var/gerrit/review_site",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

, и есть несколько файлов под этими двумяпапка, которая создается контейнером после его запуска.

До настоящего времени это нормальный пример создания анонимных томов с помощью команды VOLUME.

Однако, если я запускаю этот контейнер сследующая команда:

docker run -dit --name gerrit -v /home/test:/var/gerrit/review_site openfrontier/gerrit:2.15.3

, где /home/test не существует на хосте, тогда анонимные тома не создаются, вместо этого создается папка /home/test и она не пуста !!!, монтированиеинформация выглядит следующим образом:

"Mounts": [
            {
                "Type": "bind",
                "Source": "/home/test",
                "Destination": "/var/gerrit/review_site",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],

Я думаю, что bind mounts - это эффект, но я просто не понимаю, почему путь на хосте не пустой (поскольку bind-mount в непустой каталог на контейнере, существующее содержимое каталога скрытоbind mount.)

Я также проверяю монтирование непустой папки в контейнер, помещая какой-то файл в «/ home / test», прежде чем использовать docker run для запуска контейнера, в результате получается оригинальный файлбыл сохранен и новые файлы тоже были добавлены.

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

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

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

Примечания:

  • Это небольшое учебное пособие, которое я написал о томах для моих сотрудников.В объяснениях должны быть некоторые недостатки, но все примеры были протестированы.
  • важно, чтобы вы запускали вещи в следующем порядке, а не запускали команды повторно (потому что «новые» вещи могут затем стать «существующими»)и не соответствовал бы их назначению)
  • Я подключусь к некоторому системному пути ... это только для примера: обеспечить их существование, а также избежать умножения предварительных команд создания.

Dockerfile

FROM ubuntu

VOLUME /tmp/dockerfilevolumefromnowhere

RUN mkdir -p /dir/created/from/container
RUN touch /dir/created/from/container/emptyFile.txt
VOLUME /dir/created/from/container

CMD "sh"

давайте удалим все данные предыдущих тестов

ВНИМАНИЕ, ЧТО ЭТО УДАЛЕТ ВСЕ ОБЪЕМЫ, КОТОРЫЕ НЕ ИСПОЛЬЗУЮТСЯвключая те, которые вы, возможно, создали ранее.

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

root@host:~# docker container prune 
root@host:~# docker volume prune 
root@host:~# rm -r /does/not/exit/within/host
root@host:~# rm -r /does/not/exit/within/host2
root@host:~# rm -r /tmp/dockerfilevolumefromnowhere

некоторые изображения и создания томов и заполнения

root@host:~# docker build -t tmpcontainer .
root@host:~# docker volume create existingVolume
root@host:~# touch /var/lib/docker/volumes/existingVolume/_data/someExistingFile.txt
root@host:~# docker volume create existingVolume2
root@host:~# touch /var/lib/docker/volumes/existingVolume2/_data/anotherExistingFile.txt
root@host:~# docker volume create existingEmptyVolume

на данный момент у вас есть 3 "существующих тома":

root@host:~# docker volume ls
DRIVER              VOLUME NAME
local               existingVolume
local               existingVolume2
local               existingEmptyVolume

Давайте теперь запустим наш контейнер

docker run -it \
-v /does/not/exit/within/host:/does/not/exist/within/container \
-v /does/not/exit/within/host2:/sbin \
-v /tmp:/again/another/does/not/exist/within/container \
-v /tmp:/tmp/ \
-v newVolume:/another/does/not/exist/within/container \
-v newVolume2:/bin \
-v existingVolume:/new/path/on/container \
-v existingVolume2:/usr \
-v existingEmptyVolume:/var \
bash: groups: command not found # that's normal. you overrided the /usr... see beyond

Теперь мы подключены к нашему вновь созданному контейнеру.

Посмотрим, что у нас есть и на контейнере, и на хосте :

# -v /does/not/exit/within/host:/does/not/exist/within/container 
#no one exists on both sides : both directories are created, and are now bound to each other
root@host:~# ll /does/not/exit/within/host
. 
root@ffb82b56d64b:/# ll /does/not/exist/within/container
.

# -v /does/not/exit/within/host:/sbin 
# the directory on host is created, and the one on container is erased with this new one. Both are now bound to each other
root@host:~# ll /does/not/exit/within/host2
. 
root@ffb82b56d64b:/# ll /sbin
.


# -v /tmp:/again/another/does/not/exist/within/container
# the path on host exists, and the path on container will be created and will hold the content on container (they are bound to each other)
root@host:~# ll /tmp
    -rw-------  1 root root 65536 Feb 19 11:11 one.txt
    -rw-------  1 root root 65536 Feb 19 11:11 two.yml
root@ffb82b56d64b:/# ll /again/another/does/not/exist/within/container
    -rw-------  1 root root 65536 Feb 19 11:11 one.txt
    -rw-------  1 root root 65536 Feb 19 11:11 two.yml

# -v /tmp:/tmp
# the path on host exists, so all its content will replace the previously path on container
# there were some data on container's /tmp, but they are replaced with hosts ones
root@host:~# ll /tmp
    -rw-------  1 root root 65536 Feb 19 11:11 one.txt
    -rw-------  1 root root 65536 Feb 19 11:11 two.yml
root@ffb82b56d64b:/# ll /tmp
    -rw-------  1 root root 65536 Feb 19 11:11 one.txt
    -rw-------  1 root root 65536 Feb 19 11:11 two.yml

# -v newVolume:/another/does/not/exist/within/container \
# the newVolume does not exist, so it will be created and bound to path on container.  
# since the path on container is new, it will be created empty
root@host:~#  ll /var/lib/docker/volumes/newVolume/_data/
..
root@ffb82b56d64b:/# ll /another/does/not/exist/within/container
..

# -v newVolume2:/bin \
# once again, a volume will be created, but since it matches an existing path on container, it will hold all the content of it (no erasal!)
root@host:~#  ll /var/lib/docker/volumes/newVolume2/_data/
<all the content of Ubuntu's /bin from within container>
root@ffb82b56d64b:/# ll /bin
<whole expected content of /bin on Ubuntu>

# -v existingVolume:/new/path/on/container \
# the volume exists, and it -and all files within- will be bound to a newly created path on container
root@host:~#  ll /var/lib/docker/volumes/existingVolume/_data/
-rw-------  1 root root 65536 Feb 19 11:11 someExistingFile.txt
 root@ffb82b56d64b:/# ll /new/path/on/container
-rw-------  1 root root 65536 Feb 19 11:11 someExistingFile.txt


# -v existingVolume2:/usr \ 
# the volume exists, so does the path on container. It will replace the existing path (and thus erase the former files there) and will be bound to this replaced path.
root@host:~#  ll /var/lib/docker/volumes/existingVolume2/_data/
-rw-------  1 root root 65536 Feb 19 11:11 anotherExistingFile.txt
root@ffb82b56d64b:/# ll /usr    
-rw-------  1 root root 65536 Feb 19 11:11 anotherExistingFile.txt

# -v existingEmptyVolume:/var \ 
# the volume exists, but is empty. the path on container exists in the container and is not empty. In this case, the path on container will not be erased and this will act as a new volume.
root@host:~#  ll /var/lib/docker/volumes/existingEmptyVolume/_data/
drwxr-xr-x  2 root root  4096 Apr 24  2018 backups/
drwxr-xr-x  5 root root  4096 Feb 19 14:36 cache/
drwxr-xr-x  7 root root  4096 Feb 19 14:36 lib/
drwxrwsr-x  2 root staff 4096 Apr 24  2018 local/
lrwxrwxrwx  1 root root     9 Nov 12 21:54 lock -> /run/lock/
drwxr-xr-x  3 root root  4096 Feb 19 14:36 log/
drwxrwsr-x  2 root mail  4096 Nov 12 21:54 mail/
drwxr-xr-x  2 root root  4096 Nov 12 21:54 opt/
lrwxrwxrwx  1 root root     4 Nov 12 21:54 run -> /run/
drwxr-xr-x  2 root root  4096 Feb 19 14:36 spool/
drwxrwxrwt  2 root root  4096 Nov 12 21:56 tmp/
root@ffb82b56d64b:/# ll /var  
drwxr-xr-x  2 root root  4096 Apr 24  2018 backups/
drwxr-xr-x  5 root root  4096 Feb 19 14:36 cache/
drwxr-xr-x  7 root root  4096 Feb 19 14:36 lib/
drwxrwsr-x  2 root staff 4096 Apr 24  2018 local/
lrwxrwxrwx  1 root root     9 Nov 12 21:54 lock -> /run/lock/
drwxr-xr-x  3 root root  4096 Feb 19 14:36 log/
drwxrwsr-x  2 root mail  4096 Nov 12 21:54 mail/
drwxr-xr-x  2 root root  4096 Nov 12 21:54 opt/
lrwxrwxrwx  1 root root     4 Nov 12 21:54 run -> /run/
drwxr-xr-x  2 root root  4096 Feb 19 14:36 spool/
drwxrwxrwt  2 root root  4096 Nov 12 21:56 tmp/

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

В конце концов, все, что нам осталось, чтобы охватить все наши варианты использования, - это тома, созданные из Dockerfile .Давайте посмотрим на содержимое / var / lib / docker / volume.Помните, что мы очистили его перед выполнением наших тестов, поэтому все те, что здесь, соответствуют нашим тестам.

root@host:~# ll /var/lib/docker/volumes
drwx------  6 root root  4096 Feb 19 11:11 ./
drwx--x--x 14 root root  4096 Feb 14 09:50 ../
-rw-------  1 root root 65536 Feb 19 14:36 metadata.db #indeed 
drwxr-xr-x  3 root root  4096 Feb 19 14:36 635af95ec06f8a44c22915005189bfb12d5bcf2e5ac97c25112d3e65a72546f4/ # anonymous container 1
drwxr-xr-x  3 root root  4096 Feb 19 14:36 897b28ec17275a3c3be184bb20b6314b38c1404e638080c8fe4fc36cae2f9f65/ # anonymous container 2
drwxr-xr-x  3 root root  4096 Feb 19 14:36 existingEmptyVolume/ #we created it (empty) before the run command
drwxr-xr-x  3 root root  4096 Feb 19 14:35 existingVolume/ #we created it before the run command
drwxr-xr-x  3 root root  4096 Feb 19 14:36 existingVolume2/ #we created it before the run command
drwxr-xr-x  3 root root  4096 Feb 19 14:36 newVolume/ #we created it during run command
drwxr-xr-x  3 root root  4096 Feb 19 14:36 newVolume2/ #we created it during run command

Так что же это за анонимные контейнеры: на самом деле они созданы из вашего dockerfile.Давайте проверим их содержание.

root@host:~#  ll /var/lib/docker/volumes/63eeedcb1aa2e4d8785cca409698371381558348ce19bc614d87da372901d224/_data/
-rw-r--r-- 1 root root    0 Feb 19 10:14 emptyFile.txt
root@host:~#  ll /var/lib/docker/volumes/ea0ed5ff271cba03a8b7d35144b58e8da1b2e50b4e05c4cccda7f19b401d7f0b/_data/
..

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

Примечание : выВозможно, в это время заметил какую-то странную вещь:

root@host:~# ll /tmp/dockerfilevolumefromnowhere #YES : from HOST!
.. #it exists... but is not linked to the volume or the path in container in any way.

Здесь ... не спрашивайте: меня все еще удивляет, почему он создан на хосте.Я предполагаю, что это ошибка, но эта проблема должна обсуждаться на докере ML, а не здесь.В любом случае, вы не должны делать это таким образом: mkdir перед созданием тома поверх него!

Итак, что мы можем увидеть со всеми этими примерами:

  • связывание / монтирование с хостов путь к контейнеру (/ some / path / on / host: / a / path / on / container) создаст путь к контейнеру (независимо от того, существует ли он ранее) и заменитвсе прежние данные здесь.Обе директории (и их содержимое) теперь связаны друг с другом.
  • монтирование из тома * * (someVolumeName: / a / path / on / container) к контейнеру привязывает том кпуть на контейнере.
  • монтирование существующего непустого тома в путь контейнера заменит (возможное) содержимое внутри контейнера на содержимое из существующего тома (если оно пустое: оно будет действовать как новыйтом)
  • монтирование нового тома к пути контейнера будет связывать содержимое пути в контейнере с томом.Том теперь будет содержать это содержимое (например, для последующего использования): здесь ничего не будет стерто / заменено.
  • и том или путь (независимо от того, находится ли он в контейнере или хосте) будет создан , если он не существует.
  • том из Dockerfile будет анонимным и будет создаваться заново при каждом запуске другого контейнера из этого образа (предыдущие останутся).Они также будут созданы в / var / lib / docker / volume.
  • том, объявленный при запуске команда будет создана (т.е. будет "новыми" томами), если они не существовали ранее.В противном случае вместо них будут использоваться существующие.
  • том, созданный с помощью команды запуска , будет сохранен в / var / lib / docker / volume в каталоге, соответствующем их имени.

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

0 голосов
/ 19 февраля 2019

Объявление тома в Dockerfile устанавливает бит метаданных на образе, который указывает docker run определять анонимный том в этом месте каждый раз, когда из этого изображения создается контейнер.Это не именованный том или хост-монтирование, но он имеет много общего с обоими (по умолчанию все они являются монтируемыми, и анонимные тома перечислены в docker volume ls вместе с именованными томами).

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

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

https://sudo -bmitch.github.io / Presentations / dc2018eu / tips-and-tricks-of-the-captains.html #48

...