Как сохранить docker контейнер в файл

Обновлено: 04.07.2024

Бывают случаи, когда серверы, на которых планируется выполнять контейнеры, созданные из образов, полностью изолированы от сегмента сети и не могут скачать себе образ из реестра. В этом случае у разработчиков возникает необходимость каким-то образом сформировать образ контейнера на сервере. Команда docker build тоже часто не подходит, поскольку большинство образов при сборке скачивает зависимости и необходимые слои из сети. Если хост не имеет подключения к сети, то и скачать ничего не сможет.

К счастью, Docker предоставляет стандартный механизм для распространения образов без использования реестров с помощью импорта и экспорта. Далее в статье мы рассмотрим как воспользоваться этими возможностями для того, чтобы эффективно распространять образы на серверы, где подключение к интернет невозможно.

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

  • обеспечение развертывания без сети;
  • гарантированное развертывание с твердого носителя (USB, DVD);
  • поставка в форме готового к использовании дистрибутива;
  • минимизация инфраструктуры за счет исключения лишних элементов (registry).

Сохранение образа в архив

Для начала рассмотрим как можно выполнить сохранение образа контейнера из среды. Для выполнения данной операции используется команда docker save (man docker-save):

После выполнения данной команды образ со всеми слоями будет выгружен из пространства образов демона Docker в файловую систему в виде архива tar.

Интересно, что таким образом можно выгрузить более одного образа:

Таким образом, вы можете за одну команду выгрузить все необходимые для распространения контейнеры в один tar-архив.

Загрузка образа в среду Docker

Для выполнения загрузки ипользуется обратная операция docker load:

Таким образом вы можете распространять образы контейнеров Docker между узлами без использования реестра и даже сети, например, на DVD или накопителе USB.

Отличия Save, Load от Export, Import

Docker поддерживает команды export и import, которые часто могут быть перепутаны с save, load. Ключевое отличие данных команд в том, для чего они предназначены:

В целом, разница заключается, что save/load относятся к данным образов, а export/import к данным контейнеров.

Очень частые вопросы, которые возникают в процессе использования Docker.

  • Как делать резервное копирование Docker контейнеров?
  • Как делать резервное копирование Docker образов?
  • Как делать резервное копирование СУБД (MySQL, Redis, MongoDB и т.д.), работающей внутри контейнера?
  • Как правильно восстанавливать Docker контейнеры?
  • Как правильно восстанавливать Docker образы?

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

  • образы, если вы не используете какой-либо публичный или приватный Docker репозиторий
  • работающие контейнеры, если по каким-либо причинам вы не подключили постоянные диски к директориям, данные которых вы изменяете
  • постоянные диски со статичными данными, например, вашим web-приложением (исходный код приложения на python, php, go и т.д.)

Важно: постоянные диски с динамически изменяемыми данными, т.к. файлы СУБД бекапиться не должны. Резервное копирование данных ваших СУБД необходимо выполнять штатными средствами самой СУБД, т.к. при простом копировании данных такого диска очень легко получить их неконсистентность и невозможность восстановить работоспособность и данные СУБД в будущем.

Резервное копирование образов docker

Резервное копирование Docker образов делается при помощи двух команд:

docker save — сохраняет Docker образ в tar-архив с необходимым образом на вашей файловой системе

docker load — загружает Docker образ из tar-архива, делая его доступным в списке ваших локальных образов (команды docker images)

Пример использования

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

Сохранение образа в tar-архив выполняется командой:

Напомню, что :latest — это указание тега вашего образа. Вы можете использовать другой тег с произвольной версией, которым помечен ваш образ, например,

Восстановление образа из tar-архива выполняется командой:

Удалите скачанный образ командой:

И проверьте, что образ busybox отсутствует в выводе команды docker images.

Теперь можно восстановить образ командой:

Образ busybox появился в выводе команды docker images.

Что делать с полученными tar-архивами

Т.к. хранение резервных копий системы на сервере с самой системой не допустимо, то предлагаю вам следующие варианты сохранения резервных копий:

  • на отдельный физический или виртуальный сервер, специально выделенный под эти задачи
  • на отдельную систему резервного копирования (например, EMC Avamar)
  • на отдельное отказоустойчивое облачное объектное хранилище, т.к. объектное хранилище Облака КРОК, AWS S3, GoogleDrive, DropBox и т.д.

Резервное копирование в репозиторий

Резервное копирование в удаленный публичный или приватный репозиторий — это наиболее распространенный способ резервного копирования образов контейнеров. Для этого используются команды:

Резервное копирование контейнеров

При использовании Docker нужно помнить о двух типах данных, с которыми работают контейнеры, а именно:

  • read-write слой, самого контейнера, создаваемый поверх слоев образа, из которого запущен контейнер
  • постоянные Docker-диски

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

Резервное копирование контейнеров

Под резервным копированием контейнера мы будем понимать резервное копирование именно данных самого контейнера вместе с его read-write слоем. В результате выполнения резервного копирования контейнера вы получаете образ контейнера, содержащий все изменения выполненные на файловой системе контейнера за исключением директорий, в которые смонтированы его постоянные диски.

После резервного копирования контейнера необходимо выполнить резервное копирование его образа (см. описание процесса выше).

Сохранение изменений, выполненных в контейнере

Для того, чтобы выполнить резервное копирование контейнера, необходимо сохранить в нем все изменения, выполненные на момент времени резервного копирования при помощи команды docker commit. Предположим, у вас имеется запущенный контейнер, в котором вы выполнили какие-либо изменения:

Сохраните изменения, выполненные в контейнере, при помощи команды docker commit:

Данная команда создаст Docker образ с именем container1 и тегом 0.0.1. Проверить это можно командой docker images:

Далее образ container1 с тегом 0.0.1 может быть забекаплен одним из вышеперечисленных выше способом.

Резервное копирование постоянных дисков со статическим контентом

Резервное копирование статического контента, находящегося на постоянных диска Docker, может быть выполнено большим количеством разнообразных способов:

  • путем создания tar-архива содержимого диска
  • путем копирования содержимого диска на удаленный сервер при помощи, например, scp или rsync
  • путем копирования содержимого диска на удаленное отказоустойчивое объектное хранилище

Здесь мы рассмотрим процесс создания tar-архива, содержащего данные диска. Остальные способы могут быть реализованы вами самостоятельно по аналогии. Суть метода заключается в том, чтобы запустить отдельный контейнер, подключив к нему диски контейнера, которые необходимо забекапить, и выполнить резервное копирование внутри созданного контейнера туда, куда необходимо.

Запуск контейнера с подключением к нему дисков уже работающего контейнера может быть выполнен командой:

Данная команда запустит новый контейнер на базе ОС Ubuntu, к которому будут подключены все диски с контейнера my_backend_container, и они будут доступны по тем же путям, что и в том контейнере, резервное копирование которого вы производите. ОС не имеет значения, главное, чтобы внутри контейнера были все необходимые утилиты, которые будут выполнять резервное копирование. В директорию /backup будет смонтирован текущая директория вашего интерпретатора. Запускаемый Ubuntu контейнер выполнит команду архивирования директории /my_app_content (предполагается, что данные вашего постоянного диска смонтированы именно туда) в директорию /backup (текущая директория интерпретатора). По завершению данной операции эти данные необходимо скопированы на другой сервер или отказоустойчивое объектное хранилище.

Резервное копирование постоянных дисков с динамическим контентом

Как уже было рассказано выше, динамические данные, находящиеся на постоянных дисках ваших контейнеров, т.к. файлы СУБД MongoDB, MySQL, Postgresql, Redis и т.д. резервному копированию не подлежат. Для резервного копирования баз данных необходимо использовать утилиты mongodump, mysqldump, pg_dump, rdiff-backup и т.д., выполняя резервное копирование стандартными средствами нужной СУБД. Запуск вышеуказанных команд для резервного копирования и восстановления может быть выполнен из заранее созданного образа, содержащего эти утилиты, по аналогии с примером резервного копирования статичного контента постоянного диска.

