Розуміння інструкції “VOLUME” в DockerFile


136

Нижче наведено вміст мого "Dockerfile"

FROM node:boron

# Create app directory
RUN mkdir -p /usr/src/app

# change working dir to /usr/src/app
WORKDIR /usr/src/app

VOLUME . /usr/src/app

RUN npm install

EXPOSE 8080

CMD ["node" , "server" ]

У цьому файлі я очікую інструкцію "VOLUME. / Usr / src / app" для монтажу вмісту поточного робочого каталогу в хості, який повинен бути встановлений у / usr / src / app папку контейнера.

Будь ласка, дайте мені знати, чи це правильний шлях?

Відповіді:


88

В офіційному підручнику докера сказано:

Об'єм даних - це спеціально призначений каталог в одному або декількох контейнерах, який обходить файлову систему Union. Об'єм даних надає кілька корисних функцій для постійних або спільних даних:

  • Томи ініціалізуються при створенні контейнера. Якщо базове зображення контейнера містить дані в зазначеній точці монтажу,
    то існуючі дані копіюються в новий том при
    ініціалізації томів . (Зверніть увагу, що це не стосується монтажу хоста
    каталогу.)
  • Об'єм даних можна спільно використовувати та використовувати повторно серед контейнерів.

  • Зміни в обсязі даних вносяться безпосередньо.

  • Зміни в обсязі даних не будуть включені під час оновлення зображення.

  • Об'єм даних зберігається, навіть якщо контейнер видалений.

У Dockerfileви можете вказати тільки призначення обсягу всередині контейнера. напр/usr/src/app .

Під час запуску контейнера, наприклад docker run --volume=/opt:/usr/src/app my_image, ви можете, але не потрібно вказувати його точку монтажу ( / opt ) на хост-машині. Якщо не вказати --volumeаргумент, то точка монтажу буде обрана автоматично, як правило, під /var/lib/docker/volumes/.


275

Якщо коротко: Ні, ваша 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параметр під час запуску контейнера. Можна пам’ятати, що потрібно вийняти контейнер, але забути видалити об'єм. Крім того, навіть з найкращою людською пам’яттю може бути непростим завданням з’ясувати, який з усіх анонімних томів безпечно видалити.


2
Коли ми повинні використовувати неназвані / анонімні томи?
Searene

10
@Martin дуже дякую Тут дуже цінується ваш хакатон та його підручник.
Beezer

6
"Я не зміг надати посилання для очищення документації, що нагадує англійську мову ... Я, чесно кажучи, думаю, що це пов'язано з відсутністю документації" Я можу підтвердити. Це найдосконаліша та найсучасніша документація, яку я знайшов, і шукав цілими годинами.
user697576

4
docker volume pruneможна використовувати для очищення залишків, які не прикріплені до запущених контейнерів. Не кажучи про те, що самостійно відібрати потенційно важливих людей буде просто, ідентифікуючи себе ...
Джеремі

4
"Для результатів цього міністерства немає значення, якщо ми вкажемо vol1 vol2 або / vol1 / vol2 - не запитуйте мене, чому". @MartinAndersson це тому, що поточна робоча директорія є /, настільки vol1відносно /, яка вирішує значення /vol1. Якщо ви використовуєте WORKDIRдля вказівки робочого каталогу, який відрізняється від іншого /, vol1і /vol1більше не вказуватиме на той самий каталог.
Себастіан

41

Визначення VOLUMEрядка в Dockerfile конфігурує трохи метаданих на вашому зображенні, але важливо, як ці метадані використовуються.

По-перше, що зробили ці два рядки:

WORKDIR /usr/src/app
VOLUME . /usr/src/app

WORKDIRЛінія є створює каталог , якщо він не існує, і оновлює деякі метадані зображення , щоб визначити всі відносні шляхи, а з поточним каталогом для команд , як RUNбуде в цьому місці. VOLUMEЛінія є визначає два томи , один відносний шлях ., а інший /usr/src/app, як тільки трапляється бути той же каталог. Найчастіше VOLUMEрядок містить лише один каталог, але він може містити кілька, як ви робили, або це може бути масив форматованого json.

Ви не можете вказати джерело тому в Dockerfile : Поширене джерело плутанини, коли вказує томи в Dockerfile, намагається відповідати синтаксису виконання джерела та місця призначення під час створення зображення, це не працюватиме . Докерфайл може вказувати лише призначення тома. Це було б тривіальним експлуатуванням безпеки, якби хтось міг визначити джерело об'єму, оскільки він міг би оновити загальне зображення на докерному концентраторі, щоб змонтувати кореневий каталог у контейнер, а потім запустити фоновий процес всередині контейнера як частину вхідної точки, додає логіни до / etc / passwd, налаштовує systemd для запуску майстра біткойна при наступному перезавантаженні або шукає у файловій системі кредитні картки, SSN та приватні ключі для надсилання на віддалений сайт.

Що робить лінія VOLUME? Як згадувалося, він встановлює деякі метадані зображення, щоб сказати, що каталог всередині зображення є томом. Як використовуються ці метадані? Кожен раз, коли ви створюєте контейнер із цього зображення, докер примушує цей каталог бути томом. Якщо ви не надаєте томи в команді запуску або складіть файл, єдиний варіант докер - створити анонімний том. Це локальний названий том з довгим унікальним ідентифікатором для імені та відсутністю інших вказівок для того, для чого він був створений або які дані вони містять (анонімні томи - це дані втрачаються). Якщо ви перекриєте гучність, вказуючи на названий або хост-обсяг, ваші дані замість цього перейдуть.

VOLUME розбиває речі: Ви не можете вимкнути том, визначений в Dockerfile. І що ще важливіше, RUNкоманда в docker реалізується з тимчасовими контейнерами. Ці тимчасові контейнери отримають тимчасовий анонімний обсяг. Цей анонімний том буде ініціалізований вмістом вашого зображення. Будь-які записи всередині контейнера з вашої RUNкоманди будуть зроблені до цього тома. Після закінчення RUNкоманди зміни в зображенні зберігаються, а зміни в анонімному томі відкидаються. Через це я настійно не рекомендую визначати VOLUMEвнутрішню Dockerfile. Це призводить до несподіваної поведінки для нижчезахисних користувачів вашого зображення, які бажають розширити зображення початковими даними за місцем розташування.

Як слід вказати гучність?Щоб вказати, куди потрібно включити томи із зображенням, введіть docker-compose.yml. Користувачі можуть модифікувати це, щоб налаштувати розташування гучності до свого локального середовища, і це фіксує інші параметри виконання, наприклад публікацію портів та мережу.

Хтось повинен це документувати! Вони мають. Docker включає попередження про використання VOLUME у свою документацію на Dockerfile разом із порадами щодо визначення джерела під час виконання:

  • Зміна гучності з Dockerfile: Якщо якісь етапи збирання змінюють дані в томі після того, як вони були оголошені, ці зміни будуть відкинуті.

...

  • Каталог хостів оголошується під час виконання контейнера: Каталог хостів (точка монтажу) за своєю природою залежить від хоста. Це забезпечує збереження портативності зображень, оскільки даний каталог хостів не може бути гарантовано доступним для всіх хостів. З цієї причини ви не можете встановити каталог хостів із Dockerfile. VOLUME Інструкція не підтримує завдання host-dirпараметра. Ви повинні вказати точку монтажу під час створення або запуску контейнера.

36

VOLUMEкоманда вDockerfile цілком законна, зовсім звичайною, абсолютно нормально для використання і не рекомендується в будь-якому випадку. Просто потрібно це зрозуміти.

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

Команді просто потрібен один парам; шлях до папки, відносно, WORKDIRякщо вона встановлена, зсередини контейнера. Тоді докер створить том у своєму графіку (/ var / lib / docker) та змонтує його до папки в контейнері. Тепер контейнеру доведеться десь писати з високою продуктивністю. Без VOLUMEкоманди швидкість запису у вказану папку буде дуже повільною, оскільки зараз контейнер використовує свою copy on writeстратегію в самому контейнері. copy on writeСтратегія є основною причиною , чому існує томи.

Якщо ви переходите на папку, вказану VOLUMEкомандою, команда ніколи не виконується, оскільки VOLUMEвиконується лише при запуску контейнераENV .

В основному за допомогою VOLUMEкоманди ви отримуєте продуктивність, не встановлюючи зовнішньо ніяких томів. Дані також зберігатимуться на контейнерах без зовнішніх кріплень. Потім, коли будете готові, просто змонтуйте щось над цим.

Деякі хороші приклади використання випадків:
- журнали
- тимчасові папки

Деякі випадки поганого використання:
- статичні файли
- config
- код


2
Щодо добрих і поганих випадків використання прикладу, на сторінці "Докерфайл найкращі практики" Докера говориться: "Вам настійно рекомендується використовувати VOLUME для будь-яких змінних та / або користувальницьких частин вашого зображення." Я думаю, що там є конфігури.
OmerSch

2
Гаразд бути чітким щодо VOLUMEбрусів для налаштувань. Однак після того, як ви фактично змонтуєте конфігурацію, вам доведеться встановити над цією каталогом, і тому VOLUMEкоманда не запускається. Тому безглуздо використовувати VOLUMEкоманду на dir, визначеному для config. Також серйозна надмірна ініціалізація графіка обсягу одним статичним файлом лише для читання. Тож я стою за тим, що я сказав, немає потреби VOLUMEв командах на конфігураціях.
г-н притулок

Обсяги можуть мати різні експлуатаційні характеристики завдяки деталізації впровадження. Файли даних баз даних вміщуються в цьому випадку використання, але який би був сенс зберігати дані поряд із (ефемерними) контейнерами? Тобто віднесення існування обсягів до продуктивності невірно.
Андре Верланг

33

Щоб краще зрозуміти volumeінструкцію докерфайлу, навчимося типовому використанню обсягу в реалізації офіційного файлу mysql.

VOLUME /var/lib/mysql

Довідка: https://github.com/docker-library/mysql/blob/3362baccb4352bcf0022014f67c1ec7e6808b8c5/8.0/Dockerfile

