Приложения запускаемые внутри Docker можно разделить на две большие группы: stateful и stateless. Первые хранят и обрабатывают данные, которые записываются в файловую систему, вторые ничего не сохраняют и работают только с теми данными, которые приходят к ним снаружи.
По ходу курса, мы в основном встречались со stateless приложениями. Это наиболее простые в обслуживании приложения. Они легко могут останавливаться, переноситься на другой сервер и масштабироваться за счет параллельного запуска нескольких копий. Например Nginx, является таким приложением.
Но большая часть реальных приложений является stateful. Типичные примеры включают в себя кеширование, хранение картинок, документов, данных в базе данных и так далее. Даже Nginx, при включенном кешировании, превращается в stateful приложение.
Stateful приложения значительно сложнее в управлении, особенно если они упакованы в Docker. Представьте себе, что ваше приложение сохраняет картинки. По умолчанию это происходит внутри контейнера. Затем, по какой-то причине, контейнер удаляется и вместе с ним пропадают все картинки. Вы без проблем перезапустите контейнер с приложением, но картинок там уже не будет.
При работе с Docker важно придерживаться правила неизменяемости контейнеров. Любые данные должны сохраняться куда-то наружу. В идеале запускать контейнеры с флагом --read-only
, который запрещает любую запись внутри. Так заодно можно находить контейнеры, которые что-то пишут без предупреждения:
docker run --read-only ...
Для работы с данными в Docker существует несколько механизмов, в этом уроке мы рассмотрим обмен данными с хост-системой.
Типичная ситуация, когда есть какой-то файл, обычно конфигурационный на хост-системе, и мы хотим чтобы при запуске контейнера этот файл оказался внутри. Например мы хотим чтобы внутри контейнера была доступна история bash-команд. Пример команды:
# -v <путь до файла в хост-системе:путь до файла
docker run -it -v $HOME/.bash_history:/root/.bash_history ubuntu bash
Технически, bash
внутри контейнера будет продолжать записывать историю команд во внутренний файл /root/.bash_history
, но Docker сделает так, что этот файл будет "ссылаться" на файл в хост-системе по пути $HOME/.bash_history
.
Добавляя флаг -v
можно пробросить во внутрь любое количество файлов:
docker run -it -v $HOME/.bash_history:/root/.bash_history \
-v /somefile:/path/to/file ubuntu bash
Точно так же этот механизм работает и для целых директорий. Например ниже мы передаем всю директорию /etc/nginx
внутрь контейнера:
docker run -it -v /etc/nginx:/etc/nginx nginx
Проброс директорий открывает возможности для хранения данных снаружи. Например так можно работать с официальным образом PostgreSQL:
# выдержка из официальной документации
# /var/lib/postgresql/data - директория куда сохраняются данные базы
docker run -d \
-e POSTGRES_PASSWORD=mysecretpassword \
-v /custom/mount:/var/lib/postgresql/data \
postgres
Шаринг данных открывает возможность обмена между контейнерами. Для этого достаточно пробросить директорию в нужное количество контейнеров, которые будут туда писать и читать. Классический пример использования это общий кеш.
# пример запуска для приложения, которое кеширует данные в /tmp
docker run -v /tmp:/tmp <какое-то приложение>
У Docker есть определенные правила при указании путей:
- Путь на хост-машине может быть как относительным так и абсолютным
- Путь внутри контейнера должен быть абсолютным
- Если файла или директории не существует внутри контейнера, то они будут созданы автоматически
Ошибка дизайна шаринга в Docker
Делая шаринг нужно внимательно следить за именем файла на хост-машине. Если ошибиться в названии, Docker не укажет на ошибку, он просто создаст директорию с таким именем. Это поведение осталось от первых версий в качестве обратной совместимости. К сожалению оно приводит к трудноотловимым ошибкам.
Самостоятельная работа
Не удобно работать в контейнере, когда теряется история введенных ранее команд. Пробросьте файл с историей внутрь контейнера, по примеру, как это сделано в теории. Проверьте что все работает.
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.