Тестирование серверов с InSpec и Test Kitchen

Впервые эта статья была опубликована в блоге mkdev.me в 2016 году

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

Зачем нужно тестирование?

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

InSpec

Это и есть тот самый волшебный фреймворк, который мы будет использовать. На момент написания он еще сыроват, но работает шустро и имеет довольно богатый DSL – благодаря RSpec, на котором он основан. Как и в случае с RSpec, все ваши тесты представляют собой код на Ruby, так что можете смело использовать все возможности этого языка.

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

describe file(arguments...) do
  ...
end

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

describe file("/file.txt") do
  it { should exist }
end

Значение каждой такой проверки можно проверять разными способами.

be используется для сравнения значений с числом:

describe file('/proc/cpuinfo') do
  its('size') { should be >= 120 }
end

eq – сокращение от equivalent. Он просто проверяет на равенство. Обратите внимание, что он сравнивает без приведения типов, т.е. 2 = 2, но ‘2’ != 2.

describe sshd_config do
  its('Protocol') { should eq '2' }
end

cmp - тоже самое, что и eq, но с приведением типов:

describe sshd_config do
  its('Protocol') { should eq 2 }
end

include проверяет, есть ли значение в списке:

describe passwd do
  its('users') { should include 'my_user' }
end

match проверяет, есть ли какое значение в строке. Его особенность заключается в том, что он принимает регулярные выражения:

describe sshd_config do
  its('Ciphers') { should_not match '/cbc/' }
end

Так же вы можете объединить множество таких проверок в control. Плюс этого синтаксиса в том, что вы можете явно указать что именно вы тестируете. Это что-то в духе shared_examples в RSpec:

control "tmp" do
  title "Тестируем tmp папку"
  describe "Какое-то дополнительное описание"

  describe file('/tmp') do
    it { should exist }
    it { should be_mounted }
  end
end

Запуск тестов

InSpec поддерживает 4 способа запуска.

# 1. Локальный запуск, когда вы хотите протестировать собственную конфигурацию
inspec exec test.rb

# 2. Запустить тесты на сервере по SSH
inspec exec test.rb -t ssh://user@host

# 3. Запустить тесты на Windows сервере через WinRM
inspec exec test.rb -t winrm://admin@windhost

# 4. Запуск тестов в docker контейнере
inspec exec test.rb -t docker://conainer_id

Во всем этом есть одна проблема. Не смотря на то, что тесты есть, код все еще запускается и тестируется на реальном сервере. Допустив ошибку, вы можете что-нибудь сломать на нём или вообще вывести его из строя.

Kitchen Test

Kitchen Test представляет собой инструмент, который поднимет за вас виртуальную машину и установит туда ОС, в которой мы и будем проверять наше приложение. Он поддерживает различные платформы, инструменты и типы виртуализации, такие как Amazon EC2, Blue Box, CloudStack, OpenStack, Vagrant, Docker и т.п.

Что должно быть на нашей машине

В первую очередь Ruby, на котором и написаны все наши инструменты. Какая-то система виртуализации, я использую vagrant-libvirt, а так же Vagrant. Если вы хотите подробнее разобраться в различных способах виртуализации и выбрать что предпочтительнее использовать, вам сюда.

Test Kitchen устанавливается командой gem install test-kitchen. Как поставить и настроить остальные инструменты, вам придется найти самому, перейдя по ссылкам в предыдущем абзаце.

Давайте сразу заполним Gemfile минимально необходимым набором:

source "https://rubygems.org"
gem "test-kitchen"
gem "kitchen-vagrant"

Установим все с помощью комнды bundle install и подправим конфигурационный файл .kitchen.yml:

---
driver:
  name: vagrant

platforms:              # список ОС, в которых мы будем проверять
  - name: centos-7.2    # наши рецепты.
    driver:
      customize:
        memory: 2048    # настройки для данной ОС

suites:                 # список окружений
  - name: default       # имя окружения

Список созданных вам окружений можно посмотреть, набрав kitchen list.

Создадим описанную нами виртуалку:

