Якщо коротко: Ні, ваша VOLUME
інструкція не вірна.
У Dockerfile VOLUME
вкажіть один або кілька томів, заданих на стороні контейнера. Але це не дозволяє автору зображення вказувати шлях хосту. З боку хоста томи створюються з дуже довгим ідентифікаційним іменем всередині кореня Docker. На моїй машині це /var/lib/docker/volumes
.
Примітка: Оскільки автогенерована назва надзвичайно довга і не має сенсу з точки зору людини, ці томи часто називають "безіменними" або "анонімними".
Ваш приклад, який використовує "." символ навіть не працюватиме на моїй машині, незалежно від того, чи буду я робити крапку першим чи другим аргументом. Я отримую це повідомлення про помилку:
docker: Відповідь про помилку від daemon: oci помилка виконання: container_linux.go: 265: запуск процесу контейнера викликав "process_linux.go: 368: контейнер init викликав \" 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
(створює порожній файл у вказаному місці).
Цей файл тепер доступний на хост-машині, в одному з безіменних томів lol. Мені знадобилося дві спроби, тому що я спробував перший том, що перерахований, але врешті-решт я знайшов свій файл у другому перерахованому томі, використовуючи цю команду на хост-машині:
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
Це друкує "Привіт, світ!".
Найкраща частина полягає в тому, що ми абсолютно вільні змінити файл .java з новим повідомленням для іншого виводу на другий запуск - без необхідності перебудувати зображення =)
Заключні зауваження
Я зовсім новачок у Докер, і згаданий вище "підручник" відображає інформацію, яку я зібрав із 3-х денного хакатону командного рядка. Мені майже соромно, що я не зміг надати посилання на чітку англомовну документацію, що підтверджує мої заяви, але я чесно думаю, що це пов'язано з відсутністю документації, а не особистими зусиллями. Я знаю, що приклади працюють як рекламуються в моїй поточній установці, яка є "Windows 10 -> Vagrant 2.0.0 -> Docker 17.09.0-ce".
Підручник не вирішує проблему "як ми вказуємо шлях контейнера в Dockerfile і нехай команда run тільки вказує шлях до хоста". Може бути спосіб, я просто його не знайшов.
Нарешті, у мене є відчуття, що вказівка VOLUME
в Dockerfile не просто рідкість, але, мабуть, найкраща практика ніколи не використовувати VOLUME
. З двох причин. Перша причина, яку ми вже визначили: ми не можемо вказати шлях хосту - це добре, тому що Dockerfiles повинні бути дуже агностичними щодо специфіки хост-машини. Але друга причина - люди можуть забути використовувати цей --rm
параметр під час запуску контейнера. Можна пам’ятати, що потрібно вийняти контейнер, але забути видалити об'єм. Крім того, навіть з найкращою людською пам’яттю може бути непростим завданням з’ясувати, який з усіх анонімних томів безпечно видалити.