Bollywood HungamaKartik Aaryan and Kabir Khan reunite for inspirational real-life story Ladaku, set to release on June 18, 2027PunchFULL LIST: Nigerian stars missing from CAF 2026 Player of the Year award shortlistThe Jerusalem PostWhy is the Islamic Republic banning the Lion and Sun emblem amid anti-regime protests? - analysisInquirer EntertainmentTony Butala, co-founder of The Lettermen known for ‘When I Fall in Love,’ dies at 87InquirerWALANG PASOK: Class suspensions on Thursday due to high heat indexZDF heuteEntdecken Sie das ZDF-NachrichtenstudioESPN CricinfoIndia's seamers and Kohli 74 power them to 1-0 lead in rain-hit matchCollider'Game of Thrones' Star's Return to the Fantasy Genre Is Officially a Box Office WinnerIl Fatto QuotidianoÉ morto Tinto Brass, addio al regista simbolo del cinema erotico italianoCBS NewsDemocrats score major upsets and take control of SenateESPNRoman Reigns can continue unbeaten year with a win at Crown JewelNotJustOkFull list of nominees for the 2026 CAF Awards
The Daily Newsstand · Free, Always
Wednesday, November 4, 2026

Контейнер притворяется сервером: четыре места, где выдаёт себя

Translate

В моём тренажёре Prod is Down каждый игрок получает свой сломанный «сервер». На самом деле это контейнер, но игрок об этом думать не должен: он набирает те же команды, что и на настоящем дежурстве. systemctl restart nginx, journalctl -u api, free -m, top.

Из коробки половина этих команд в Docker либо не работает, либо врёт. Ниже четыре места, где контейнер выдаёт себя.

1. systemctl: PID 1 — не systemd

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

$ systemctl restart nginx
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

Вариантов три, и у каждого своя цена:

Вариант

Что даёт

Чем платите

Настоящий systemd как PID 1

Всё работает как на сервере: юниты, Restart=, journald

Нужны доступ к cgroup, tmpfs на /run, часто --privileged. Для контейнера с чужим root это плохая идея

Замена systemctl

Скрипт читает юнит-файлы и сам запускает ExecStart. Готовый пример — docker-systemctl-replacement

Подмножество systemd: что-то из юнитов будет проигнорировано

Свой скрипт запуска

Ровно то, что нужно вашим сервисам

Всё, чего в нём нет, придётся дописывать самому

В Prod is Down сервисы кейса поднимает отдельный шаг entrypoint — pd-systemd-boot, а терминал запускается последним. Первый вариант для меня отпадал сразу: в контейнере сидит незнакомый человек с sudo, и давать ему привилегированный контейнер ради настоящего systemd нельзя.

2. Сигналы и зомби: чем особенен PID 1

У первого процесса две обязанности, о которых на сервере заботится systemd, а в контейнере - никто.

сСигналы. Ядро не применяет к PID 1 действие по умолчанию. Если скрипт не установил обработчик SIGTERM, сигнал просто игнорируется. docker stop ждёт 10 секунд и добивает контейнер SIGKILL, а сервисы внутри не успевают завершиться чисто.

Зомби. Когда родитель умирает, его дети переходят к PID 1, и он должен забирать их статус выхода. Обычное приложение этого не делает, и в ps копятся процессы <defunct>. В тренажёре это вдвойне неприятно: игрок ищет в ps причину инцидента и находит мусор, который к кейсу отношения не имеет.

Лечится одним флагом: docker run -init (в compose - init: true). Первым процессом станет маленький init из комплекта Docker. Он передаст сигналы вашему entrypoint и будет собирать зомби. А в самом entrypoint последний процесс стоит запускать через exec, чтобы не оставлять лишний слой bash.

3. journalctl и логи

Нет systemd - нет и journald. Игрок набирает первую команду любого разбора и получает:

$ journalctl -u nginx
No journal files were found.

При этом у Docker свои логи, и они тоже не помогают: docker logs собирает только stdout и stderr первого процесса. Всё, что nginx или PostgreSQL пишут в свои файлы, туда не попадает. Да и игроку изнутри контейнера docker logs недоступен.

Потому решите заранее, где человек будет искать причину:

  • либо сервисы пишут в привычные файлы в /var/log, и вы проверяете, что они там правда появляются: многие образы направляют логи в stdout симлинком;

  • либо journalctl должен работать, и тогда нужен journald или замена systemctl, которая умеет отдавать логи сервиса.

Худший вариант - когда логи не пишутся никуда. На настоящем сервере такого не бывает, и человек долго ищет ошибку там, где её нет.

4. free и top показывают хост, а не лимиты

Контейнеру выдали 512 МБ и четверть ядра. Игрок набирает free -m и видит всю память сервера, а top и nprocпоказывают все его ядра. Эти утилиты читают /proc/meminfo и /proc/stat, а в обычном Docker это файлы хоста. nproc учитывает --cpuset-cpus, но не квоту.

Для тренажёра это ловушка. В кейсе про утечку памяти free покажет гигабайты свободного места, а OOM killer всё равно придёт. У контейнера свой потолок, и виден он только в cgroup:

cat /sys/fs/cgroup/memory.max       # 536870912 — настоящий лимит
cat /sys/fs/cgroup/memory.current   # сколько занято сейчас
cat /sys/fs/cgroup/cpu.max          # 25000 100000 — четверть ядра

Решений два. LXCFS (ссылка на GitHub) подменяет /proc/meminfo и компанию так, чтобы они показывали лимиты контейнера, и привычные утилиты начинают говорить правду. Или можно показывать цифры самому. Дашборд Prod is Down, например, считает CPU от лимита контейнера: процесс, съевший свою четверть ядра, выглядит как привычные 100%.

И одно предупреждение. В песочницах вроде gVisor /proc эмулируется, и часть цифр ведёт себя по-своему. Всё, что увидит человек, проверяйте в том рантайме, в котором он будет работать.

Итог

Если контейнер должен выглядеть для человека как сервер, проверьте четыре вещи:

  1. Кто у вас PID 1 и что отвечает на systemctl.

  2. Кто передаёт сигналы и собирает зомби (--init).

  3. Где человек найдёт логи сервиса и работает ли journalctl.

  4. Что показывают free, top и nproc: лимиты контейнера или хост.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.