Системный программист
0,0
рейтинг
22 апреля 2013 в 12:20

Разработка → В преддверии очередного релиза CRIU

Сегодня я хочу продолжить серию статей о проекте CRIU (Checkpoint/restore mostly in the userspace). Проекту чуть более года, а по возможностям он уже в плотную приблизился к подобной функциональности в OpenVZ.
Первая часть статьи расскажет о новой функциональности, которая появилась в CRIU за последние несколько месяцев. Вторая часть расскажет о нашем опыте внедрения новых технологий для улучшения процесса разработки.

Новая функциональность


Снапшот памяти и итеративная миграция


Киллер фича следующего релиза — итеративные снапшоты состояния процесса и, как следствие, итеративная миграция. В обоих случаях на каждой следующей итерации, сохраняется только та часть памяти, которая изменилась с предыдущего раза. В первом случае это позволяет сократить время и количество данных на диске. В случае миграции существенно сокращается время простоя (downtime) системы, потому что на первой итерации копирования памяти процессы не замораживаются.

Основная проблема была в реализации механизма, который позволит отследить какие регионы памяти изменились с предыдущего раза. Работает он достаточно просто. Есть возможность пометить все существующие страницы процесса как «чистые». В этом случае ядро запрещает запись в них и если кто-то попытается записать в такую страницу, будет вызвано исключение (pagefault). В результате ядро вернёт странице права на запись и пометит её, как «грязную» (PME_SOFT_DIRTY). Все свойства страницы можно прочитать из файла /proc/PID/pagemap pagemap2. Да, пришлось создать вторую версию файла, так как в формате первого кончились биты, причём часть из них, на самом деле, всегда нули. Именно их и удалось высвободить во второй версии.

Миграция без использования диска


Буквально в первые месяцы существования CRIU, к нам обратился товарищ из немецкого института, с просьбой сделать миграцию без использования диска. Зачастую, скорость передачи данных между машинами в несколько раз превышает скорость записи данных на диск, поэтому его просьба выглядела разумной. Меньше месяца назад эта функциональность была добавлена. Работает она следующим образом. На удалённом конце запускается сервер, которому передаются все данные, и он же инициирует процедуру восстановления.

Особенность здесь в том, что мы попытались по максимуму избежать лишнего копирования памяти. Ядро представляет ряд системных вызовов для этих целей (vmsplic, splice, etc). К примеру, память процесса можно послать через пайп (pipe), не сделав ни одного копирования.

Сигналы


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

Так же было и в этот раз. Можно было добавить ещё одну команду к ptrace (интерфейс, который используют отладчики), но казалось, что его применение слишком узкое, в то время как в Linux уже давно существовал системный вызов signalfd, который делал что-то очень похожее. С началом реализации, начали всплывать проблемы. В обычной жизни, процессы не знают из какой очереди они получают сигнал (очередей две, одна на процесс целиком и по одной на каждый тред), но в случае CRIU — это важно. Мы не можем восстановить сигналы одного треда другому. Так же хотелось подсматривать сигналы, но не забирать их из очереди, потому что процесс дампа не должен быть деструктивным. Обе эти проблемы не выглядели серьёзными, что-то подобное мы уже проделывали над другими объектами. Третья проблема заключалась в том, что ни один из форматов представления siginfo в пользовательское пространство не давал полной информации, достаточной для восстановления. До нас в ядре было два формата. Первый — это то, что мы получаем в обработчике сигналов, и он наиболее приближен к тому, что хранится в ядре, за одним исключением — в нем отсутствует тип объекта. Ядро просто обрезает тип перед тем, как передать его в пространство пользователя. Второй формат — это то что возвращает signalfd. В нем по косвенным признакам можно определить тип сообщения, но в некоторых случаях часть информации просто отсутствует. Решение этой проблемы очевидно — нужен третий формат, который будет возвращать siginfo в том же виде, в котором его хранит ядро.

Четвёртая ключевая проблема заключается в том, что дескриптор signalfd не привязывается к конкретному процессу, в то время как семантика файлового дескриптора предполагает, что после fork() он будет указывать на тот же объект, что и до форка. Это противоречие было добавлено первыми разработчиками signalfd и, для сохранения обратной совместимости, не может быть изменено. Для текущего интерфейса это противоречие ни на что не влияет, но вот когда мы решили расширять возможности signalfd, то начались проблемы. Всем хотелось сделать работу с дескриптор signalfd, максимально похоже на работу с другими дескрипторами, но из-за вышеописанного противоречия найти разумное решение так и не удалось. Я предпринял порядка 5-7 попыток и в результате пришлось сделать первоначальный вариант с ptrace-ом.

