Администратор Discourse с 10-летним опытом самохостинга спрашивает: почему очистка лаунчера не включена в процесс пересборки?

Всем привет. Меня зовут Ли, и я занимаюсь самостоятельным хостингом Discourse с переменным успехом с 2013 года. Помню, как приходилось возиться с rbenv, чтобы вообще начать. Помню, как приходилось компилировать nginx с Phusion Passenger, чтобы всё заработало. Помню, как около десяти лет назад спорил с @sam, что переход на Docker — это капитуляция перед слабостью разработчиков, у которых «всё работает на их домашней директории и в их кошмаре с dot-файлами» (и я был абсолютно неправ!). Помню первый раз, когда услышал фразу «bike-shedding». Цитируя того самого человека: я помню всё.

После нескольких лет отсутствия у меня появилась возможность вернуться к самостоятельному хостингу Discourse в качестве замены нативным комментариям WordPress на погодном сайте в Хьюстоне, который обычно обслуживает около 10 тыс. просмотров в день, но во время ураганов может достигать 2 млн просмотров в день и 1 млн уникальных посетителей. Мы годами боролись с нативными комментариями WordPress, но по состоянию на прошлую среду мы перешли на самостоятельный хостинг Discourse. (И к тому же на Graviton3! Серьёзно, это просто работает, и это здорово.)

Вот к чему я веду: сейчас 2025 год, и как самостоятельный хостер я всё ещё вынужден вручную управлять пространством образов Docker. Приведу историю про /dev/root, рассказанную через фрагменты кода, после менее чем недели работы в продакшене:

[11:49:56] 0 ✓ (1.8ms)
root@discourse:/var/discourse # df -h
Filesystem       Size  Used Avail Use% Mounted on
/dev/root         30G   21G  9.6G  69% /
tmpfs            7.7G     0  7.7G   0% /dev/shm
tmpfs            3.1G  1.1M  3.1G   1% /run
tmpfs            5.0M     0  5.0M   0% /run/lock
efivarfs         128K  3.6K  125K   3% /sys/firmware/efi/efivars
/dev/nvme1n1p16  891M  109M  720M  14% /boot
/dev/nvme1n1p15   98M  6.4M   92M   7% /boot/efi
/dev/nvme0n1      32G  346M   30G   2% /var/discourse
tmpfs            1.6G   12K  1.6G   1% /run/user/1001
overlay           30G   21G  9.6G  69% /var/lib/docker/overlay2/5a649418bbfc064f488e895572eec1ace487a3eaa324fe1d8e3b395e6c5e3645/merged

[11:49:59] 0 ✓ (4.8ms)
root@discourse:/var/discourse # ./launcher cleanup
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y 
Total reclaimed space: 0B
WARNING! This will remove all images without at least one container associated to them.
Are you sure you want to continue? [y/N] y
Deleted Images:
untagged: discourse/base@sha256:3696bdf18652b5455bd33795ec3b8e0f201c17a04f0e0126fc0317ed821373cd
....

[целая куча строк удалена]

....
Total reclaimed space: 12.43GB

[11:50:34] 0 ✓ (27.8s)
root@discourse:/var/discourse # df -h
Filesystem       Size  Used Avail Use% Mounted on
/dev/root         30G  6.9G   24G  23% /
tmpfs            7.7G     0  7.7G   0% /dev/shm
tmpfs            3.1G  1.1M  3.1G   1% /run
tmpfs            5.0M     0  5.0M   0% /run/lock
efivarfs         128K  3.6K  125K   3% /sys/firmware/efi/efivars
/dev/nvme1n1p16  891M  109M  720M  14% /boot
/dev/nvme1n1p15   98M  6.4M   92M   7% /boot/efi
/dev/nvme0n1      32G  346M   30G   2% /var/discourse
tmpfs            1.6G   12K  1.6G   1% /run/user/1001
overlay           30G  6.9G   24G  23% /var/lib/docker/overlay2/5a649418bbfc064f488e895572eec1ace487a3eaa324fe1d8e3b395e6c5e3645/merged

[11:55:28] 0 ✓ (3.3ms)
root@discourse:/var/discourse #

Я вас люблю, ребята. Я люблю Discourse. Я предан этому продукту и планирую продолжать его использовать, по большому счёту, вечно.

Но, знаете ли… просто, почему. Почему сейчас 2025 год, и я лично, в одиночку, всё ещё возюсь с launcher cleanup? Почему управление образами не является встроенной функцией launcher?

Снова повторяю: я вас люблю, ребята. Я выбрал Discourse для SCW, потому что верю в то, что вы создали, и мне нравится его использовать. Но, знаете ли… половина объёма загрузочного тома моей бедной AMI занята бесполезным мусором, который, по крайней мере, если я правильно понимаю техническую сторону вопроса, мог бы управляться автоматически.