kitchen create default-centos-72

Теперь вы можете зайти на нее с помощью kitchen create default-centos-72 и делать все там. Больше можете не бояться того, что вы угробите что-то на реальной железке :) Сейчас вы можете выполнять вашу настройку в созданной виртуальной машине и либо переносить ваши тесты на неё и запускать, либо сказать InSpec зайти по ssh и проверить. Доступ по ssh такой же, как и для всего, что поднял vagrant: ssh vagrant@localhost -p 2222.

Все это, конечно, здорово, но не очень удобно.

Test Kitchen + InSpec + Chef

Да-да, все это можно объединить вместе и получить очень даже удобную систему разработки. Что такое Chef и с чем его едят вы можете прочитать тут. Если для управления конфигурацией вы предпочитаете Puppet, Ansible или что-нибудь еще – используйте, это не принципиально. В рамках этой статьи мы напишем небольшой рецептик для Chef, устанавливающий Nginx, который и протестируем в нашей виртуальной машине.

Первичная настройка

В нашем Chef репозитории создадим тестовый кукбук:

chef generate cookbook cookbooks/nginx_test

Перейдем в папочку с кукбуком и заведет там Gemfile со следующим содержимым:

source "https://rubygems.org"
gem "inspec"
gem "berkshelf"
gem "test-kitchen"
gem "kitchen-vagrant"
gem "kitchen-inspec"

После чего установим гемы и инициализирует Kitchen:

bundle install
kitchen init

Test Kitchen создаст конфиг .kitchen.yml со своими настройками. Давайте сразу подправим его.

---
driver:
  name: vagrant # виртуализация

provisioner:
  name: chef_zero # кто будет выполнять команды. Тут может быть еще
                  # Puppet, Ansible и т.д.

verifier:
  name: inspec  # чем прогонять тесты

platforms:              # список ОС, в которых мы будем проверять
  - name: centos-7.2    # наши рецепты.
    driver:
      customize:
        memory: 2048    # настройки для данной ОС

suites:                 # список окружений
  - name: default       # имя окружения должно совпадать с папкой тестов
    run_list:           # run_list Chef
      - recipe[nginx_test::default]
    attributes:

Создадим описанную нами виртуалку:

kitchen create default-centos-72

Можете убедится, что она поднялась и работает, зайдя на нее при помощи kitchen login.

Пишем рецепт

Особо заморачиваться не будем, поэтому просто подключим уже существующий рецепт установки nginx и вызовем его:

# metadata.rb

...
depends 'nginx'
depends 'yum'
# recipes/default.rb

include_recipe 'nginx'

Теперь попросим berks подтянуть зависимости и выполним рецепт в виртуалке:

berks install
kitchen converge default-centos-72

Пишем тест

Тесты должны лежать в test/integration/имя виртуалки/. Если вы планируете использовать несколько тестовых фремворков, то нужно создавать еще подпапки с их названием.

# test/integration/default/test_spec.rb

describe package("nginx") do
  it { should be_installed } # пакет должен быть установлен
end

describe file("/etc/nginx/sites-available/default") do
  it { should exist }  # файл настройки должен существовать
end

describe command("curl localhost") do
  its("stdout") { should match "404 Not Found" }
  # при запросе nginx должен отдать нам 404
end

Время запустить наш тест!

kitchen verify default-centos-72

Удалить созданную машину можно так: kitchen destroy default-centos-72.

Хочу заметить, что, если у вас уже проделан весь этот фронт работ, вам не обязательно каждый раз сначала создавать машину, потом выполнять в ней рецепт и затем запускать проверку. Все эти 3 действия вы можете реализовать одной командой: kitchen test default-centos-72

Ну вот, собственно, и все. Мы научились создавать виртуалки для наших Chef рецептов и тестировать их с помощью InSpec. Удачи вам с вашим приложением :)

Я консультирую о том о чем пишу, связаться со мной можно через telegram @aladmit или по почте [email protected]

Подпишись на Tg канал или RSS, чтобы не пропустить новые статьи.
Заметки