Блог

docker_load_average

Ми помітили суттєве перевантаження сервера після переходу на запуск cron-завдань у Docker-контейнерах. Кількість і логіка завдань не змінилися – лише спосіб виконання. Це стало поштовхом до глибшого аналізу.

Виявлення причини

Першим кроком було налаштування моніторингу використання ресурсів на рівні процесів. Ми розгорнули process-exporter і створили окремий дашборд. Спробували розрізнити джерела навантаження:

  1. Python-процеси
  2. процеси docker-compose
  3. пов’язані з Docker shim процеси

Зібрані дані показали, що основне навантаження створювали саме Python-процеси. Docker-процеси не мали суттєвого впливу. Але залишалося питання – чому перевантаження почалось лише після переходу на Docker-крони?

Формування гіпотез

Було висунуто дві основні гіпотези:

  • process-exporter може мати надто рідкісну вибірку або створювати значне власне навантаження (він обходить увесь /proc, де під час запусків cron може бути понад 5000 PIDів)
  • Особливості запуску Docker можуть приховувати реальний оверхед: контейнер готується через runc init, виконується кілька кроків налаштування, перш ніж запускається команда. Ці короткотривалі піки навантаження можуть бути непомітними у звичайних інтервалах моніторингу.

Початкова схема запуску cron

До оптимізації cron-завдання запускались так:

docker-compose run --rm app python script.py arguments

Кожне завдання створювало новий контейнер, який одразу після виконання видалявся.

Оптимізація

Було створено постійний фоновий контейнер для повторного використання:

docker-compose run --rm -d --name cron-runner app bash -c "while :; do sleep 60; done"

Після чого запуск cron-завдань перенесли на docker exec у вже існуючий контейнер:

docker exec cron-runner python script.py arguments

Результати

Середнє навантаження (Load Average)

Після переходу на docker exec середнє навантаження значно знизилось. На графіку за 2025-03-11 (06:00–22:00) видно чітке зменшення після ~15:50, коли було внесено зміни.

Використання CPU

Навантаження на CPU стало імпульсним – високі піки короткої тривалості замість широких плато з помірним навантаженням. Це свідчить про ефективніше використання CPU.

Зменшення IO

Також спостерігалося помітне зниження навантаження на диск між 14:00 та 18:00 того ж дня.

Аналіз

Причина перевантаження була не в Docker як такому, а в неправильному використанні Docker. Кожен docker-compose run запускав:

  • новий процес runc
  • створення namespace’ів
  • монтування overlayfs
  • fork і налаштування контейнера
  • виконання exec цільової команди

Ці дії споживають ресурси і створюють затримки до фактичного запуску завдання.

Висновок

Якщо ви спостерігаєте неочікуване навантаження після переходу на Docker для cron-завдань – уникайте запуску нового контейнера для кожної задачі. Замість цього використовуйте довготривалий контейнер і docker exec. Це суттєво знижує оверхед і покращує ефективність використання CPU та IO.

Додаткові посилання