Русский
Русский
English
Статистика
Реклама

Gitlab ci

Новые ограничения в использовании Docker Hub и как GitLab реагировал на их ввод

30.11.2020 22:22:08 | Автор: admin

Ни для кого уже не новость, что начиная с 2 ноября 2020 года Docker Hub ввел ограничения на скачивание образов: для анонимных пользователей он будет равен одной сотне за шесть часов, а для авторизованных пользователей будет зависеть от уровня подписки.

В компании GitLab мы очень внимательно следили за этими изменениями, так как они затрагивали практически всех наших пользователей и клиентов. Сегодня я хотел бы рассказать о том, какие изменения мы внесли и как адаптировались к новым правилам.

Что же произошло?

Публичный регистр контейнеров Docker Hub очень широко и часто используется сообществом DevOps для достижения разнообразных целей: запуска CI/CD задач, стандартизации процессов, выката контейнерных приложений как в sandbox, так и в production. Как только мы узнали о введении новых ограничения по количеству запросов, мы начали анализ новых правил, чтобы понять, как они повлияют на работу наших пользователей, и как мы можем почем решить возможные проблемы.

Согласно новым правилам после 100 запросов за 6 часов с одного клиентского IP адреса каждый новый docker pull вернет ошибку 429 - too many requests, что несомненно приведет к сломанным CI/CD конвейерам, невозможности выкатить приложения и целому букету ошибок в ваших Kubernetes кластерах. Вполне понятно, что этот лимит может быть очень быстро достигнут, особенно если все задачи выполняются с одного и того же GitLab Runner агента, или если команда из нескольких инженеров работает с одного и того же публичного адреса.

Аутсорсинг Dependency Proxy

Как отметил мой коллега Tim Rizzi "Нам следует срочно всем рассказать о Dependency Proxy", который изначально создавался для проксирования и кэширования образов Docker Hub. Этот функционал существует в GitLab уже довольнейший давно (начиная с версии 11.11), но до сегодняшнего дня был доступен только для пользователей Enterprise подписки уровня Premium. Перед продуктовой командой встал вполне резонный вопрос: "Стоит ли нам вынести Dependency Proxy в open source версию продукта, помогая таким образом широкому сообществу пользователей минимизировать проблемы из-за новых ограничений Docker Hub?"

Не вдаваясь в подробности, ответ на этот вопрос был ДА. При принятии решения, в какой уровень подписки должен попадать тот или иной функционал продуктовая команда GitLab всегда руководствуется вопросом "Кто является целевым пользователем?". Согласно этому принципу те возможности, которые чаще всего запрашивает индивидуальный участник или разработчик, попадают в Core или Open Source версию продукта. Скачивание образов с Docker Hub вполне соответсвует этому описанию. Более того аутсорсинг Dependency Proxy поможет большому количеству разработчиков повысить надежность и производительность их CI/CD конвейеров.

Как результат, начиная с версии 13.6 вышедшей 22 ноября 2020 года, проксирование и кэширование образов в GitLab стало абсолютно бесплатным для всех наших пользователей!

Что дальше?

К сожалению, идеальные решения не появляются мгновенно. Мы как никто другой знаем, что нашему продукты есть куда расти, поэтому активно работаем над улучшением функционала Dependency Proxy и уже в ближайших релизах планируем добавить новые возможности / решить имеющиеся проблемы:

  • 13.7 (22 декабря, 2020)

    • gitlab-#11582 сделает возможным использование Dependency Proxy для приватных групп в GitLab (сегодня работает только для публичных проектов)

    • gitlab-#241639 позволит использовать закэшированный образ даже если Docker Hub недоступен. На сегодня это невозможно, так как даже при наличии закэшированного образа, его манифест всегда скачивается из конечного регистра

    • gitlab-#21619 добавит новый параметр pull_policy в YAML описании CI/CD задач, позволяющий разработчикам самим указывать политику скачивания контейнера (always, if-not-present, never) вместо того, чтобы полагаться на настройки GitLab Runner

    • gitlab-runner-#26558 позволит конфигурировать GitLab Runner с набором политик для скачивания образов (always, if-not-present)

Мониторинг ограничений

Решения обозначенные выше, а также в блог посте моего коллеги Steve Azzopardi, помогут упростить работу с новыми ограничениями, но не избавят от них на все 100%. Поэтому мы также выработали набор советов и инструментов, цель которых - помочь широкому сообществу адаптироваться к новым лимитам за счет их мониторинга.

Как проверить текущее значение лимита?

Документация Dockerрекомендует использовать HTTP запрос для проверки текущего значения ограничений запросов в Docker Hub.

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

$ IMAGE="ratelimitpreview/test"$ TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$IMAGE:pull" | jq -r .token)$ echo $TOKEN

Следующий шаг - симуляция запросаdocker pull. Вместо использования методаGETотправляемHEADзапрос (он не учитывается при подсчете ограничений). Ответ на этот запрос содержит параметрыRateLimit-Limit и RateLimit-Remaining.

$ curl --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/$IMAGE/manifests/latest

В примере ниже количество запросов ограничено2500, из которых2495еще доступны.21600определяет шестичасовой период (в секундах)

RateLimit-Limit: 2500;w=21600RateLimit-Remaining: 2495;w=21600

Автоматизация проверки лимитов

Michael Friedrich, один из евангелистов GitLab, поделился готовым решением на Python для проверки ограничений. Проект включает подробную документацию по установке и использованию

