Хабрахабр

[Из песочницы] Настройка ClickHouse для интеграционного тестирования в gitlab-ci

У нас был сервис на golang, отдельный топик kafka, clickhouse, gitlab-ci и падающий пайплайн, протухший ssh-ключ и вот это вот все, а еще сезон отпусков, жуткие ливни в городе, сломавшийся ноутбук, алерты по ночам, и горящий прод. Не то, чтобы это все было нужно для этой статьи, но раз показываешь типичные будни тестировщика, то иди в своем намерении до конца. Единственное, что меня беспокоило — это p0. В мире нет ничего более отчаянного, мрачного и подавленного, чем тестировщик, который пропустил это на прод. Но я знала, что довольно скоро я в это окунусь.

Зачем все это?

Cуществует распространенная связка сервисов — сам сервис, который что-то делает, — и база данных, в которую эти результаты записываются. иногда это происходит напрямую, то есть “сервис — база”. В моем случае запись происходит через посредника, то есть “сервис — очередь — база”. 

Они просто не могут там не появиться. Итого, есть несколько элементов, и граница этих элементов — выход одного и вход другого — это то самое место, где появляются проблемы.

Или уже более грустный пример — база не крашится, но в логах ошибки записи данных в базу нет. Яркий пример: в сервисе поле price обрабатывается как float32, в базе оно настроено как decimal(18, 5), подаем с выхода сервиса на базу максимальное значение float32 в качестве тесткейса — ой, база не отвечает. Или запись проходит, но с потерей данных или с искажением: поле выходит из сервиса как float64, а записывается как float32.  просто в базу данные перестают наливаться.

Поле уже давно реализовано на проде, но вот необходимо его отредактировать. Или в процессе жизненного цикла сервиса решили, что надо поменять тип того или иного поля. Хоба, что-то опять пошло не так. И конечно же поменяли это только в одном месте.

Задача

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

Выход: интеграционные тесты! 

Реализация и трудности 

Где ломать?

Есть dev-окружение: жутко нестабильное и обычно используется разработчиками как песочница. Там творится хаос и анархия, характерные для жесткого бекенда. 

и еще это окружение часто обновляется. Есть test-окружение или qa-стенд: настроено уже получше, за ним даже следят devops, но пока их не пнешь, ничего не произойдет. а еще чаще там что-то сломано. 

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

Правильно, создавать свое! Так что же делать с окружением, когда оно или нестабильное, или боевое?

Что делать с базой?

Базу можно запускать несколькими способами.

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

На первой же успешной попытке положить подобную базу, загрустил и ci. Во-первых, можно поднять покостылить clickhouse-server с нужными настройками, раскатать на нем необходимые sql и общаться с ним посредством clickhouse-client. Скажем так, до меня до сих пор остается загадкой, почему оно вообще запустилось. тесты зафейлились, сервер не потух и продолжил работать. Не советую этот вариант. (оно само, я ни при чем).

Clickhouse для старта нужен config.xml с настройками. Удобный вариант из коробки — использование docker образа.
Скачиваем нужную версию к себе на машину. Указываем в нем, что хотим скопировать config.xl в папку, докидываем другие требуемые конфиги. Подробнее тут
Для переиспользуемого образа клика надо создать правильный dockerfile. Обязательно копируем скрипты для разворачивания своей базы. 

Клик работает на 8123 по http и на 9000 по tcp. Так как к образу мы будем обращаться извне, то надо открыть те порты, по которым будем общаться с кликхаусом.

Получаем следующий dockerfile:

From yandex/clickhouse-server Expose 8123
Expose 9000 Add config.xml /etc/clickhouse-server/config.xml
Add my_init_script.sql /docker-entrypoint-initdb.d/

Как закинуть образ в ci? 

Чтобы с docker-образом как-то работать в ci, его надо там как-то вызвать. 

Только вот docker-образ клика весит под 350мб. Можно закоммитить и запушить образ в свой репозиторий и в рамках запуска тестов выполнять docker run с нужными параметрами. неприлично такие файлы держать в git.

