Как размонтировать диск, пока контейнер работает?

Вопрос или проблема

У меня есть машина Linux (6.6.x) с шасси с горячей заменой, которое позволяет вставлять и извлекать диски без простоя.

В системе стоят следующие диски:

  • /dev/sda (операционная система)
  • /dev/sdb (съемное хранилище)

В каталоге, где смонтирован /dev/sda, я запускаю любое контейнерное приложение с помощью docker, podman и т. д. Оно касается только файлов, которые хранятся на /dev/sda.

Затем я выполняю umount /dev/sdb, что успешно завершается, если его ничего не использует. Но когда я выполняю grep sdb /proc/*/mounts, все процессы, запущенные в контейнере, продолжают держать монтирование /dev/sdb открытым, хотя оно и было размонтировано на уровне корня. Я также проверил это в /sys/fs, увидев, что файловая система все еще активна.

  1. Я пробовал устанавливать разную пропагацию shared, slave, private в fstab, безуспешно. Размонтирование, кажется, не распространяется из верхнего пространства имен в пространство имен контейнера.
  2. Я попытался выполнить размонтирование внутри контейнера с помощью nsenter. Но это вызывает ошибку EINVAL target is locked; see mount_namespaces(7).

Согласно mount_namespaces(7), когда контейнерное приложение запускается, «список монтирований пространства имен ребенка является копией списка монтирований в пространстве имен монтирования родительского процесса»

Также согласно mount_namespaces(7) «монтирования, поступающие как единое целое из более привилегированного пространства имен монтирования, связываются вместе и не могут быть разделены»


a. Означают ли эти ограничения, что каждый контейнер в системе должен быть остановлен, прежде чем любой диск на компьютере может быть размонтирован?
b. Или есть способ исключить нерелевантные диски из копирования в пространства имен монтирования контейнера?
c. Или есть решение, позволяющее размонтированию распространиться от контекста верхнего уровня к всем контейнерам?

Ответ или решение

Для решения задачи безопасного размонтирования диска в Linux, когда контейнер продолжает работать, необходимо глубоко понимать работу пространств имен (namespaces) в Linux и особенности их взаимодействия с контейнерами. Рассмотренные вами вопросы не являются простыми, но попробуем систематически разобраться в вашей проблеме и возможных путях ее решения.

Теория

Пространства имен в Linux

Пространства имен монтирования (mount namespaces) в Linux позволяют отдельным процессам иметь независимое представление о файловой системе. Когда вы запускаете контейнер через Docker или Podman, создается новое пространство имен монтирования, которое изначально является копией текущего пространства имен родительского процесса. Это значит, что все монтирования, видимые для хост-системы в момент запуска контейнера, будут также видны внутри контейнера.

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

При запуске контейнера задействованы монтирования, по умолчанию открытые для повторного использования контейнером, из-за чего контейнеры захватывают привилегии на монтирование. Размонтирование из контейнера может быть затруднительным, если в ходе этого процесса возникают запреты на уровне системы имен, такие как «EINVAL target is locked».

Общая концепция пропаганды монтирований

Существует несколько механизмов распространения изменений монтирования между пространствами имен:

  1. Shared — изменения монтирования, такие как монтирования и размонтирования, распространяются на все связанные пространства имен.
  2. Private — изменения видны только в текущем пространстве имен и не автоматически не влияют ни на одно другое пространство имен.
  3. Slave — изменения из родительского пространства имен могут распространяться в дочернее, но не наоборот.

Эти концепции должны быть учтены при планировании операций с монтированием, чтобы обеспечить корректное распространение изменений по необходимым пространствам имен.

Пример

Допустим, у вас есть хост-система с диском /dev/sda, который используется для выполнения всех основных операций и диском /dev/sdb, который является съемным носителем данных. Если контейнер запущен в пространстве /dev/sda, все файлы, необходимые для работы контейнера, находятся на этом диске. Однако, /dev/sdb присутствует как часть текущего пространства имен монтирования в момент запуска контейнера.

Предположим, вы намерены размонитровать /dev/sdb. Важное условие, чтобы состояние монтирования /dev/sdb в контейнере не влияло на возможность размонтирования в хост-системе. Именно из-за копирования имен монтирования, размонтирование на уровне системы не отражается на контейнере.

Применение

Возможные решения
  1. Отказ от наследования монтирований. Если возможно, избегайте включения ненужных монтирований в контейнер при его запуске. Это можно сделать, изменив рабочую директорию или ограничив монтирования, используемые контейнером. При запуске контейнера с использованием Docker, политика монтирования может быть определена с помощью флага —mount или -v. Ограничьте видимость диска /dev/sdb, если это не предполагается в рамках приложения контейнера.
  2. Rootless контейнеры. Использование контейнеров без привилегий (rootless) может помочь ограничить автоматические наследования монтирований, что потенциально позволит контролировать видимость дисков.
  3. Автономные монтирования. При возможности, создавайте индивидуальные пространства имен для монтирования каждый раз, когда вы запускаете контейнер, позволяя при этом монтирование/размонтирование без влияния на другие контейнеры.
  4. Использование environment variables и scripts. Набор скриптов может быть предварительно настроен для автоматического отслеживания и управления привязкой дисков, что позволит автоматически настроить новое пространство монтирования.
  5. Закрытие отдельных использований. Хотя в вашем случае пространствам не удалось изолировать использование, повторное разворачивание через nsenter может помочь временно отключить использование, если это безопасно.
  6. Альтернативные реализации контейнеризации. Наконец, рассмотрите использование Podman вместо Docker, так как последний может иметь меньше ограничений по распространению изменений монтирования в зависимости от конфигурации, специфичных для вашего окружения.

Указанные методы могут в помочь при настройке контейнеров, которые не будут удерживать нежелательные монтирования из пространства имен системы после его изменения/удаления. Однако выполнение может потребовать специализированного подхода и глубокого тестирования на индивидуальном уровне системы.