Недавно почитывал последний выпуск Technology Radar и нашёл вот какую интересную штуку в разделе новых техник программирования: «разработка контейнеров по TDD». Мдя. Ментально я пока ещё не могу провести соединительную линию между Докером и TDD, но инструменты, которые там упомянуты, оказались достаточно интересными.
Первый — serverspec. С его помощью можно прогонять локальные и удалённые сервера-контейнеры по набору BDD-подобных юнит-тестов. Тулза выглядит вполне зрелой и продуманной, поддерживает не только Linux, и всё было бы хорошо, если бы один огромный косяк (возможно, только для меня): serverspec написан на Ruby, и значит, что он абсолютно не совместим со стеком, в котором я обычно работаю.
Вторая тулза — goss
— оставляет впечатление небольшого швейцарского ножика. Вроде и то немного умеет, и это. Обычно я к таким инструментам отношусь настороженно, но… goss какой-то странно прикольный, так что на него хочется посмотреть поближе.
Что такое goss
goss
— это инструмент, который проверяет, соответствует ли локальный сервер или контейнер спецификации. В спецификацию может входить много чего: от проверки запущенного процесса и установленного пакета, до поиска пользовательских аккаунтов и строк в файлах и HTTP ответах. К глубокому огорчению некоторых категорий граждан, goss
работает только на никсах. Муа-ха-ха.
Быстрый пример
Я собрал простенький Vagrantfile, чтобы по-быстрому развернуть виртуальную Убунту с goss
внутри. В такой и поиграться не страшно, и удалить не жалко:
1 2 3 4 5 6 7 8 9 |
# -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| config.vm.box = "ubuntu/xenial64" config.vm.provision "shell", inline: <<-SHELL curl -fsSL https://goss.rocks/install | sh SHELL end |
Задаём спецификацию сервера
Как и большинство современных инструментов, goss и его спецификации конфигурируются через YAML файлы. Их можно было бы и руками насоздавать, но есть путь попроще: отсканировать нужные фичи эталонной машине и создавать спецификацию на их основе.
Например, на моей Убунте есть пользователь по имени ubuntu
, запущен sshd
сервис, а порты 80
и 443
никем не заняты. Это выглядит как замечательный набор качеств для любой созданной в будущем виртуальной машины, так что я попрошу goss создать спецификацию на их основе, чтобы потом погонять её на остальных серверах командой goss validate
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
goss add service sshd #Adding Service to './goss.yaml': # #sshd: # enabled: true # running: true goss add user ubuntu # Adding User to './goss.yaml': # #ubuntu: # exists: true # groups: #... goss add port 80 443 # ... #tcp:80: # listening: false # ip: [] #... |
Результирующий goss.yaml
файл пришлось немного почистить, потому что мне совсем не интересно проверять, в каких группах состоит пользователь ubuntu, или на каких именно сетевых интерфейсах не должны быть задействованы порты 80 и 443. В итоге файл получился каким-то таким:
1 2 3 4 5 6 7 8 9 10 11 12 |
port: tcp:80: listening: false tcp:443: listening: false service: sshd: enabled: true running: true user: ubuntu: exists: true |
Проверяем, соответствует ли машина спецификации
Тут вообще всё просто:
1 2 3 4 5 |
goss validate #..... # #Total Duration: 0.008s #Count: 5, Failed: 0, Skipped: 0 |
Проверяем, соответствует ли контейнер спецификации
goss
поставляется вместе с братом-акробатом dgoss
, который, оказывается, знает, как тестировать Docker контейнеры. Инструкцию к нему очень просто запомнить:
docker run ubuntu sleep 1
запускает контейнер, используя свежайший образ Убунты, ждёт одну секунду и завершается, аdgoss run ubuntu sleep 1
проверяет этот же контейнер по ближайшемуgoss.yaml
.
В моём случае dgoss
честно признался, что контейнерная Убунта спецификации никак не соответствует:
1 2 3 4 |
sudo dgoss run ubuntu sleep 1 # ... # Count: 5, Failed: 3, Skipped: 0 # ... |
Так как, в зависимости от того, прошли тесты или нет, и dgoss
, и goss
возвращают код ошибки 0 или 1, их можно запросто подключить к своей CI системе и тестировать, например, Dockerfile’ы после каждого коммита.
Любопытные побочные эффекты
Если честно, то меня не сильно впечатляет просто возможность тестировать серверы по конфигурации. Но в goss
нашлась и пара побочных эффектов, которые мне действительно понравились.
Во-первых, команда goss validate
умеет ждать:
1 |
goss validate --sleep 5s --retry-timeout 1m |
Тут мы запускаем тесты раз в пять секунд до тех пор, пока они не выполнятся успешно, либо пока не пройдёт минута. И это очень круто, потому что если мой контейнер с веб сервисом должен запуститься только после контейнера с Postgres, я могу сделать goss.yaml, который убедится, что на постгресовском порту 5432 творится что-нибудь вразумительное:
1 2 3 |
FROM... # ... CMD goss validate --sleep 5s --retry-timeout 1m && start-my-service.sh |
Во-вторых, мы можем использовать goss
в проверках состояния Docker контейнеров или Kubernetes зондах на живучесть и готовность. В одном из старых постов я проверял здоровье контейнера, делая запрос на его localhost через curl
: ответил — здоров. Не ответил — перегружаем:
1 2 3 4 5 6 |
#... healthcheck: test: curl -sS http://127.0.0.1 || exit 1 interval: 5s timeout: 10s #... |
Вместо curl и exit можно было бы использовать goss
и положить все мыслимые проверки в него:
1 2 3 4 5 6 |
#... healthcheck: test: goss -g /goss.yaml validate interval: 5s timeout: 10s #... |
Кстати, постоянно запускать goss validate
не обязательно. Можно запустить его как сервис — goss serve
— и спрашивать по http://127.0.0.1:8080/healthz
, всё ли там хорошо. URL вернёт результаты тестов и 200 OK
статус код, если всё прекрасно, и 503 Service Unavailable
и грустные результаты, если всё плохо. И Docker и Kubernetes знают, как интерпретировать HTTP коды, так что их сразу можно натравливать на этот URL.
1 2 3 4 5 |
curl -v localhost:8080/healthz #> GET /healthz HTTP/1.1 # ... #< HTTP/1.1 200 OK # ... |
Мораль
Хотя goss
и выглядит, как адекватный инструмент для проверки спецификаций сервера, я больше впечатлён перспективой использовать его для проверки живучести Kubernetes и Docker контейнеров. Серьёзно, я уже сейчас готов положить какой-нибудь goss.yaml
во вспомогательный контейнер какого-нибудь Kubernetes pod’а, натравить на него readiness probe, и радоваться, видя, как Kubernetes направляет траффик в обход pod’а, потому что тот ещё недостаточно запущен. Конечно, всего этого можно было бы достигнуть и обычным bash скриптом, но зачем, если есть уже готовые инструменты.
Плагиат! https://codeblog.dotsandbrackets.com/unit-test-server-goss/
А то, что цель плагиата живёт в том же домене, идёт с тем же URL (+ ‘-ru’ в конце) и подписана тем же автором не наводит ни на какие мысли, нет?