Це місце /var/lib/mysqlза замовчуванням MySQL, яке зберігає файли даних.

Якщо ви запускаєте тестовий контейнер лише з метою тестування, ви можете не вказати його точку монтажу, наприклад

docker run mysql:8

тоді екземпляр контейнера mysql буде використовувати шлях монтажу за замовчуванням, який визначений volumeінструкцією в dockerfile. томи створюються з дуже довгим ідентифікаційним іменем всередині кореня Docker, це називається "безіменним" або "анонімним" томом. У папці основної хост-системи / var / lib / docker / том.

/var/lib/docker/volumes/320752e0e70d1590e905b02d484c22689e69adcbd764a69e39b17bc330b984e4

Це дуже зручно для швидких тестових цілей без необхідності вказувати точку монтажу, але все ж можна отримати найкращі показники, використовуючи Volume для зберігання даних, а не шар контейнера.

Для офіційного використання вам потрібно буде вказати шлях монтажу, використовуючи названий том або прив'язувати кріплення, наприклад

docker run  -v /my/own/datadir:/var/lib/mysql mysql:8

Команда монтує каталог / my / own / datadir з базової системи хостів як / var / lib / mysql всередині контейнера. Каталог даних / my / own / datadir не буде автоматично видалений, навіть контейнер видалений.

Використання офіційного зображення mysql (Перевірте розділ "Де зберігати дані"):

Довідка: https://hub.docker.com/_/mysql/


2
Мені дуже подобається ваше пояснення.
ЛукашТарашка

Але докер все одно зберігає зміни. Також ви можете встановити шлях монтування, -vвикористовуючи його, не встановлюючи гучність у Dockerfile
Alex78191

1

Я не вважаю використання VOLUME хорошим ні в якому разі, за винятком випадків, якщо ви створюєте зображення для себе і ніхто більше не збирається ним користуватися.

На мене вплинуло негативно через об'єм, що розкрився в базових зображеннях, які я розширив, і дізнався про проблему лише після того, як зображення вже запущено, як Wordpress, який оголошує /var/www/html папку як VOLUME , і це означало, що будь-які файли додані або змінені під час Етап збирання не враховується, і живі зміни зберігаються, навіть якщо ви цього не знаєте. Існує некрасиве рішення щодо визначення веб-каталогу в іншому місці, але це лише неправильне рішення для більш простого: просто видаліть директиву VOLUME.

Ви можете досягти наміру обсягу легко, використовуючи -v опцію, це не тільки дає зрозуміти, якими будуть обсяги контейнера (без необхідності оглядати Dockerfile та батьківські Dockerfiles), але це також дає споживачеві можливість використовувати гучність чи ні.

В основному погано користуватися ОБ'ЄМНІМИ через наступні причини, про що говорить ця відповідь :

Однак інструкція VOLUME дійсно коштує.

  • Користувачі можуть не знати про створені безіменні томи і продовжують займати місця для зберігання на своєму хості Docker після видалення контейнерів.
  • Немає можливості видалити том, оголошений в Dockerfile. Зображення внизу потоку не можуть додавати дані до шляхів, де існують томи.

Останній випуск призводить до таких проблем.

Можливість скасувати оголошення про том допоможе, але тільки якщо ви Можливість скасувати визначення знаєте томи, визначені в dockerfile, який генерував зображення (та батьківські докерфайли!). Крім того, VOLUME може бути доданий у новіших версіях Dockerfile і несподівано порушити речі для споживачів зображення.

Ще одне хороше пояснення ( про зображення оракула, що має VOLUME , який було видалено ): https://github.com/oracle/docker-images/isissue/640#issuecomment-412647328

Більше випадків, коли VOLUME ламав речі для людей:

Запит тягнути , щоб додати опції для скидання Властивості батьківського образу (включаючи обсяг), був закритий , і в даний час обговорюється тут (і ви можете побачити кілька випадків з людей , які постраждали негативно з - за обсяги , визначених у dockerfiles), який має коментар з хорошим пояснення проти VOLUME:

Використовувати VOLUME в Dockerfile марно. Якщо користувачеві потрібна наполегливість, він обов'язково надасть відображення обсягу під час запуску зазначеного контейнера. Було дуже важко відстежити, що моя проблема про неможливість встановлення права власності на каталог (/ var / lib / influxdb) була пов'язана з декларацією VOLUME в Dockerfile InfluxDB. Без опції типу UNVOLUME або позбавлення її взагалі я не можу змінити нічого, пов’язане із вказаною папкою. Це менш ніж ідеально, особливо, коли ви знаєте про безпеку та бажаєте вказати певний UID, зображення слід розміщувати так, щоб уникнути випадкового користувача з більшою кількістю дозволів, ніж потрібно, для роботи програмного забезпечення на своєму хості.

Я також вважаю EXPOSE поганим, але він має менше побічних ефектів. Єдине добре, що я можу побачити про VOLUME та EXPOSE - це документація, і я вважав би їх гарними, якби вони служили лише для цього (без побічних ефектів).

TL; DR

Я вважаю, що найкраще використання VOLUME - це застаріле.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.