Не хочу жаловаться — просто снова заглянул после нескольких лет отсутствия в кресле администратора. Мне очень нравится AI-обнаружение спама и AI-сортировка, особенно на погодном форуме, где политически заряженные посты о климатических изменениях (как за, так и против) являются регулярным явлением. Спасибо вам за всё <3

Рады снова видеть вас, Ли! :sunflower:

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

Привет, рад снова видеть вас здесь!

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

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

Я сталкивался с проблемами на сайтах с самостоятельным хостингом, где очистка также удаляла новый базовый образ Discourse, который мне нужно было создать, что приводило к ужасной проблеме «курица или яйцо». То, что это могло остаться незамеченным из-за автоматического запуска, вероятно, стало бы довольно серьёзной проблемой для выяснения причин.

Простое предложение здесь могло бы заключаться в том, чтобы запланировать выполнение docker system prune или launcher clean через cron, но на ваш собственный страх и риск. Это могло бы сработать?

Потому что иногда он может удалить единственный рабочий контейнер, который у вас есть.

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

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

Как можно ответить «да», выполняя ./launcher cleanup через cron? Для меня контейнеры не являются такой уж большой проблемой, а вот оставшиеся образы — да.

Нет причин делать это через cron; вы создаёте новые образы только при сборке с помощью launcher. Это нужно делать только до или после сборки образа.

Если вы хотите избежать подсказок от launcher, можно использовать команды docker, как предложено выше. Вот одна из них (но изучите документацию, чтобы убедиться, что она делает именно то, что вам нужно):

/usr/bin/docker image prune -a -f

Надо будет проверить. Спасибо.

Я ничего не знаю, кроме как сегодня, снова сборка не удалась, потому что у меня оставалось меньше 5 ГБ свободного места. Конечно, cleanup справился с задачей, и это было не что иное, как немного раздражающе. И всё же, я бы хотел не сталкиваться с такими ситуациями.

А здесь видно, как мало я понимаю, как работает Docker :joy: Если я правильно понял, те образы, которые были уничтожены, потому что они не использовались никаким контейнером, вообще не были изображениями в смысле картинок, как я всегда думал :face_with_peeking_eye: :rofl:

Прямой ответ: вы можете выполнить echo y | launcher cleanup, чтобы заранее отправить “y”.

Косвенный ответ: фактическая очистка лаунчера (после этого) эквивалентна выполнению этих двух команд:

docker container prune --force --filter until=24h
docker image prune --all --force --filter until=24h

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

rm -rf /var/discourse/shared/standalone/postgres_data_old*

Вы можете отказаться от зависимости от лаунчера и использовать эти команды напрямую.

На самом деле я имею в виду вопросы, которые возникли у меня при выполнении команды ./launcher cleanup. Сначала она удаляет все остановленные контейнеры. Затем предлагает удалить все образы, которые не используются хотя бы одним контейнером — и именно этот этап освобождает для меня место: в прошлый раз было освобождено почти 40 ГБ.

Вот почему я был довольно сбит с толку, так как не мог понять, почему у меня оказалось так много «сиротских» образов (jpg, png и т. д.). Но речь здесь идёт о совершенно других образах, верно?

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

Будет ли каждый раз создаваться новый образ — я не знаю.

Каждая пересборка создаёт новый образ — поэтому они будут накапливаться, если их не удалять.

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

Это может быть неприятно, если вы запускаете её из скрипта; скрипт просто зависнет в ожидании ответа (полагаю, именно поэтому в него нужно передавать yes). Я просто делаю очистку, если на диске остаётся меньше 10 ГБ свободного места.

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

Я рассматриваю возможность добавления настройки data-root в файл /etc/docker/daemon.json, чтобы проверить, не заставит ли это Docker размещать свои образы — в данном случае образы Discourse, поскольку на этом сервере ничего другого не размещено — в менее критичном месте, которое не приведёт к переполнению тома загрузки.

Поиск по Meta по прошлым темам на эту тему дал мне несколько результатов, которые не очень-то проясняют ситуацию. И прежде чем я случайно обрушу свою рабочую инстанцию Discourse в кучу дымящихся обломков, хотел бы спросить, жизнеспособен ли такой подход :slight_smile:

Я выбрал другой подход: смонтировал отдельную файловую систему в /var/lib/docker.

В моём случае, по очень специфичным причинам, связанным с данным сайтом, я создал отдельные файловые системы для каждого из следующих каталогов: /var/discourse/shared, /var/discourse/shared/data, /var/discourse/shared/app/uploads/default/original и /var/lib/docker. Однако, если вы хотите просто выделить /var/discourse как отдельную файловую систему, вы, вероятно, можете создать каталог /var/discourse/share/docker и смонтировать его как привязку (bind-mount) в /var/lib/docker (разумеется, выполняя это на остановленной системе и перемещая файлы при необходимости).

Это ещё лучшая идея, чем возиться с внутренностями Docker. Спасибо!!