Можно использовать хранилище образов docker registry
Считаем, что в нашем проекте он уже есть и используется. Кроме того, если один и тот же docker-образ нужен на разных проектах (например, разные сервисы пишут в одну и ту же базу), то тем более так делать не стоит. Поэтому логинимся, собираем docker-образ и пушим его туда. 

docker build -t my_clickhouse_image .
docker login my_registry_path.domain.com
docker push my_clickhouse_image

Вжух и наш образ улетел в registry. Обязательно указываем тег при сборке!

База готова.

Подробнее про registry тут

Что делать с ci ?

Как в рамках одного шага запустить и свой сервис, и базу? 

Если с сервисом работать как с docker-образом, да и вообще весь .gitlab-ci.yml работает только с ними, то все просто. 
Существует приблуда dind — docker-in-docker. Все зависит от того, как у нас запускается и используется сервис. Она указывается как основной сервис, с которым работает ci, и позволяет и докером полноценно пользоваться, и вообще не напрягаться.

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

image: docker:stable
services:
- docker:dind stages:
- build …
- test-click
...
- test
- release … test-click: variables: VERY_IMPORTANT_VARIABLE: “its value” before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - docker pull My_Service_Image - docker pull My_ClickHouse_Image - docker run -FLAGS My_ClickHouse_Image - docker run My_Service_Image /path/to/tests

В официальном докере докера указывается, что не рекомендуется использовать dind, но если очень надо…

Тут и начинается магия
Для этого нужно использовать базу как сервис. В моем проекте сервис надо тестировать через запуск бинарника. Даже приведены примеры настроек mysql, postress и redis. Официальная документация gitlab-ci приводит использование контейнера с базой в качестве примера самого распространенного варианта использования docker-контейнера в ci. Но мы же не ищем легких путей, нам нужен clickhouse.

Обязательно указываем alias. Подключаем базу! То есть, будет непонятно, как именно к ней обращаться. если его не указать, то базе будет приписываться рандомное имя и рандомный ip. С alias такой проблемы не будет — в коде тестов обращение будет выглядеть как, например, по хттп http://my_alias_name:8123.

для скачивания образа необходимо выполнить docker login и docker pull, только ci не знает, что такое docker — надо его установить. Для тестов все так же требуется образ базы, который мы старательно запушили в registry.

Итоговый код для шага в gitlab-ci.yml:

Integration tests:
Services:
- name: my_clickhouse:latest alias: clicktest
Stage: tests
Variables:
Variables_for_my_service: “value”
Before_script:
- curl -ssl https://get.docker.com/ | sh
- docker login -u gitlab-ci-token -p $ci_build_token my_registry_path.domain.com
Script:
- ./bin/my_service &
- go test -v ./tests -tags=integration
Dependencies:
- build

Профит

  • У меня есть работающая связка сервис-базка.
  • В рамках автотеста легко обратиться к базе — просто по alias.
  • Обнуляю записи и настройки базы в рамках setup теста, вызываю работу сервиса, он пишет в базу, обращаюсь к базе, смотрю, что база не отвалилась, смотрю, что пришло, валидирую. накидываю побольше тестов.
  • Можно ручками не тестировать!

Результаты

Казалось бы, пара строчек настройки в gitlab-ci. Собрать docker образ — просто. Запустить базку локально — просто. у меня за сутки появилась интеграция с первыми тестами, которые нашли проблемы. Но попытки запустить в это в ci обратились в неделю боли и безысходности. А теперь и в недели боли и безысходности разработчиков, которым придется чинить все, что они там напрограммировали.

Что мы сумели сделать?

  • Мы настроили контейнер с clickhouse.
  • Запушили контейнер в локальное хранилище.
  • Научились подтягивать этот образ в шаг ci.
  • Запустили его в раннере.

Легко отправили данные в базу и обратились к ней из теста.

Автоматизация — это довольно простой способ избавить себя от рутины ручного протыкивания интеграции.

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть