сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Linux и bash

Память, CPU и диски в Linux

Память, процессор, лимиты

Снял показания с рабочего сервера. Всего памяти 3916 мегабайт, свободной - 331, то есть 8%. Выглядит как повод паниковать. Но строка «доступно» показывает 1751 мегабайт, 44%: разница - это кэш файлов, который ядро отдаст приложению по первому требованию. Ещё на этой машине 2 ядра и загрузка 0,48 - то есть 24%.

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

// Формулировки: «свободной памяти почти нет, это плохо?», «загрузка 12 при простое процессора», «почему приложение в контейнере не видит свой лимит?»

Свободная память и кэш

Разберу цифры с сервера подробно. Из 3916 мегабайт: свободно 331, под кэшем файлов 1286, под буферами 51. Ядро специально не оставляет память простаивать - оно держит в ней недавно прочитанные куски файлов, чтобы следующее чтение шло из памяти, а не с диска. Как только приложению понадобится память, кэш отдаётся мгновенно.

Поэтому строка «свободно» на рабочем сервере всегда близка к нулю, и это норма, а не тревога. Смотреть надо на строку «доступно» - это оценка ядра, сколько можно выделить прямо сейчас, не прибегая к подкачке - вытеснению страниц памяти на диск. У меня это 1751 мегабайт из 3916.

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

кэш файлов
ядро держит в свободной памяти куски файлов; отдаёт приложениям по требованию
строка «доступно»
сколько памяти реально можно выделить сейчас; смотреть надо на неё

Средняя загрузка считает и ожидание диска

Средняя загрузка - это три числа за минуту, пять и пятнадцать. У меня на сервере 0,48, 0,52 и 0,51 при двух ядрах, то есть примерно четверть мощности. Первое правило: это число сравнивают с количеством ядер, а не с сотней. Загрузка 12 на четырёх ядрах - очередь втрое длиннее возможностей, та же 12 на 32 ядрах - обычный день.

Второе правило важнее и специфично именно для Linux. Сюда входят и задачи, которые считают или готовы считать, и те, что стоят в непрерываемом ожидании ввода-вывода. Поэтому картина «загрузка улетела, а процессор скучает» - совершенно нормальная и означает она обычно залипшее хранилище, а не нехватку процессора.

// Разбирают это так: смотрят долю времени, которое система проводит в ожидании ввода-вывода, статистику по дисковым устройствам (среднее время ожидания запроса и то, какую долю времени устройство было занято) и список задач в состоянии непрерываемого сна - у меня на спокойной машине их ноль. И читают все три числа сразу: растущее первое при спокойных остальных - свежая проблема, обратное - затухающая.

средняя загрузка
среднее число задач, которые считают или ждут ввода-вывода; сравнивают с числом ядер
ожидание ввода-вывода
доля времени, когда процессор простаивает в ожидании диска или сети

Контейнер видит хост, а не свой лимит

Замер, который объясняет половину падений контейнеров. Запустил контейнер с лимитом 256 мегабайт памяти и половиной ядра. Внутри спросил систему о памяти - она ответила 3916 мегабайт, то есть всю память сервера. Спросил число ядер - ответила 2, тоже как на сервере. И только настройки самого лимита показали правду: 256 мегабайт и квота процессора «50000 из 100000 микросекунд», то есть ровно половина ядра.

Причина в том, что механизм лимитов и то, откуда программы читают размер памяти и число ядер, - разные вещи. Лимит живёт в отдельной подсистеме ядра, а привычные счётчики от неё не изолированы и продолжают показывать хост.

// Последствие прямое: среда исполнения, которая считает размер своих пулов «в долях от системной памяти», возьмёт за основу гигабайты сервера, вырастет за лимит - и ядро снимет процесс. Контейнер завершится с кодом 137, и в логах приложения не будет ничего: оно не падало, его убили. Я это воспроизвёл: контейнер с лимитом 128 мегабайт, попытка занять 400 - код выхода 137. Лечится либо явными настройками размеров, либо флагами, которые учат приложение читать настоящий лимит.

лимиты контейнера
живут в отдельной подсистеме ядра; привычные счётчики их не показывают
код 137 у контейнера
процесс не падал сам - его снял ядерный обработчик нехватки памяти

Как отвечать: «Средняя загрузка 12 на 4 ядрах, процессор простаивает на 70%. Что это?»

Скорее всего, машина упирается не в процессор, а в ввод-вывод. В Linux средняя загрузка считает и задачи, которые работают или готовы работать, и те, что стоят в непрерываемом ожидании - обычно диска или сетевого тома. Поэтому картина «загрузка улетела, процессор скучает» указывает на залипшее хранилище, а не на нехватку вычислений. Проверяю это тремя вещами: долей времени в ожидании ввода-вывода, статистикой по дисковым устройствам - там смотрю среднее время ожидания и утилизацию, - и списком задач в непрерываемом сне, потому что именно они и надули число. Отдельно проверяю сетевые монтирования: залипшая сетевая файловая система даёт ровно эту картину. И сразу оговорюсь про масштаб: двенадцать сравнивают с числом ядер, а не с сотней процентов. На четырёх ядрах это очередь втрое длиннее возможностей, а на тридцати двух то же число было бы спокойным днём. Профилировать процессор в такой ситуации бессмысленно.

Ответ объясняет особенность именно Linux, переводит разговор в конкретные инструменты и добавляет правило сравнения с числом ядер.