Конвертер из OpenVZ имиджей


В предыдущих статьях уже говорилось, что CRIU должен заменить существующий механизм чекпоинтинга в OpenVZ и конечно обратная совместимость должна быть сохранена. Скажу по секрету, что команда OpenVZ в данный момент работает над новой стабильной версией ядра. Как обычно, оно будет базироваться на ядре из следующей версии RHEL (7). Вместе с этим процессом ведётся разработка конвертера из OpenVZ образов в образы CRIU. Эта задача не столько сложная, сколько трудоёмкая.

Netlink сокеты, миграция TCP соединений, конвертация vdso


Так же в данный момент ведётся работа над восстановление vDSO библиотеки. Основная сложность в том, что библиотека предоставляется ядром, и механизм ее взаимодействия с ядром не фиксирован. Эта библиотека загружается динамически, так что и адреса функций могут изменяться от ядра к ядру. Мы решили, что проще всего будет создать прокси, которая будет выглядеть как старая библиотека, но вызывать функции из новой. Даже на этом пути не все гладко. К примеру, процесс мог зайти в код библиотеки и быть прерван обработкой сигнала. В обработчике сигнала процесс может делать что угодно и понять как он туда попал в общем случае невозможно. В LKML даже мелькнула мысль сделать “стабильную” библиотеку vdso, код которой не будет меняться. Пока мы не придумаем решение лучше, будем жить с прокси и надеяться что в коде vdso процесс не прервут.

До недавнего времени все TCP соединения использовали глобальный счетчик для выставления временных меток на пакеты (TCP timestamp). Счетчик этот сбрасывается при каждой перезагрузке. Эта схема мешала миграции TCP соединений с одной машины на другую. В следующем ядре Linux появится возможность выставлять смещение для каждого сокета в отдельности, что позволит CRIU мигрировать соединения.
Еще одной не большой фичей стала поддержка Netlink сокетов. Текущий интерфейс представляемый файлом /proc/net/netlink не дает достаточной информации, поэтому пришлось расширить socket diag для netlink сокетов.

Технологии


Непрерывная интеграция (англ. Continuous Integration)


Процесс разработки CRIU строится по образу и подобию разработки ядра Linux. Основные принципы, которым мы следуем:
  • Один коммит — одно логическое изменение. Каждый коммит — это одна законченная мысль. Такой принцип существенно облегчает процесс проверки (review) и повышает его качество.
  • Любой коммит не должен ломать билд, т е в любой точке проект должен компилироваться и все тесты должны проходить. Этот принцип помогает находить привнесённые проблемы (бисектить).

Первый принцип контролируется человеком, который просматривает изменения и заносит их в основной репозиторий. Второй принцип можно проверить только экспериментально. Обычно этот процесс называют «continuous integration», для автоматизации которого существует несколько решений. Мы выбрали популярный на сегодняшний день проект Jenkins. Установка и настройка не отнимает много времени. По результатам прошедших двух месяцев, мы можем смело сказать, что усилия были потрачены не зря. Не, билд мы ломаем крайне редко, но из-за большого количества прогонов юнит тестов, он поймал несколько багов, связанных с гонками ресурсов или стечением каких-то обстоятельств.

Технология будет работать по полной, когда мы начнем запускать все тесты (не только юнит), во всех вариациях (проверка обратной совместимости, проверка на не деструктивность дампа), на всех конфигурациях (две архитектуры, две версии ядра). Эта та работа, которую разработчику проделывать самому накладно.

Статические анализаторы кода


К статическим анализаторам кода я всегда относился скептически, по какой-то причине было мнение, что пользы они принесут не много, а вот время утилизировать помогут. Иногда в процессе работы случаются такие моменты, когда делать ничего не хочется и надо отвлечься. Я в такие моменты люблю попробовать что-то новое. Именно так мы начали использовать Jenkins и именно так, наш код был прогнан через clang-analyzer. Нельзя сказать, что мы получили какие-то сверхъестественные результаты, но пару багов на путях обработки ошибок он указал.

Вдохновленный полученными результатами, другой разработчик зарегистрировал наш проект на scan.coverity.com. Здесь движок несколько мощнее, чем у clang-analyzer-а, но и количество «ложных срабатываний» выше. Моё мнение о статических анализаторах такое: пользу они приносят, но приоритет их не сильно высокий. Если ваш проект хорошо покрыт тестами и багов они не находят, то можно и на статические анализаторы время потратить.

Ссылки


lwn.net/Articles/546966
lwn.net/Articles/531939
habrahabr.ru/post/152903
habrahabr.ru/post/148413
jenkins-ci.org
ru.wikipedia.org/wiki/CRIU
criu.org
Вагин Андрей @avagin
карма
53,0
рейтинг 0,0
Системный программист
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (10)

  • +2
    А задумывался ли кто-либо о применении CRIU на десктопе? Вместо того, чтобы закрывать приложение полностью, сохраняем его состояние, а потом открываем сразу вместе со всеми ранее открытыми документами. Эдакая мини-гибернация, избирательно для отдельно взятого приложения.
    • +2
      Да, мы думаем об этом, но сейчас все ресурсы брошены на «серверные» нужды. Если у вас есть желание, вы можете поресерчить, как дампить иксовые приложения.
      На затравку у нас есть небольшое видео, где дампится видеоплеер youtu.be/j4wlYY7lTDw. Правда тут он был запущен в vncserver-е. Таким же образом мы проверяли, что CRIU может дампить Open Office, Gimp. Вот тут есть небольшая инструкция, как это все делать criu.org/VNC
      • 0
        Подскажите, а как обрабатываются ситуации с многопоточными приложениями? Чаще всего приложение считывает системную информацию при старте и дальше использует её в работе. Соответственно, при «переезде» приложения с ноды с бОльшим количеством CPU/ядер на ноду с меньшим количеством CPU/ядер, приложение завершится с ошибкой? Или же есть какие-то решения в этой области?
        • 0
          Можете сформулировать проблему более конкретно. Количество потоков приложения может быть больше, чем количество ядер на машине, это достаточно обычное дело. Если посмотреть глубже, то сейчас количество ядер на машине, может меняться во время работы. На пример следующая команда выключает один из «процессоров».
          echo 0 > /sys/devices/system/cpu/cpu1/online
          Бывают ситуации, когда приложения привязываются к конкретным процессорам из соображения оптимизации. Таких приложения не много и если их отвязать от процессора, то они продолжат работать.
          • 0
            Бывают ситуации, когда приложения привязываются к конкретным процессорам из соображения оптимизации.

            Да, я именно это и имел в виду. Т.е. происходит ли смена маски (CPU affinity mask) в случае, если она не удовлетворяет условиям на новой ноде?
            • 0
              Пока никакой логики по этому поводу у нас нет, но есть общее правило: Если что-то не получается сделать так же как было, то мы требуем разрешение от пользователя сделать как-то по другому. Если вариантов решения (обхода) проблемы несколько, то пользователь может выбрать, какой использовать в конкретном случае.
              Процесс дампа и миграции не является деструктивным. Если что-то пойдет не так, то crtools выходит с ошибкой, а процессы продолжают свою работу.
  • –1
    «Замечание: это проект, разрабатываемый разными сумасшедшими русскими..." Линус Торвальдс
    Работая с Plesk каждый день — считаю, что он прав.
    • 0
      Прав в чем? В том, что мы сумасшедшие? Так все мы тут не без греха. Какой нормальный человек будет отвечать вам в третьем часу ночи. А вот в том, что это когда-то заработает, я думаю, уже никто не сомневается, оно уже работает!

      Что касается Plesk-а, поверьте мне, команда разработчиков CRIU не написала в нем ни одной строчки кода и он абсолютно чист от наших сумасшедших идей;). Я бы не судил о компании по одному продукту. Да и тот же плеск в последнее время идет на поправку.
    • 0
      Между прочим все же часть изменений в ядро от разрабов CRIU он принял, и надеюсь так и будет продолжать. Подтвердите что не вру, а то вдруг не до конца решилась история. Так что я думал про сумашедших это был некого рода комплимент.
      • 0
        Не врете. Принял и продолжает принимать. К слову большую часть интерфейсов удается сделать универсальными, т е не специфичными для CRIU и их не оборачивают в макрос про который говорил Линус.
        Вот тут можно ознакомиться с изменениями в ядре от CRIU команды: criu.org/Commits

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