image
Часто бывает, когда что-то не работает. И никто не хочет, чтобы что-то не работало по его вине. В контексте больших инфраструктур и распределенных приложений ошибка конфигурации может быть фатальной.

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

Статья будет интересна командам, которые практикуют DevOps или SRE, ответственным Dev, и прочим хорошим людям.

Следует сказать, что реализовывать концепцию Infrastructure as Code можно и без тестирования. И даже будет работать. Но разве есть предел совершенству?

Тестирование к нам пришло из мира разработки, тестирование – это целая огромная эпопея, которая включает в себя такие варианты реализаций:

  • Юнит тесты (тестируют код) — есть функция, которая принимает на вход один параметр, проводит вычисления и возвращает результат. Юниты — проверка того, что результат соответствует ожидаемому, грубо говоря, что 1 == 1.
  • Интеграционные тесты (тестируют взаимосвязь компонентов) — есть приложение, которому необходима база для корректной работы, и в ней табличка `users`. Этот тест проверит что есть база, что есть табличка, что все готово к работе.
  • Системные тесты (тестируют приложение в целом) — есть приложение, оно должно конвертировать файл. Тест проверяет, что приложение конвертирует, и результат нас устраивает.
  • Дымовые тесты (имитируют поведение пользователя) — тестирует, что пользователь может открыть в браузере сайт, сделать log in, загрузить изображение, и сконвертировать его.

И тут может прийти лживое осознание, что в нашем мире Ansible/Chef/Puppet/Salt все очень примитивно, все очень просто, и в экосистеме наших тулзовин нету реализации чего-то подобного.

А вот как-бы не так.

Все это есть, все это используют уже давно и успешно.

Чем тестировать


Уточню сразу, что мы не будем рассматривать высокоуровневое программирование для Configuration management. Например, если вы написали свой Ansible модуль, или Chef LWRP – ваших знаний достаточно, чтобы понять как правильно тестировать ваше творение.

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

Везде будем решать одну задачу для примера: мы должны убедиться в том, что пакет 'apache2' у нас установлен, и сервис 'apache2' запущен и работает. В конце проверяем, что порт 80 кто-то слушает.

ServerSpec


image

Первый фреймворк для тестирования, который мы рассмотрим — Serverspec.

Пример использования:

require 'spec_helper'

describe package('apache2') do
  it { should be_installed }
end

describe service('apache2') do
  it { should be_enabled   }
  it { should be_running   }
end

describe port(80) do
  it { should be_listening }
end

InSpec


image

Второй фреймворк — InSpec.
Синтаксис идентичный Serverspec.

Testinfra


image

Третий фреймворк – Testinfra.

def test_apache_is_installed(Package):
    apache = Package("apache2")
    assert apache.is_installed

def test_apache_running_and_enabled(Service):
    apache = Service("apache2")
    assert apache.is_running
    assert apache.is_enabled

def test_apache_port(Socket):
    sock = Socket("tcp://80")
    assert sock.is_listening  

Goss


image

Четвертый фреймворк – Goss.

package:
  apache2:
    installed: true
service:
  apache2:
    enabled: true
    running: true
port:
  tcp:80:
    listening: true

Serverspec/InSpec/Testinfra/Goss?


Как видно по синтаксису – есть из чего выбрать. Кому что больше нравится, то лучше всего и использовать. Например, если у вас Chef – отлично ляжет InSpec или Serverspec. Если Ansible или Salt – круто подойдет Testinfra или Goss.

Все отличия – это наличие или отсутствие готовых модулей, первым делом необходимо подготовить список необходимых требований для фреймворка и искать тот, в котором есть все что нужно (или почти все).

Окей, выбрали. Что же именно тестировать?

Что тестировать


Несколько недель назад мы общались в нашем DevOps Community по поводу того, что именно нужно тестировать, нужно ли вообще что-то тестировать, сколько лет лететь до Trappist-1 и так далее.

Например, ctrlok считает что тестировать декларативные штуки не правильно. И я с ним согласен.

Оговорюсь, что точно не нужно тестировать:

  • Разные декларативные штуки (наличие пакета, версию пакета, присутствие файла) – обо всем этом позаботится ваш Configuration management.

Что нужно тестировать:

  1. Вещи, которые когда-то/недавно уже ломались
  2. Вещи, которые потенциально могут поломаться
  3. Вещи, которые кто-то (коллега) может поломать из-за незнания
  4. Вещи, без которых ничего точно работать не будет

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

Для того, чтобы правильно оценить приложение и подготовить его для тестирования – нужно думать как тестировщик. Или, например, проконсультироваться у команды QA.

Во время общения необходимо продумать основные тест-кейсы и попытаться покрыть все из них.

Практический пример (на нашем приложении):

  • Конвертация из pdf в html
  • Конвертация из html в pdf
  • Конвертация из doc/docx в pdf

В нашем случае отдельные пункты – это отдельные модули приложения, именно так мы их и будем тестировать.

Давайте тестировать!


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

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

# Validate binary exists and working
describe bash('pdf2htmlEX --version') do
  its('stderr') { should match /pdf2htmlEX version 0.14.6/ }
  its('exit_status') { should eq 0 }
end 

Теперь давайте протестируем, что этот бинарник реально умеет конвертировать тестовый документ, и результат нас устраивает:

# Try to convert and validate result html
describe bash('pdf2htmlEX /opt/test/pdf-sample.pdf /tmp/pdf-sample.html') do
  its('stderr') { should match /Working/ }
  its('exit_status') { should eq 0 }
end

describe file('/tmp/pdf-sample.html') do
  it { should exist }
  its('content') { should match /<!DOCTYPE html>/}
  its('size') { should eq 267189 }
end

В данном случае, если тест пройдет успешно – мы можем четко декларировать, что модуль приложения, который отвечает за конвертацию из PDF в HTML работает. Мало того, он работает корректно, нас устраивает результат его работы.

Сложно и долго делать такой тест? Не думаю.

Сколько это сэкономит денег бизнесу? У каждого своя цена ошибки.

Benefits


Стоит сказать, что тестировать окружения мы начали не сразу. Зачем тратить время на такие вещи – это же очевидно, что все мы супермены, и пишем код для Configuration Management сразу без ошибок?

И такой подход работает, когда DevOps команда состоит из 1-2 человек. В таком случае каждый инженер знает как должно работать приложение, знает как готовить для него окружение, и как его «потыкать» — посмотреть, что оно будет работать на площадке, которую он только что подготовил.

Все заканчивается тогда, когда в production среде все должно работать, но не работает. Начинается debug процесс руками на проде или откаты-роллбеки, а каждая минута очень дорого стоит для бизнеса.

Именно поэтому тестирование инфраструктуры – это инвестиция в будущее.
Теперь, когда что-то ломается – мы абсолютно уверены, что наша часть работы была сделана на 100%, и с нашей стороны все вопросы закрыты.
Поэтому мы и уверены в том, что только что развернули.

Выводы


Все мы работаем с приложениями, которые очень часто меняются в очень динамичном мире благодаря нашим же правильно настроенным процессам – CI  и CD. Мы все очень серьезно относимся к работе самих приложений, тестируя со всех сторон одно и тоже. И в это же время халатно относиться к инфраструктуре, на которой это приложение работает – не очень разумно. Тем более, это чревато большими последствиями.

Для себя мы решили, что будем тестировать в таких случаях:

  • если в модуле системы есть сложная логика (комплексность)
  • если модуль уже сейчас часто ломается (доступность)
  • если нужно передать специфичные знания (коммуникация)
  • если ошибка будет стоить нам больше Х денег (бизнес-ценность)

Основным посылом было донести всю важность тестирования инфраструктуры и побудить использовать приведенные инструменты если не прямо сейчас, то в ближайшем будущем.

А сколько процентов вашей инфраструктуры покрыто тестами?
Поделиться с друзьями
-->

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


  1. tytar
    09.03.2017 15:42
    +4

    Я могу грубо ошибаться, но кажется дымовые тесты они же smoke testing это самый минимальный тест, если код не прошел, то дальше нет смысла его тестировать. А у Вас они описаны так, что обычно тестируют уже сами тестировщики.


    1. mukolaich
      10.03.2017 12:07

      Самый минимальный тест — это unit, набор всех юнит тестов (порядка нескольких тысяч) выполняется секунды. Дальше по времени выполнения идут интеграционные и системные (их тоже много), выполняются около минуты.

      Смоки делятся на 2 категории: Тестирование сборки и Приемочные тесты. У нас это выглядит как последовательный автоматизированный набор тест-кейсов, которые проходят примерно 30 минут. Если все ок — дальше заходят manual QA и нажимают кнопочки, прорабатывая не автоматизированные кейсы.

      Самые длинные — это регресионные тесты (порядка нескольких часов). Это детальное тестирование всего, что должно работать в принципе.

      Такой подход позволяет нам быть уверенными в релизе, и деливерить максимально качественный продукт за минимальные сроки :)


  1. popstas
    09.03.2017 20:52
    +1

    Для меня тема тестирования ansible ролей и плейбуков сейчас очень интересна, поэтому задам пару вопросов:

    1. Знаете ли вы репозитории, где можно посмотреть примеры адекватных тестов, без проверки того, что ansible уже проверяет?

    2. Каким фреймворком пользуетесь вы для тестирования (kitchen, molecule, самописный)?

    3. Используете ли вы тесты для проверки продакшена, или только на CI? Если используете, то запускаете ли вы тесты периодически (как healthcheck) или только сразу после разворачивания?

    4. Чем из перечисленного тестируете вы? Мне по читаемости больше нравится goss, но мне почему-то кажется, что выживет testinfra.


    1. mukolaich
      10.03.2017 12:14
      +1

      1. На github.com есть отличные примеры открытый ролей, где проводится тестирование конечного результата. Следует понимать, что универсальных решений не существует, то что подходит всем — может не подходить вам. Именно поэтому и была написана статья — оцените, что должна делать Ваша роль или плейбук — и протестируйте результат.

      2. У нас построен глубокий CI для написания Configuration management, и тулзы которые Вы указали — там есть.

      3. Конечно используем, т.к. мы деплоимся достаточно часто, и декларируем хорошее качество — у нас каждую ночь проходят глубокие регресионные тесты, с кастомными интеграциями типа: упал тест -> создается тикет в Jira.

      4. Для Ansible мы используем Testinfra, для Chef — Inspec.


    1. kharkevich
      13.03.2017 13:31

      https://molecule.readthedocs.io
      Вполне достойный инструмент для проверки ansible ролей и плейбуков


      1. popstas
        13.03.2017 15:56

        Да, я им и пользуюсь, но подозрительно мало инфы о нем.


        1. kharkevich
          14.03.2017 15:40

          Документация, на мой взгляд, — ок.
          Использование +- понятное.
          Да и последний коммит был меньше суток назад — вполне живой проект.


          1. popstas
            14.03.2017 17:01

            Да, как пользователь я вижу, что проект живет. Если полазить по ишьюсам, то становится понятно, что metacloud сейчас активно пишут 2-ю версию molecule, там будут сценарии, которых мне сейчас не хватает.