Особенности тестирования технологии C/R в Linux

28cef3d1e1354f159c436db74d5b1379.png

В 2012 году Эндрю Мортон был пессимистично настроен в отношении будущего проекта CRIU (Checkpoint and Restore In Userspace), когда принимал первые изменения в Linux ядро для поддержки C/R (Checkpoint/Restore). Идея реализовать функциональность сохранения и восстановления запущенных процессов в пространстве пользователя выглядела сумасшедшей, а спустя 4 года проект не только жив, а всё больше вызывает интерес к себе. До старта проекта CRIU предпринимались попытки реализовать C/R в Linux (DMTCP, BLCR, OpenVZ, CKPT и т.д.), но и все они по разным причинам были обречены на провал в то время как CRIU стал жизнеспособным проектом. К сожалению от этого задача C/R в Linux не стала проще. В этой статье я расскажу об особенностях тестирования CRIU.

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

Сам по себе процесс разработки CRIU ничем не отличается от разработки ядра Linux: каждое новое изменение это одна законченная мысль. Все патчи приходят в рассылку criu@, где подвергаются ревью. Патчи прошедшие ревью добавляются в репозиторий ментейнером проекта. Хотя на стадии ревью и выявляется часть проблем в коде, но свести их к нулю не получится из-за количества сценариев и конфигураций. Поэтому для выявления деградаций мы запускаем тесты для каждого нового изменения. Гарант постоянного запуска тестов это автоматические рабочие тесты.

На ранних этапах разработки мы начали использовать функциональные тесты из тестового набора ZDTM (Zero DownTime Migration), которыми уже успешно тестировали in-kernel реализацию C/R в OpenVZ. Сейчас каждый тест из этого набора запускается отдельно и проходит 3 стадии: подготовка окружения, «демонизация» и ожидание сигнала (который сигнализирует тесту о том, что пора проверять своё состояние), проверка результата. Тесты условно разделены для две группы. Первая группа — это статические тесты, которые подготавливают некое постоянное окружение или состояние и «засыпают» в ожидании сигнала. Вторая группа — динамические тесты, которые постоянно меняют своё состояние и/или окружение (к примеру пересылают данные через TCP соединение). Если в 2012 году система юнит-тестирования CRIU насчитывала порядка 70 отдельных тестовых программ, то на сегодняшний день их количество увеличилось до 200. Но поистине ужасает количество комбинаций, которое требуется запустить для полноценного тестирования CRIU.

Основная конфигурация — запуск всего набора тестов на хосте, при котором каждая тестовая программа садится в определённую позу, процесс теста сохраняют и восстанавливают и потом просят проверить в той же позе он остался или нет. Следующей по важности конфигурацией является проверка, что C/R не только работает, но и после C основной процесс не поломался. Поэтому каждый тест нужно прогнать ещё и в варианте когда выполнена только первая часть (без восстановления) и проверить, что поза соблюдена. Это безресторный тест. Восстановленный процесс может оказаться в той же позе, но не пригоден к повторному C/R. Так появляется ещё одна конфигурация — повторный C/R. Потом появляются конфигурации со снапшотами, C/R в окружении пространств имён, C/R с правами обычного пользователя, C/R с проверкой обратной совместимости, проверка успешного восстановления на BTRFS и NFS (потому что эти ФС имеют свои «особенности»). Но помимо C/R для отдельных процессов, можно делать групповые C/R — сохранение группы процессов, когда все процессы находятся в одной позе и когда каждый процесс находится в своей позе.

CRIU поддерживает несколько аппаратных архитектур, сейчас это x86_64, ARM, AArch64, PPC64le и на подходе i386. Суровая реальность заставляет нас тестировать еще и несколько версий ядер: последний официальный релиз ванильного ядра, ядро RHEL7 (которое базируется на 3.11) и ветку linux-next. Длительность тестов небольшая (2–10 мин), но если учесть количество комбинаций существующих сценариев и возможных конфигураций, то получается внушительная цифра.

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

Мы используем две CI системы: Travis CI используется для проверки компиляции на всех поддерживаемых аппаратных архитектурах. Так как Travis CI использует ядро ниже версии 3.8, в котором отсутствует большая часть патчей, необходимых для CRIU, то Travis не подходит для запуска тестов и дополнительно мы используем широко известный Jenkins CI.

Выводы

  • сборка, тестирование и измерения покрытия кода должны быть автоматизированы
  • много тестов не бывает, у нас соотношение полезного кода к тестовому кода примерно 1.6 (48 KLOC vs 30 KLOC) и есть куда стремиться
  • если количество конфигураций для тестирования огромно, приоритезируйте
  • рабочих рук как всегда не хватает, приходите к нам в CRIU, а?

Проект CRIU был начат в 2012 году инженерами компании Virtuozzo, но позднее его поддержали и другие компании, заинтересованные в создании технологии C/R в Linux.

© Habrahabr.ru