Если вы не заметили, список задач очищается каждый раз при запуске контейнера. Почему? Давайте подробно рассмотрим работу контейнера.

Файловая система контейнера

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

Рассмотрим это на практике

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

Запустите контейнер ubuntu , который создаст файл с именем /data.txt со случайным числом от 1 до 10 000.

Если вас интересует команда, то она запускает оболочку bash и вызывает две команды (поэтому в ней есть оператор && ). Первая часть выбирает одно случайное число и записывает его в файл /data.txt . Вторая команда просто наблюдает за файлом, чтобы работа контейнера не завершалась.

Убедитесь, что вы можете увидеть результат, используя exec , чтобы попасть в контейнер. Для этого откройте расширение VS Code и выберите команду Attach Shell (Присоединить оболочку). Эта команда использует exec , чтобы открыть оболочку контейнера в терминале VS Code.

Открыть интерфейс командной строки VS Code в контейнере ubuntu

Откроется терминал с запущенной оболочкой в контейнере Ubuntu. Выполните следующую команду, чтобы просмотреть содержимое файла /data.txt . Затем снова закройте этот терминал.

Если вы предпочитаете командную строку, то для получения того же результата можно использовать команду docker exec . Необходимо получить идентификатор контейнера (используйте команду docker ps для его получения) и получить содержимое с помощью следующей команды.

Должно отобразиться случайное число.

Теперь запустите другой контейнер ubuntu (тот же образ), и вы увидите, что у вас нет этого файла.

Смотрите! Файла data.txt нет. Это обусловлено тем, что он был записан в область временных файлов только для первого контейнера.

Удалите первый контейнер с помощью команды docker rm -f .

Тома контейнеров

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

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

Существует два основных типа томов. Мы будем использовать оба, но начнем с именованных томов.

Сохранение данных списка задач

По умолчанию приложение todo сохраняет данные в базе данных SQLite в /etc/todos/todo.db . Если вы не знакомы с SQLite, не стоит беспокоиться. Это просто реляционная база данных, в которой все данные хранятся в одном файле. Хотя это и не лучшее решение для масштабных приложений, она хорошо подходит для небольших примеров. Мы поговорим о переходе на полноценную базу данных позже.

Поскольку база данных представляет собой один файл, если вы можете сохранить его на хосте и сделать его доступным для следующего контейнера, он сможет продолжить работу с того места, где остановился предыдущий. Создавая том и присоединяя его (или "подключая") к каталогу, в котором хранятся данные, можно сохранить данные. Когда контейнер записывает данные в файл todo.db , он сохраняется на узле в томе.

Как уже упоминалось, мы будем использовать именованный том. Именованный том следует рассматривать как просто набор данных. Docker сохраняет физическое расположение на диске, и вам нужно запомнить только имя тома. При каждом использовании тома Docker обеспечивает правильность предоставленных данных.

Создайте том с помощью команды docker volume create .

Снова закройте контейнер приложения todo в представлении Docker (или с помощью команды docker rm -f <id> ), так как он все еще выполняется без использования постоянного тома.

Запустите контейнер приложения todo, но добавьте флаг -v , чтобы указать подключение тома. Мы будем использовать именованный том и подключим его к папке /etc/todos , и он будет записывать все файлы, созданные по этому пути.

После запуска контейнера откройте приложение и добавьте несколько пунктов в список дел.

Пункты, добавленные в список дел

Удалите контейнер приложения todo. Используйте представление Docker или команду docker ps для получения идентификатора контейнера и команду docker rm -f <id> для его удаления.

Запустите новый контейнер с помощью команды, которую мы использовали выше.

Запустите приложение. Вы должны увидеть, что элементы по-прежнему находятся в вашем списке!

После просмотра списка удалите контейнер.

Поздравляем! Теперь вы узнали, как сохранять данные.

Хотя именованные тома и подключения BIND (которые мы обсудим через минуту) — это два основных типа томов, поддерживаемых установкой подсистемы Docker по умолчанию, существует множество подключаемых модулей драйверов томов для поддержки NFS, SFTP, NetApp и многих других технологий. Это будет особенно важно после запуска контейнеров на нескольких узлах в кластерной среде с помощью Swarm, Kubernetes и т. д.

Подробнее о томах

Многие люди часто спрашивают: "где на самом деле хранятся данные при использовании именованного тома?" Если вы хотите узнать это, используйте команду docker volume inspect .

Mountpoint — это фактическое расположение на диске, в котором хранятся данные. Обратите внимание, что на большинстве компьютеров необходимы права администратора для доступа к этому каталогу с хоста. Именно здесь они и хранятся.

Доступ к данным тома непосредственно в Docker Desktop. При работе в Docker Desktop команды Docker на самом деле выполняются внутри небольшой виртуальной машины на компьютере. Если вы хотите просмотреть фактическое содержимое каталога Mountpoint, сначала необходимо получить его в виртуальной машине. В WSL 2 он находится внутри дистрибутива WSL 2, и доступ к нему можно получить с помощью проводника.

Резюме

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

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

Я новичок в докере и контейнерах. Я выполнил следующие шаги для переноса моей базы данных postgres, работающей в док-контейнере:

На сервере назначения (192.10.2.5):

Но когда я вхожу в образ докера, базы данных там нет. Как сохранить образ докера со всеми таблицами базы данных и данными.

4 ответа

Там нет базы данных, потому что команды docker save и docker commit не сохраняют тома контейнера .

Вот скрипт для этого:

Ваш контейнер был основан на изображении при запуске, но после запуска все изменения не будут влиять на изображение.

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

Примерно так: см. здесь.

Затем сохраните новое изображение в tar и сделайте то, что вы хотели.

Зафиксировать текущее состояние изображения

Сохранить изображение как файл tar

Загрузить контейнер с картинки

Попытка переместить контейнер как это не лучшая практика. Однако может потребоваться некоторая настройка, чтобы привести ваш контейнер в правильное состояние.

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

Теперь вы можете делать с контейнером все, что захотите, даже docker rm его, но до тех пор, пока /data/mysql на вашем хосте не затрагивает ваши данные Это хорошо. Это также означает, что вы можете использовать обычный scp для копирования данных на другой хост и docker run там же контейнер, что является более типичным путем миграции.

Если вы создаете и запускаете пользовательские образы, необходимо проверить несколько вещей:

Вы действительно должны использовать своего рода реестр Docker, будь то Docker Hub, что-то, что предлагает ваш облачный провайдер, что-то стороннее или что-то самообслуживаемое. docker save перемещение изображений - последнее средство, которое редко требуется.

Сами изображения должны быть построены из Dockerfiles и, как и все остальное в вашем приложении, должны быть включены в систему контроля версий. В идеале у вас есть автоматизированная система сборки (непрерывная интеграция), запускающая docker build && docker push для вас. Никогда не используйте docker commit или docker export : это рецепт для получения разных версий программного обеспечения в разных системах и не запоминания того, как вы туда попали.

Ваше приложение должно минимизировать объем данных, которые оно хранит локально. Лучший вариант - хранить все во внешней базе данных, а затем вы можете просто указать на базу данных; если локальное файловое хранилище действительно неизбежно, попробуйте поместить все это в один каталог, который не входит в исходное дерево вашего приложения.

Локально вы должны иметь возможность docker stop; docker rm; docker run свободно использовать один и тот же контейнер и не терять данные. Проверьте это, прежде чем выполнять миграцию между хостами.

Если вы можете структурировать свое приложение так, чтобы все его данные находились во внешней базе данных, рассмотрите возможность запуска базы данных на выделенном хосте с чрезвычайно хорошими процедурами резервного копирования. Если вы сделали все до этого момента, вы можете получить серьезный скоординированный сбой жесткого диска и потерять все ваши хосты Docker, а на самом деле ничего не потерять (потому что вы можете docker build снова получить изображения из источника и < > контейнеры и данные вне хоста).

Читайте также: