CICD

Материал из wiki.nntc.nnov.ru
Перейти к навигации Перейти к поиску

Содержание

Практика по CI/CD

Тема 1. Основы работы с системой контроля версий git

Тема:

Основы работы с системой контроля версий git. 

Отчёт:

В отчёт необходимо включить результат последовательного выполнения шагов 1.* и вывод по достигнутым результатам.

Каждому шагу в отчёте необходимо дать осмысленное название.

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

Шаг 1.1

Создайте проект с исходным кодом программного обеспечения на компьютере разработки:

mkdir ~/my_program

Перейдите в проект:

cd ~/my_program

Создайте файл main.c:

touch main.c

поместите в созданный файл следующее содержимое:

#include <stdio.h>

int main() {
    printf("Hello from program!\n");
    return 0;
}


Создайте файл Makefile:

touch Makefile

поместите в созданный файл следующее содержимое:

CC = gcc

TARGET = main
SRC = main.c

CFLAGS = -Wall -Wextra

all: $(TARGET)
$(TARGET): $(SRC)
	$(CC) $(CFLAGS) $(SRC) -o $(TARGET)

run: $(TARGET)
	chmod +x $(TARGET)
	./$(TARGET)

clean:
	rm -f $(TARGET)


Создайте файл .gitignore:

touch .gitignore

поместите в созданный файл следующее содержимое:

main

Шаг 1.2

Перейдите в проект:

cd ~/my_program

Соберите программы:

make

Сделайте файл получившейся программы исполняемым и запустите:

make run

Просмотрите файлы проекта:

ls -l

Опишите проделанную работу в отчёте.

На этом этапе была создана простейшая программа на языке си с простейшим файлом сборки, произведена сборка программы и её запуск.

Шаг 1.3

Перейдите в проект:

cd ~/my_program

Инициализируйте контроль версий кода проекта посредством git:

git init

Переименуйте главную ветку из master в main согласно современным договорённостям разработчиков:

git branch -m main

Посмотрите содержимое файла .git/config

cat .git/config

Добавьте к содержимому этого файла следующие параметры:

[user]
	name = <ваше имя>
	email = <ваш емэйл>

Например:

[user]
	name = petrov_ii
	email = petrov@mail.com


Замените утилиту постраничного просмотра git на cat (это предотвратит необходимость выхода из постраничного навигатора):

git config core.pager cat


Посмотрите повторно содержимое файла .git/config

cat .git/config

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

Подсказка: Для установки параметров name и email в разделе user вместо ручного редактирования файла можно было бы выполнить команды:

git config user.name petrov_ii
git config user.email petrov@mail.com

Кстати, если понадобится их поменять, то можно попробовать этот вариант тоже.

Шаг 1.4

Добавьте содержимое каталога к будущему коммиту

git add .

Сделайте коммит:

git commit -m "My first commit"

Просмотрите журнал коммитов:

git log

Внесите изменения в файл main.c (например, измените выводимое текстовое сообщение). Затем просмотрите изменённые файлы в проекте:

git status

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

git diff

Верните состояние файла main.c к состоянию, в котором он был на предыдущем коммите:

git checkout -f main.c

Проверьте содержимое файла

cat main.c

Повторно просмоте статус проекта:

git status

Внесите ещё раз изменения в файл main.c (например, измените выводимое текстовое сообщение). Затем просмотрите изменённые файлы в проекте:

git status

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

git diff

На этот раз сохраните изменения в файле путём создания нового коммита. Для этого добавьте изменённый файл к отслеживанию системой контроля версий:

git add main.c

(здесь важно понимать, что, не смотря на то, что когда-то вы уже делали это (git add .), после изменения содержимого файла для добавления изменений в коммит его (файл) нужно повторно добавить к отслеживанию. Т.е. мы добавляем не сам файл, а факт изменения этого файла)

Теперь повторно выполните команду:

git status

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

После сделайте новый коммит:

git commit -m "My second commit"

Просмотрите журнал коммитов:

git log

Также не лишним будет повторно посмотреть и статус

Шаг 1.5

Перейдите в проект

cd ~/my_program

Просмотрите доступные ветки проекта

git branch

Скорее всего в ответ придёт что-то вроде:

* main

Это означает что у вас в проекте одна ветка (branch) и она называется

main

Просмотрите журнал коммитов:

git log

В ответ придёт что-то вроде:

commit 477a3a1f243a7e62806c8239b3f193f839007c02 (HEAD -> main)
Author: petrov_ii <petrov@mail.com>
Date:   Mon Jan 19 11:36:04 2026 +0300

    Second

commit d0b5b111f559379b90f98cf417a372bd55d02774
Author: petrov_ii <petrov@mail.com>
Date:   Mon Jan 19 11:31:20 2026 +0300

    First

Ранее мы могли вернуть изменённый файл main.c к состоянию последнего коммита командой

git checkout -f main.c

Эта команда возвращает содержимое файла main.c на состояние, сохранённое коммитом. При этом то состояние, которое у файла было до выполнения этой команды, будет полностью потеряно.

Имея историю коммитов, при необходимости, можно вернуть состояние всего проекта к состоянию любого из коммитов.

Для этого необходимо выполнить команду:

git checkout <id коммита>

Например

git checkout d0b5b111f559379b90f98cf417a372bd55d02774

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

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

После переключения на конкретный коммит и выполнения действий с файлами проекта необходимо обратно вернуться на базовое состояние текущей ветки:

git checkout <имя ветки>

Например, в случае с веткой main:

git checkout main

Шаг 1.6

На шаге 1.1 был создан файл:

.gitignore

в котором описаны файлы и каталоги, которые система контроля версий git должна игнорировать.

По этой причине создаваемый при сборке исполняемый файл:

main

Не отображается при выполнении команды:

git status

и не был добавлен при выполнении команды:

git add .

(точка здесь означает все файлы, которые могут быть добавлены к контролю версий. Однако, файла main не было добавлен!).

В git-проекте принято хранить исходный код без продуктов сборки (чем является исполняемый файл main). По этой причине он был добавлен в файл:

.gitignore

, чтобы git игнорировал его.

Шаг 1.7

Измените несколько раз содержимое файла:

main.c

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

Выполняя коммит необходимо наиболее полно составлять текстовый комментарий, который должен отражать реальное состояние внесённых изменений.

В отчёте необходимо отразить работу с внесением изменений в проект, по результатом которой в журнале должно быть отображено не менее 10-ти коммитов.


Шаг 1.8

Ответьте на вопросы.

Разместите вопросы и ответы на них в отчёте.

Список вопросов:

1. Какая команда позволяет инициализировать процесс контроля версий в каталоге проекта посредством системы контроля версий git?
2. Какой командой добавить файл test.txt к отслеживанию системой контроля версий git?
3. Какой командой можно сделать коммит, указав сообщение коммита?
3. Укажите путь до имени конфигурационного файла git-проекта относительно каталога git-проекта?
4. Какой командой можно переключить состояние проекта на конкретный коммит?
5. Какой командой можно переключить состояние проекта на самый новый по времени создания коммит?

Тема 2. Основы работы с CI/CD платформой gitlab

Тема:

Основы работы с CI/CD платформой gitlab.

Отчёт:

В отчёт необходимо включить результат последовательного выполнения шагов 2.* и вывод по достигнутым результатам.

Каждому шагу в отчёте необходимо дать осмысленное название.

Шаг 2.1

В теме 1 были рассмотрены основы работы с системой контроля версий git.

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

Однако при совместной работе над кодом только локального контроля версий недостаточно.

Обычно, для синхронизации проекта между разработчиками (или между рабочими местами одного разработчика) в дополнение к системе контроля версий git используются платформы для хранения и обеспечения дополнительных условий для совместной работы над кодом, такие как: GitHub, GitLab, GitFlic и другие...

К общим функциям таких платформ можно отнести:

1. Хранение кода и механизмы предоставления доступа к коду между разработчиками;
2. Подсистема для хранения и обсуждения ошибок и неисправностей (issues) в коде, тесно интегрированная с существующим механизмом коммитов системы контроля версий;
3. Подсистемы обеспечения непрерывной интеграции (CI - Continuous Integration);
4. Подсистемы обеспечения непрерывной доставки (CD - Continuous Delivery);
5. Подсистемы публикации релизов программного обеспечения.

Возможности современных CI/CD платформ, таких как GitHub или GitLab позволяют автоматизировать многие рутинные операции, такие как: тестирование кода, сборка, развёртывание (deployment), выпуск новых версий ПО и др.

Платформа GitLab позволяет использовать свою версию с открытым исходным кодом для самостоятельного развёртывания (self-hosted) энтузиастами или небольшими организациями в пределах локальной сети предприятия или в пределах облачного хостинга организации.

С этой точки зрения для использования в образовательных целях эта платформа также представляет большой интерес.

Дальнейшие основы CI/CD в рамках этого практического пособия будут даны на примере CI/CD платформы GitLab, расположенной по адресу https://gitlab.nntc.nnov.ru

Шаг 2.2

Войдите в свой аккаунт на https://gitlab.nntc.nnov.ru и создайте новый git-репозиторий.

Имя репозитория должно содержать фамилию и инициалы обучающегося.

Например, для обучающегося с ФИО Петров Иван Иванович, проходящего учебную практику, название репозитория может быть таким:

up_petrov_ii_01

В результате будет создан пустой проект.

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

git clone https://gitlab.nntc.nnov.ru/<имя пользователя>/<название проекта>.git

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

Как правило эта инструкция автоматически генерируется платформой gitlab и находится в файле:

README.md

созданного проекта. На этапе, когда проект только создан, этот файл является единственным файлом проекта. При клонировании проекта этот файл клонируется вместе с проектом и является частью проекта.

Хорошим тоном для любого разработчика является переработка файла:

README.md

в соответствии с задачами и целями проекта.


Другими словами: файл

README.md

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


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


Сделать это можно как минимум двумя способами.

Способ 1 (простой)

Склонируйте репозиторий по полной ссылке. Например так:

git clone https://gitlab.nntc.nnov.ru/petrov_ii/up_petrov_ii_01.git

Далее скопируйте из проекта, созданного в процессе изучения темы 1, все файлы, кроме скрытого каталога

.git

(точка в имени означает что каталог скрыт и для его просмотра может понадобиться навык просмотра скрытых файлов и каталогов в зависимости от операционной системы).


Почему нельзя копировать каталог

.git

?

Потому что в склонированном проекте есть уже свой каталог

.git

со своим файлом

config

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


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


Чтобы скопированные файлы оказались в репозитории их необходимо добавить к отслеживанию системой контроля версий, затем сделать коммит.

Выполните это используя навык, полученный при выполнении задания 1.


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

git push

При выполнении этой команды утилита git запустит процесс синхронизации содержимого локального репозитория с удалённым.

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

У команды

git push

есть "обратная" команда:

git pull

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


Другими словами: отправить изменения в удалённый репозиторий:

git push

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

git pull


Преимущества и недостатки

Недостатком такого способа является потеря история коммитов, сделанных ранее (поскольку по сути это новый git-репозиторий, с добавленными в него новыми изменениями).


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


Такой способ удобно использовать, когда код программы до создания репозитория на gitlab не имел версионного контроля ранее (т.е. в каталоге с кодом не был инициализирован git репозиторий. Или был, но историю коммитов "не жалко").

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

Способ 2 (правильный)

Для того чтобы связать существующий локальный git-репозиторий с удалённым репозиторием на платформе gitlab необходимо в локальном репозитории выполнить команду:

git remote add origin https://gitlab.nntc.nnov.ru/<имя пользователя>/<название проекта>.git

Например:

git remote add origin https://gitlab.nntc.nnov.ru/petrov_ii/up_petrov_ii_01.git

затем установить ветку проекта по умолчанию:

git branch -M main

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

git push -uf origin main

В результате в конфигурационный файл локального git-проекта будут внесены соответствующие дополнения и этот проект также будет настроен для синхронизации изменения посредством

git pull

или

git push

аналогично тому, как описано в предыдущем варианте.

При этом вся история коммитов также будет сохранена и синхронизирована.

Преимущества и недостатки

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

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

Шаг 2.3

Команды

git pull

и

git push

требуют аутентификации. В качестве базового способа аутентификации используется логин и пароль от платформы gitlab.

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


В различных операционных системах эта проблема может решаться по разному. Например, Windows может запоминать логин и пароль в специальной подсистеме операционной системы, в различных дистрибутивах GNU/Linux также могут быть свои нюансы при вводе логина и пароля для аутентификации.


Чтобы унифицировать доступ к репозиторию в любой операционной системе и при этом не вводить логин и пароль для аутентификации можно использовать специальный токен.


Для этого находясь на странице проекта платформы gitlab выберите в меню "Настройки" раздел "Токены доступа".

В результате будет открыта страница со списком токенов доступа (у нового проекта может не быть токенов, если вы их ранее не создавали).


Существующие токены можно или отозвать (т.е. фактически удалить) или обновить (например, если закончился срок действия или безопасность токена была скомпрометирована).

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

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


Для организации аутентификации проекта необходимо создать токен и задать ему необходимое время жизни. Это можно сделать нажав на кнопку:

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


Важно задать как минимум название токена, выбрать роль (для первого теста выберите роль Owner), указать срок действия, выбрать область действия (для первого теста достаточно установить флажок только у пункта "api"), затем нажать на кнопку создания токена.

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


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

Обычно токен выглядит примерно так:

glpat-kZdA2AMiqk3_WuoENw_K


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


В уже склонированном проекте необходимо открыть файл:

.git/config

найти в нём примерно такую конструкцию:

 [remote "origin"]
	url = https://gitlab.nntc.nnov.ru/petrov_ii/up_petrov_ii_01.git
	fetch = +refs/heads/*:refs/remotes/origin/*

и заменить её на следующую:

 [remote "origin"]
	url = https://-:glpat-kZdA2AMiqk3_WuoENw_K@gitlab.nntc.nnov.ru/petrov_ii/up_petrov_ii_01.git
	fetch = +refs/heads/*:refs/remotes/origin/*

затем сохранить файл.


Для клонирования проекта впервые сразу с использованием токена необходимо из стандартного url проекта:

https://gitlab.nntc.nnov.ru/petrov_ii/up_petrov_ii_01.git

получить url с токеном:

https://-:glpat-kZdA2AMiqk3_WuoENw_K@gitlab.nntc.nnov.ru/petrov_ii/up_petrov_ii_01.git

затем склорировать проект по полученному url:

git clone https://-:glpat-kZdA2AMiqk3_WuoENw_K@gitlab.nntc.nnov.ru/petrov_ii/up_petrov_ii_01.git

В результате проект будет склонирован по изменённому url и этот изменённый url сразу окажется в конфигурационном файле внутри склонированного репозитория.


Принцип подстановки токена в url аналогичен принципам базовой аутентификации в протоколах типа ftp://, http:// и других. В общем виде это выглядит так:

<протокол>://<логин>:<пароль>@<хост>/<строка_пути>

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

https://-:glpat-kZdA2AMiqk3_WuoENw_K@gitlab.nntc.nnov.ru/petrov_ii/up_petrov_ii_01.git

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


В целях безопасности рекомендуется устанавливать не слишком большое время жизни токена.


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

Шаг 2.4

Разместите вопросы и ответы на них в отчёте.

Список вопросов:

1. Что происходит при выполнении команды git push?
2. Что происходит при выполнении команды git pull?
3. Какие команды из перечисленных в этой теме требуют аутентификации?
4. Необходима ли аутентификация для обеспечения контроля версий в локальном репозитории?
5. Необходима ли команда git commit для обеспечения контроля версий в локальном репозитории?
6. Можно ли получить значение токена аутентификации, если выйти из аккаунта платформы gitlab а затем войти вновь?

Тема 3. Установка gitlab-runner на GNU/Linux хост

Тема:

Организация доступа на GNU/Linux хост посредством протокола ssh. Установка gitlab-runner на GNU/Linux хост.

Отчёт:

В отчёт необходимо включить результат последовательного выполнения шагов 3.* и вывод по достигнутым результатам.

Каждому шагу в отчёте необходимо дать осмысленное название.

Шаг 3.1

Посредством утилиты:

ssh-keygen

создайте ключевую пару (открытую и закрытую части криптографического ключа).

Обычно результатом создания ключевой пары являются файлы:

id_ed25519
id_ed25519.pub

или:

id_rsa
id_rsa.pub

(в зависимости от типа шифрования, настроенного операционной системе, на которой создаётся ключевая пара).

Файл с суффиксом:

.pub

содержит открытую часть ключа. Второй файл, соответственно, содержит закрытую часть ключа.

Закрытую часть сохраните у себя и ни кому не передавайте (это ваш будущий ключ от удалённого GNU/Linux хоста).

Содержимое открытой части ключа добавьте к содержимому файла:

authorized_keys

, который обычно находится в каталоге:

.ssh

внутри домашнего каталога пользователя, под которым вы планируете подключаться на GNU/Linux хост по протоколу ssh.

Да, верно: открытую часть ключа следует доставить на удалённый GNU/Linux хост и положить в выше упомянутый файл в выше упомянутом каталоге того пользователя удалённого GNU/Linux хоста, под которым планируется подключение.


Шаг 3.2

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

ssh-copy-id

(если это позволяют настройки безопасности удалённого GNU/Linux хоста) либо в ручном режиме переместить содержимое открытого ключа в необходимый файл на удалённом хосте, предварительно подключившись на удалённый GNU/Linux хост по протоколу ssh по логину и паролю.


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

ssh -i </путь/до/закрытой_части_ключа> <пользователь>@<хост_или_ip_адрес> -p <порт>

Если порт ssh сервера, к которому производится подключение, стандартный (22), то ключ -p с номером порта в команде можно опустить.

Пример команды:

ssh -i ~/.ssh/id_ed25519 root@10.207.123.123 -p 22

При выполнении такой команды подразумевается, что содержимое файла:

id_ed25519.pub

находится в файле:

/home/user/.ssh/authorized_keys

на GNU/Linux хосте с ip-адресом:

10.207.123.123

а ssh сервер работает на этом же хосте на стандартном порту

22

(как уже говорилось выше, ключ -p 22 в команде можно опустить, если порт сервера стандартный -- 22-й).


Для более удобного подключения к удалённому серверу создайте в домашнем каталоге пользователя (на компьютере, с которого предполагается подключение по ssh) внутри каталога:

.ssh

в файл (создайте его, если он не существует):

config

добавьте следующие параметры:

Host myhost
    Hostname 10.207.123.123
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    User root

В общем виде эти настройки выглядят так:

Host <понятное человеку название хоста>
    Hostname <ip-адрес или доменное имя сервера>
    Port <порт>
    IdentityFile <путь к закрытой части ключа>
    User <пользователь>


Теперь вместо длинной команды:

ssh -i ~/.ssh/id_ed25519 root@10.207.123.123 -p 22

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

ssh myhost

Шаг 3.3

Предыдущие два шага демонстрируют процесс настройки БЕЗОПАСНОГО доступа на уалённый сервер.

Безопасным доступом в этом контексте является доступ с аутентификацией по ключам, а не по паролю (пароль может быть подобран извне специализированным программным обеспечением).

Следовательно, категорически не рекомендуется оставлять доступ по паролю на удалённый сервер!

Когда доступ на удалённый сервер (GNU/Linux хост) получен, на нём необходимо установить и настроить средство выполнения для gitlab.

Это программное обеспечение называется:

gitlab-runner

В некоторых GNU/Linux дистрибутивов gitlab-runner может входить в состав репозитория программного обеспечения.

Далее будет рассмотрена процедура установки средства выполнения "gitlab-runner" универсальным способом.

Шаг 3.4

1. Подключитесь к серверу по протоколу ssh;

2. Перейдите в режим суперпользователя;

3. Загрузите бинарную версию средства выполнения "gitlab-runner" для сервера архитектуры x86_64 (она же amd64) командой:

curl -L --output /usr/local/bin/gitlab-runner https://wiki.nntc.nnov.ru/download/gitlab-runner-linux-amd64

4. Сделайте загруженный файл исполняемым:

chmod +x /usr/local/bin/gitlab-runner

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

useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

6. Установите и запустите средство выполнения как сервис

gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
gitlab-runner start

7. Проверьте статус работы средства выполнения

gitlab-runner status

Шаг 3.5

В операционных системах GNU/Linux, в которых отсутствует механизм:

sudo

, процедура запуска gitlab-runner от пользователя gitlab-runner (как описано выше в пп. 5 и 6 шага 3.4) может работать с ошибками доступа к некоторым файлам.

Используемая операционная система "Альт" в целях повышения безопасности не содержит механизма sudo. Поэтому вместо шагов 5 и 6 необходимо выполнить шаг по запуску gitlab-runner от суперпользователя (root).

Для этого, если gitlab-runner ранее был запущен командами шагов 5 и 6, необходимо его отключить:

gitlab-runner stop

далее деинсталлировать командой:

gitlab-runner uninstall

Затем инсталлировать командой:

gitlab-runner install --user=root --working-directory=/root

, после чего включить командой:

gitlab-runner start

Также для предотвращения некоторых ошибок, связанных с настройками окружения пользователя, от которого работает gitlab-runner (в текущей ситуации это будет суперпользователь root), необходимо открыть на редактирование файл:

/root/.bash_logout

и закомментировать (поставить в начале строки знак решётки (#)) в нём все строчки, которые не закоментированы (которые не начинаются на знак решётки (#)), затем сохранить файл.

На этом этапе всё должно быть настроено.

Шаг 3.6

Готовые бинарные сборки средства выполнения "gitlab-runner" существуют также под архитектуры 386, arm и arm64.

Для снижения нагрузки на интернет-канал лабораторий колледжа в команде (п.3) установки используется локальная ссылка.

Более подробную информацию по установке средства выполнения можно получить здесь

На этом этапе на GNU/Linux хост установлено средство выполнения "gitlab-runner", посредством которого можно будет выполнять непрерывную интеграцию (CI) и непрерывную поставку/доставку контента (CD).

Для этого необходимо связать gitlab-репозиторий со средством выполнения. Об этом подробно расскзано в теме 4.

Шаг 3.7

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

Шаг 3.8

Ответьте на вопросы и разместите вопросы и ответы на них в отчёте.

Список вопросов:

1. Для чего нужна утилита ssh-keygen?
2. Для чего нужна утилита ssh-copy-id?
3. Для чего нужен файл ~/.ssh/authorized_keys?
4. Сколько файлов создаётся при создании ключа ssh?
5. Для чего служит средство выполнения "gitlab-runner"?

Тема 4. Настройка gitlab-runner на работу с репозиторием gitlab

Тема:

Настройка gitlab-runner на работу с репозиторием gitlab.

Отчёт:

В отчёт необходимо включить результат последовательного выполнения шагов 4.* и вывод по достигнутым результатам.
Название gitlab-проекта обязательно должно включать фамилию обучающегося.

Отчёт должен содержать ссылку на существующий git-репозиторий, описанный в отчёте.

Каждому шагу в отчёте необходимо дать осмысленное название.

Шаг 4.1

Войдите в свой аккаунт на https://gitlab.nntc.nnov.ru и создайте новый git-репозиторий.

Имя репозитория должно содержать фамилию и инициалы обучающегося.

Например, для обучающегося с ФИО Петров Иван Иванович, проходящего учебную практику, название репозитория может быть таким:

up_petrov_ii_02

Шаг 4.2

Перейдите на страницу созданного репозитория.

Найдите раздел "Средства выполнения" в меню "Настройки - CI/CD"

Создайте средство выполнения, заполнив форму следующим образом:

Теги:

test

Остальные параметры оставьте по умолчанию, затем нажмите кнопку:

Создать средство выполнения


На следующей странице ознакомьтесь с пошаговой инструкцией по запуску средства выполнения gitlab-runner (пройдите по предлагаемым ссылкам инструкции чтобы ознакомиться наиболее полно!), скопируйте команды, которые в последующем понадобятся для запуска средства выполнения на GNU/Linux хосте, затем нажмите на кнопку:

Просмотр средств выполнения


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

#20 (LDBWhOpr)

Шаг 4.3

Рядом с созданным на предыдущем шаге средством выполнения будет показан треугольный значок с восклицательным знаком. Это означает, что gitlab не подключился к средству выполнения на GNU/Linux хосте. Теперь необходимо это настроить.

Используйте скопированные ранее команды чтобы зарегистрировать средство выполнения на GNU/Linux хосте. Для этого:

1. Войдите на GNU/Linux хост по протоколу ssh;

2. Выполните команду для регистрации средства выполнения;

3. Перейдите на страницу "Средства выполнения" проекта;

4. Убедитесь что треугольный значок с восклицательным знаком пропал и на его месте появился зелёный кружок, символизирующий успешное подключение средства выполнения (gitlab-runner) к gitlab.


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

Шаг 4.4

Склонируйте ваш репозиторий себе на компьютер и создайте внутри файл:

.gitlab-ci.yml

со следующим содержимым:

stages:
  - test

hello_job:
  stage: test
  tags:
    - test
  script:
    - echo "Hello from GitLab CI"
    - uname -a
    - echo "Hello from GitLab CI" > /tmp/test
    - uname -a >> /tmp/test

Сделайте коммит (git commit) и синхронизируйте изменения с репозиторием (git push).

Затем перейдите в средство выполнения ("Сборка" - "Конвейеры") в меню вашего репозитория в gitlab и посмотрите статус задания.

Опишите результат в отчёт.


Затем войдите по протоколу ssh на GNU/Linux хост, на котором работает средство выполнения, и просмотрите содержимое файла:

/tmp/test

Опишите результат исследования файла на GNU/Linux хосте.

Шаг 4.5

Сделайте вывод о проделанной работе по теме 1.

Опишите связь между работой средства выполнения и содержимым файла:

.gitlab-ci.yml

Шаг 4.6

Ответьте на вопросы.

Разместите вопросы и ответы на них в отчёте.

Список вопросов:

1. Сколько команд будет выполнено средством выполнения?
2. Какие именно это команды?
3. Какие команды формируют содержимое файла test в каталоге /tmp GNU/Linux хоста?
4. Как связано название стадии (stage) и названия тегов, указываемых при конфигурировании средства выполнения?
5. На что влияют названия тегов, указываемых при конфигурирования средства выполнения?

Тема 5. Стадии CI (stages) и взаимодействие между ними посредством артефактов

Тема:

Стадии CI и взаимодействие между ними посредством артефактов.

Отчёт:

В отчёт необходимо включить результат последовательного выполнения шагов 5.* и вывод по достигнутым результатам.

Каждому шагу в отчёте необходимо дать осмысленное название.

Шаг 5.1

Войдите в свой аккаунт на https://gitlab.nntc.nnov.ru и создайте новый git-репозиторий.

Имя репозитория должно содержать фамилию и инициалы обучающегося.

Например, для обучающегося с ФИО Петров Иван Иванович, проходящего учебную практику, название репозитория может быть таким:

up_petrov_ii_03

Шаг 5.2

Отредактируйте средство выполнения, зарегистрированное в п 4.2: добавьте к списку тегов тег:

test_stages

Шаг 5.3

Используйте навыки, полученные в результате изучения тем 1-4, чтобы создать следующую структуру проекта:

.                          <- каталог (корень git-репозитория (точка означает, что мы стоим в этом каталоге))
├── src/                   <- каталог
│   └── sysinfo.c          <- текстовый файл
├── tests/                 <- каталог
│   └── test_version.sh    <- текстовый файл
├── .gitlab-ci.yml         <- текстовый файл (скрытый (точка в начале имени!))
└── VERSION                <- текстовый файл

Шаг 5.4

Откройте файл:

src/sysinfo.c

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void print_help() {
    printf("Usage:\n");
    printf("  sysinfo --version\n");
    printf("  sysinfo --uname\n");
    printf("  sysinfo --hash <file>\n");
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        print_help();
        return 1;
    }

    if (strcmp(argv[1], "--version") == 0) {
        FILE *f = fopen("VERSION", "r");
        if (!f) {
            perror("Cannot open VERSION");
            return 1;
        }
        char version[64];
        fgets(version, sizeof(version), f);
        fclose(f);
        printf("sysinfo version: %s", version);
        return 0;
    }

    if (strcmp(argv[1], "--uname") == 0) {
        system("uname -a");
        return 0;
    }

    if (strcmp(argv[1], "--hash") == 0) {
        if (argc < 3) {
            fprintf(stderr, "File not specified\n");
            return 1;
        }
        char cmd[256];
        snprintf(cmd, sizeof(cmd), "sha256sum %s", argv[2]);
        system(cmd);
        return 0;
    }

    print_help();
    return 1;
}


В файл:

VERSION

поместите версию:

1.0.0


В файл:

tests/test_version.sh

поместите следующий код:

#!/bin/sh
set -e

./sysinfo --version | grep "1.0.0"

Шаг 5.5

Соберите программу из исходного кода:

gcc src/sysinfo.c -o sysinfo

Проверьте работоспособность программы с различными параметрами.

Вариант 1:

./sysinfo --version


Вариант 2:

./sysinfo --uname


Вариант 3:

./sysinfo --hash sysinfo


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

Makefile

и утилита:

make

, а на текущем шаге сборка осуществляется непосредственно выполнением команды:

gcc src/sysinfo.c -o sysinfo

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

./sysinfo --version
./sysinfo --uname
./sysinfo --hash sysinfo


Подумайте как бы мог вылядеть файл:

Makefile

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

Добавьте вашу версию Makefile в репозиторий текущего проекта и в отчёт.


Шаг 5.6

На текущий момент в репозитории проекта существует исходный код программы, файл для тестового запуска программы и файл с версией, соответствующей версии, выдаваемой программой.

Из всех созданных в п. 5.3. файлов проекта пустым остаётся только файл:

.gitlab-ci.yml

По теме 4 мы знакомы с этим файлов как с конфигурацией для процесса непрерывной интеграции (CI), выполняемой средством выполнения (gitlab-runner (его запуск и настройка были изучены в темах 3 и 4)).

Здесь назначение этого файла точно такое же. В отличии от темы 4, здесь файл:

.gitlab-ci.yml

будет значительно сложнее и, как следствие, CI-функционал, реализуемый посредством него, будет значительно обширнее.

Теперь файл будет содержать несколько стадий (stages), которые будут выполнены одна за другой.

Список стадий

Откройте файл:

.gitlab-ci.yml

и добавьте в него следующее содержимое:

stages:
  - build
  - test
  - package
  - release

этот фрагмент yml-разметки описывает 4 CI-стадии: build (сборка), test (тестирование), package (пакетирование), release (выпуск).

На текущий момент это всего лишь список. Сами стадии пока не описаны.

Опишите стадии одну за другой последовательно в файле:

.gitlab-ci.yml

сразу после списка стадий.

Стадия 1 (build)

Добавьте описание первой стадии:

build:
  stage: build
  tags:
    - test_stages
  script:
    - gcc src/sysinfo.c -o sysinfo
  artifacts:
    paths:
      - sysinfo
    expire_in: 1 hour

На этой стадии средством выполнения выполняется скрипт:

gcc src/sysinfo.c -o sysinfo

точно так же, как выполнялись команды скрипта при изучении темы 4.

В результате собирается программа (sysinfo) из исходных кодов (src/sysinfo.c).


Эта собранная программа будет необходима на следующей CI-стадии.


CI-стадии устроены так, что результаты работы одной стадии (создаваемые файлы результатов) напрямую недоступны последующим стадиям. Чтобы стадии могли обмениваться такими файлами существует понятие артефакта.


Артефакт - описанный файл или каталог (или их комбинация), которую(ые) стадия передаст (расшарит) в последующую(ие) стадии.


Для стадии 1 это описано следующим образом:

  artifacts:
    paths:
      - sysinfo
    expire_in: 1 hour

Здесь в качестве артифакта сборки описан собранный исполняемый файл:

sysinfo

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

Дополнительный параметр:

expire_in: 1 hour

говорит о том, что этот артефакт будет доступен 1 час с момента создания. Затем файл будет автоматически удалён.

Предполагается что времени в один час вполне хватит для передачи этого файла между CI-стадиями для выполнения всех необходимых действий с ним.

Стадия 2 (test)

Добавьте описание следующей стадии:

test:
  stage: test
  tags:
    - test_stages
  dependencies:
    - build
  script:
    - chmod +x sysinfo
    - ./sysinfo --version
    - ./sysinfo --uname
    - echo "TEST OK" > test_report.txt
  artifacts:
    paths:
      - test_report.txt


На этой стадии средством выполнения выполняется скрипт:

chmod +x sysinfo
./sysinfo --version
./sysinfo --uname
echo "TEST OK" > test_report.txt

Здесь скрипт состоит из 4х последовательно выполняемых команд.

Скрипты в gitlab CI предполагают последовательное выполнение команд до тех пор пока каждая последующая команда возвращает успешный код результата (0).

Следовательно, если одна из предыдущих команд выполнится некорректно, то файл:

test_report.txt

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

На данной стадии артефактом будет являться файл:

test_report.txt

Также следует отметить секцию:

dependencies:
  - build

, описывающую зависимость текущей стадии (test) от предыдущей стадии (build). Таким образом соблюдается последовательность выполнения стадий.

Стадия 3 (package)

Добавьте описание третей стадии:

package:
  stage: package
  tags:
    - test_stages
  dependencies:
    - build
    - test
  script:
    - mkdir release
    - cp sysinfo release/
    - cp VERSION release/
    - cp test_report.txt release/
    - tar czf sysinfo-release.tar.gz release
  artifacts:
    paths:
      - sysinfo-release.tar.gz

Данная стадия зависит от предыдущих двух стадий:

dependencies:
  - build
  - test

и выполняет следующий скрипт:

mkdir release
cp sysinfo release/
cp VERSION release/
cp test_report.txt release/
tar czf sysinfo-release.tar.gz release


Скрипт создаёт каталог release, копирует в этот каталог файл sysinfo (артефакт, созданный стадией 1), файл VARSION (этот файл был в репозитории изначально), файл test_report.txt (артефакт, созданный стадией 2), затем создаёт архив sysinfo-release.tar.gz, помещая в него каталог release.

Созданный архив:

sysinfo-release.tar.gz

стадия сохраняет как артефакт.

Стадия 4 (release)

И, наконец, опишите последнюю стадию:

release:
  stage: release
  dependencies:
    - package
  tags:
    - test_stages
  script:
    - tar xzf sysinfo-release.tar.gz
    - ./release/sysinfo --version
    - echo "Release verified"

Эта стадия выполняет скрипт:

tar xzf sysinfo-release.tar.gz
./release/sysinfo --version
echo "Release verified"

, состоящий из последовательно выполняемых трёх команд.

Как уже выше было сказано, команды будут выполняться последовательно до тех пор, пока каждая предыдущая команда завершается успешным кодом результата (0).

Суть этой стадии в том, что последняя команда скрипта:

echo "Release verified"

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

tar xzf sysinfo-release.tar.gz
./release/sysinfo --version

выполнятся успешно. Т.е. команда:

tar xzf sysinfo-release.tar.gz

успешно распакует архив (в котором находится каталог release, в котором, в свою очередь, находится исполняемый файл программы sysinfo) и вернёт успешный код результата (0), а команда:

./release/sysinfo --version

(этот файл был получен из архива предыдущей командой!) покажет версию программы и также вернёт успешный код результата (0).

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

5.7

Ответьте на вопросы.

Разместите вопросы и ответы на них в отчёте.

Список вопросов:

1. Что такое стадия (stage)?
2. Что такое артефакт?
3. Зачем задавать время жизни артефакта?
4. Какое назначение у артефакта?
5. Как задать чёткую последовательность выполнения стадий?

Тема 6. Управление конфигурацией и секретами в gitlab CI с помощью переменных

Тема:

Управление конфигурацией и секретами в gitlab CI с помощью переменных.

Отчёт:

В отчёт необходимо включить результат последовательного выполнения шагов 6.* и вывод по достигнутым результатам.

Каждому шагу в отчёте необходимо дать осмысленное название.

Работа по своей структуре будет похожа на работы описанные последовательно в темах 4 и 5.

Следовательно, многие действия не будут подробно описаны непосредственно в этой работе. Предполагается, что обучающийся имеет навыки работы, полученные в темах 4 и 5.

Переменные и секреты конфигурации задаются из веб-интерфейса gitlab и предназначены для решения проблемы хранения изменяемых и чувствительных данных конфигурации (например, URL, логины, пароли, токены доступа и другие параметры).

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

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

Другими словами: репозиторий должен содержать только актуальный программный код. Программный код не должен содержать так называемого "хардкода" (конфигурационные данные непосредственно среди исходного кода).

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

.example

, приписанным в конце имени файла.

Для решения проблем хранения и передачи изменяемых и чувствительных данных на этапе CI/CD используются специальные переменные и "секреты" платформы gitlab, работа с которыми является ключевым навыком, приобретаемым обучающимся в процессе практической работы по текущей теме (Теме 6).

Шаг 6.1

Конвейер стадий CI текущего проекта (его также могут называть пайплайн (pipeline)) будет следующим:

stages:
  - prepare
  - build
  - test
  - deploy


Переменные, значения которых будут храниться в переменных платформы gitlab, будут такие:

APP_NAME      <- название приложения
LOG_LEVEL     <- уровень журналирования
APP_ENV       <- вид окружения (dev или prod)

Чувствительные переменные, значения которых будут храниться в секретах платформы gitlab, будут такие:

API_TOKEN

Шаг 6.2

Создайте новый репозиторий в gitlab и склонируйте его на рабочий компьютер (см. предыдущие темы - там написано по шагам как это сделать).

В репозитории, склонированном на компьютер, создайте следующую структуру проекта:

.                     <-- корень проекта
├── main.c            <-- файл исходного кода программы
├── Makefile          <-- конфигурация сборочной среды Make
└── .gitlab-ci.yml    <-- конфигурация CI для платформы gitlab

Шаг 6.3

Добавьте в файл:

main.c

следующее содержимое:

#include <stdio.h>
#include <stdlib.h>

int main() {
    const char *app_name = getenv("APP_NAME");
    const char *app_env  = getenv("APP_ENV");
    const char *version  = getenv("BUILD_VERSION");

    if (!app_name || !app_env || !version) {
        fprintf(stderr, "Required environment variables are not set\n");
        return 1;
    }

    printf("Application: %s\n", app_name);
    printf("Environment: %s\n", app_env);
    printf("Version: %s\n", version);

    return 0;
}


Добавьте в файл:

Makefile

следующее содержимое:

CC=gcc
CFLAGS=-Wall -Wextra -O2

all:
	$(CC) $(CFLAGS) main.c -o app

clean:
	rm -f app


Исключите из отслеживания системой контроля версий файл результата сборки приложения.

Для этого необходимо по содержимому файла:

Makefile

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

Шаг 6.4

В интерфейсе gitlab в меню "Настройки" выберите пункт "CI/CD". Далее на открывшейся странице разверните раздел "Переменные".

Не защищённые переменные

Добавьте не защищённую переменную.

Для этого в поле "Ключ" напишите:

APP_NAME

, а в поле значение напишите название вашего приложения. Например:

MySuperAppV0.1

Остальные параметры оставьте по умолчанию. Нажмите кнопку:

Добавить переменную

для завершения процесса добавления.


Аналогично добавлению переменной:

APP_NAME

добавьте ещё одну, где ключ будет:

LOG_LEVEL

, а значение будет:

debug

В результате должны быть созданы две не защищённых переменных.

Защищённые переменные

Добавьте защищённую переменную:

API_TOKEN

со значением:

my-super-secret-token

Для этой переменной установите параметр "Видимость" в значение:

Маскируемая

и активируйте флаг:

Защитить переменную

затем нажмите кнопку:

Добавить переменную

для завершения процесса добавления.

В результате должны быть создана маскируемая и защищённая переменная.

Переменные с областью видимости

Добавьте переменную:

APP_ENV

со значением:

dev

Для этой переменной установите параметр "Окружения" в значение:

dev

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

Добавить переменную

для завершения процесса добавления.


Теперь добавьте точно такую же переменную:

APP_ENV

, но значение задайте:

prod

и параметр "Окружения" установите в значение:

prod

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

Добавить переменную

для завершения процесса добавления.


В результате должны быть созданы две переменные с одинаковым ключом, но разными значениями, в зависимости от области видимости (Environment scope).

Область видимости далее будет задаваться внутри стадий (stages).

Шаг 6.5

В файле:

.gitlab-ci.yml

создайте список стадий:

stages:
  - prepare
  - build
  - test
  - deploy


Опишите стадию проверки переменных (prepare):

prepare:
  stage: prepare
  script:
    - echo "Checking variables..."
    - test -n "$APP_NAME"
    - test -n "$LOG_LEVEL"


Опишите стадию сборки и генерации версий (build):

build:
  stage: build
  script:
    - make
    - export BUILD_VERSION="1.0.$CI_PIPELINE_IID"
    - echo "BUILD_VERSION=$BUILD_VERSION" > build.env
  artifacts:
    paths:
      - app
    reports:
      dotenv: build.env


Опишите стадию запуска программы (test):

test:
  stage: test
  script:
    - ./app

Опишите стадию развёртывания приложения (deploy) в зависимости от окружения.

Для окружения dev (environment.name = dev):

deploy_dev:
  stage: deploy
  environment:
    name: dev
  script:
    - echo "Deploying to DEV"
    - ./app
  rules:
    - if: '$CI_COMMIT_BRANCH == "develop"'


Для окружения prod (environment.name = prod):

deploy_prod:
  stage: deploy
  environment:
    name: prod
  script:
    - echo "Deploying to PROD"
    - ./app
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

Шаг 6.6

Замените утилиту постраничного просмотра git на cat (это предотвратит необходимость выхода из постраничного навигатора):

git config core.pager cat

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

Синхронизируйте изменения локального репозитория с удалённым репозиторием.

Проверьте работу CI-конвейера в gitlab. Сделайте выводы, опишите их в отчёт.


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

develop

, поскольку в репозитории проекта (и локальном и удалённом) существует только единственная ветка:

main


Если средство выполнения не запускает задания на конвейере, то проверьте настройку средства выполнения.

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

Следовательно, для того, чтобы средство выполнения начало выполнять задачи стадий CI-конфигурации, необходимо или добавить к каждой стадии необходимые теги (которые обрабатывает средство выполнения), или в настройках средства выполнения разрешить запуск заданий без тегов.


После того, как средство выполнение начнёт выполнять стадии CI-конфигурации, стадия:

test

выполнится неудачно.

Загляните в результат выполнения задания, определите причину такого поведения и устраните её.

Подсказка: чтобы определить и устранить причину такого поведения необходимо вникнуть в логику работы программы (посмотреть в её исходный код), а также принимать во внимание аспекты создания переменных, описанные выше.

Опишите процесс устранения этой проблемы в отчёте.

Посмотрите на цепочку стадий конвейера. Обратите внимание на название последней стадии (deploy_prod).


В репозитории на рабочем компьютере создайте дополнительную ветку.

Для этого проверьте что на текущий момент ваша ветка называется:

main

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

git branch


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

develop

командой:

git checkout -b develop

Посмотрите список доступных веток:

git branch

На текущий момент веток должно быть две. Ответ от команды должен быть похож на этот:

* develop
  main

Теперь у репозитория появилось дополнительное состояние (дополнительная ветка).

На момент создания новой ветки состояние файлов в ней идентично состоянию файлов основной ветки. Однако теперь, когда проект переключён на ветку:

develop

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

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

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

Переключаться между ветками можно командой:

git checkout <имя ветки>

Например, для переключения на ветку:

main

выполните команду:

git checkout main

, а для переключения на ветку:

develop

выполните команду:

git checkout develop


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

develop

выполните синхронизацию репозитория учитывая новую ветку:

git push --set-upstream origin develop

, затем посмотрите на цепочку стадий конвейера. Обратите внимание на название последней стадии (deploy_dev).


После выполнения команды:

git push --set-upstream origin develop

посмотрите содержимое конфигурационного файла git-проекта. Определите какие изменения в нём произошли после добавления новой ветки и синхронизации репозиториев.


Сопоставьте результаты работы на шаге 6.6 с CI-конфигурацией. Сделайте выводы опишите их в отчёте.

Шаг 6.7

В разделе 6.4 была сделана специальная переменная с областью видимости:

APP_ENV


Стадии deploy (deploy_dev):

deploy_dev:
  stage: deploy
  environment:
    name: dev
  script:
    - echo "Deploying to DEV"
    - ./app
  rules:
    - if: '$CI_COMMIT_BRANCH == "develop"'


и deploy (deploy_prod):

deploy_prod:
  stage: deploy
  environment:
    name: prod
  script:
    - echo "Deploying to PROD"
    - ./app
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

, описанные в файле:

.gitlab-ci.yml

, в разделе 6.5 называются одинаково (stage: deploy) и, на первый взгляд, выполняют одинаковые действия (запускают программу).


Однако обратите внимание на раздел:

rules

у каждой стадии.

Это правило, которое задаёт ограничения на запуск стадии в зависимости от того, на какой ветке репозитория произошёл commit (и затем push).


Задача стадии deploy (stage: deploy) -- запустить собранное на предыдущих стадиях приложение.


В коде приложения реализован функционал вывода значений переменных:

const char *app_name = getenv("APP_NAME");            <- тут переменные читаем
const char *app_env  = getenv("APP_ENV");             <- тут переменные читаем
const char *version  = getenv("BUILD_VERSION");       <- тут переменные читаем

if (!app_name || !app_env || !version) {
    fprintf(stderr, "Required environment variables are not set\n");
    return 1;
}

printf("Application: %s\n", app_name);              <- а здесь -- выводим
printf("Environment: %s\n", app_env);               <- а здесь -- выводим
printf("Version: %s\n", version);                   <- а здесь -- выводим

(полный код программы смотрите в п. 6.3).

А переменная с областью видимости:

APP_ENV

отличается от других переменных тем, что в зависимости от параметра:

environment:
   name: prod

у стадии (мы смотрим на две разновидности стадии deploy (stage: deploy)), принимает разные значения.

Т.е. для:

environment:
   name: prod

значение переменной:

APP_ENV

будет:

prod

, а для:

environment:
  name: dev

значение переменной:

APP_ENV

будет:

dev

Сами же стадии, в свою очередь, имеют ограничения на запуск в зависимости от git-ветки, на которой произошёл коммит.

Следовательно, в зависимости от ветки, на которой произошёл коммит, будут запущены разные версии стадии deploy (stage: deploy), а поскольку разные версии этой стадии имеют разные значения для:

environment:
  name:

, то и запускаемая программа в разных версиях стадии deploy (stage: deploy) должна показывать разные значения прочитанной переменной:

APP_ENV

Это свойство применяется при необходимости иметь разные значения переменных для выполнения процессов CI/CD в зависимости от задачи: на продуктовый (prod, production, т.е. вариант релиза) пайплайн применяется продуктовое значение переменной, а, например, для отладочного варианта (dev, development, вариант для разработки и тестирования) значение переменной отличается от продуктового.

Шаг 6.8

Ответьте на вопросы.

Разместите вопросы и ответы на них в отчёте.

Список вопросов:

1. Откуда берётся BUILD_VERSION?
2. Откуда берётся CI_COMMIT_BRANCH?
3. Почему APP_ENV разный для dev и prod?
4. Почему API_TOKEN нельзя вывести через echo?
5. Почему в представленной конфигурации стадия test выполнится с ошибкой? Что нужно сделать для устранения этой ошибки?

Тема 7. Создание релизов программного обеспечения

Тема:

Создание релизов программного обеспечения.

Отчёт:

В отчёт необходимо включить результат последовательного выполнения шагов 7.* и вывод по достигнутым результатам.

Каждому шагу в отчёте необходимо дать осмысленное название.

До текущего момента рассматривался функционал CI-автоматизации, который демонстрировал примеры подготовки, сборки, тестирования, пакетирования программного обеспечения и не рассматривался функционал выпуска релизов.

Наиболее простым способом автоматизации релизов программного обеспечения на базе CI-автоматизации является способ, когда релиз автоматически подготавливается и размещается в проекте при коммите в главную ветку проекта.

Обычно, главное веткой проекта считается ветка:

main

или ветка

master

(как было ранее в мире git).

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

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

main
develop

, следовательно, в этой теме главной веткой будем считать ветку:

main

а веткой для разработки и тестирования будем считать ветку:

develop

(тем более, что стадии CI-автоматизации семантически совпадают с названиями этих веток).


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

.gitlab-ci.yml

, добавив в него отдельную стадию:

release

и настроив её соответствующим образом.


На стадии:

release

необходимо будет воспользоваться программным интерфейсом (API) gitlab для загрузки в него архива с релизом, а также для создания релиза (как если бы мы это делали через веб интерфейс).

Поскольку API платформы gitlab реализовано в формате HTTP API, инструментом (клиентом по протоколу HTTP) в скрипте стадии будет использован клиент:

curl

Также, поскольку для доступа к функционалу API требуется аутентификация, для выполнения HTTP-запросов посредством curl потребуется точно такой же токен (можно даже тот же), как был использован на шаге 2.3.

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


Приступим...

Шаг 7.1

Создайте защищённую переменную названием ключа:

GITLAB_API_TOKEN

и в качестве значения задайте токен gitlab. Например такой:

glpat-kZdA2AMiqk3_WuoENw_K

Важно! Здесь должен быть валидный токен!

Шаг 7.2

В файл

.gitlab-ci.yml

Добавьте следующее содержимое:

release:
  stage: release
  needs:
    - job: build
      artifacts: true
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
  script:
    - echo "Creating release $BUILD_VERSION"

    # Архивация
    - tar -czf app-$BUILD_VERSION.tar.gz app
    # Загрузка файла
    - |
      UPLOAD_RESPONSE=$(curl --silent --request POST \
        --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
        --form "file=@app-$BUILD_VERSION.tar.gz" \
        "$CI_API_V4_URL/projects/$CI_PROJECT_ID/uploads")

    - FILE_MARKDOWN=$(echo "$UPLOAD_RESPONSE" | grep -oP '"markdown":"\K[^"]+')

    # Создание релиза
    - |
      curl --request POST \
        --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
        --header "Content-Type: application/json" \
        --data "{
          \"name\": \"Release $BUILD_VERSION\",
          \"tag_name\": \"$BUILD_VERSION\",
          \"ref\": \"main\",
          \"description\": \"## Release $BUILD_VERSION\n\nСборка:\n\n$FILE_MARKDOWN\"
        }" \
        "$CI_API_V4_URL/projects/$CI_PROJECT_ID/releases"

В скрипте этой стадии наибольший интерес представляют такие фрагменты как:

...
# Загрузка файла
- |
  UPLOAD_RESPONSE=$(curl --silent --request POST \
    --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
    --form "file=@app-$BUILD_VERSION.tar.gz" \
    "$CI_API_V4_URL/projects/$CI_PROJECT_ID/uploads")
- FILE_MARKDOWN=$(echo "$UPLOAD_RESPONSE" | grep -oP '"markdown":"\K[^"]+')
...

и:

# Создание релиза
...
- |
  curl --request POST \
    --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \
    --header "Content-Type: application/json" \
    --data "{
      \"name\": \"Release $BUILD_VERSION\",
      \"tag_name\": \"$BUILD_VERSION\",
      \"ref\": \"main\",
      \"description\": \"## Release $BUILD_VERSION\n\nСборка:\n\n$FILE_MARKDOWN\"
    }" \
    "$CI_API_V4_URL/projects/$CI_PROJECT_ID/releases"
...

Обе этих части скрипта по сути представляют из себя отправку HTTP запроса на API платформы gitlab.

Сначала файл, переданный с предыдущей стадии и упакованный в архив загружается во внутреннее хранилище платформы gitlab, затем из результата запроса на загрузку посредством утилиты grep вынимается внутренняя для платформы gitlab ссылка на загруженный файл, которая помещается в переменную:

FILE_MARKDOWN

, затем следующим запросом происходит создание релиза также путём отправки HTTP запроса на API платформы gitlab.

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

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


Проанализируйте переменные, значения которых участвуют в образовании команд для выполнения запросов посредством:

curl

, изучите справочную систему утилиты, ответьте на вопросы:

1. Для чего используется параметр --header
2. Для чего используется параметр --data
3. Для чего используется параметр --request
4. Напишите curl-команды на загрузку файла и создания релиза в общем виде

Вопросы и результаты ответов на них зафиксируйте в отчёте.

Шаг 7.3

Сохраните файл

.gitlab-ci.yml

после добавления стадии:

release


К стадиям:

prepare
build
test

Добавьте параметр:

rules:
  - if: '$CI_COMMIT_TAG == null'

, а к стадиям:

deploy
release

в аналогичном разделе добавьте дополнительное условие. Например, условие было:

rules:
  - if: '$CI_COMMIT_BRANCH == "main"'

, а должно стать:

rules:
 - if: '$CI_COMMIT_TAG == null && $CI_COMMIT_BRANCH == "main"'

Добавьте изменения к отслеживанию системой контроля версий git и синхронизируйте изменения с удалённым репозиторием.


Проследите процесс запуска конвейера, убедитесь в запуске добавленной стадии.

В случае успешной работы стадии:

release

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

Также в релизе можно найти архивы с исходным кодом и другую полезную информацию.


Подумайте: зачем понадобилось накладывать условия (rules.if) на каждую из стадий?

Попробуйте убрать дополнительные условия и выполнить commit/push из ветки main. Посмотрите на запуск конвейеров. Что произойдёт?

Шаг 7.4

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

main

Однако, на текущий момент эта ветка является основной у проекта.


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

Поэтому в качестве основной ветки удобнее иметь не ту, из которой должен собираться релиз.

Например, в текущей ситуации удобно разработку вести в ветке:

develop

, а релиз выполнять из ветки:

main


На странице проекта в gitlab выберите ветку develop в качестве ветки по умолчанию. Для этого выберите пункт "Репозиторий" в меню "Настройки", затем на открывшейся странице разверните раздел "Настройки ветки по умолчанию" и измените ветку по умолчанию с:

main

на:

develop

затем нажмите кнопку:

Сохранить изменения

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

develop


Теперь при клонировании проекта данная ветка будет настроена по умолчанию и работа с проектом по умолчанию будет происходить в ветке:

develop


Чтобы настроить уже склонированный локальный репозиторий на работу по умолчанию с веткой:

develop

вместо ветки:

main

переключите репозиторий на ветку:

devel

командой:

git checkout develop

затем выполните команду:

git push -u origin develop


С этого момента команды:

git push
git pull

будут работать с веткой:

develop

, которая является веткой по умолчанию в удалённом репозитории.

Шаг 7.5

Рассмотрим минимально функциональный рабочий процесс, при котором работа с кодом ведётся в ветке:

develop

, а релизы программы собираются из ветки:

main


Чтобы понять как это работает необходимо ввести понятие слияния веток (merge).


Находясь на ветке:

develop

откройте файл исходного кода программы и внесите в него изменения таким образом, чтобы при запуске программы была видна разница между прошлым поведением программы и настоящим (например, чтобы программа выводила "Hi, World", вместо "Hello, World").

Добавьте изменения к отслеживанию системой контроля версий, сделайте коммит.

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

develop

Важно тут то, что релиз создан не будет.


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

Как только необходимость назрела, следует выполнить слияние ветки:

develop

с главной веткой (т.е. с той веткой, от изменений в которой зависит запуск стадии сборки релиза). В нашем случае это ветка:

main


Следует помнить, что на текущий момент в ветке:

main

код отличается от ветки:

develop

Другими ловами, код там старый. Т.е. тот, который соответствует старому релизу.

Чтобы обновить код в ветке:

main

и привести его к состоянию как в ветке:

develop

необходимо выполнить слияние веток.

Чтобы выполнить слияние ветки develop с веткой main перейдите на ветку main:

git checkout main

, выполнить синхронизацию ветки с удалённым репозиторием:

git pull origin main

, затем выполнить слияние ветки (мы на ветке main) с веткой develop:

git merge develop --no-edit


В результате состояние репозитория на ветке main будет синхронизировано с состоянием на ветке develop.

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

Работа с конфликтами при слиянии -- отдельная большая тема, сильно выходящая за рамки этой практики. Однако, стоит знать что просто такое может быть...


Итак, на текущий момент произошло слияние и ветка main теперь идентична по коду ветке develop. Если теперь сделать push, то стадия release не будет запущена, т.к. в ветке main не было новых коммитов.

Для запуска стадии:

release

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

git commit --allow-empty -m "Trigger release pipeline"

, после чего выполнить команду:

git push


Это запустит соответствующую стадию релиза.


Проделайте описанные действия, убедитесь в работе стадии по сборке релиза, сделайте выводы и запишите результаты в отчёт.