Задача о призрачных рестартах контейнера - Академия Selectel

Задача о призрачных рестартах контейнера

Тирекс
Тирекс Самый зубастый автор
12 мая 2026

Будет полезна бэкенд-разработчикам, системным администраторам и SRE/DevOps-специалистам.

Изображение записи

Материал подготовил Роман Шубин, CTO и автор Telegram-канала Bash Days.

Условие

Представьте, что вы развернули сервер на Ubuntu 22.0 и запустили приложение с помощью Podman. Сервис работает стабильно, но каждые 15–20 минут происходят фантомные баги:

  • на пару секунд пропадают ответы,
  • пользователи получают 502 ошибку,
  • затем все само чинится.

Первичная диагностика показала, что контейнер не падает, CPU и RAM в пределах нормы, а в логах приложения — тишина. Проблема явно где-то глубже. 

Также известно, что сервис доступен на :8080.

Контейнер стартует через systemd unit:


      /etc/systemd/system/app.service

А логи можно посмотреть через:


      journalctl -u app.service
podman logs <container>

Задача

Определите причину кратковременной недоступности сервиса. В качестве ответа укажите:

  • что именно вызывает проблему,
  • почему проблема возникает,
  • как ее исправить.

Решение

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

Пример приложения:


      from http.server import BaseHTTPRequestHandler, HTTPServer

 class Handler(BaseHTTPRequestHandler):
     def do_GET(self):
         self.send_response(200)
         self.end_headers()
         self.wfile.write(b"Service is running")

 server = HTTPServer(("0.0.0.0", 8080), Handler)
 print("Server started on 8080")
 server.serve_forever()

Пример unit-файла:


      [Unit]
 Description=App container
 After=network.target

 [Service]
 ExecStart=/usr/bin/podman run --rm --name app -p 8080:8080 app-image
 ExecStop=/usr/bin/podman stop -t 10 app

 [Install]
 WantedBy=multi-user.target

Таймер, который все ломает:


      [Unit] 
 Description=Periodic restart of app 
  
 [Timer] 
 OnBootSec=5min 
 OnUnitActiveSec=15min 
 Unit=app-restart.service 
  
 [Install] 
 WantedBy=timers.target

Каждые 15 минут контейнер перезапускается. Даунтайм составляет 1–2 секунды.

Проверяем по очереди:

  • вывод команды podman ps — с ним все отлично;
  • логи приложения podman logs app — тут тоже все хорошо;
  • логи сервиса journalctl -u app.service — а здесь начинается магия.

      Stopping App container
Starting App container

Так, это странно. Смотрим список таймеров:


      systemctl list-timers

И видим очевидный подводный камень: app-restart.timer

Удаляем таймер:


      systemctl disable --now app-restart.timer

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

Можно усложнить задачу и назвать таймер совсем неочевидно, например logrotate-app.timer. В этом случае порядок действий такой.

Сначала нужно доказать, что сервис реально перезапускается:


      journalctl -u app.service --since "1 hour ago"

Здесь как раз и увидим эти самые строчки:


      Stopping App container
Starting App container

Ключевой инсайт здесь — не падение, а именно рестарт.

Проверяем, кто инициализировал рестарт:


      systemctl status app.service

В большинстве случаев можно увидеть строчку вида:


      TriggeredBy:  logrotate-app.timer

Если вам повезло и такая строчка есть — дело в шляпе. Если же строки нет, смотрим все таймеры:


      systemctl list-timers --all

Ищем примерную периодичность в 15 минут:

    Ищем таймеры в терминале.


    Визуально определяем периодичность и сопоставляем с проблемой.

    Смотрим подозрительный таймер: systemctl cat logrotate-app.timer и видим:

    
          [Timer]
     OnUnitActiveSec=15min
     Unit=logrotate-app.service
    

    Дело закрыто! Иногда можно пойти по ложному следу:

    
          podman inspect app | grep RestartPolicy
    

    Пусто! И так тоже ничего:

    
          podman logs app
    
    

    А если так?

    
          dmesg | grep -i kill
    

    — про OOM тишина.

    В таких задачах главное — иметь чек-лист, по которому вы идете и проверяете всё постепенно, потому что легко забыть про systemd и искать причину совсем не там, где нужно.