GitLab CI Harbor
Harbor
Harbor — это открытый реестр с управлением контейнерами с открытым исходным кодом, предназначенный для предприятий. Он построен на основе Docker Registry и добавляет дополнительные функции, такие как поддержка Helm Charts, сборка с использованием BuildKit, а также поддержка нескольких репозиториев.
Harbor также предоставляет дополнительные функции, такие как аудит, учетные записи пользователей, ролевое управление доступом и автоматическое обновление доменов. Он может быть развернут на Kubernetes или на выделенных серверах и может быть настроен для работы в кластере высокой доступности.
Harbor может быть интегрирован с инструментами CI/CD, такими как Jenkins, GitLab CI/CD и TeamCity, для автоматической сборки и развертывания приложений на основе изменений кода. Он также может быть интегрирован с системами управления уязвимостями, такими как WhiteSource и Snyk, для обеспечения безопасности приложений. Кроме того, Harbor поддерживает различные форматы образов, такие как Docker, Open Container Initiative (OCI) и Flatcar Container Linux (FCL).
Создание проекта в Harbor
Создадим проект в harbor и robot account для аутентификации.
Разрешим этого роботу работать только с Registry и запретим удалять образы. Копируем токен и сохраняем, его будем использовать позже в Gitlab.
Теперь нам надо сформировать переменные окружение, которые мы будем использовать в нашем будущем CI. Запишем полученные от harbor данные в таком виде. Их мы будем использовать дальше, в наших CI скриптах.
harbor.yml
variables:
HARBOR_HOST: harbor.teta-mts.ru
HARBOR_PROJECT: demo
HARBOR_USER: robot_demo+gitlab
HARBOR_PASSWORD: $__HARBOR_PASSWORD
GItLab
Gitlab — это популярная система контроля версий, изначально написанная на ruby, однако многие части позже переписаны на GoLang, которая предназначена для разработчиков программного обеспечения. Она имеет множество возможностей, которые делают ее привлекательной для использования в различных проектах.
Одной из главных возможностей Gitlab является возможность управления репозиториями. Вы можете создавать новые репозитории, добавлять в них файлы и работать с ними. Также Gitlab позволяет работать с ветками кода, что позволяет разработчикам работать над разными задачами одновременно, не мешая друг другу.
Еще одной важной возможностью Gitlab является поддержка различных протоколов, таких как SSH, HTTPS и GIT.
Gitlab также имеет встроенный CI/CD (Continuous Integration/Continuous Delivery) инструмент, который позволяет автоматизировать процесс сборки, тестирования и развертывания приложений. Это значительно ускоряет процесс разработки и делает его более эффективным.
Кроме того, Gitlab имеет встроенный сервис управления задачами, который позволяет командам обмениваться информацией о задачах и контролировать их выполнение.
Версии GitLab
GitLab Community Edition (CE) является бесплатной версией GitLab с открытым исходным кодом. Она предназначена для небольших команд и индивидуальных разработчиков и предлагает базовые функции для контроля версий, такие как создание репозиториев, ветки кода, слияния и разрешения конфликтов.
Premium/Ultimate версия GitLab, также известная как GitLab Enterprise Edition (EE), предлагает расширенные функции и возможности, которые могут быть полезны для более крупных и сложных команд. Некоторые из этих функций включают в себя:
- Улучшенная масштабируемость и производительность;
- Управление доступом на основе ролей (Role-Based Access Control, RBAC);
- Интеграция со сторонними системами, такими как Jira и Trello
- Шифрование репозиториев с помощью OpenPGP;
- Поддержка нескольких сред выполнения, таких как Docker и Kubernetes;
- Мониторинг и аудит действий пользователей;
- Расширенные возможности CI/CD;
- Интеграция с системами управления уязвимостями, такими как WhiteSource;
- Многое другое.
Важно отметить, что Premium версия также предлагает поддержку от команды GitLab и доступ к обновлениям безопасности и новым функциям, которые могут не быть доступны в Community Edition.
Интерфейс
GitLab имеет простой и интуитивно понятный интерфейс, который позволяет пользователям легко находить нужную информацию и выполнять необходимые действия. Основной экран GitLab содержит список проектов, к которым у пользователя есть доступ, а также информацию о последних действиях в этих проектах.
Для каждого проекта отображается его название, владелец, описание, количество звезд и количество коммитов. При нажатии на проект открывается страница с подробной информацией о проекте, его репозиториях, merge request, issues и wiki. На странице репозитория можно просмотреть историю коммитов, скачать код или создать новый merge request или issue.
На странице merge request можно просмотреть все открытые и закрытые merge request по проекту, а также создать новый merge request из ветки в репозитории или из внешнего репозитория другой системы контроля версий.
На странице issues можно создавать, редактировать и закрывать задачи, а также назначать их конкретным разработчикам или группам. Wiki-страница позволяет создавать и редактировать документацию по проекту, которую могут просматривать все участники проекта.
В целом, интерфейс GitLab удобен для использования и позволяет быстро находить нужную информацию, управлять проектами и репозиториями, а также взаимодействовать с другими участниками команды.
Репозитории
Репозитории организованы в группы, вложенность групп может быть любая, а уникальность имен групп и репозиториев должна обеспечиваться только в рамках каждой группы. Таким образом, в рамках одного GitLab может существовать множество репозиториев с одним названием.
Например repo1, но находиться они должны в разных группах.
Например, наличие repo1:
Корректно
/group1/subgroup1/repo1
/group2/subgroup1/repo1
/group3/subgroup2/repo1
Каждый репозиторий имеет свои настройки. Однако принимает настройки всех вышестоящих над ним групп. Не обязательно настраивать каждый репозиторий отдельно, можно управлять настройками в рамках групп.
Группы видимости, доступа
В GitLab существуют следующие типы репозиториев:
Публичные репозитории — доступны для всех пользователей GitLab и могут быть клонированы любым пользователем.
Приватные репозитории — создаются для определенной команды или проекта и доступны только для авторизованных пользователей.
Внутренние репозитории (Internal Repositories) — используются для хранения кода внутри компании и доступны только сотрудникам компании.
Внешние репозитории (External Repositories) — позволяют работать с репозиториями сторонних систем контроля версий, таких как GitHub, Bitbucket и других.
Список доступа к репозиторию
Роли доступа
В GitLab есть несколько ролей доступа, которые определяют, какие действия пользователь может выполнять в проекте. Некоторые из них включают:
Owner — владелец проекта имеет полный доступ ко всем аспектам проекта, включая репозитории, merge requests, issues, wiki и настройки проекта.
Maintainer — сопровождающий имеет доступ к репозиториям и может создавать merge requests, но не может изменять настройки проекта или удалять репозитории.
Developer — разработчик имеет доступ только к репозиториям, в которых он участвует, и может создавать и отправлять изменения в коде.
Reporter (репортер) — это роль в GitLab, которая позволяет пользователю создавать и обновлять задачи (issues) в проекте. Репортеры могут описывать проблемы, добавлять ссылки на код и описывать предлагаемые решения. Они также могут комментировать на задачах и голосовать за них.
Guest — гость имеет доступ только к просмотру информации о проекте и может оставлять комментарии на issues и merge requests.
Подробно о правах ролей в документации:
https://docs.gitlab.com/ee/user/permissions.html
Protected branch and tags
Protected branch — это функция в GitLab, которая позволяет защитить определенные ветки в репозитории от случайных или нежелательных изменений. Она может быть полезна для веток, содержащих стабильную версию кода, или для веток с экспериментальным кодом, который не должен быть случайно объединен в основную ветку.
Protected branches позволяют определить правила, которые должны быть выполнены перед тем, как можно будет выполнить слияние в защищенную ветку. Эти правила могут включать требования к прохождению автоматических тестов, наличие определенного количества голосов за слияние или даже необходимость утверждения слияния от определенного члена команды.
Аналогичные настройки можно применять и к тэгам. Это помогает обеспечить качество кода и согласованность процесса разработки, а также предотвращает случайные изменения в важных частях кодовой базы.
Интеграции
GitLab имеет множество интеграций с другими системами, включая:
Jira для управления задачами и назначения их разработчикам;
Trello для организации работы в командах;
Jenkins для автоматического выполнения тестов и сборки проектов;
Slack для обмена сообщениями и уведомлениями о событиях в GitLab;
GitHub для совместной работы с репозиториями и обмена кодом между проектами;
Docker для управления контейнерами и их образами;
Kubernetes для управления кластерами и развертывания приложений в облаке.
Полный список можно посмотреть в GitLab или документации:
https://archives.docs.gitlab.com/15.11/ee/user/project/integrations/index.html
web-hooks
GitLab поддерживает несколько типов веб-хуков, которые позволяют автоматически выполнять определенные действия при изменении репозитория или других событиях в системе.
Некоторые из них включают:
Push Hook — срабатывает при каждом push-обновлении репозитория;
Merge Request Hook — срабатывает при создании, изменении или удалении merge request;
Issue Hook — срабатывает при создании, обновлении или закрытии issue;
Pipeline Hook — срабатывает после успешного завершения pipeline;
Tag Push Hook — срабатывает при добавлении новой тега в репозиторий.
Токены доступа
Токен доступа в GitLab используется для аутентификации при выполнении Git-запросов. Он позволяет заменить ваш пароль и обеспечить более безопасный способ подключения к репозиторию. Уровень доступа к токену определяет, какие операции можно выполнять с проектом, например, только чтение или чтение/запись. В GitLab существует несколько типов токенов доступа:
Personal access tokens (PAT) — персональный токен доступа, который может использоваться для доступа к GitLab API. Он может быть создан пользователем в своем профиле и имеет ограниченный срок действия.
Deploy tokens — токены развертывания, которые используются для аутентификации при выполнении задач развертывания. Они создаются проектом и имеют ограниченный срок действия и область видимости.
Job tokens — токены заданий, используемые для аутентификации в GitLab CI/CD. Они создаются заданиями и имеют ограниченный срок действия, область видимости и могут быть использованы только один раз.
System OAuth tokens — системные токены OAuth, которые используются для авторизации приложений на основе OAuth. Они создаются администратором GitLab и имеют неограниченный срок действия.
Merge Request
Merge request — это механизм в GitLab, который позволяет разработчикам предлагать изменения в основном репозитории на рассмотрение. Когда разработчик хочет внести изменения в существующий репозиторий, он создает новую ветку с изменениями, затем отправляет запрос на слияние (merge request) в основную ветку репозитория.
MR — основной функционал Gitlab. Имеет продвинутые настройки и гибкие настройки.
MR позволяет автоматизировать вливание нового кода в ветки, с помощью автоматизированных проверок через pipeline. А так же валидировать код устанавливая различные ограничения на вливание веток, например список лиц Reviewers — кто валидирует вносимые доработки.
Другие разработчики могут просмотреть предложенные изменения, обсудить их и принять или отклонить слияние. Если слияние принято, оно автоматически выполняется, и изменения добавляются в основную ветку репозитория. Если слияние отклонено, разработчик может внести изменения в свою ветку и отправить новый merge request.
GitLab API
GitLab API — это набор методов и протоколов, которые позволяют разработчикам и другим пользователям взаимодействовать с GitLab через программы и скрипты. Он предоставляет RESTful интерфейс для выполнения различных операций, таких как создание проектов, управление репозиториями, создание и обновление задач, просмотр статистики и многое другое.
API может быть использовано для автоматизации рутинных задач, интеграции GitLab с другими системами и приложениями, а также для создания пользовательских инструментов и расширений, которые улучшают работу с GitLab. GitLab API предоставляет следующие возможности:
Создание и управление проектами
Управление репозиториями
Создание, обновление и закрытие задач (issues)
Управление пулл-реквестами (pull requests)
Просмотр статистики проекта
Интеграция с другими системами через Webhooks
Аутентификация пользователей
Управление правами доступа пользователей к проектам и репозиториям
Получение информации о ходе выполнения Pipeline-ов
Работа с метками (tags)
и многое другое… https://docs.gitlab.com/ee/api/api_resources.html
GitLab CI
Рассмотрим интегрированный в GitLab инструмент для разработки CI/CD пайплайнов — GitLab CI. Верхнеуровнего GitLab CI состоит из следующих компонентов:
проект — код, над которым выполняются различные действия (сборка, статический анализ, запуск тестов, развертывание и т. д.), для конфигурирования пайплайнов в проекте (или группе проектов) можно задавать переменные CI/CD;
раннер — сервер, запускающий задачи CI/CD; он регулярно опрашивает сервер GitLab;
пайплайн — набор запускаемых задач CI/CD, каждая задача запускается независимо (иногда на разных раннерах).
GitLab СI пайплайн представляет собой YAML файл, задающий задачи и их зависимости. В задаче содержится информация, когда запускать задачу (правила), где ее запускать (теги раннера) и что в ней делать (скрипты). Действия в задаче описываются shell скриптом.
Локальная отладка пайплайнов
Для начала рассмотрим инструменты для локальной валидации и запуска пайплайнов. Мы, очевидно, можем загружать все изменения в GitLab и проверять пайплайны, запуская их, но это довольно долго.
GitLab CLI (glab)
В GitLab есть встроенный инструмент для синтаксической проверки. gitlab-ci.yml: ci lint. Его можно запускать из консольной утилиты glab.
Для начала создадим персональный токен доступа:
- Перейдите по ссылке;
- Введите в поле ввода Token name произвольное имя, например glab или gitlab-cli;
- Очистите Expiration date, нажав на крест;
- Выберите права: api;
- Нажмите на кнопку Create personal access token;
- Скопируйте сгенерированный токен из появившегося поля Your new personal access token;
- Сохраните токен в файл token. txt на ВМ1.
Теперь загрузим и настроим эту утилиту:
$ filename=$(curl -LOsw '%{filename_effective}' https://gitlab.com/gitlab-org/cli/-/releases/v1.34.0/downloads/glab_1.34.0_Linux_x86_64.deb)
$ sudo apt install --yes $(realpath -- "$filename")
$ glab auth login --stdin --hostname git.devops-teta.ru <token.txt
Запуск проверки синтаксиса с помощью glab:
$ glab ci lint .gitlab-ci.yml
Validating...
✓ CI/CD YAML is valid!
GitLab CI Local
Также существует сторонняя утилита gitlab-ci-local, которая позволяет валидировать пайплайны, запускать их целиком и задавать произвольные переменные. При помощи этой утилиты можно почти полностью тестировать пайплайны, но она реимплементирует GitLab CI, поэтому не стоит полностью полагаться на ее поведение.
Эта утилита написана на NodeJS, установим NodeJS 18 из Nodesource (в репозиториях Ubuntu находится версия 12, слишком старая для этого пакета):
$ sudo apt-get install --yes curl gnupg ca-certificates
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
$ echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
$ sudo apt-get update
$ sudo apt install nodejs -y
Запускать gitlab-ci-local будем командой:
$ npx --user --package gitlab-ci-local@4.44.0 -- gitlab-ci-local
Добавим алиас для этой команды в ~/.bashrc (или ~/.bash_profile), чтобы упростить запуск:
$ echo "alias gitlab-ci-local='npx --user --package gitlab-ci-local@4.44.0 -- gitlab-ci-local'" >> ~/.bashrc
Использовать эти инструменты будем далее.
Первый пайплайн
Вернемся к проекту counter-frontend и напишем для него сборочный пайплайн, который будет:
- компилировать проект;
- проверять исходный код при помощи eslint;
- публиковать Docker-образ в Harbor.
Fork проекта и .gitlab-ci.yml
Для начала форкнем проект:
- Перейдите в проект counter-frontend;
- Нажмите на кнопку Fork сверху справа;
- В качестве целевой выберите группу из п. 1.
После этого склонируйте проект и создайте в его корне файл. gitlab-ci.yml:
$ git clone git@git.devops-teta.ru:${YOUR_SUBGROUP}/counter-frontend.git
$ cd counter-frontend
$ touch .gitlab-ci.yml
Пайплайны GitLab CI состоят из задач (jobs), разделенных на стадии (stages). По-умолчанию задачи выполняются в соответствии с их стадиями, в одной стадии задачи выполняются параллельно.
Напишем скелет нашего пайплайна:
stages:
- build
- publish
# @NoArtifactsToSource
Lint:
stage: .pre
# @NoArtifactsToSource
Build Package:
stage: build
# @NoArtifactsToSource
Publish Package:
stage: publish
# @NoArtifactsToSource — аннотация gitlab-ci-local, пока мы ее проигнорируем
stages
Стадии задаются директивой stages, если она не указана, то пайплайн будет содержать следующие стадии: build, test, deploy. Также пайплайн всегда содержит две служебные стадии .pre и .post, задачи в .pre выполняются перед всеми остальными, .post — после.
Для более точного упорядочивания задач используют директиву needs, которая явно указывает зависимые задачи:
stages:
- build
- publish
# @NoArtifactsToSource
Lint:
stage: .pre
needs: []
# @NoArtifactsToSource
Build Package:
stage: build
needs: []
# @NoArtifactsToSource
Publish Package:
stage: publish
needs:
- Build Package
При указании needs задачи игнорируют свои стадии и запускаются после родительских. Далее мы будем указывать зависимости задач явно.
Теперь укажем, что именно выполняется в задаче.
Для этого воспользуемся директивами image и script. Первая указывает в каком Docker-образе запускается задача, вторая — shell-скрипт, выполняющийся в задаче.
stages:
- build
- publish
# @NoArtifactsToSource
Lint:
stage: .pre
needs: []
image:
name: registry.devops-teta.ru/materials/ci/images/nodejs:18.18.2-bookworm
entrypoint: [""]
script:
- echo 1
# @NoArtifactsToSource
Build Package:
stage: build
needs: []
image:
name: registry.devops-teta.ru/materials/ci/images/nodejs:18.18.2-bookworm
entrypoint: [""]
script:
- echo 2
# @NoArtifactsToSource
Publish Package:
stage: publish
needs:
- Build Package
image:
name: registry.devops-teta.ru/materials/ci/images/kaniko:1.9.1
entrypoint: [""]
script:
- echo 3
variables
Параметры пайплайна задаются при помощи переменных, директивой variables в пайплайне или в настройках проекта/группы. Эти переменные пробрасываются в скрипты как переменные окружения, также большинство директив пайплайна поддерживают подстановку переменных с синтаксисом, аналогичным Shell.
Вынесем имена образов в переменные:
stages:
- build
- publish
variables:
NODEJS_IMAGE: registry.devops-teta.ru/materials/ci/images/nodejs:18.18.2-bookworm
KANIKO_IMAGE: registry.devops-teta.ru/materials/ci/images/kaniko:1.9.1
# @NoArtifactsToSource
Lint:
stage: .pre
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
script:
- echo 1
# @NoArtifactsToSource
Build Package:
stage: build
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
script:
- echo 2
# @NoArtifactsToSource
Publish Package:
stage: publish
needs:
- job: Build Package
artifacts: true
image:
name: $KANIKO_IMAGE
entrypoint: [""]
script:
- echo 3
Build Package
Начнем наполнять пайплайн, сначала напишем скрипт сборки в Build Package:
variables:
NODEJS_IMAGE: registry.devops-teta.ru/materials/ci/images/nodejs:18.18.2-bookworm
# @NoArtifactsToSource
Build Package:
stage: build
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
script:
- npm clean-install
- npm run build
- mv .next/standalone dist
- mv public dist/public
- mv .next/static dist/.next/static
Запустим Build Package при помощи gitlab-ci-local:
$ gitlab-ci-local 'Build Package'
Using fallback git user.name
Using fallback git user.email
parsing and downloads finished in 82 ms
Build Package starting registry.devops-teta.ru/materials/ci/images/nodejs:18.18.2-bookworm (build)
Build Package copied to docker volumes in 5 s
Build Package $ npm clean-install
Build Package >
Build Package > added 282 packages, and audited 283 packages in 25s
Build Package >
Build Package > 107 packages are looking for funding
Build Package > run `npm fund` for details
Build Package >
Build Package > 3 vulnerabilities (1 low, 2 moderate)
Build Package >
Build Package > To address all issues, run:
Build Package > npm audit fix --force
Build Package >
Build Package > Run `npm audit` for details.
Build Package $ npm run build
Build Package >
Build Package > > counter@0.1.0 build
Build Package > > next build
Build Package >
Build Package > ⚠ No build cache found. Please configure build caching for faster rebuilds. Read more: https://nextjs.org/docs/messages/no-cache
Build Package > Attention: Next.js now collects completely anonymous telemetry regarding usage.
Build Package > This information is used to shape Next.js' roadmap and prioritize features.
Build Package > You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
Build Package > https://nextjs.org/telemetry
Build Package >
Build Package > Creating an optimized production build ...
Build Package > ✓ Compiled successfully
Build Package > Linting and checking validity of types ...
Build Package > Collecting page data ...
Build Package > Generating static pages (0/4) ...
Generating static pages (1/4)
Generating static pages (2/4)
Generating static pages (3/4)
✓ Generating static pages (4/4)
Build Package > Finalizing page optimization ...
Build Package >
Build Package > Route (app) Size First Load JS
Build Package > ┌ ○ / 897 B 80.1 kB
Build Package > └ ○ /_not-found 878 B 80.1 kB
Build Package > + First Load JS shared by all 79.2 kB
Build Package > ├ chunks/864-65fc85184976041b.js 26.6 kB
Build Package > ├ chunks/fd9d1056-cda7bdae809f14dd.js 50.8 kB
Build Package > ├ chunks/main-app-0cd0fef21f043671.js 221 B
Build Package > └ chunks/webpack-bf1a64d1eafd2816.js 1.61 kB
Build Package >
Build Package >
Build Package > ○ (Static) automatically rendered as static HTML (uses no initial props)
Build Package >
Build Package $ mv .next/standalone dist
Build Package $ mv public dist/public
Build Package $ mv .next/static dist/.next/static
Обратим внимание на предупреждения сборщика NextJS: ему не нравится отсутствие кеша сборки, также нас предупреждают о сборе телеметрии. Выключим сбор телеметрии и добавим кеширование.
Телеметрия выключается переменной окружения NEXT_TELEMETRY_DISABLED:
Build Package:
stage: build
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
variables:
NEXT_TELEMETRY_DISABLED: "1"
script:
- npm clean-install
- npm run build
- mv .next/standalone dist
- mv public dist/public
- mv .next/static dist/.next/static
Переменные можно задавать на уровне пайплайна и конкретных задач. Используйте глобальные переменные, чтобы указывать переменные использующиеся в нескольких задачах или параметры пайплайна, которые могут быть переопределены пользователями в параметрах группы/проекта или при ручном запуске пайплайна/задачи. Переменные задач следует использовать для непереопределяемых констант.
cache
Перейдем к кешированию. GitLab CI поддерживает кеширование файлов для ускорения сборок. В отличие от артефактов кеш хранится в раннере и необязателен для успешного запуска задачи. В Build Package мы можем закешировать кеш пакетного менеджера npm, установленные пакеты в папке node_modules и сборочный кеш NextJS в .next/cache:
# @NoArtifactsToSource
Build Package:
stage: build
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
variables:
NEXT_TELEMETRY_DISABLED: "1"
NPM_CONFIG_CACHE: $CI_PROJECT_DIR/.cache/npm
cache:
- key: npm-packages
paths:
- .cache
unprotect: true
- key:
prefix: npm-node-modules
files:
- package-lock.json
paths:
- node_modules
unprotect: true
- key: npm-next-cache
paths:
- .next/cache
script:
- if [ ! -e node_modules ] ; then npm clean-install ; fi
- npm run build
- mv .next/standalone dist
- mv public dist/public
- mv .next/static dist/.next/static
Параметры кеширования задаются директивой cache. Ключом кеширования является либо строка, либо хеш файлов. Здесь мы используем lock-файл как ключ кеширования установленных модулей, чтобы инвалидировать кеш при любом изменении зависимостей.
По умолчанию GitLab CI отделяет кеш для запущенных в защищенных ветках задач, опция unprotect: true отключает это поведение. Таким образом мы ускорим сборки в защищенных ветках, но пользователи смогут переопределить пайплайн в незащищенной ветке и переписать значение кеша.
artifacts
Собранный проект нужно передать в задачу Publish Package, чтобы упаковать его в Docker-образ. Для передачи информации между задачами используются артефакты:
# @NoArtifactsToSource
Build Package:
stage: build
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
variables:
NEXT_TELEMETRY_DISABLED: "1"
NPM_CONFIG_CACHE: $CI_PROJECT_DIR/.cache/npm
cache:
- key: npm-packages
paths:
- .cache
unprotect: true
- key:
prefix: npm-node-modules
files:
- package-lock.json
paths:
- node_modules
unprotect: true
- key: npm-next-cache
paths:
- .next/cache
script:
- if [ ! -e node_modules ] ; then npm clean-install ; fi
- npm run build
- mv .next/standalone dist
- mv public dist/public
- mv .next/static dist/.next/static
artifacts:
expire_in: 1h
paths:
- dist
Директива artifacts задает параметры экспорта артефактов из задачи. Время жизни артефактов задается параметром expire_in, здесь мы указали один час. Артефакты задач из последнего запущенного пайплайна не подчиняются этому правилу, они хранятся постоянно, если такое поведение не выключено в настройках проекта.
Lint
Напишем скрипт для задачи Lint:
# @NoArtifactsToSource
Lint:
stage: .pre
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
variables:
ESLINT_CODE_QUALITY_REPORT: eslint.codequality.json
NEXT_TELEMETRY_DISABLED: "1"
NPM_CONFIG_CACHE: $CI_PROJECT_DIR/.cache/npm
cache:
- key: npm-packages
paths:
- .cache
- key:
prefix: npm-node-modules
files:
- package-lock.json
paths:
- node_modules
script:
- if [ ! -e node_modules ] ; then npm clean-install ; fi
- npm run lint -- --format gitlab ## В ситуациях, когда npm-скрипт запускается из другого npm-скрипта, перед передачей флага CLI также необходимо добавить два тире.
artifacts:
paths:
- $ESLINT_CODE_QUALITY_REPORT
reports:
codequality: $ESLINT_CODE_QUALITY_REPORT
Артефакты также используются для генерации различных отчетов. Здесь мы сканируем проект статическим анализатором eslint и экспортируем его вывод в формате, поддерживаемом GitLab. Другие поддерживаемые отчеты смотрите в документации.
Запустим задачу Lint:
$ gitlab-ci-local Lint
parsing and downloads finished in 71 ms
Lint starting registry.devops-teta.ru/materials/ci/images/nodejs:18.18.2-bookworm (.pre)
Lint copied to docker volumes in 5 s
Lint imported cache 'npm-packages' in 5 s
Lint $ if [ ! -e node_modules ] ; then npm clean-install ; fi
Lint >
Lint > added 282 packages, and audited 283 packages in 13s
Lint >
Lint > 107 packages are looking for funding
Lint > run `npm fund` for details
Lint >
Lint > 3 vulnerabilities (1 low, 2 moderate)
Lint >
Lint > To address all issues, run:
Lint > npm audit fix --force
Lint >
Lint > Run `npm audit` for details.
Lint $ npm run lint -- --format gitlab
Lint >
Lint > > counter@0.1.0 lint
Lint > > next lint --format GitLab
Lint >
Lint > ✔ No ESLint warnings or errors
Lint finished in 5 s
Lint exported cache .cache 'npm-packages' in 5 s
Lint exported cache node_modules 'md-d21182c067aca226931e07dfadf5d468a9ab9e7d' in 5 s
Lint exported artifacts in 5 s
Publish Package
Docker DIND
Docker DIND (Docker in Docker) — это техника, которая позволяет запускать контейнеры Docker внутри других контейнеров Docker. Это полезно в ситуациях, когда требуется изоляция или когда основное приложение должно работать внутри контейнера для обеспечения безопасности или стандартизации окружения.
Docker DIND работает, запуская Docker-демона (обычно dockerd) внутри уже работающего контейнера Docker. Это позволяет использовать Docker-команды внутри контейнера, не влияя на хост-систему. Основной Docker-демон (на хост-системе) управляет контейнерами, которые запускаются внутри других контейнеров. Это обеспечивает изоляцию контейнеров и гарантирует, что они не будут иметь доступ к ресурсам хост-системы.
В результате Docker DIND обеспечивает повышенную безопасность и изоляцию, позволяя разработчикам и системным администраторам запускать Docker-контейнеры внутри контейнеров для тестирования или разработки.
Kaniko
Kaniko — это инструмент для создания Docker-образов из Git-репозиториев. Он работает путем извлечения исходного кода из Git, компиляции его в целевой системе и создания Docker-образа из скомпилированных файлов.
Для сборки образа воспользуемся kaniko. Она поддерживает не все директивы Dockerfile, но позволяет собирать контейнеры в кластерах Kubernetes и Docker контейнерах без дополнительных настроек.
Детали по сборке при помощи kaniko смотрите в документации.
Напишем скрипт Publish Package:
Publish Package:
stage: publish
needs:
- job: Build Package
artifacts: true
interruptible: false
image:
name: $KANIKO_IMAGE
entrypoint: [""]
variables:
HARBOR_HOST: harbor.devops-teta.ru
HARBOR_PROJECT: demo
HARBOR_USER: robot_demo+gitlab
HARBOR_PASSWORD: $__HARBOR_PASSWORD
script:
- b64_auth=$(printf '%s:%s' "$HARBOR_USER" "$HARBOR_PASSWORD" | base64 | tr -d '\n')
- >-
printf '{"auths": {"%s": {"auth": "%s"}}}' "$HARBOR_HOST" "$b64_auth"
>/kaniko/.docker/config.json
- >-
/kaniko/executor
--cache
--use-new-run
--skip-unused-stages
--context "$CI_PROJECT_DIR"
--dockerfile "$CI_PROJECT_DIR/Dockerfile"
--destination "$HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}"
--cache-repo "$HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME/cache"
Здесь мы используем переменные которые получили, когда создавали проект в harbor:
HARBOR_HOST — адрес реестра контейнеров;
HARBOR_USER — имя пользователя для авторизации в реестре;
HARBOR_PASSWORD — пароль для авторизации в реестре;
HARBOR_PROJECT — имя загружаемого образа;
В качестве тега воспользуемся комбинацией имени ветки (CI_COMMIT_REF_NAME) и первых восьми символов хеша коммита (CI_COMMIT_SHORT_SHA).
Обе эти переменные предопределны и доступны всем задачам, также доступно множество других предопределенных переменных, полный список смотрите в документации.
Также обратим внимание на директиву interruptible: false — она значит, что задача не будет отменена, если запустится новый такой же пайплайн. Используйте этот параметр для задач, создающих побочные эффекты во внешних системах, особенно в задачах, выполняющих развертывания.
Получим следующий пайплайн:
stages:
- build
- publish
variables:
NODEJS_IMAGE: registry.devops-teta.ru/materials/ci/images/nodejs:18.18.2-bookworm
KANIKO_IMAGE: registry.devops-teta.ru/materials/ci/images/kaniko:1.9.1
# @NoArtifactsToSource
Lint:
stage: .pre
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
variables:
ESLINT_CODE_QUALITY_REPORT: eslint.codequality.json
NEXT_TELEMETRY_DISABLED: "1"
NPM_CONFIG_CACHE: $CI_PROJECT_DIR/.cache/npm
cache:
- key: npm-packages
paths:
- .cache
- key:
prefix: npm-node-modules
files:
- package-lock.json
paths:
- node_modules
script:
- if [ ! -e node_modules ] ; then npm clean-install ; fi
- npm run lint -- --format gitlab
artifacts:
paths:
- $ESLINT_CODE_QUALITY_REPORT
reports:
codequality: $ESLINT_CODE_QUALITY_REPORT
# @NoArtifactsToSource
Build Package:
stage: build
needs: []
image:
name: $NODEJS_IMAGE
entrypoint: [""]
variables:
NEXT_TELEMETRY_DISABLED: "1"
NPM_CONFIG_CACHE: $CI_PROJECT_DIR/.cache/npm
cache:
- key: npm-packages
paths:
- .cache
unprotect: true
- key:
prefix: npm-node-modules
files:
- package-lock.json
paths:
- node_modules
unprotect: true
- key: npm-next-cache
paths:
- .next/cache
script:
- if [ ! -e node_modules ] ; then npm clean-install ; fi
- npm run build
- mv .next/standalone dist
- mv public dist/public
- mv .next/static dist/.next/static
artifacts:
expire_in: 1h
paths:
- dist
Publish Package:
stage: publish
needs:
- job: Build Package
artifacts: true
interruptible: false
image:
name: $KANIKO_IMAGE
entrypoint: [""]
variables:
HARBOR_HOST: harbor.devops-teta.ru
HARBOR_PROJECT: demo
HARBOR_USER: robot_demo+gitlab
HARBOR_PASSWORD: $__HARBOR_PASSWORD
script:
- b64_auth=$(printf '%s:%s' "$HARBOR_USER" "$HARBOR_PASSWORD" | base64 | tr -d '\n')
- >-
printf '{"auths": {"%s": {"auth": "%s"}}}' "$HARBOR_HOST" "$b64_auth"
>/kaniko/.docker/config.json
- >-
/kaniko/executor
--cache
--use-new-run
--skip-unused-stages
--context "$CI_PROJECT_DIR"
--dockerfile "$CI_PROJECT_DIR/Dockerfile"
--destination "$HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}"
--cache-repo "$HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME/cache"
Update Cache
Вынесем повторяющиеся переменные на уровень пайплайна и добавим задачу Update Cache, которая будет инициализировать кеш npm. Ключи кеширования общие на проект, поэтому мы сможем переиспользовать кеш в нескольких задачах. При этом следует скопировать код инициализации кеша во все задачи, которые его используют, т.к. ошибка при получении кеша не приводит к падению задачи.
stages:
- build
- publish
variables:
NODEJS_IMAGE: registry.devops-teta.ru/materials/ci/images/nodejs:18.18.2-bookworm
KANIKO_IMAGE: registry.devops-teta.ru/materials/ci/images/kaniko:1.9.1
NEXT_TELEMETRY_DISABLED: "1"
NPM_CONFIG_CACHE: $CI_PROJECT_DIR/.cache/npm
Update Cache:
stage: .pre
needs: []
allow_failure: true
image:
name: $NODEJS_IMAGE
entrypoint: [""]
cache:
- key: npm-packages
paths:
- .cache
unprotect: true
- key:
prefix: npm-node-modules
files:
- package-lock.json
paths:
- node_modules
unprotect: true
script:
- if [ ! -e node_modules ] ; then npm clean-install ; fi
# @NoArtifactsToSource
Lint:
stage: .pre
needs:
- job: Update Cache
artifacts: false
allow_failure: true
image:
name: $NODEJS_IMAGE
entrypoint: [""]
variables:
ESLINT_CODE_QUALITY_REPORT: eslint.codequality.json
cache:
- key: npm-packages
paths:
- .cache
unprotect: true
policy: pull
- key:
prefix: npm-node-modules
files:
- package-lock.json
paths:
- node_modules
unprotect: true
policy: pull
script:
- if [ ! -e node_modules ] ; then npm clean-install ; fi
- npm run lint -- --format gitlab
artifacts:
paths:
- $ESLINT_CODE_QUALITY_REPORT
reports:
codequality: $ESLINT_CODE_QUALITY_REPORT
# @NoArtifactsToSource
Build Package:
stage: build
needs:
- job: Update Cache
artifacts: false
image:
name: $NODEJS_IMAGE
entrypoint: [""]
cache:
- key: npm-packages
paths:
- .cache
unprotect: true
policy: pull
- key:
prefix: npm-node-modules
files:
- package-lock.json
paths:
- node_modules
unprotect: true
policy: pull
- key: npm-next-cache
paths:
- .next/cache
script:
- if [ ! -e node_modules ] ; then npm clean-install ; fi
- npm run build
- mv .next/standalone dist
- mv public dist/public
- mv .next/static dist/.next/static
artifacts:
expire_in: 1h
paths:
- dist
Publish Package:
stage: publish
needs:
- job: Build Package
artifacts: true
interruptible: false
image:
name: $KANIKO_IMAGE
entrypoint: [""]
variables:
HARBOR_HOST: harbor.devops-teta.ru
HARBOR_PROJECT: demo
HARBOR_USER: robot_demo+gitlab
HARBOR_PASSWORD: $__HARBOR_PASSWORD
script:
- b64_auth=$(printf '%s:%s' "$HARBOR_USER" "$HARBOR_PASSWORD" | base64 | tr -d '\n')
- >-
printf '{"auths": {"%s": {"auth": "%s"}}}' "$HARBOR_HOST" "$b64_auth"
>/kaniko/.docker/config.json
- >-
/kaniko/executor
--cache
--use-new-run
--skip-unused-stages
--context "$CI_PROJECT_DIR"
--dockerfile "$CI_PROJECT_DIR/Dockerfile"
--destination "$HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME:${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHORT_SHA}"
--cache-repo "$HARBOR_HOST/$HARBOR_PROJECT/$CI_PROJECT_NAME/cache"
Добавим к задачам Update Cache и Lint параметр allow_failure: true, который указывает, что падение этой задачи не должно приводить к падению пайплайна. Также добавим параметр policy: pull к кешам в задачах Lint и Build Package, которая укажет, что после выполнения этих задач не следует обновлять кеш.
Мы получили свой первый пайплайн собирающий наше приложение и загружающий финальный докер образ в Harbor.
Так же имея образ в харбор, можно видеть работу еще одного модуля Harbor — Trivy.