На чём валятся

  • Паникуют из-за нулевой свободной памяти: на моём сервере свободно 8%, а доступно 44%.
  • Сбрасывают кэш на рабочем сервере и получают всплеск чтений с диска.
  • Сравнивают среднюю загрузку с числом процессов или с сотней процентов вместо числа ядер.
  • Ставят контейнеру лимит и не говорят приложению, сколько ему можно: оно читает память хоста и получает код 137.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.

  1. #dvo_linux_resources1 / 5
    На 4-ядерной машине load average 12, при этом top показывает 70% idle. Что это значит?
    A)Часть задач ждёт диск или сеть: в Linux они тоже входят в LA
    B)LA суммируется по ядрам, 12 на 4 ядра — обычная картина
    C)top берёт idle с задержкой и при пиках занижает нагрузку
    D)LA считает потоки, а top — процессы, отсюда расхождение
    показать ответ и разбор
    +A)Часть задач ждёт диск или сеть: в Linux они тоже входят в LA

    // разбор: Load average в Linux — среднее число задач в состоянии R (бегут или готовы) плюс D (uninterruptible sleep, обычно ожидание ввода-вывода). Отсюда классическая картина: залип диск или сетевой том, CPU скучает, а LA улетает. Разбирают через iowait в iostat и список задач в D (ps -eo state,pid,cmd), а не профилированием CPU.

  2. #dvo_linux_resources2 / 5
    Рантайм в контейнере с лимитом 512Mi считает пулы от памяти хоста и падает по OOM. Почему он не видит лимит?
    A)Лимит применяется к пикам потребления, а не к видимой памяти
    B)Лимит контейнера мягкий: ядро режет процесс при нехватке на хосте
    C)Нужен ulimit -v, иначе лимит cgroup к процессу не применяется
    D)/proc/meminfo не изолирован: там память хоста, а лимит живёт в cgroup
    показать ответ и разбор
    +D)/proc/meminfo не изолирован: там память хоста, а лимит живёт в cgroup

    // разбор: Memory-лимит контейнера — это cgroup (memory.max), а /proc/meminfo и nproc показывают хост: procfs по памяти и CPU не изолирован namespace'ами. Рантайм, который берёт «половину системной памяти», видит хостовые гигабайты, упирается в лимит и получает OOM-kill с кодом 137. Лечится явными настройками размера кучи или флагами контейнер-осведомлённости, читающими cgroup.

  3. #dvo_linux_resources3 / 5
    top показывает у процесса %CPU 350%. Это баг мониторинга?
    A)Нет: top суммирует по ядрам, 100% = одно ядро
    B)Да, больше 100% быть не может — это явный сбой счётчика
    C)Это суммарная загрузка процессора за всё время жизни процесса
    D)Процент считается от одного ядра, и он уже упёрся в потолок
    показать ответ и разбор
    +A)Нет: top суммирует по ядрам, 100% = одно ядро

    // разбор: В top (режим Irix on) %CPU считается так, что одно полностью занятое ядро = 100%. Многопоточный процесс на 4 ядрах покажет до 400%. 350% значит «занимает примерно три с половиной ядра». Чтобы видеть долю всей машины (Solaris mode), в top жмут I. Так что 350% — не ошибка, а признак хорошего распараллеливания.

  4. #dvo_linux_resources4 / 5
    Сервис тормозит, CPU почти простаивает, но top показывает высокий %wa (iowait). О чём это и чем копать?
    A)iowait — это про нагрузку на сеть, копать надо tcpdump
    B)Высокий iowait однозначно означает физически сломанный диск
    C)Это время работы в ядре, лечится обновлением версии kernel
    D)Процессы ждут диск; смотреть iostat (%util, await)
    показать ответ и разбор
    +D)Процессы ждут диск; смотреть iostat (%util, await)

    // разбор: iowait (%wa) — доля времени, когда процессор простаивал, потому что все готовые задачи ждут завершения дисковых операций. CPU «свободен», но упор в диск. Диагностика: iostat -x 1 — если %util близок к 100 и растёт await, диск насыщен; iotop/pidstat -d покажут, кто грузит IO. Лечится оптимизацией паттерна доступа, кэшем, более быстрым диском — но не добавлением CPU.

  5. #dvo_linux_resources5 / 5
    Контейнеру дали лимит 0.5 CPU. По среднему он «не упирается в CPU», но у него регулярные всплески latency. Что происходит?
    A)0.5 CPU округляется до нуля, и процесс вообще не получает время
    B)CFS-квота троттлит: исчерпал квоту периода — ждёшь следующего окна
    C)Это сетевые таймауты внешних вызовов, к лимиту CPU не относится
    D)Планировщик Linux не умеет дробные лимиты и просто их игнорирует
    показать ответ и разбор
    +B)CFS-квота троттлит: исчерпал квоту периода — ждёшь следующего окна

    // разбор: Лимит CPU в cgroup — это CFS-квота: на период cfs_period_us (обычно 100ms) выдаётся cfs_quota_us времени (0.5 CPU = 50ms). Если процесс «съел» квоту за первые 30ms всплеска, его останавливают до конца периода, и запросы в это окно ждут. Средняя загрузка низкая, но p99 скачет. Смотрят nr_throttled/throttled_time в cpu.stat. Лечат поднятием лимита, увеличением периода или отказом от жёсткого лимита в пользу requests-планирования.

дальше

Теорию прочитали. Навык ставится повторением

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