Gitlab CI-CD для запуска тестов в Laravel

В этой статье будет рассмотрена настройка Gitlab CI-CD для автоматического запуска ваших тестов в Laravel-приложении. Кроме тестов также запустим проверку оформления php-кода и статический анализ через phpstan. Перед запуском проверок будет создаваться Docker-образ с php нужной версии и необходимыми расширениями. Наши тесты будут работать с базой Mysql.

Содержание

Задачи и этапы

И так, вы наверное уже слышали, что CI-CD в Gitlab состоит из описания задач (jobs). В каждой задаче есть одна или более команд.

Также там есть этапы (stages). Каждая задача принадлежит какому-либо этапу. Названия и этапов и задач мы назначаем сами.

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

Чтобы настроить CI-CD в Gitlab нам необходимо создать файл .gitlab-ci.yml и описать в нем этапы и задачи.

В нашем примере будет 3 этапа. Файл начнется с их перечисления:

stages:
  - build
  - compose
  - check

У нас три этапа: build (сборка образа с нужной версией php и расширениями), compose (установка зависимостей через composer и check (все наши проверки и тесты).

Сервисы Gitlab

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

Обычно в качестве сервисов используются контейнеры на базе образов баз данных. Но мы вольны использовать любые образы с Docker Hub или другого реестра.

Наш сервис будет описываться так:

services:
  - name: mysql:8.0
    alias: mysql

variables:
  MYSQL_DATABASE: hello_world_db
  MYSQL_ROOT_PASSWORD: root_secret

Ключевое слово services используется для того, чтобы указать какие сервисы мы будем использовать. В name указываем название и версию docker-образа. Используем образ mysql.

Ключевое слово variables, как ясно из названия, задает переменные. Контейнер сервиса mysql будет использовать эти переменные (их имена можно увидеть в документации к образу mysql). В нашем примере создается БД с конкретным именем и задается пароль root. Эти переменные объявлены в глобальном пространстве и могут использоваться не только этим сервисом, а например Laravel-приложением при запуске тестов в нем.

Создание основного образа

Для того, чтобы задачи из нашего файла .gitlab-ci.yml выполнялись, должен работать Gitlab-runner. Такие runner-ы бывают разными. Для Docker используется соответствующий. При обработке задач docker runner запускает контейнер указанного образа для каждого шага. И здесь кроется первая проблема. Для создания своего образа Docker в задаче (этап build) нам нужно обращаться к docker из docker-контейнера.

Для создания docker-образа в CI-CD в основном используется два подхода:

  • dind – Docker-in-docker (почему один в другом – описано выше)
  • kaniko. Инструмент, который собирает образы контейнеров из Dockerfile без Docker

Dind

Для dind задача по созданию образа выглядит так:

gitlab:
  stage: build
  image: docker
  services:
    - name: docker:dind
      alias: docker
  script:
    - echo "${CI_REGISTRY_PASSWORD}" | docker login $CI_REGISTRY -u "${CI_REGISTRY_USER}" --password-stdin
    - docker build --pull -t ${CI_REGISTRY_IMAGE}/gitlab:${CI_COMMIT_REF_NAME} -f ./docker/gitlab/Dockerfile ./
    - docker push ${CI_REGISTRY_IMAGE}/gitlab:${CI_COMMIT_REF_NAME}

Опишем основные элементы секции.

Для задачи указывается имя. Здесь указано gitlab, но оно может быть любым. Указывается этап, к которому относится задача – stage: build. В секции services указан сервис docker:dind, который используется в командах задачи. И, наконец, в секции script указываются конкретные запускаемые команды. В последних используются предустановленные переменные Gitlab (Например, CI_REGISTRY_USER).

В командах мы создаем образ на базе файла docker/gitlab/Dockerfile, который должен находиться в нашем репозитории. Затем образ отправляется в реестр gitlab.

Kaniko

Для Kaniko эта задача выглядит таким образом:

kaniko-build:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:v1.23.1-debug
    entrypoint: [""]
  variables:
    DOCKER_CONFIG: /kaniko/.docker/
  script:
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor
      --context $CI_PROJECT_DIR
      --dockerfile $CI_PROJECT_DIR/docker/gitlab/Dockerfile
      --destination ${CI_REGISTRY_IMAGE}/gitlab:${CI_COMMIT_REF_NAME}

Секции здесь уже знакомые. Но, как видно, мы здесь не обращаемся к docker. Всю магию делает /kaniko/executor.

Важно чтобы в image.name был указан образ -debug. Этот образ содержит sh, который используется Gitlab-runner. Без этого работать не будет.

Dind или Kaniko?

Какой же из вариантов выбрать?

Вариант dind позволяет использовать привычные команды Docker для сборки, запуска и управления контейнерами. Это упрощает переносимость существующих скриптов и процессов. В этом случае можно выполнять любые операции с Docker, включая запуск контейнеров, создание сетей и управление томами.

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

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

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

Другие варианты: Dood и Sysbox

Еще есть возможность использовать docker host-системы путём монтирования docker.sock в контейнер. Называется это Docker outside of Docker (DooD). Он также, как dind, имеет недостатки в плане безопасности.

Также стоит упомянуть, что можно настроить Gitlab-runner для работы с sysbox. В этом случае работать будет по принципу dind, но не потребует привилегированного доступа, что решит основную проблему этого варианта.

Об этих способах написано в статье Использование Docker in Docker в GitLab.

Содержимое Dockerfile

В любом из этих случаев нам потребуется Dockerfile с описание ПО, которое мы используем.

FROM php:8.2-cli-alpine

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions \
    && install-php-extensions mbstring zip pcntl bcmath pdo_mysql dom redis \
    && rm -rf /tmp/* /var/tmp/* /usr/share/doc/* /var/cache/apk/* /usr/share/php8 \
;

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

Здесь мы используем пакет mlocati/docker-php-extension-installer, который делает установку php-расширений проще. Можно было бы ставить расширения через пакетный менеджер, но ряд проблем с зависимостями при этом мог бы создать трудности.

Также здесь мы сразу добавляем composer из одноименного образа.

Установка зависимостей composer

Для запуска тестов Laravel-приложения нам потребуется установить зависимости через composer.

composer-dev:
  stage: compose
  image: ${CI_REGISTRY_IMAGE}/gitlab:${CI_COMMIT_REF_NAME}
  cache:
    key: composer-dev-${CI_COMMIT_REF_SLUG}
    paths:
      - vendor/
  artifacts:
    paths:
      - vendor/
    expire_in: 1 day
  script:
    - composer install

Здесь мы указываем следующий этап после сборки образа. В качестве базового образа мы указываем тот, который собрали. Команда в этом шаге очень простая – composer install.

Но есть пару новых вещей – кеш и артефакты.

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

А артефакты позволяют оставить какие-либо файлы результата работы доступными для последующих задач (в других этапах). Нам ведь понадобится папка vendor.

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

Теперь мы подошли к тому, чтобы запустить наши тесты. Эта задача будет выглядеть так:

phpunit:
  stage: check
  dependencies:
    - composer-dev
  image: ${CI_REGISTRY_IMAGE}/gitlab:${CI_COMMIT_REF_NAME}
  variables:
    APP_NAME: 'MyLaravel'
    APP_KEY: 'base64:pHLqwKYCbGRrp3YrrJnH7FBR2buuhdhAsJWaWZe2Xqo='
    APP_ENV: testing
    APP_DEBUG: true
    DB_CONNECTION: mysql
    DB_HOST: mysql
    DB_PORT: 3306
    DB_DATABASE: $MYSQL_DATABASE
    DB_USERNAME: root
    DB_PASSWORD: $MYSQL_ROOT_PASSWORD
  script:
    - php artisan test

Здесь мы видим указание следующего этапа в stage и использование образа, который мы создали ранее (image).

В секции dependencies мы указываем, что данная задача зависит от артефактов, которые создаются в задаче composer-dev.

Приложению Laravel понадобится ряд переменных окружения, которые мы обычно задаем в файле .env. Часть данных укажем явно, а часть (имя БД и пароль) возьмем из глобальных переменных, которые создали ранее.

Команда задачи запускает тесты php artisan test. При этом нам необходимо, чтобы миграции выполнились. Если вы используете в тестах trait Illuminate\Foundation\Testing\RefreshDatabase, то он позаботится о том, чтобы миграции были применены перед тестами. В иных случая в команды (script) данной задачи необходимо будет добавить и php artisan migrate.

Проверки кода

В одной из предыдущих статей рассказывалось о дополнительных способах проверки php-кода помимо тестов. Эти инструменты будет полезно также запускать в рамках CI-CD. Поэтому добавим в наш файл еще задач.

phpcs:
  stage: check
  image: ${CI_REGISTRY_IMAGE}/gitlab:${CI_COMMIT_REF_NAME}
  dependencies:
    - composer-dev
  cache:
    key: phpcs-${CI_COMMIT_REF_SLUG}
    paths:
      - .phpCsCache
  script:
    - vendor/bin/phpcs --parallel=4 --cache=.phpCsCache

phpstan:
  stage: check
  image: ${CI_REGISTRY_IMAGE}/gitlab:${CI_COMMIT_REF_NAME}
  dependencies:
    - composer-dev
  cache:
    key: phpstan-${CI_COMMIT_REF_SLUG}
    paths:
      - .phpStanCache/
  script:
    - vendor/bin/phpstan analyse -c phpstan.neon --no-progress --memory-limit=512M -vvv

Все параметры этих шагов уже описывались ранее. Здесь используется Docker-образ, который мы создали на самом первом шаге. Также здесь используются артефакты из composer-dev. При этом в каждой задаче создается свой кеш, который экономит время в последующих запусках.

Итог

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

Запуск отрабатывает успешно. Все запуски можно найти в разделе Build -> Pipelines

Pipeline CI-CD для приложения Laravel
Pipeline CI-CD для приложения Laravel

Полный код примера данного CI-CD с приложением Laravel можно найти в репозитории Gitlab.

4 1 голос
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии