Pull to refresh
Флант
DevOps-as-a-Service, Kubernetes, обслуживание 24×7

Автоматизируем сборку и деплой приложения в GitLab CI/CD: подробное руководство с примерами

Level of difficultyEasy
Reading time22 min
Views20K

При разработке приложений рано или поздно наступает момент, когда заниматься развёртыванием вручную становится затратно и неудобно. Как следствие на помощь приходит автоматизация этого процесса с помощью специально настроенных пайплайнов непрерывной интеграции и непрерывной доставки (Continuous Integration & Continuous Delivery — CI/CD). Для разных систем управления репозиториями исходного кода существуют свои способы настройки CI/CD.

В этой статье мы рассмотрим, как использовать GitLab для организации автоматической сборки и деплоя приложения в кластер Kubernetes. Сам кластер будет работать под управлением Deckhouse Kubernetes Platform (код приложения и процесс настройки кластера мы описывали в предыдущей статье цикла), а автоматизировать процесс будем с помощью werf — Open Source CLI-утилиты, организующей полный цикл доставки приложения в Kubernetes и использующей Git как единый источник истины для состояния приложения, развёрнутого в кластере.

Содержание:

Установка GitLab

Системные требования и подготовка окружения

Установить GitLab можно несколькими способами, начиная от установки пакета через пакетный менеджер дистрибутива и заканчивая Helm-чартом на случай установки в кластер Kubernetes. Мы воспользуемся первым вариантом, поэтому для начала нам нужно подготовить ВМ для будущего инстанса GitLab. Кстати, устанавливать мы будем бесплатную версию — GitLab Community Edition.

Подробнее о версиях и лицензиях на GitLab

Лирическое отступление или немного о редакциях GitLab

GitLab — одно из популярных решений для хранения исходного кода и организации CI/CD. Этот инструмент позволяет в удобном веб-интерфейсе управлять Git-репозиториями, организовать базу знаний с помощью встроенной Wiki, отслеживать ошибки и управлять сборкой и тестированием приложения в гибко настраиваемых пайплайнах CI/CD.

Появился GitLab в 2011 году. Изначально это был проект двух разработчиков (Дмитрия Запорожца и Валерия Сизова), созданный для собственных нужд с целью улучшения совместной разработки и управления исходным кодом. Но со временем он превратился в интегрированное решение, охватывающее весь жизненный цикл разработки ПО, а затем и DevOps.

Это свободное программное обеспечение, распространяемое под лицензией MIT. Но тем не менее, помимо основного ядра, у GitLab есть дополнительные модули, доступные к использованию только с покупкой лицензии.

GitLab предоставляет возможность использовать как SaaS-решение, так и self-hosted-установку. Для SaaS предоставляются три варианта цен — Free, Premium и Ultimate. Подробнее с ними можно ознакомиться на официальной странице.

Для установки на собственное железо лицензий всего две: Community Edition и Enterprise Edition. Причем с недавних пор они слиты воедино: устанавливается всегда EE, но если не оплачена лицензия на платные модули, она работает как CE, просто такие модули будут недоступны, а пользователю предложат купить лицензию.

После начала всем известных событий компания GitLab Inc. прекратила поддержку и продажи EE на территории РФ, а также заблокировала доступ к репозиториям ЕЕ с IP-адресов страны. Это вызвало некоторые проблемы при установке свежих версий и обновлении старых: обновление пакетов не происходит, возвращая ошибку 403. Убедиться в этом можно, пройдя по ссылке с пакетами ЕЕ:

При этом CE доступна как и раньше без каких-либо ограничений:

Решить проблему с доступом можно попытаться различными средствами, многие из которых уже давно описаны в интернете (VPN или реверс-прокси на VDS, где есть доступ к нужным репозиториям). Но для задач, решение которых мы рассмотрим в этой статье, достаточно и CE, поэтому устанавливать мы будем именно её.

Минимальные требования к виртуальной машине такие: минимум 4 ядра CPU и 4 ГБ RAM. Если вы планируете, что у вас будет более 500 пользователей, то требования будут выше — подробно ознакомиться с ними можно на соответствующей странице документации. Объёмы хранилища также сильно зависят от задач, для которых будет использоваться GitLab: например, container registry необходимо много места, а просто для хранения исходного кода в Git-репозитории требования довольно скромные. Для теста мы возьмём машину с 40 ГБ места на SSD.

На ВМ должна быть установлена одна из поддерживаемых ОС, в нашем случае — Ubuntu 22.04 LTS. Если возможности выбрать ОС у вас нет, рекомендуем ознакомиться со списком версий, которые больше не поддерживаются.

Также нам необходимо подготовить доменное имя, по которому будет доступен инстанс GitLab. Лучше иметь для этого реальный домен, например gitlab.example.com, ведущий на IP-адрес ВМ, так как для получения HTTPS-сертификата Let's Encrypt понадобится доступ к машине извне по этому домену для проверки валидности. В случае же тестового запуска этим можно пренебречь и просто прописать нужный адрес в hosts или воспользоваться сервисом вроде sslip.io.

Установка GitLab

Пришло время выполнить шаги, описанные в официальной документации.

Сначала установим необходимые зависимости:

$ sudo apt-get update
$ sudo apt-get install -y curl openssh-server ca-certificates tzdata perl

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

Следующим шагом установим Postfix — он необходим для отправки сообщений на электронную почту:

sudo apt-get install -y postfix

В конце установки Postfix надо будет ответить на вопрос об адресе, с которого будет отправляться почта:

Здесь нужно указать доменное имя ВМ: gitlab.example.com. По этому доменному имени должен быть доступен ваш инстанс GitLab.

Теперь добавим репозиторий GitLab в список источников:

curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash

Обратите внимание, что в документации по умолчанию стоит gitlab-ee, но нам для установки бесплатной версии CE необходимо заменить это значение на gitlab-ce (в нашем примере всё уже исправлено). В противном случае при установке мы столкнёмся с ошибкой 403 во время обновления списка пакетов.

В результате выполнения команды будет добавлен новый источник в /etc/apt/sources.list:

Detected operating system as Ubuntu/jammy.
Checking for curl...
Detected curl...
Checking for gpg...
Detected gpg...
Running apt-get update... done.
Installing apt-transport-https... done.
Installing /etc/apt/sources.list.d/gitlab_gitlab-ce.list...done.
Importing packagecloud gpg key... done.
Running apt-get update... done.


The repository is setup! You can now install packages.

Запустим установку GitLab следующей командой:

sudo EXTERNAL_URL="https://gitlab.example.com" apt-get install gitlab-ce

Обратите внимание на переменную окружения EXTERNAL_URL — в ней необходимо указать доменное имя, по которому будет доступен инстанс GitLab.

Установка может занять продолжительное время. После скачивания и распаковки пакета (1,2 ГБ) сразу начнётся процесс его конфигурации. По окончании в терминале отобразится сообщение об успешной установке и настройке:

gitlab Reconfigured!


       *.                  *.
      ***                 ***
     *****               *****
    .******             *******
    ********            ********
   ,,,,,,,,,***********,,,,,,,,,
  ,,,,,,,,,,,*********,,,,,,,,,,,
  .,,,,,,,,,,,*******,,,,,,,,,,,,
      ,,,,,,,,,*****,,,,,,,,,.
         ,,,,,,,****,,,,,,
            .,,,***,,,,
                ,*,.






     _______ __  __          __
    / ____(_) /_/ /   ____ _/ /_
   / / __/ / __/ /   / __ `/ __ \
  / /_/ / / /_/ /___/ /_/ / /_/ /
  \____/_/\__/_____/\__,_/_.___/




Thank you for installing GitLab!
GitLab should be available at https://gitlab.zhbert.ru


For a comprehensive list of configuration options please see the Omnibus GitLab readme
https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md


Help us improve the installation experience, let us know how we did with a 1 minute survey:
https://gitlab.fra1.qualtrics.com/jfe/form/SV_6kVqZANThUQ1bZb?installation=omnibus&release=16-3

Перейдем по адресу, указанному как EXTERNAL_URL:

GitLab развёрнут и готов к работе!

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

Notes:
Default admin account has been configured with following details:
Username: root
Password: You didn't opt-in to print initial root password to STDOUT.
Password stored to /etc/gitlab/initial_root_password. This file will be cleaned up in first reconfigure run after 24 hours.

Пароль будет доступен в течение 24 часов, поэтому лучше сразу его извлечь:

$ sudo cat /etc/gitlab/initial_root_password
# WARNING: This value is valid only in the following conditions
#          1. If provided manually (either via `GITLAB_ROOT_PASSWORD` environment variable or via `gitlab_rails['initial_root_password']` setting in `gitlab.rb`, it was provided before database was seeded for the first time (usually, the first reconfigure run).
#          2. Password hasn't been changed manually, either via UI or via command line.
#
#          If the password shown here doesn't work, you must reset the admin password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password.


Password: O/pagDzHRbNIBzMKTqSwWlu1BK6+QVo=


# NOTE: This file will be automatically deleted in the first reconfigure run after 24 hours.

…и войти в систему. Здесь нам сразу предложат деактивировать возможность свободной регистрации в системе, и мы рекомендуем сделать это, если, конечно, вам не нужно сделать этот инстанс GitLab доступным для всего интернета:

После нажатия на кнопку необходимо снять галочку с пункта «Sign-up enabled»:

Теперь осталось только изменить пароль и данные учётной записи (имя пользователя). Вместо стоящего по умолчанию root можно придумать свой логин:

Обратите внимание, что доступ к GitLab уже осуществляется по HTTPS: во время установки и конфигурации он сам получил сертификаты по указанному доменному имени. Если оно недоступно снаружи — из интернета, GitLab не сможет получить сертификаты.

Теперь сменим пароль — для этого надо будет ввести текущий пароль и придумать новый:

Включение сборщика мусора для container registry

Хранилище образов включено по умолчанию, но сборка мусора в нём не выполняется автоматически. Включить её можно, добавив в файл /etc/cron.d/registry-garbage-collect следующее содержимое:

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin


# Run every Sunday at 04:05am
4 * * 0  root gitlab-ctl registry-garbage-collect

Это включит автоматический запуск сборки мусора по воскресеньям в 4:05 утра, который будет проверять содержимое registry и удалять неиспользуемые слои и манифесты, чтобы не засорять место.

Ура! GitLab готов к работе!

Создание проекта в GitLab

Настройка доступа по SSH

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

Получим публичную часть нашего SSH-ключа:

cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yFnpC9tQirSUAUbtlXKGM29xAaQlYQfOm+8RuXkca07BJ1d39Yhrhj6A21gHdcUKYMT0iRs6R83URz36vc5FW0FU1e+DIvXUQ/1QZRQ6l39FsHTOAbNfCCGMJu2MIanrjgJAI0Wew00t+kPwHK/GgtzYG8Bx7YLJaDgfAH1ZBKsl9KxPD2kddt5S0xeYDo2l5/j7P3wmZ/x4yOhvmlCWsuuOIr3wpVXzdwZKU9gUQQRg3mUMxAxVazDrBDvhdUqoVubyqRUTfFWHyOlCw6hc= zhbert@MacBook-Pro-Konstantin.local

Если вы ещё не сгенерировали себе ключ SSH, сделать это можно командой ssh-keygen -t rsa.

Перейдём в настройки пользователя:

В разделе «SSH Keys» нажмём кнопку «Add new key»:

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

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

Вернувшись на страницу со всеми ключами, можно проверить, что наш новый ключ появился в списке:

Если нажать на его название, раскроется полное описание ключа:

Здесь же ключ можно удалить.

Теперь у нас есть доступ к репозиториям в GitLab по SSH.

Создание нового проекта в GitLab

Теперь перенесём проект в новый репозиторий в нашем свежеустановленном GitLab. Проект мы уже подготовили, скачать его можно по ссылке.

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

Для начала создадим новый проект. Это можно сделать через главную страницу: либо нажав большую иконку «Create a project», либо кликнув по кнопке с плюсом в верхнем левом углу экрана:

В открывшемся окне выбираем «Create blank project»:

На следующем экране указываем название проекта и тип «Private» (чтобы он был доступен только зарегистрированным пользователям с определёнными правами доступа). В поле «Project URL» нужно выбрать либо своего пользователя, либо группу, в которой будет размещён проект. Групп у нас нет, так что мы выбираем своего пользователя.


Нажмём «Create project». Наш проект создан, а кроме того, в нём автоматически сгенерировался объёмный файл README, потому что по умолчанию стоит флажок «Initialize repository with a README»:

Перенос проекта

Для переноса проекта сначала клонируем его на компьютер. Для этого скопируем адрес репозитория из выпадающего меню, доступного по кнопке «Clone»:

Если вы не настраивали доступ по SSH на предыдущем шаге, клонировать репозиторий нужно по ссылке «Clone with HTTPS». При этом понадобится ввести имя пользователя и пароль.

Перейдём в каталог, в котором будем работать, и клонируем в него репозиторий:

$ cd ~/test
~/test$ git clone git@gitlab.zhbert.ru:zhbert/habr-test.git
Cloning into 'habr-test'...
remote: Enumerating objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 3
Receiving objects: 100% (3/3), done.

Клонированный репозиторий находится в каталоге с именем habr-test, соответствующим названию проекта в GitLab. Откроем его и перенесём туда наше приложение. В результате должно получиться что-то подобное:

~/test/habr-test$ tree -a -L 1
.
├── .DS_Store
├── .configs
├── .git
├── .gitignore
├── .helm
├── README.md
├── backend
├── docker-compose.yml
├── frontend
└── werf.yaml

Закоммитим изменения и отправим их в удалённый репозиторий в GitLab:

~/test/habr-test$ git add -A


~/test/habr-test$ git commit -m "Add application files"
[main 195c886] Add application files
files changed, 12602 insertions(+), 92 deletions(-)
 create mode 100644 .configs/nginx.conf
 create mode 100644 .gitignore
 create mode 100644 .helm/templates/database.yaml
 create mode 100644 .helm/templates/deployment-backend.yaml
 create mode 100644 .helm/templates/deployment-frontend.yaml
 create mode 100644 .helm/templates/ingress.yaml
 create mode 100644 .helm/templates/job-db-setup-and-migrate.yaml
 create mode 100644 .helm/templates/service-backend.yaml
 create mode 100644 backend/Dockerfile
 create mode 100644 backend/cmd/main.go
 create mode 100644 backend/db/migrations/000001_create_talkers_table.down.sql
 create mode 100644 backend/db/migrations/000001_create_talkers_table.up.sql
 create mode 100644 backend/go.mod
 create mode 100644 backend/go.sum
 create mode 100644 backend/internal/app/app.go
 create mode 100644 backend/internal/common/json_logger_filter.go
 create mode 100644 backend/internal/controllers/db_controllers.go
 create mode 100644 backend/internal/services/db_service.go
 create mode 100644 docker-compose.yml
 create mode 100644 frontend/.gitignore
 create mode 100644 frontend/Dockerfile
 create mode 100644 frontend/README.md
 create mode 100644 frontend/babel.config.js
 create mode 100644 frontend/jsconfig.json
 create mode 100644 frontend/package-lock.json
 create mode 100644 frontend/package.json
 create mode 100644 frontend/public/favicon.ico
 create mode 100644 frontend/public/index.html
 create mode 100644 frontend/src/App.vue
 create mode 100644 frontend/src/assets/logo.png
 create mode 100644 frontend/src/components/HelloWorld.vue
 create mode 100644 frontend/src/main.js
 create mode 100644 frontend/vue.config.js
 create mode 100644 werf.yaml


~/test/habr-test$ git push origin main
Enumerating objects: 56, done.
Counting objects: 100% (56/56), done.
Delta compression using up to 10 threads
Compressing objects: 100% (43/43), done.
Writing objects: 100% (54/54), 124.04 KiB | 7.75 MiB/s, done.
Total 54 (delta 0), reused 0 (delta 0), pack-reused 0
To gitlab.zhbert.ru:zhbert/habr-test.git
   fdd826b..195c886  main -> main

Все изменения зафиксированы и отправлены. Проверим, что они отобразились в веб-интерфейсе:

Приложение загружено.

Подготовка CI/CD

В этом разделе мы подготовим машину, на которой будут выполнять задачи CI/CD, и подключим её к GitLab.

Конфигурация раннера

Для выполнения каких-либо заданий в рамках пайплайна CI/CD GitLab’у требуется раннер, на котором он будет выполнять работу. Это такая утилита, устанавливаемая на машину, которая связывается с GitLab и позволяет выполнять на этой машине задачи, указанные в пайплайне CI/CD. Раннер может быть развернут как на ВМ с Linux, macOS, FreeBSD или Windows на борту, так и в Docker-контейнере или даже в самом кластере Kubernetes.

Для нашей задачи подойдёт вариант с отдельной ВМ, на которой будут выполняться все работы. Подготовим машину со следующими характеристиками: 2 ядра CPU, 4 ГБ RAM, 40 ГБ свободного места и ОС Ubuntu 22.04 LTS.

На машине должны быть установлены:

  • Bash;

  • Git версии 2.18.0 или выше;

  • GPG;

  • Docker Engine.

Скорее всего, всё, кроме Docker Engine, будет сразу установлено на вашей машине, так что мы опишем только его установку (вот ссылка на официальную инструкцию).

Установка Docker Engine

Обновим индексы пакетов и установим необходимые зависимости:

$ sudo apt-get update
$ sudo apt-get install ca-certificates curl gnupg

Добавим GPG-ключ для проверки подписей устанавливаемых пакетов:

$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg

Подключим новый репозиторий:

$ echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Обновим список доступных пакетов и установим Docker Engine:

$ sudo apt-get update


$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Убедимся, что всё работает:

$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
719385e32844: Pull complete
Digest: sha256:dcba6daec718f547568c562956fa47e1b03673dd010fe6ee58ca806767031d1c
Status: Downloaded newer image for hello-world:latest


Hello from Docker!
This message shows that your installation appears to be working correctly.


To generate this message, Docker took the following steps:
The Docker client contacted the Docker daemon.
The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.


To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash


Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/


For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Установка, настройка и подключение GitLab Runner

Чтобы GitLab мог использовать ВМ как раннер, нужно установить на неё специальный агент, который позволит им наладить связь между собой.

Добавим репозиторий с пакетами:

$ curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash

Установим нужный пакет:

$ sudo apt-get install gitlab-runner

Добавим пользователя gitlab-runner в группу docker:

$ sudo usermod -aG docker gitlab-runner

Установка werf

За сборку и публикацию контейнеров в registry будет отвечать werf. Затем она развернёт собранные контейнеры в кластере в соответствии с состоянием репозитория, из которого был запущен пайплайн.

Выполним команду:

$ curl -sSL https://werf.io/install.sh | bash -s -- --ci

Установка и настройка kubectl

Для доступа к кластеру обычно используется утилита kubectl. Она позволяет получить информацию о состоянии подов или узлов кластера, а также управлять их состоянием и выполнять различные действия с сущностями кластера. Параметры доступа определяются «контекстом» кластера, заданным для kubectl.

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

Установим утилиту kubectl по официальной инструкции. Для этого выполним следующие команды:

$ sudo apt-get update && sudo apt-get install -y apt-transport-https
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
$ sudo apt-get update
$ sudo apt-get install -y kubectl

Теперь перейдём по адресу https://kubeconfig.kube.example.com. Это адрес сервиса kubeconfig кластера, предоставляющего параметры доступа для пользователя, под которым осуществлён вход в веб-интерфейсы Deckhouse.

Здесь нам предложат команды, выполнив которые, kubectl получит доступ к кластеру:

Выполним эти команды по очереди. Проверим, что доступ получен:

$ kubectl get no
NAME                  STATUS   ROLES                  AGE   VERSION
habr-deckhouse-werf   Ready    control-plane,master   16d   v1.25.12

Если отображается список узлов кластера так, как в примере выше, — всё работает как надо.

Скопируем конфигурацию в домашний каталог пользователя gitlab-runner, чтобы он также имел доступ к кластеру:

sudo mkdir /home/gitlab-runner/.kube &&
sudo cp -i ~/.kube/config /home/gitlab-runner/.kube/config &&
sudo chown -R gitlab-runner:gitlab-runner /home/gitlab-runner/.kube

Регистрация раннера в GitLab

ВМ с раннером готова, теперь необходимо зарегистрировать раннер в GitLab.

Для этого перейдём в раздел «Settings»«CI/CD» нашего репозитория с приложением:

Здесь раскроем вкладку «Runners»:

Галочку «Enable shared runners for this project» рекомендуем сделать неактивной, так как у нас в GitLab нет других раннеров. Если бы они были в других проектах и были доступны всем, то они бы появились здесь в списке.

Нажмем кнопку «New project runner»:

На следующей странице выберем параметры нашего раннера: ОС Linux, тег «werf», галочка «Lock to current projects» (это сделает его закрытым и доступным только для этого проекта) и время таймаута джобы 3600 секунд (1 час). Задать можно любое время в зависимости от степени сложности задачи.

Нажмем «Create runner». Нас перебросит на страницу созданного раннера:

Теперь переходим обратно на ВМ, в которой установлен gitlab-runner, и выполняем предложенную на экране выше команду:

$ sudo gitlab-runner register  --url https://gitlab.example.com  --token sdfgdsgdsggsdg
Runtime platform                                    arch=amd64 os=linux pid=26399 revision=8ec04662 version=16.3.0
WARNING: Running in user-mode.
WARNING: The user-mode requires you to manually start builds processing:
WARNING: $ gitlab-runner run
WARNING: Use sudo for system-mode:
WARNING: $ sudo gitlab-runner...


Created missing unique system ID                    system_id=s_40c491e75468
Enter the GitLab instance URL (for example, https://gitlab.com/):
[https://gitlab.example.com]:
Verifying runner... is valid                        runner=SSd3XHZj9
Enter a name for the runner. This is stored only in the local config.toml file:
[habr-gitlab-runner]:
Enter an executor: docker+machine, instance, kubernetes, docker-windows, ssh, virtualbox, shell, docker-autoscaler, custom, docker, parallels:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!


Configuration (with the authentication token) was saved in "/home/ubuntu/.gitlab-runner/config.toml"

На всех вопросах можно оставлять стандартные ответы, а на вопрос о выборе executor’а указать shell.

Для проверки выполним команду:

$ sudo gitlab-runner run

Подождём некоторое время и проверим страницу с настройками раннера:

Появилось сообщение о том, что создан новый раннер. Вернёмся на страницу с перечнем доступных раннеров в нашем проекте в GitLab:

Созданный раннер подсвечен зелёным и готов к работе.

Настройка CI/CD

Итак, проект лежит в GitLab, раннер настроен и готов к работе. Теперь нужно создать пайплайн, который будет выполняться при определённых условиях и деплоить проект в кластер.

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

В качестве первого и тестового запуска мы настроим автоматическое развёртывание приложения в кластер сразу после коммита в главную ветку репозитория. Коммит здесь может быть как прямой (на практике так делать нельзя!), так и через merge request, финальным результатом мерджа которого является коммит в главную ветку.

Подготовка конфигурации

Описание всего пайплайна происходит в файле .gitlab-ci.yml, который лежит в корне проекта. Для начала настроим сборку проекта. Создадим в корне нашего проекта файл .gitlab-ci.yml и добавим в него следующее содержимое (можно сделать это сразу в веб-интерфейсе GitLab):

stages:
  - build
  - deploy
  - dismiss
  - cleanup


before_script:
  - type trdl && . $(trdl use werf 1.2 stable)
  - type werf && source $(werf ci-env gitlab --as-file)


Build and Publish:
  stage: build
  script:
    - werf build
  except: [schedules]
  tags: [werf]

Вот что мы тут описали:

  • stages — стадии пайплайна. Всего их четыре:

    • build — сборка образов и публикация их в registry;

    • deploy — деплой приложения в кластер;

    • dismiss — удаление развёрнутого приложения из кластера. Используется при организации review-окружений — в продакшене, конечно же, это не нужно;

    • cleanup — очистка registry.

  • before_script — предварительные действия, выполняемые перед основными;

  • Build and Publish — первый этап пайплайна, в ходе которого выполняются сборка образов и публикация их в registry.

Для публикации образов используется встроенный в GitLab container registry. Параметры доступа к нему передаются через вызов команды werf ci-env из самого GitLab в разделе before_script.

Выполнение пайплайна

Сохраним файл. Если вы редактировали его локально, сделайте коммит и push на удалённый сервер. Если же вы редактировали файл прямо в интерфейсе GitLab, сохраните изменения через коммит в главную ветку (внизу есть выбор сохранения — напрямую или через создание merge request).

Сразу после сохранения GitLab проверит валидность созданного пайплайна и запустит его. Результат его выполнения будет отображён в виде зелёной галочки возле хэш-суммы коммита:

При нажатии на зелёную галочку отобразится сам пайплайн и его состояние:

Если нажать на «Build and Publish», отобразится полный лог выполнения команды на раннере:

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

Проверим, что образы собраны и опубликованы в registry проекта. Перейдём в раздел «Deploy»«Container Registry»:

Здесь отображён репозиторий с образами нашего проекта. Зайдём в него и посмотрим на содержимое:

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

Деплой приложения

Теперь добавим в конфигурацию развёртывание приложения из собранных образов. Тут есть несколько вариантов настройки деплоя: по пушу изменений в main-ветку, по кнопке, по тегу или только для merge request. Мы настроим автоматическое развёртывание изменений из main-ветки по новому коммиту в неё. Это более правильный путь построения классического CI/CD — изменения в продакшен выкатываются автоматически, как только попадают в главную ветку (соблюдается идеология непрерывной доставки).

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

.base_deploy:
  stage: deploy
  script:
    - werf converge --skip-build --set "env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
  except: [schedules]
  tags: [werf]

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

Теперь добавим непосредственно часть пайплайна, отвечающую за деплой приложения после внесения изменений в ветку main:

Deploy to Production:
  extends: .base_deploy
  environment:
    name: production
    url: https://habrapp.example.com
  only: [main]

Здесь мы говорим системе, что при появлении новых изменений в ветке main необходимо сразу выполнить стадию .base_deploy.

Сохраним изменения в файле. Обратите внимание, что при каждом сохранении изменений GitLab проверяет файл пайплайна на валидность:

Финальная версия файла .gitlab-ci.yml выглядит так:

stages:
  - build
  - deploy
  - dismiss
  - cleanup


before_script:
  - type trdl && . $(trdl use werf 1.2 stable)
  - type werf && source $(werf ci-env gitlab --as-file)


Build and Publish:
  stage: build
  script:
    - werf build
  except: [schedules]
  tags: [werf]


.base_deploy:
  stage: deploy
  script:
    - werf converge --require-built-images --set "env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
  except: [schedules]
  tags: [werf]


Deploy to Production:
  extends: .base_deploy
  environment:
    name: production
    url: https://habrapp.zhbert.ru
  only: [main]

Помимо непосредственно деплоя, здесь описаны также ещё две секции:

  • Build and Publish — добавляет функциональность только сборки и публикации контейнеров в registry без развёртывания в кластере. Будет отображаться в интерфейсе пайплайна в GitLab как отдельная кнопка.

  • before_script — выполняется в самом начале и активирует werf: обновляет или устанавливает её с использованием рекомендуемого способа установки через trdl, а затем выполняет команду werf ci-env для получения параметров доступа к registry, переданных GitLab.

Теперь при внесении изменений в ветку main будут запускаться сборка проекта, публикация его в registry, а затем и развёртывание в кластере.

Зайдем в Pipelines и… увидим ошибку, так как кластер не сможет скачать образы из нашего registry:

Происходит это потому, что мы не добавили Secret с параметрами доступа к новому registry в кластер. Он необходим в процессе работы для возможности использовать образы из приватного container registry и хранит учётные данные пользователя. Здесь есть одна особенность — Secret должен располагаться в том же namespace, что и приложение.

Обратите внимание, что несмотря на то, что Kubernetes не смог выполнить загрузку образов из registry, werf при этом спокойно их собрал и запушил. Происходит это потому, что GitLab передаёт временные параметры доступа в рамках пайплайна в раннер, и werf воспользовалась именно ими. В кластер же эти параметры не попадают, поэтому для доступа к registry из Kubernetes нужно создавать Secret с ними.

В зависимости от версии GitLab настроить доступ к его registry для Kubernetes можно разными способами. В ранних версиях можно было создать Secret с логином и паролем пользователя, из-под которого происходит работа в GitLab, и этого было достаточно. В новых нужно создавать специальный токен и использовать его.

У нас используется последняя актуальная версия GitLab 16.9, поэтому создадим токен с правом чтения registry. Для этого перейдём в раздел «Settings»«Repository» проекта:

На открывшейся странице раскроем список «Deploy tokens», нажав на кнопку «Expand»:

Теперь нажмём на кнопку «Add token»:

В раскрывшемся списке введём название токена и логин:

Поле с датой окончания его действия можно оставить незаполненным, так как в данном случае нам не важно, сколько времени он будет активен. В разделе «Scopes (select at least one)» поставим галочку «read_registry», чтобы доступ по этому токену осуществлялся только в режиме чтения. И нажмём «Create deploy token». Токен и его пароль отобразятся наверху секции:

Обязательно сохраните пароль до закрытия этого окна, так как больше его увидеть не получится!

Залогинимся в registry GitLab'а с полученными учётными данными и токеном. Для этого сначала посмотрим адрес registry, перейдя в раздел «Deploy»«Container registry» и нажав вверху кнопку «CLI commands»:

Скопируем и выполним команду из раздела «Login»:

$ docker login gitlab.zhbert.ru:5050
Username: kube-pull
Password:
WARNING! Your password will be stored unencrypted in /home/ubuntu/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

Если вы будете выполнять эту команду на машине, где уже был выполнен логин в Docker Registry, предварительно забэкапьте файл ~/.docker/config.json, в котором хранятся параметры доступа, и удалите его. После выполнения дальнейших шагов файл можно вернуть или залогиниться в нужные registry заново.

Создадим Secret. Посмотрим содержимое файла с параметрами доступа:

$ cat ~/.docker/config.json
{
	"auths": {
		"gitlab.zhbert.ru:5050": {
			"auth": "a3ViZS1wdWxsOmdsZHQtdW1SellhR1JBSm4tb2o3ZC13Tnk="
		}
	}
}

Cконвертируем полученные данные в Base64:

$ cat ~/.docker/config.json | base64
ewoJImF1dGhzIjogewoJCSJnaXRsYWIuemhiZXJ0LnJ1OjUwNTAiOiB7CgkJCSJhdXRoIjogImEz
VmlaUzF3ZFd4c09tZHNaSFF0ZFcxU2VsbGhSMUpCU200dGIybzNaQzEzVG5rPSIKCQl9Cgl9Cn0=

Теперь создадим файл с шаблоном Secret'а в каталоге .helm/templates проекта в GitLab. Назовём его kube-pull-secret.yml:

apiVersion: v1
kind: Secret
metadata:
  name: registrysecret
data:
  .dockerconfigjson: ewoJImF1dGhzIjogewoJCSJnaXRsYWIuemhiZXJ0LnJ1OjUwNTAiOiB7CgkJCSJhdXRoIjogImEzVmlaUzF3ZFd4c09tZHNaSFF0ZFcxU2VsbGhSMUpCU200dGIybzNaQzEzVG5rPSIKCQl9Cgl9Cn0=
type: kubernetes.io/dockerconfigjson

Полученную строку с параметрами доступа скопируем в поле .dockerconfigjson

Secret уже указан в разделе imagePullSecrets деплойментов, поэтому закоммитим изменения и отправим в репозиторий. Пайплайн автоматически перезапустится:

Всё прошло успешно!

Проверка работоспособности

Теперь у нас настроен CI/CD, который будет автоматически разворачивать приложение из главной ветки репозитория при любых изменениях в ней. Проверим, что приложение развёрнуто и готово к работе:

Теперь изменим в приложении наименование таблицы прямо из веб-интерфейса. Перейдём на нужный нам файл (/frontend/src/components/HelloWorld.vue) и нажмем «Edit» → «Edit single file»:

В открывшемся редакторе заменим название таблицы на «Messages from users»:

Далее нажмём «Commit changes». По умолчанию будет создан коммит в главную ветку. Изменения сохраняются и автоматически запускается пайплайн. Его статус будет отображён в кружочке около хэш-суммы коммита в шапке:

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

Всё обновилось.

Заключение

Мы развернули инстанс GitLab и настроили в нём автоматическую сборку приложения для всех новых коммитов в главную ветку. Для управления сборкой используется werf, берущая на себя сборку и публикацию контейнеров, а также отслеживающая и приводящая состояние кластера в соответствие с состоянием репозитория с приложением. Такой подход позволяет организовать непрерывную доставку изменений в приложении до пользователей, мгновенно собирая новые версии и развёртывая их в кластере.

Исходные коды приложения можно найти в репозитории.

P. S.

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

Также можно ознакомиться с самоучителем по Kubernetes от разработчиков werf и записями встреч с комьюнити: 

Tags:
Hubs:
Total votes 36: ↑35 and ↓1+34
Comments17

Articles

Information

Website
flant.ru
Registered
Founded
Employees
201–500 employees
Location
Россия
Representative
Тимур Тукаев