Сохранение данных

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

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

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

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

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

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

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    

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

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

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

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

    cat /data.txt
    

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

    docker exec <container-id> cat /data.txt
    

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

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

    docker run -it ubuntu ls /
    

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

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

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

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

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

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

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

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

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

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

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

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

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

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    
  4. После запуска контейнера откройте приложение и добавьте несколько пунктов в список дел.

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

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

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

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

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

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

Совет

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

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

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

docker volume inspect todo-db
[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

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

Примечание

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

Резюме

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

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

Дальнейшие действия

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