$ python check_docker_hub_limit.py --helpusage: check_docker_hub_limit.py [-h] [-w WARNING] [-c CRITICAL] [-v] [-t TIMEOUT]Version: 2.0.0optional arguments:  -h, --help            show this help message and exit  -w WARNING, --warning WARNING                        warning threshold for remaining  -c CRITICAL, --critical CRITICAL                        critical threshold for remaining  -v, --verbose         increase output verbosity  -t TIMEOUT, --timeout TIMEOUT                        Timeout in seconds (default 10s)

Скрипт возвращает следующие exit коды в зависимости от указанных параметров

  • 0- OK

  • 1- WARNING

  • 2- CRITICAL

$ python3 check_docker_hub_limit.pyOK - Docker Hub: Limit is 5000 remaining 4997|'limit'=5000 'remaining'=4997$ echo $?0$ python3 check_docker_hub_limit.py -w 10000 -c 3000WARNING - Docker Hub: Limit is 5000 remaining 4999|'limit'=5000 'remaining'=4999$ echo $?1$ python3 check_docker_hub_limit.py -w 10000 -c 5000CRITICAL - Docker Hub: Limit is 5000 remaining 4998|'limit'=5000 'remaining'=4998$ echo $?2

Экспортер Prometheus

Кроме этого мы подготовили и выпустили экспортер для Prometheus, который позволит экспортировать метрики ограничений и реализовать их полноценный мониторинг.

Репозиторий проекта включает демо контейнер, включающий себе экспортер, Prometheus, Grafana, и docker-compose инструкции для его выката

$ cd example/docker-compose$ docker-compose up -d

Перейдите по адресу http://localhost:3000 для доступа к дэшборду Grafana

Надеюсь, наш опыт и рекомендации окажутся для вас полезными!

Подробнее..

Из песочницы Тесты на pytest с генерацией отчетов в Allure с использованием Docker и Gitlab Pages и частично selenium

02.08.2020 00:20:53 | Автор: admin

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


Пример отчета, получающийся в allure


Когда я хотел добавить в gitlab автотесты в стеке python, allure, docker, то я выяснил, что толковых статей на эту тему нет. Пришлось разбираться самостоятельно и как результат проб и ошибок появилась эта статья, которая скорее является гайдом, частично затрагивающим написание тестов, но наибольший фокус именно на выстраивании инфраструктуры. Если у вас уже написаны тесты на allure, то вы сразу можете переходить к разделу настройки инфраструктуры. Отмечу, что текст НЕ затрагивает написание UI тестов, но я затрону инфраструктуру для них в отдельном блоке.


Здесь не описаны лучшие практики по написанию тестов и выстраиванию инфраструктуры. Скорее ознакомление с тем, как можно все это организовать. Примеры выполнены на windows, на других системах подход примерно такой же.


Этот гайд предполагает, что у вас уже установлен python, настроен пустой репозиторий, куда вы будете отправлять свой код. При чтении статьи не обязательно знать pytest, но очень желательно знать что из себя представляет фикстура, как ей пользоваться, а так же параметризация тестов. Для тестирования будет использован Dog API. Если вас интересует локальный запуск тестов через gitlab, то вам необходимо установить docker.


Если у вас не установлено вышеперечисленное ПО, то ниже вы можете найти полезные ссылки:



Содержание


  • Подготовка к написанию тестов
  • Написание фикстуры и небольшого клиента для API тестов
  • Пишем тесты
  • Добавляем allure в тесты
  • Делаем инфраструктуру для тестов
  • Настройка репозитория
  • Установка и настройка Gitlab Runner
  • Настройка пайплайна в .gitlab-ci.yml
  • Запуск тестов и просмотр отчета
  • Настройка пайплайна для UI тестов
  • Полезные ссылки

Подготовка к написанию тестов


Я назвал свой репозиторий для этой статьи allure_pages и скопировал его на свой компьютер.


Нужно создать такую структуру в директории с репозиторием:



  • conftest.py будет использован для написания фикстур
  • requirements.txt для установки необходимых модулей в контейнере с python
  • test_dog_api.py внутри отдельной директории tests для написания самих тестов

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


python -m venv venv


Открываем папку с репозиторием как проект в PyCharm



Удобнее всего, на мой взгляд устанавливать нужные нам пакеты через встроенный в PyCharm терминал. Для этого нужно добавить интерпретатор, и открыть терминал.


  1. Жмем на Add Interpreter и если вы ранее создали в директории с проектом виртуальное окружение, то PyCharm сразу найдет нужный интерпретатор, который будет использован в проекте
  2. Открываем терминал по этой кнопке


Устанавливаем нужные пакеты для наших тестов в виртуальном окружении:


Для этого введите в терминал следующие команды.


  • pip install requests для запросов, которые мы будем делать к сервису Dog Api
  • pip install pytest для тестов, само собой
  • pip install pytest-xdist для параллелизации тестов
  • pip install allure-pytest для генерации отчетов в allure

Очень важно после этого написать в терминале
pip freeze > requirements.txt.


Файл requirements.txt нам понадобится для запуска тестов внутри докер контейнера.

Написание фикстуры и небольшого клиента для API тестов


Для того, чтобы упростить наши тесты создадим небольшой клиент для GET и POST запросов. Далее будет подробное описание этого клиента. Если вам это не нужно или кажется сложным, то пропустите эту часть статьи и переходите к следующей. На примере её использования станет ясно как пользоваться клиентом.


Сама фикстура


@pytest.fixturedef dog_api():    return ApiClient(base_address="http://personeltest.ru/aways/dog.ceo/api/")

