Содержание

Gitea + Harbor + Jenkins как замена GitLab

С чего всё началось. С того, что уже установленный в моей локальной сети экземпляр сервиса GitLab CE вполне успешно работал, но был расположен на обычном HDD, т.е. не SSD и не NVMe. И вполне, какзалось бы, успешно работал, но при длительном аптайме он сжирал все ресурсы оперативной памяти, которой я ему выделил - 16 Гб (что видимо мало), начинал активно использовать swap, и когда планировщик начинал заниматься очисткой/оптимизацией структуры репозиториев, мог начинать тормозить с невероятной силой, из-за того, что исчерпывал ресурc IOPS диска. Конечно, перезагрузка хоста всё решала, но сама ситуация была несколько неприятной. К тому же, для каких-то небольших проектов и небольшой инфраструктуры такой комбайн как GitLab, мне кажется, не очень-то и нужен. Плюс ко всему, у меня еще были вопросы к формату описания сценариев CI/CD пайплайнов, которые, как мне кажется, в Jenkins можно делать значительно гибче чем в GitLab. Словом, GitLab безусловно крут и незаменим в наши дни, если вы работаете в больщой команде где много подгрупп и разых проектов, но для каких-то более простых задач можно обойтись инструментами установку и настройку которых я буду описывать ниже.

Для установки будем использовать ОС Ubuntu Linux 22.04 LTS на всех разворачиваемых хостах.

Установка Gitea (“Git с чашечкой чая!”)

Установка PostgreSQL и создание БД

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

Сначала устанавливаем сам PostgreSQL:

1
sudo apt install postgresql

После установки проверяем в файле /etc/postgresql/14/main/postgresql.conf значения параметров:

  • listen_addresses: по умолчанию это localhost, и если БД и Gitea на одном и том же хосте, то можно так и оставить, либо указать IP-адрес интерфейса на котором должен отвечать сервер БД. Если мы хотим чтобы БД работала на всех интерфейсах, то можно задать значение 0.0.0.0, либо несколько IP через запятую.
  • password_encryption: рекомендуется использовать scram-sha-256, по этому, чтобы “наверняка”, можно раскомментировать данную строчку в файле конфигурации

Далее, запускаем консольный клиент PostreSQL от имени пользователя postgres и выполняем следующий наобор действий:

  • 1
    
    sudo -u postgres psql
    
  • 1
    
    CREATE ROLE gitea WITH LOGIN PASSWORD 'gitea';
    

    Естественно, логин и пароль следует заменить на какой-то другой

  • 1
    
    CREATE DATABASE giteadb WITH OWNER gitea TEMPLATE template0 ENCODING UTF8 LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8';
    
  • В файл nano /etc/postgresql/14/main/pg_hba.conf добавляем строчку:

    • для локального подключения:

      1
      
      local    giteadb    gitea    scram-sha-256
      
    • для удаленного подключения:

      1
      
      host    giteadb    gitea    192.0.2.10/32    scram-sha-256
      

      где имя БД, пользователя, IP должны соответствовать вашим условиям

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

  • перезапускаем PostgreSQL для применения изменений:

    1
    
    sudo systemctl restart postgresql.service
    

    После чего можно проверить подключение к БД:

    • локально:
      1
      
      psql -U gitea -d giteadb
      
    • удаленно:
      1
      
      psql "postgres://gitea@203.0.113.3/giteadb"
      

Установка сервиса Gitea

Скачиваем бинарный файл и GPG-подпись:

1
2
3
wget -O gitea https://dl.gitea.com/gitea/1.20.4/gitea-1.20.4-linux-amd64
wget -O gitea.asc https://dl.gitea.com/gitea/1.20.4/gitea-1.20.4-linux-amd64.asc
chmod +x gitea

Проверяем GPG-подпись (не обязательно):

1
2
gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
gpg --verify gitea.asc gitea

Успехом проверки можно считать наличие в выводе строки: Good signature from "Teabot <teabot@gitea.io>", но остальные сообщения можно не обращать внимание

Если всё прошло успешно, то копируем файл в папку /usr/local/bin/:

1
2
sudo cp gitea /usr/local/bin/gitea
sudo chown root:root /usr/local/bin/gitea

Создаем пользователя для сервиса gitea:

1
2
3
4
5
6
7
8
sudo adduser \
   --system \
   --shell /bin/bash \
   --gecos 'Git Version Control' \
   --group \
   --disabled-password \
   --home /home/git \
   git

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

1
2
3
4
5
6
sudo mkdir -p /var/lib/gitea/{custom,data,log}
sudo chown -R git:git /var/lib/gitea/
sudo chmod -R 750 /var/lib/gitea/
sudo mkdir /etc/gitea
sudo chown root:git /etc/gitea
sudo chmod 770 /etc/gitea

Создаем файл определения сервиса systemd:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cat << EOF > /etc/systemd/system/gitea.service
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target

Wants=postgresql.service
After=postgresql.service

[Service]
# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that
# LimitNOFILE=524288:524288
RestartSec=2s
Type=notify
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
WatchdogSec=30s
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
#AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
EOF
Примечание
Если вы решите развернуть сервер БД на отдельном хосте, то строки не забудьте закоменировать строки Wants=postgresql.service и After=postgresql.service

Перезапускаем демона systemd и запускаем сервис:

1
2
3
systemctl daemon-reload
systemctl enable gitea.service
systemctl start gitea.service

Если всё успешно, то открываем в браузере http://[IP вашего сервера]:3000, где должна открыться страничка установки Gitea, на которой необходимо установить:

  • тип БД: PostgreSQL
  • указать IP-адрес или оставить localhost, если БД находится на том же хосте
  • вписать логин и пароль от созданной ранее БД
  • в самом низу страницы можно задать логин и пароль администратора сервиса. Если этого не делать, то можно после установки создать пользователя командой:
    1
    
    sudo -u git gitea --config /etc/gitea/app.ini admin user create --username gitea --password gitea123 --must-change-password --email root@gitea.local --admin
    
    Естественно, имя пользователя и пароль могут быть другими

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

1
2
chmod 750 /etc/gitea
chmod 640 /etc/gitea/app.ini

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

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

1
2
3
4
5
6
7
8
9
[attachment]
MAX_SIZE = 1000
MAX_FILES = 500

[migrations]
ALLOW_LOCALNETWORKS=true

[webhook]
ALLOWED_HOST_LIST = *

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

1
systemctl restart gitea.service

Настройка прокси Nginx для Gitea

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

Устанавливаем Nginx:

1
2
sudo apt update
sudo apt install nginx

Создаем файл конфигурации:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
cat << EOF > /etc/nginx/sites-available/gitea.conf
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    #server_name git.myserver.org;

    location / {
        client_max_body_size 1024M;
        proxy_pass http://localhost:3000;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }
}
EOF

И меняем дефолный конфиг на наш:

1
2
rm /etc/nginx/sites-enabled/default
ln -sr /etc/nginx/sites-available/gitea.conf /etc/nginx/sites-enabled/gitea.conf

Перезапускаем Nginx и проверяем что всё работает на обычном порту http:

1
systemctl restart nginx.service

Настройка сервера Harbor (Docker registry)

Установка Docker

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

1
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

Добавляем репозиторий apt:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
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

# Add the repository to Apt sources:
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
sudo apt-get update

Устанавливаем сам Docker:

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

Установка Harbor

Выбираем какой нибудь релиз отсюда и скачиваем его и GPG-ключ с помощью wget:

1
2
wget https://github.com/goharbor/harbor/releases/download/v2.9.0/harbor-offline-installer-v2.9.0.tgz
wget https://github.com/goharbor/harbor/releases/download/v2.9.0/harbor-offline-installer-v2.9.0.tgz.asc

Проверяем GPG-сигнатуру:

1
2
gpg --keyserver hkps://keyserver.ubuntu.com --receive-keys 644FF454C0B4115C
gpg -v --keyserver hkps://keyserver.ubuntu.com --verify harbor-offline-installer-v2.9.0.tgz.asc

Интересующая нас строка - gpg: Good signature from "Harbor-sign (The key for signing Harbor build) <jiangd@vmware.com>" [unknown], на все последующие можно не обращать внимание.

Далее, распаковываем:

1
tar zxf harbor-offline-installer-v2.9.0.tgz

Копируем шаблон конфигурации:

1
cp ./harbor.yml.tmpl harbor.yml

Вносим в него необходимы правки, такие как: hostname, harbor_admin_password, пароль БД и т.д. Так же можно закомментировать всё что связано с https, так как в этом нет необходимости внутри локальной сети. Если понадобится вывести сервис в интернет, то, как мне кажется, проще будет сделать это через какой-то реверс-прокси.

