Короче: нет, ваша VOLUME
инструкция не верна.
Dockerfile VOLUME
указывает один или несколько томов с указанием путей на стороне контейнера. Но это не позволяет автору изображения указывать путь к хосту. На стороне хоста тома создаются с очень длинным ID-именем внутри корня Docker. На моей машине это есть /var/lib/docker/volumes
.
Примечание. Поскольку автоматически сгенерированное имя очень длинное и не имеет смысла с точки зрения человека, эти тома часто называют «безымянным» или «анонимным».
Ваш пример, в котором используется '.' персонаж даже не будет работать на моей машине, независимо от того, сделаю ли я точку первым или вторым аргументом. Я получаю это сообщение об ошибке:
docker: ответ об ошибке от демона: ошибка времени выполнения oci: container_linux.go: 265: запуск процесса контейнера вызвал "process_linux.go: 368: инициализация контейнера вызвала \" open / dev / ptmx: нет такого файла или каталога \ "".
Я знаю, что то, что было сказано по этому поводу, вероятно, не очень ценно для того, кто пытается понять, VOLUME
и, -v
конечно же, не дает решения для того, чего вы пытаетесь достичь. Так что, надеюсь, следующие примеры прольют больше света на эти проблемы.
Минутучебник: Определение объемов
Учитывая этот Dockerfile:
FROM openjdk:8u131-jdk-alpine
VOLUME vol1 vol2
(Что касается результата этого мини-руководства, не имеет значения, укажем мы vol1 vol2
или /vol1 /vol2
- не спрашивайте меня, почему)
Построить это:
docker build -t my-openjdk
Бегать:
docker run --rm -it my-openjdk
Внутри контейнера запустите ls
командную строку, и вы заметите, что существуют два каталога; /vol1
и /vol2
.
Запуск контейнера также создает два каталога или «тома» на стороне хоста.
Когда контейнер запущен, выполните его docker volume ls
на хост-машине, и вы увидите что-то вроде этого (я заменил среднюю часть имени тремя точками для краткости):
DRIVER VOLUME NAME
local c984...e4fc
local f670...49f0
Вернувшись в контейнер , выполните touch /vol1/weird-ass-file
(создает пустой файл в указанном месте).
Этот файл теперь доступен на хост-машине в одном из безымянных томов, лол. Мне потребовалось две попытки, потому что я сначала попробовал первый указанный том, но в конце концов я нашел свой файл во втором указанном томе, используя эту команду на хост-машине:
sudo ls /var/lib/docker/volumes/f670...49f0/_data
Точно так же вы можете попытаться удалить этот файл на хосте, и он также будет удален в контейнере.
Примечание. _data
Папка также называется «точкой монтирования».
Выйдите из контейнера и перечислите тома на хосте. Они ушли. Мы использовали --rm
флаг при запуске контейнера, и этот параметр эффективно стирает не только контейнер при выходе, но и тома.
Запустите новый контейнер, но укажите том, используя -v
:
docker run --rm -it -v /vol3 my-openjdk
Это добавляет третий том, и вся система в конечном итоге имеет три безымянных тома. Команда бы вылетела, если бы мы указали только -v vol3
. Аргумент должен быть абсолютным путем внутри контейнера. На стороне хоста новый третий том анонимен и находится вместе с двумя другими томами в /var/lib/docker/volumes/
.
Ранее было сказано, что Dockerfile
невозможно сопоставить путь к хосту, что создает для нас проблему при попытке перенести файлы с хоста в контейнер во время выполнения. -v
Эту проблему решает другой синтаксис.
Представьте, что у меня есть подпапка в каталоге моего проекта, ./src
которую я хочу синхронизировать /src
внутри контейнера. Эта команда делает свое дело:
docker run -it -v $(pwd)/src:/src my-openjdk
Обе стороны :
персонажа ожидают абсолютного пути. Левая сторона - это абсолютный путь на главной машине, правая сторона - это абсолютный путь внутри контейнера. pwd
это команда, которая «печатает текущий / рабочий каталог». Ввод команды $()
принимает команду в скобках, запускает ее в подоболочке и возвращает абсолютный путь к нашему каталогу проекта.
Собирая все вместе, предположим, что у нас есть ./src/Hello.java
папка нашего проекта на хост-машине со следующим содержимым:
public class Hello {
public static void main(String... ignored) {
System.out.println("Hello, World!");
}
}
Мы создаем этот Dockerfile:
FROM openjdk:8u131-jdk-alpine
WORKDIR /src
ENTRYPOINT javac Hello.java && java Hello
Запускаем эту команду:
docker run -v $(pwd)/src:/src my-openjdk
Это напечатает «Hello, World!».
Самое приятное то, что мы полностью свободны изменять файл .java с новым сообщением для другого вывода при втором запуске - без необходимости перестраивать изображение =)
Заключительные замечания
Я новичок в Docker, и вышеупомянутый «учебник» отражает информацию, которую я собрал на трехдневном хакатоне по командной строке. Мне почти стыдно, что я не смог предоставить ссылки на понятную англоязычную документацию, подтверждающую мои утверждения, но я честно думаю, что это связано с отсутствием документации, а не с личными усилиями. Я знаю, что примеры работают так, как рекламируется, используя мою текущую настройку: «Windows 10 -> Vagrant 2.0.0 -> Docker 17.09.0-ce».
Учебник не решает проблему «как указать путь к контейнеру в Dockerfile и позволить команде run указывать только путь к хосту». Может быть, способ есть, просто я его не нашел.
Наконец, у меня VOLUME
интуитивное предчувствие, что указание в файле Dockerfile не просто необычно, но, вероятно, лучше никогда не использовать VOLUME
. По двум причинам. Первую причину, которую мы уже определили: мы не можем указать путь к хосту - это хорошо, потому что файлы Dockerfiles должны быть очень независимы от специфики хост-машины. Но вторая причина заключается в том, что люди могут забыть использовать эту --rm
опцию при запуске контейнера. Можно не забыть снять контейнер, но забыть удалить объем. Кроме того, даже при наличии лучшей человеческой памяти может оказаться непростой задачей выяснить, какие из всех анонимных томов можно безопасно удалить.