Ниже конструктор класса для клиента. При инициализации объекта класс принимает в себя аргумент base_address, на его место нужно записать в фикстуре общий адрес для всех запросов в нашем случае будет использован https://dog.ceo/api/. Обратите внимание, что фикстура возвращает объект с базовым адресом для дальнейших GET и POST запросов.


class ApiClient:    def __init__(self, base_address):        self.base_address = base_address

Добавим в класс метод GET для отправки запросов.


    def get(self, path="/", params=None, headers=None):        url = f"{self.base_address}{path}"        return requests.get(url=url, params=params, headers=headers)

Здесь можно заметить, что наш клиент является оберткой над библиотекой requests. В классе ApiClient аргументы path, params, headers метода get выступают в роли аргументов, которые передаются в requests.get(url=url, params=params, headers=headers). Именно эта строчка и отвечает за выполнение запросов. Из адреса, использованного при инициализации объекта и пути, который мы передадим в дальнейшем в ходе теста, будет складываться полный адрес, который и будет использоваться в requests.get.


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


    def post(self, path="/", params=None, data=None, json=None, headers=None):        url = f"{self.base_address}{path}"        return requests.post(url=url, params=params, data=data, json=json, headers=headers)

В итоге у нас получается такой файл conftest.py:


import pytestimport requestsclass ApiClient:    def __init__(self, base_address):        self.base_address = base_address    def post(self, path="/", params=None, data=None, json=None, headers=None):        url = f"{self.base_address}{path}"        return requests.post(url=url, params=params, data=data, json=json, headers=headers)    def get(self, path="/", params=None, headers=None):        url = f"{self.base_address}{path}"        return requests.get(url=url, params=params, headers=headers)@pytest.fixturedef dog_api():    return ApiClient(base_address="http://personeltest.ru/aways/dog.ceo/api/")

Пишем тесты


В самом сервисе Dog API есть много разных запросов. Если у вас есть опыт в написании тестов, то можете самостоятельно написать несколько тестов. Так как акцент в этой статье заключается в постройке инфраструктуры для тестов, я не буду вдаваться в подробности тестов. Ранее я это сделал, чтобы мой код был более менее понятен. Несколько параметризованных тестов специально сломаны, чтобы показать как они будут показаны в отчете. Так выглядит мой файл с тестами test_dog_api.py


import pytestdef test_get_random_dog(dog_api):    response = dog_api.get("breeds/image/random")    with allure.step("Запрос отправлен, посмотрим код ответа"):        assert response.status_code == 200, f"Неверный код ответа, получен {response.status_code}"    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()        assert response["status"] == "success"    with allure.step(f"Посмотрим что получили {response}"):        with allure.step(f"Вложим шаги друг в друга по приколу"):            with allure.step(f"Наверняка получится что-то интересное"):                pass@pytest.mark.parametrize("breed", [    "afghan",    "basset",    "blood",    "english",    "ibizan",    "plott",    "walker"])def test_get_random_breed_image(dog_api, breed):    response = dog_api.get(f"breed/hound/{breed}/images/random")    response = response.json()    assert breed in response["message"], f"Нет ссылки на изображение с указанной породой, ответ {response}"@pytest.mark.parametrize("file", ['.md', '.MD', '.exe', '.txt'])def test_get_breed_images(dog_api, file):    response = dog_api.get("breed/hound/images")    response = response.json()    result = '\n'.join(response["message"])    assert file not in result, f"В сообщении есть файл с расширением {file}"@pytest.mark.parametrize("breed", [    "african",    "boxer",    "entlebucher",    "elkhound",    "shiba",    "whippet",    "spaniel",    "dvornyaga"])def test_get_random_breed_images(dog_api, breed):    response = dog_api.get(f"breed/{breed}/images/")    response = response.json()    assert response["status"] == "success", f"Не удалось получить список изображений породы {breed}"@pytest.mark.parametrize("number_of_images", [i for i in range(1, 10)])def test_get_few_sub_breed_random_images(dog_api, number_of_images):    response = dog_api.get(f"breed/hound/afghan/images/random/{number_of_images}")    response = response.json()    final_len = len(response["message"])    assert final_len == number_of_images, f"Количество фото не {number_of_images}, а {final_len}"

Добавляем allure в тесты


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


with allure.step('step 1'): с помощью этого контекстного менеджера тело теста делится на шаги, понятные в отчете.
@allure.feature('Dog Api') @allure.story('Send few requests') декораторы, с помощью которых сами тесты или тестовые наборы будут структурированы в отчете


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

Добавим в наш API клиент несколько allure шагов.


class ApiClient:    def __init__(self, base_address):        self.base_address = base_address    def post(self, path="/", params=None, data=None, json=None, headers=None):        url = f"{self.base_address}{path}"        with allure.step(f'POST request to: {url}'):            return requests.post(url=url, params=params, data=data, json=json, headers=headers)    def get(self, path="/", params=None, headers=None):        url = f"{self.base_address}{path}"        with allure.step(f'GET request to: {url}'):            return requests.get(url=url, params=params, headers=headers)

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


@allure.feature('Random dog')@allure.story('Получение фото случайной собаки и вложенные друг в друга шаги')def test_get_random_dog(dog_api):    response = dog_api.get("breeds/image/random")    with allure.step("Запрос отправлен, посмотрим код ответа"):        assert response.status_code == 200, f"Неверный код ответа, получен {response.status_code}"    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()        assert response["status"] == "success"    with allure.step(f"Посмотрим что получили {response}"):        with allure.step(f"Вложим шаги друг в друга по приколу"):            with allure.step(f"Наверняка получится что-то интересное"):                pass