Подробное описание параметров можно посмотреть тут: https://goharbor.io/docs/2.0.0/install-config/configure-yml-file/

После чего можно запускать скрипт install.sh

Если установка прошла успешно, то мониторим состояние развертывания через watch docker compose ps до тех пор, пока стстус всех контейнеров не перейдет в состояние healthy. После этого можно открыть адрес хоста в браузере и попробовать создать какой-то тестовый проект.

Для того чтобы сам докер мог работать с этим реестром через http, нужно добавить в файл конфигурации /etc/docker/daemon.json нечто подобное:

1
2
3
{
"insecure-registries" : ["myregistrydomain.com"]
}

и перезапустить сам Docker:

1
systemctl restart docker.service

После этого авторизуемся в нашем Docker registry под пользователем admin:

1
docker login myregistrydomain.com

И пробуем туда что-нибудь запушить:

1
2
3
docker pull ubuntu:22.04
docker tag ubuntu:22.04 myregistrydomain.com/library/ubuntu:22.04
docker push myregistrydomain.com/library/ubuntu:22.04

Если всё прошло успешно, и образ появился в проекте library, то будем считать, что на данном этапе процесс настройки хранилища Docker-образов завершен.

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

Начальная установка

Устанавливаем Java Runtime:

1
2
sudo apt update
sudo apt install openjdk-17-jre

Подключаем репозиторий Apt и устанавливаем Jenkins:

1
2
3
4
5
6
7
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee \
  /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt-get update
sudo apt-get install jenkins

Далее, открываем в браузере страницу http://[адрес хоста]:8080, где должна быть страница куда нужно ввести пароль администратора для разблокировки. Местоположение файла содержащего пароль так же должно быть указано на этой странице. Вставляем содержимое этого файла в соответствующее поле и жмем кнопку Continue.

На следующей странице предлогается выбрать: “Установить предлогаемые плагины” (“Install suggested plugins”) или “Выбрать плагины самостоятельно” (“Select plugins to install”). Выбираем первый вариант, т.к. в нем уже есть большая часть того что нам могло бы понадобиться, что-то еще можно будет установить позже, по мере необходимости.

После красивой странички, где будет отображаться прогресс установки дополнениий, появится страница где будет предложено создать первого пользователя - администратора. Тут, думаю, все понятно: логин, пароль, email - это на ваше усмотрение.

Следующим шагом нужно указать Jenkins URL, на странице под заголовком “Instance Configuration”.

Если всё прошло успешно, то в конце мы увидим надпись “Jenkins is ready! Your Jenkins setup is complete.”

После нажатия на кнопку “Start using Jenkins” мы увидим основной дашборд, в котором сразу бросается в глаза красная еденичка, в правом-верхнем углу страницы, рядом с иконкой изображающей щит. Кликнув на которую, мы увидим уведомление, что выполнять сборки на той же ноде где и контроллер может быть не безопасно и предложено установить “агент”. Агент - это сервис, который выполняет команды контроллера на какой-то локальной или удаленной машине. Лучше, чтобы все агенты были на удаленных машинах, отдельно от “контроллера”, который мы только что установили. Чтобы работа “контроллера” стала безопасной, следующим шагом мы настроим “агент” на отдельной машине.

Настройка “постоянного агента” (“permanent agent”)

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

  • установить OpenSSH Server, через который Jenkins будет взаимодействовать с агентом
  • установить Java Runtime
  • установить Docker, так же как мы это делали на шаге установки Harbor, для того чтобы мы могли исповать его в наших пайплайнах

Устанавливаем Java Runtime и OpenSSH Server:

1
2
sudo apt update
sudo apt install openjdk-17-jre openssh-server

Существует два варианта подключения агента:

  1. через SSH, когда Jenkins-контроллер сам подключается к нужному хосту по SSH, копирует и запускает таи своего агента, передавая ему необходимые параметры для подключения к контроллеру
  2. когда мы самотоятельно копируем jar-файл приложения агента и организуем его запуск на удаленном хосте, указывая необходимые параметры для подключения к контроллеру

Первый вариант мне видится проще, по этому мы будем использовать именно его. Для этого нам сначала необходимо сгенерировать пару SSH-ключей - приватный и публичный. Приватный ключ, соответственно, будет использоваться контроллером для авторизации на исполнительной ноде (worker’е), а публичный будет добавляться в список авторизованных ключей на исполнителе.

1
ssh-keygen -b 4096 -t rsa -f jenkins-agent-1

В результате чего мы получим два файла: jenkins-agent-1 - это приватный ключ, и jenkins-agent-1.pub - публичный ключ.

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

1
2
sudo useradd --base-dir /var/jenkins --home-dir /var/jenkins --create-home --user-group --shell /bin/bash --groups docker jenkins
sudo -u jenkins mkdir -p /var/jenkins/.ssh

Добавиляем содержимое из полученного ранее файла ключа jenkins-agent-1.pub в файл /var/jenkins/.ssh/authorized_keys. После этого, на хосте контроллера, необходимо добавить новый хост в список известных SSH-хостов:

1
2
sudo -u jenkins mkdir -p /var/lib/jenkins/.ssh
ssh-keyscan -H [IP или имя worker-ноды] | sudo -u jenkins tee -a /var/lib/jenkins/.ssh/known_hosts

Теперь можно добавить агента в разделе настроек веб-интерфейса Jenkins. Для этого переходим в раздел Manage Jenkins -> Nodes и нажимаем кнопку New Node. На появившейся странице задаем какое-то имя ноды, например jenkins-agent-1, выбираем тип “Permanent Agent” и жмем кнопку Create.

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

  • Number of executers: параметр отвечающий за колличество одновременно выполняемых заданий на исполнении. По умолчанию равен еденице, но можно сделать больше, чтобы какие-то задачи могли выполняться параллельно
  • Remote root directory: корневая папка для агента. Укажем домшнюю папку пользователя, которого мы создавали для агента - /var/jenkins
  • Launch method: выбираем Launch agents via SSH
    • ниже, в поле Host, указываем адрес исполняющей ноды

    • внизу поля Credentials нажимаем на кнопку Add, и выбираем хранилище авторизационных данных Jenkins

      В появившемся окне, необходимо заполнить поля:

      • Domain: Global credentials (unrestricted)
      • Kind: SSH Username with private key
      • ID: можно оставить пустым, тогда сгенерируется случайный идентификатор, либо вписать что-то своё
      • Username: указываем пользователя, которого мы создавали, т.е. jenkins
      • Private Key: выбираем Enter directly и в появившемся ниже поле нажимаем кнопку Add. После чего в появившееся пространство вставляем содержимое файла приватного ключа SSH (jenkins-agent-1), который мы генерировали ранее.
      • Passphrase: если приватный ключ был защищен паролем, то вписываем его в это поле, если нет, то оставляем его пустым
    • после добавления, выбираем из списка, находящегося между кнопкой Add и заголовком поля, только что созданную запись авторизационных данных

  • Availability: оставляем Keep this agent as much as possible

Всё остальное оставляем как есть и нажимаем кнопку Save.

Теперь нужно сделать так, чтобы задания не могли попадать на ноду контроллера. Для этого, на той же странице натройки нод, кликаем на значек “шестеренки” справа, в строке ноды с именем “Built-In Node”, и в поле Number of executors устанавливаем значение 0, затем, сохраняем кнопкой Save

Чтобы проверить, что подчиненная нода работает нормально, нужно в писке нод кликнуть по её имени, и на открывшейся странице проверить список сообщений в разделе Log, в конце которого дожно быть сообщение “Agent successfully connected and online”. А так же можно попробовать выполнить какие-то скриптовые комманды в разделе Script Console.

Например, результатом выполнения комманды println System.getenv("PATH") может быть нечто подобное:

1
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

или результат выполнения команды println "lsb_release -a".execute().text, нечто вроде:

1
2
3
4
Distributor ID:	Ubuntu
Description:	Ubuntu 22.04.3 LTS
Release:	22.04
Codename:	jammy

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

Заключение

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

Следующими шагами будет:

  • сопряжение Gitea с Jenkins и выполнение сценария из Jenkinsfile, находящегося в git-репоитории проекта
  • создание Docker-образов из Pipeline’a и сохранение его в Harbor Docker Registry
  • выполнение сценария внутри Docker-контейнера
  • взаимодействие по SSH со сторонним из пайплайна

Но все эти шаги я опишу уже в следующей статье.