Сходным образом заполним шагами и декораторами остальные тесты. Финальный файл test_dog_api.py


import pytestimport allure@allure.feature('Random dog')@allure.story('Получение фото случайной собаки и вложенные друг в друга шаги')def test_get_random_dog(dog_api):    response = dog_api.get("breeds/image/random")    with allure.step("Запрос отправлен, посмотрим код ответа"):        assert response.status_code == 200, f"Неверный код ответа, получен {response.status_code}"    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()        assert response["status"] == "success"    with allure.step(f"Посмотрим что получили {response}"):        with allure.step(f"Вложим шаги друг в друга по приколу"):            with allure.step(f"Наверняка получится что-то интересное"):                pass@allure.feature('Random dog')@allure.story('Фото случайной собаки из определенной породы')@pytest.mark.parametrize("breed", [    "afghan",    "basset",    "blood",    "english",    "ibizan",    "plott",    "walker"])def test_get_random_breed_image(dog_api, breed):    response = dog_api.get(f"breed/hound/{breed}/images/random")    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()    assert breed in response["message"], f"Нет ссылки на фото с указанной породой, ответ {response}"@allure.feature('List of dog images')@allure.story('Список всех фото собак списком содержит только изображения')@pytest.mark.parametrize("file", ['.md', '.MD', '.exe', '.txt'])def test_get_breed_images(dog_api, file):    response = dog_api.get("breed/hound/images")    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()    with allure.step("Соединим все ссылки в ответе из списка в строку"):        result = '\n'.join(response["message"])    assert file not in result, f"В сообщении есть файл с расширением {file}"@allure.feature('List of dog images')@allure.story('Список фото определенных пород')@pytest.mark.parametrize("breed", [    "african",    "boxer",    "entlebucher",    "elkhound",    "shiba",    "whippet",    "spaniel",    "dvornyaga"])def test_get_random_breed_images(dog_api, breed):    response = dog_api.get(f"breed/{breed}/images/")    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()    assert response["status"] == "success", f"Не удалось получить список изображений породы {breed}"@allure.feature('List of dog images')@allure.story('Список определенного количества случайных фото')@pytest.mark.parametrize("number_of_images", [i for i in range(1, 10)])def test_get_few_sub_breed_random_images(dog_api, number_of_images):    response = dog_api.get(f"breed/hound/afghan/images/random/{number_of_images}")    with allure.step("Запрос отправлен. Десериализируем ответ из json в словарь."):        response = response.json()    with allure.step("Посмотрим длину списка со ссылками на фото"):        final_len = len(response["message"])    assert final_len == number_of_images, f"Количество фото не {number_of_images}, а {final_len}"

Делаем инфраструктуру для тестов


Здесь есть несколько моментов, о которых стоит написать:


  • .gitlab-ci.yml файл, где на языке разметки yaml описаны инструкции что нужно делать gitlab runner
  • gitlab-runner это проект, написанный на языке Go. Он будет выполнять инструкции. Есть несколько вариантов его использования. Мы будем писать инструкцию для gitlab runner, где он в свою очередь будет использовать docker для запуска тестов и всего остального. Далее по тексту он будет обозначаться просто "раннер". Что это за сущность и с чем её едят можно найти здесь.

Если у вас в компании уже настроена инфраструктура и вы можете попросить своего devops выделить вам раннер где-то в облаке, то сделайте это. Раннер можно использовать и локально на своем компьютере. В этом случае вам нужен установленный docker desktop на windows. В этом гайде мы будем использовать локальный раннер, но для облачного запуска вам нужно будет просто указать в .gitlab-ci нужный раннер.


Если у вас нет docker desktop, то здесь инструкция как его заиметь.

Настройка репозитория


Нужно зайти в настройки репозитория Settings -> General -> Visibility, project features, permissions, активировать Pipelines и сохранить изменения.



После этого в разделе настроек появится раздел CI / CD. Переходим Settings -> CI / CD -> Runners



Здесь переходим по ссылке в пункте 1 и согласно инструкции ставим себе на компьютер Gitlab Runner. Далее в статье адаптация инструкции на русском.


Установка и настройка Gitlab Runner


Ниже инструкция, которую вы можете найти здесь. Можете пройтись по ней самостоятельно. Далее в тексте будет сделано все по инструкции.


  1. Создайте папку где-нибудь в системе, например: C:\GitLab-Runner.
  2. Скачайте бинарник для x86 или amd64 и положите его в созданную папку. Переименуйте скачанный файл в gitlab-runner.exe. Вы можете скачать бинарник для любой доступной версии здесь
  3. Запустите командную строку с правами администратора. (Сам я буду использовать обычный powershell с правами администратора)
  4. Зарегестрируйте раннер
  5. Установите раннер как сервис и запустите его. Вы можете запустить службу, используя встроенную системную учетную запись (рекомендуется) или учетную запись пользователя.

С первыми тремя пунктами инструкции все понятно. Создаем папку, кладем в неё переименованный раннер и запускаем в директории powershell, то с четвертым и пятым пунктом могут возникнуть проблемы и недопонимание, если не потратить некоторое время на чтение документации. Далее простыми словами описаны шаги, которые нужно сделать, чтобы раннер заработал.


Я создал папку C:\gitlab_runners и сохранил туда скачанный для моей системы раннер, предварительно переименовав в gitlab-runner.exe


Как только вы справились с первыми тремя пунктами, то нужно сделать следующее:


  1. Ввести команду:
    ./gitlab-runner.exe register
  2. Ввести url, указанный на странице настроек раннеров в репозитори в пункте 2. Например, у меня так:
    https://gitlab.somesubdomain.com/
  3. Ввести токен указанный на странице настроек раннеров в репозитори в пункте 3. Например, у меня так:
    tJTUaJ7JxfL4yafEyF3k
  4. Вводим описание раннера. Его потом можно будет изменить через UI в настроках раннера в репозитории. Например так:
    Runner on windows for autotests
  5. Добавляем теги для раннера, для того, чтобы описывать нужный раннер в .gitlab-ci.yml, используя определенные теги. Это в основном нужно, когда раннеров больше одного.
    docker, windows
  6. Выбираем тип раннера, который нам нужен. Здесь выберем docker
    docker
  7. Вводим дефолтный image, который будет использоваться раннером. Его можно будет изменить в конфиге раннера или указать конкретый в .gitlab-ci.yml
    python:3.8-alpine

Скрншот с выполнеными шагами



Наш раннер зарегистрирован, теперь его нужно запустить.


Проверить статус раннера можно так:
.\gitlab-runner.exe status


Запустите раннер с помощью:
.\gitlab-runner.exe run


Теперь, если вы зайдете в настроки раннеров в репозитории Settings -> CI / CD -> Runners, то увидите что-то такое:



Это означает, что у репозитория есть линк с раннером и он видит его статус. Осталось добавить чекбокс в раннере. Для этого нужно нажать на карандаш рядом с именем раннера в гитлабе.



Все хорошо, теперь можно перейти к пайплайну.


Настройка пайплайна в .gitlab-ci.yml


Инструкции для раннера описываются в .gitlab-ci.yml. Полная документация описана здесь.


Первым делом нам нужно описать stages. Шаги пайплайна. Их будет 4. Каждый stage отдельный job, который будет выполнять раннер.


stages:  - testing # Запуск тестов  - history_copy # Копирование результата тестов из предыдущего запуска тестов  - reports # Генерация отчета  - deploy # Публикация отчета на gitlab pages

Шаг первый. Testing


docker_job: # Название job  stage: testing # Первый stage, который нужно выполнить  tags:    - docker # С помощью этого тега gitlab поймет, какой раннер нужно запустить. Он запустит докер контейнер, из образа, который мы указывали в 6 шаге регистрации раннера.  before_script:    - pip install -r requirements.txt # Устанавливаем пакеты в поднятом контейнере перед запуском самих тестов  script:    - pytest -n=4 --alluredir=./allure-results tests/test_dog_api.py # Запускаем тесты параллельно(-n=4 обеспечивает нам это), указав папку с результатами тестов через --alluredir=  allow_failure: true # Это позволит нам продолжить выполнение пайплайна в случае, если тесты упали.  artifacts: # Сущность, с помощью которой, мы сохраним результат тестирования.    when: always # Сохранять всегда    paths:      - ./allure-results # Здесь будет сохранен отчет    expire_in: 1 day # Да, он будет удален через день. Нет смысла хранить его в течение длительного срока.

Шаг второй. history_copy


history_job: # Название job  stage: history_copy # Это второй stage, который нужно выполнить  tags:    - docker # Пользуемся тем же самым раннером  image: storytel/alpine-bash-curl # Но теперь укажем раннеру использовать другой образ, для того чтобы скачать результаты теста из предыдущего пайплайна. Нам же нужна история тестов, верно?  script:    - 'curl --location --output artifacts.zip "https://gitlab.smarthead.ru/api/v4/projects/(АЙДИ ВАШЕГО РЕПОЗИТОРИЯ)/jobs/artifacts/master/download?job=pages&job_token=$CI_JOB_TOKEN"'  # С помощью api гитлаба скачиваем файлы из job, который будет указан ниже. Обратите внимание на текст на русском в ссылке. Очень важно указать вместо текста и скобок номер вашего репозиториия    - unzip artifacts.zip # Распаковываем файлы    - chmod -R 777 public # Даем права любые манипуляции с содержимым    - cp -r ./public/history ./allure-results # Копируем историю в папку с результатами теста  allow_failure: true # Так как при первом запуске пайплайна истории нет, это позволит нам избежать падения пайплайна. В дальнейшем эту строчку можно спокойно удалить.  artifacts:     paths:      - ./allure-results # Сохраняем данные    expire_in: 1 day  rules:    - when: always # Сохранять всегда

Шаг третий. reports


allure_job: # Название job  stage: reports # Третий stage, который будет выполнен  tags:    - docker # Пользуемся тем же самым раннером  image: frankescobar/allure-docker-service # Указываем раннеру использовать образ с allure. В нем мы будем генерировать отчет.  script:     - allure generate -c ./allure-results -o ./allure-report # Генерируем отчет из ./allure-results внутрь папки ./allure-report  artifacts:    paths:      - ./allure-results # Примонтируем две этих директории для получения результатов тестирования и генерации отчетов соответственно      - ./allure-report    expire_in: 1 day  rules:    - when: always

Шаг четвертый. deploy


pages: # Названием этой job говорим гитлабу, чтобы захостил результат у себя в pages  stage: deploy # Четвертый stage, который будет выполнен  script:    - mkdir public # Создаем папку public. По умолчанию гитлаб хостит в gitlab pages только из папки public    - mv ./allure-report/* public # Перемещаем в папку public сгенерированный отчет.  artifacts:    paths:      - public  rules:    - when: always

Финальный .gitlab-ci.yml


stages:  - testing # Запуск тестов  - history_copy # Копирование результата тестов из предыдущего запуска тестов  - reports # Генерация отчета  - deploy # Публикация отчета на gitlab pagesdocker_job: # Название job  stage: testing # Первый stage, который нужно выполнить  tags:    - docker # С помощью этого тега gitlab поймет, какой раннер нужно запустить. Он запустит докер контейнер, из образа, который мы указывали в 6 шаге регистрации раннера.  before_script:    - pip install -r requirements.txt # Устанавливаем пакеты в поднятом контейнере перед запуском самих тестов  script:    - pytest -n=4 --alluredir=./allure-results tests/test_dog_api.py # Запускаем тесты параллельно(-n=4 обеспечивает нам это), указав папку с результатами тестов через --alluredir=  allow_failure: true # Это позволит нам продолжить выполнение пайплайна в случае, если тесты упали.  artifacts: # Сущность, с помощью которой, мы сохраним результат тестирования.    when: always # Сохранять всегда    paths:      - ./allure-results # Здесь будет сохранен отчет    expire_in: 1 day # Да, он будет удален через день. Нет смысла хранить его в течение длительного срока.history_job: # Название job  stage: history_copy # Это второй stage, который нужно выполнить  tags:    - docker # Пользуемся тем же самым раннером  image: storytel/alpine-bash-curl # Но теперь укажем раннеру использовать другой образ, для того чтобы скачать результаты теста из предыдущего пайплайна. Нам же нужна история тестов, верно?  script:    - 'curl --location --output artifacts.zip "https://gitlab.smarthead.ru/api/v4/projects/(АЙДИ ВАШЕГО РЕПОЗИТОРИЯ)/jobs/artifacts/master/download?job=pages&job_token=$CI_JOB_TOKEN"'  # С помощью api гитлаба скачиваем файлы из job, который будет указан ниже. Обратите внимание на текст на русском в ссылке. Очень важно указать вместо текста и скобок номер вашего репозиториия    - unzip artifacts.zip # Распаковываем файлы    - chmod -R 777 public # Даем права любые манипуляции с содержимым    - cp -r ./public/history ./allure-results # Копируем историю в папку с результатами теста  allow_failure: true # Так как при первом запуске пайплайна истории нет, это позволит нам избежать падения пайплайна. В дальнейшем эту строчку можно спокойно удалить.  artifacts:     paths:      - ./allure-results # Сохраняем данные    expire_in: 1 day  rules:    - when: always # Сохранять всегдаallure_job: # Название job  stage: reports # Третий stage, который будет выполнен  tags:    - docker # Пользуемся тем же самым раннером  image: frankescobar/allure-docker-service # Указываем раннеру использовать образ с allure. В нем мы будем генерировать отчет.  script:     - allure generate -c ./allure-results -o ./allure-report # Генерируем отчет из ./allure-results внутрь папки ./allure-report  artifacts:    paths:      - ./allure-results # Примонтируем две этих директории для получения результатов тестирования и генерации отчетов соответственно      - ./allure-report    expire_in: 1 day  rules:    - when: alwayspages: # Названием этой job говорим гитлабу, чтобы захостил результат у себя в pages  stage: deploy # Четвертый stage, который будет выполнен  script:    - mkdir public # Создаем папку public. По умолчанию гитлаб хостит в gitlab pages только из папки public    - mv ./allure-report/* public # Перемещаем в папку public сгенерированный отчет.  artifacts:    paths:      - public  rules:    - when: always

Запуск тестов и просмотр отчета


Осталось отправить в репозиторий все необходимые файлы, а именно:


  1. conftest.py
  2. Директорию tests
  3. requirements.txt (Убедитесь, что там есть все нужные зависимости)
  4. .gitlab-ci.yml

Отправляем и смотрим что получилось.


В первый раз пайплайн будет запускаться немного дольше, чем в следующие разы. Потому что ему нужно будет скачать нужные образы, запустить их и все такое. Заходим в репозиторий и через сайдбар переходим в CI / CD -> Pipelines


Здесь вы сможете увидеть статус пайплайна, а также перейти к Ci линтеру, где можете проверить конкретно свой .gitlab-ci.yml.




Когда пайплайн пройдет, увидеть ссылку и сам отчет можно через Settings -> Pages. При первом использовании pages в репозитории для просмотра страницы может понадобиться до 30 минут. Проходим по ссылке и смотрим отчет.



Получаем результат. Советую побродить по отчету и посмотреть результаты.



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



Как видите второй шаг в этот раз выполнился без ошибок. Это потому, на stage history_job проблем с копированием истории предыдущих пайплайнов не возникло. Теперь посмотрим результат тестов.



Видна динамика, количество упавших тестов и все остальное. Для просмотра подробностей по тестам рекомендую посмотреть отчет самостоятельно.



Настройка пайплайна для UI тестов


Реализовать их можно с помощью специальной сущности гитлаба [services](http://personeltest.ru/aways/docs.gitlab.com/ee/ci/services/). Это ключевое слово для использования докер образов, которые будут запущены до хода шагов в script. Для UI тестов нужно добавить в job пайплайна следующее:


  services:    - selenium/standalone-chrome:latest

Так как нам нужно обращаться к контейнеру, который поднимет гитлаб по определенному url, то он каким-то образом назначает этому сервису url, по которому можно слать запросы. Логика здесь такая:


  • Все после : отбрасывается
  • Слеш / заменяется двойным подчеркиванием __ и создается главный алиас
  • Слеш / заменяется одиночным дефисом - и создается дополнительный алиас (Необходим Gitlab Runner v1.1.0 и выше)

И теперь нужно изменить в фикстуре executor (по умолчанию используется chromedriver на вашей машине, но сейчас мы все это запускаем в контейнерах) для запуска ui тестов:


browser = webdriver.Remote(command_executor="http://personeltest.ru/away/selenium__standalone-chrome:4444/wd/hub")

Обратите внимание, что было в .gitlab-ci.yml:
selenium/standalone-chrome:latest
И по какому адресу нужно обращаться фикстуре для запуска тестов:
selenium__standalone-chrome


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


chrome_job:  stage: testing  services:    - selenium/standalone-chrome  image: python:3.8  tags:    - docker  before_script:    - pip install -r requirements.txt  script:    - pytest --alluredir=./allure-results tests/  allow_failure: true  artifacts:    when: always    paths:      - ./allure-results    expire_in: 1 day

Полезные ссылки


Помимо ссылок в статье, хочется поделиться еще несколькими, которые немного разъяснять вам что такое allure и как писать gitlab ci.


Статья об allure на хабре
Введение в gitlab ci

Подробнее..

Приглашаем на Live-Вебинар Автоматизация процессов с GitLab CICD 29 Окт., 1500 -1600 (MST)

06.10.2020 12:23:44 | Автор: admin

Расширяем знания и переходим на следующий уровень.




Вы только начинаете изучать основные принципы Continuous Integration / Continuous Delivery или написали уже не один десяток пайплайнов? Вне зависимости от уровня Ваших знаний, присоединяйтесь к нашему вебинару, чтобы на практике разобраться, почему тысячи организаций по всему миру выбирают GitLab в качестве ключевого инструмента для автоматизации IT процессов.

Традиционные инструменты усложняют работу команд, требуют дополнительного внимания для настройки множества плагинов и интеграций и не позволяют по-настоящему масштабировать их использование. Не позволяйте Вашим инструментам тратить Ваше время.

В этом вебинаре мы разберем:
  • Основные элементы автоматизации в GitLab CI/CD
  • Принципы Pipelines-as-Code
  • AutoDevOps автоматический полноценный CI/CD конвейер, который самостоятельно автоматизирует весь процесс
  • Расширенные настройки и оптимизацию GitLab CI/CD

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

Запускаем тесты на GitLab Runner с werf на примере SonarQube

09.11.2020 10:07:58 | Автор: admin


Если в качестве инфраструктуры, где разворачивается приложение, выступает Kubernetes, можно сказать, что существует два способа запуска тестов (и других утилит для анализа кода) в CI/CD:

  • непосредственно в кластере K8s с помощью отдельных Job или Helm hooks;
  • снаружи K8s например, на сервере сборки/деплоя или локально у разработчиков.

Первый подход мы достаточно подробно описывали на интересном примере с базами данных в статье Как мы выносили СУБД (и не только) из review-окружений в статическое. В этой статье рассмотрен более простой путь запуск вне K8s-кластера. Делать мы это будем на примере SonarQube-тестов в рамках CI/CD, построенного на базе GitLab с использованием werf.

Стоит помнить, что такой метод подходит только для тех тестов, которые не требуют подключения к другим объектам в инфраструктуре. С другой стороны, этот вариант хорош тем, что позволяет экономить кластерные ресурсы: нам не нужно выделять дополнительные ресурсы в K8s, поскольку вместо этого используются внешние мощности (локальных машин или сервера сборки/деплоя).

Для запуска тестов будет использоваться CI/CD-утилита werf, которая позволяет работать с полным жизненным циклом доставки приложений, а в частности удобно собирать Docker-образы, а потом запускать их локально. Если вы уже используете werf, встраивание дополнительных команд будет совсем тривиальным, а если нет на данном примере можно увидеть, как удобно контролировать все процессы CI/CD в одном месте.

Теперь к непосредственной практике.

Подготовка: внедряем тесты в пайплайн приложения


Основная особенность использования тестов и других анализаторов кода в CI/CD необходимость устанавливать дополнительные пакеты/конфигурационные файлы в Docker-образ приложения. Установка все в один образ увеличивает размер итогового образа и добавляет неиспользуемые файлы во время запуска самого приложения.

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

* Известная многим лучшая практика гласит, что для всех целей применения (от тестов и отладки до запуска в production) должен собираться единственный образ. Поэтому вариант со сборкой другого образа может быть допустим разве что для случаев, когда требуется тестировать только исходный код приложения. В таком варианте можно основной образ приложения использовать в виде артефакта в образе для тестов.

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

В werf реализация задуманного осуществляется с помощью переменных окружения, устанавливаемых в определенной Job в CI/CD-пайплайне.

Для конфигурации на стороне GitLab CI/CD потребуется определить Job с тестами (в .gitlab-ci.yml) примерно так:

Build sonar-tests:  stage: build  script:    - werf build-and-publish  variables:    SONAR_TESTS: "yes"  tags:    - werf  only:    - schedules

Как видно, в variables определяется переменная SONAR_TESTS, которая и будет задействована в сборке через werf.

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

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

{{ if eq (env "SONAR_TESTS") "yes" }}---image: sonar-testsfrom: node:10.16.0# остальные этапы сборки# ...{{ end }}

Т.е. теперь мы можем обратиться к той переменной SONAR_TESTS, что задали в GitLab CI/CD. Так легко контролировать этапы сборки с помощью переменных.

Собираем образы тестов в werf


Перейдем к непосредственному созданию инструкций для сборки Docker-образа с помощью werf. В этом образе требуются следующие элементы:

  1. исходный код приложения;
  2. необходимые пакеты/файлы для запуска Sonar-тестов;
  3. информация о токене, URL до Sonarqube dashboard и названии приложения в конфигурационном файле.

Получается следующий набор инструкций для werf:

{{ $sonar_version := "4.4.0.2170"}}{{ if eq (env "SONAR_TESTS") "yes" }}---image: sonar-testsfrom: node:10.16.0git:  - add: /    to: /appansible:  install:  - name: "npm install sonar"    shell: |      npm install --no-save -D sonarqube-scanner    args:      chdir: /app  - name: "Install default-jdk"    apt:      update_cache: yes      state: present      name:      - default-jdk  - name: "install sonarqube scanner"    args:      chdir: /app    shell: |      wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-{{ $sonar_version }}.zip      mkdir -p /root/.sonar/native-sonar-scanner/      unzip -qq sonar-scanner-cli-{{ $sonar_version }}.zip -d /root/.sonar/native-sonar-scanner/      mv /root/.sonar/native-sonar-scanner/sonar-scanner-{{ $sonar_version }}/ /root/.sonar/native-sonar-scanner/sonar-scanner-{{ $sonar_version }}-linux/  - name: "Setup /app/sonar.js"    copy:      content: |{{ tpl (.Files.Get ".werf/sonar.js") . | indent 8 }}      dest: /app/sonar.js{{ end }}{{ end }}

Опять же, полную информацию по структуре и содержанию werf.yaml можно найти в документации. Здесь мы рассмотрим только основные инструкции. Итак, установка необходимых компонентов в образ состоит из следующих операций:

  1. Добавление исходного кода приложения в образ с помощью Git-модуля werf.
  2. Инсталляция через NPM пакетов sonar и sonarqube-scanner. Эти пакеты требуются для поддержки SonarQube в Node.js-приложении.
  3. Установка JDK, который потребуется для запуска тестов.
  4. Загрузка .jar-файла sonar-scanner, для которого мы и устанавливаем JDK.
  5. Добавление конфигурационного файла sonar.js с помощью функции .Files.Get. Подробнее см. далее.

Что за файл, находящийся по адресу .werf/sonar.js? Это конфиг, в котором хранится вся необходимая информация для запуска Sonar-тестов. Посмотрим на его содержимое:

{{ $_ := env "SONAR_TOKEN" | set . "Token" }}{{ $_ := env "SONAR_PROJECT" | set . "Project" }}{{ $_ := env "SONAR_URL" | set . "Url" }}    const sonarqubeScanner = require('sonarqube-scanner');    sonarqubeScanner( {      serverUrl : "{{ .Url }}",       token : "{{ .Token }}",      options: {        'sonar.projectName': "{{ .Project }}",        'sonar.projectDescription': 'Description for "{{ .Project }}" project...',        'sonar.sources': '.',        'sonar.projectKey': "{{ .Project }}",        'sonar.projectBaseDir': '/app'      }      },      () => process.exit()    )

В трёх первых строчках перечисляются переменные окружения, которые определяются, например, в GitLab CI/CD Variables. В дальнейшем, т.е. во время сборки/деплоя, эти переменные могут использоваться аналогично тому, как мы уже показывали в werf.yaml (через встроенную конструкцию werf).

Подробнее об определяемых переменных:

  1. SONAR_TOKEN токен, который вы создаете в самом SonarQube; именно через него производится авторизация в SonarQube;
  2. SONAR_PROJECT название созданного проекта в SonarQube;
  3. SONAR_URL URL к SonarQube (может быть как внешним URL, так и адресом до сервиса).

В результате сборки (werf build) по таким конфигам (werf.yaml
и sonar.js) получается Docker-образ (под названием sonar-tests), который можно использовать для запуска Sonar-тестов, чем мы и займемся в следующей главе.

NB: Аналогичным образом можно собирать и запускать linting-тесты (речь не про что-то компилируемое), интеграционные тесты и т.д., причем даже в Kubernetes-окружении, а не только локально (в любом случае как минимум экономится место в основном образе).

Запускаем тесты с помощью команды werf run


Последний этап запуск тестов локально на GitLab Runner.

Для этого в GitLab CI/CD определяется отдельная Job, в которой будут запускаться тесты. Например:

Deploy sonar-tests:  script:    - werf run sonar-tests -- bash -c "node /app/sonar.js"  tags:    - werf  stage: deploy  environment:    name: sonar-tests  only:    - schedules  variables:    SONAR_TESTS: "yes"  after_script:    - echo "Scan results are here https://sonar/dashboard?id=${SONAR_PROJECT}"  when: always  dependencies:    - Build sonar-tests

Здесь используется единственная команда:

werf run sonar-tests -- bash -c "node /app/sonar.js"

В её первой части указывается название образа для запуска (sonar-tests, берётся из werf.yaml), во второй команда для выполнения внутри контейнера.

Теперь пайплайн будет запускать Sonar-тесты на GitLab Runner, а их результаты публиковать в SonarQube Dashboard.



Заключение


Описанный вариант запуска тестов в первую очередь будет полезен тем, кто уже использует werf или собирается его внедрить в своем проекте. С werf run можно выполнять тесты непосредственно на самом Runner'е и вместо отдельных Docker-инструкций описывать всё непосредственно в werf, аккумулируя подобные элементы в одном месте.

Этот подход, конечно, позволяет запускать не только SonarQube-тесты: подойдут и любые другие решения, не требующие создания отдельного окружения для них (в виде отдельного сервера БД, очередей сообщений и т.д.).

Также он подойдет, если вы хотите запускать тесты не в кластере, а в локальном окружении или на самом Runner'е, не используя ресурсы кластера.

P.S.


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

Читайте также в нашем блоге:

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru