Всем привет! Хочу рассказать о своём опыте настройки домашнего билд-тест-сервера.

Допустим, у вас есть хобби-проект, и вы работаете на нём в одиночку. Или вам просто нечем заняться, и вы решили поиграться с CI/CD, Linux администрированием, web разработкой.

Во-первых, почему именно домашний?

Ну, "домашний" значит, что вам не нужно ни за что платить (условно), что ресурсы ваши не ограничены - дисковое пространство, CPU (тоже условно), что у вас всё под контролем (root доступ, выбор операционной системы). Что вам не нужно вникать в правила пользования каким-то сторонним CI/CD сервисом, что вам придётся-таки разобраться с нюансами DevOps, пускай и в домашних условиях, пускай и на коленке.

Итак, допустим, у вас есть старый компьютер. У меня вот, например, такой завалялся: Fanless Mini PC Dual LAN Celeron J1900.

И я считаю, он идеально подходит для моих задач. Не шумит, маленький, у него есть wi-fi.

Следующий этап - это выбор операционной системы. Я давно пользуюсь LXLE и могу только рекомендовать этот дистрибутив.

Установка и настройка LXLE у меня проходит с подключенным монитором, мышкой, клавиатурой. На этом этапе важно добиться хорошего интернет соединения. Я рекомендую настроить как wi-fi, так и ethernet. Подумайте, куда вы запрячете ваш билд-сервер, чтобы у него был хороший доступ к интернету и в то же время можно было дотянуться рукой и перегрузить его, если что. (Если он повиснет, например, или сеть в нём глюканёт. Такое случается чаще, чем вы можете себе представить).

Итак, после перегрузки вашего билд-сервера, он должен появляться в локальной сети и быть доступен через ssh. Активация ssh на билд-сервере:

sudo apt update
sudo apt install openssh-server
sudo systemctl status ssh
sudo ufw allow ssh

Подключение через Putty или с другой Linux машины:

ssh username@ip_address

IP-адрес новоиспечённой машины можно узнать командой:

ifconfig

выполненной на самой машине, естественно :)

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

Вторая строка - это мой сервер. Это действие необходимо, чтобы сервер всегда был доступен по одному и тому же IP-адресу.

Если у вас два подключения (wi-fi и ethernet), то и IP-адреса будет два. Оба будут ссылаться на вашу машину.

Потренируйтесь удалённо перегружать сервер.

sudo reboot

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

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

Теперь переходим к настройке софта.

Мой выбор: Jenkins, NginX, Maven.

Мой стэк: Java (Spring Boot), Angular.

С чего начать?

Установите nginx и добейтесь nginx-приветствия по адресу http://<your_build_server_ip_address>

Перегрузите сервер и убедитесь, что nginx стартует автоматически.

Установите Jenkins. Вот отличная статья, как это сделать.

По умолчанию Jenkins стартует на порту 8080. У себя я настроил reverse proxy в nginx конфигурации. Jenkins доступен по адресу http://192.168.2.11/

sudo vim /etc/nginx/sites-enabled/default
        location / {
                proxy_pass http://127.0.0.1:8080;
                proxy_set_header    X-Forwarded-Host $http_host;

        }

Снова перегружаем билд-сервер, убеждаемся, что всё автоматически стартует: nginx, jenkins, что jenkins доступен на 80-том порту.

Переходим к конфигурации Jenkins.

Начнём, как всегда, издалека :)

Создадим репозиторий на GitHub, добавим в него простое Spring Boot приложение. Потестируем его локально. Обратите внимание, что по умолчанию порт в Spring Boot такой же, как и у Jenkins: 8080.

Это легко исправить с помощью файла src/main/resources/application.properties в нашем тестовом приложении:

server.port=8000

Наша задача - автоматически редеплоить (перепускать обновленное) тестовое приложение на билд-сервере.

Создадим Jenkins Job. Для логина к закрытому репозиторию я использую Github personal access token:

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

Проверять изменения в репозитории будем каждые 15 минут:

А вот и наш билд:

Рассмотрим его поподробнее. Особенно следующую часть:

sudo systemctl stop lametric.service
file="/opt/lametric/lametric.jar"
if [ -f "$file" ] ; then
    rm "$file"
fi
cp /var/lib/jenkins/workspace/LaMetric/target/lametric-*.jar /opt/lametric/lametric.jar
sudo systemctl start lametric.service

Итак, мы выполняем shell команды на той же машине, где запускается build job.

Что из себя представляет lametric.service?

vim /etc/systemd/system/lametric.service
[Unit]
Description=Notify LaMetric about different events.
After=syslog.target

[Service]
User=geniot
Group=users
WorkingDirectory=/opt/lametric
ExecStart=/usr/bin/java -jar /opt/lametric/lametric.jar
SuccessExitStatus=143
TimeoutStopSec=10
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Т.е. этот сервис запускает java процесс с параметрами:

/usr/bin/java -jar /opt/lametric/lametric.jar

Jenkins Job при успешном выполнении maven команд clean package начинает деплоить результат билда:

  1. останавливает локально (для билд-сервера) запущенный lametric.service

  2. удаляет старое приложение lametric.jar

  3. копирует новое (только что собранное) с переименованием

  4. запускает lametric.service

Чтобы всё это работало, необходимо создать папку /opt/lametric. Выдать ей права.

sudo mkdir /opt/lametric
sudo chown geniot:users /opt/lametric -R
sudo chmod 777 /opt/lametric -R

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

sudo visudo
jenkins ALL=(ALL) NOPASSWD: ALL

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

Ещё нюансы.

Добавим контекст приложения в application.properties:

server.servlet.context-path=/lametric

Чтобы он не путался с Jenkins, который расположен в корне.

Добавим ещё один proxy в nginx:

        location /lametric {
                proxy_pass http://127.0.0.1:8000;
                proxy_set_header    X-Forwarded-Host $http_host;
        }

не забываем перепускать nginx.

lametric.service добавляем в автостарт:

sudo systemctl enable lametric.service

Тестируем:

  1. меняем тестовое приложение (например, response от HelloWorldRestController)

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldRestController {
    Logger logger = LoggerFactory.getLogger(HelloWorldRestController.class);

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String handle() {
        return "New hello";
    }
}
  1. commit, push

  2. Ждём 15 минут (или запускаем job ручками), заходим на http://192.168.2.11/lametric/hello Убеждаемся, что всё работает (изменения видны).

Далее: добавление Angular, деплой в prod, настройка автоматического тестирования.

Расскажу об этом в следующей статье.

Комментарии (7)


  1. puyol_dev2
    18.01.2022 15:41
    +3

    Ещё один пост на тему очумелые ручки. Практическая ценность -> 0


    1. brnovk
      18.01.2022 16:28

      Ну почему же. В последнее время с облачными CI/CD системами какая-то чертовщина творится, периодически задумываюсь о своём билд сервере.

      Например - AWS CodePipeline, последнюю неделю отваливается в 11 вечера в gmt+3. Источник - Bitbucket. С 11 до часа ночи висит намертво, потом частично оживает. Ближе к 4 утра уже норм. За всю неделю в статусах это отражено только 2 дня, но на самом деле воспроизводится баг ежедневно:
      https://bitbucket.status.atlassian.com/
      И с этим ты ничего не сделаешь, в отличии от своего билд-сервера - только ждать когда "добрые дяди разрешат тебе продолжить работу".

      Или 23 декабря 2021 - среди рабочего дня AWS отвалился, часа 2 коммиты в Bitbucket не пушились. И в Slack картинки не грузились.
      https://habr.com/ru/news/t/597391/

      Подобные сбои в облаках всё чаще и чаще.


      1. tzlom
        18.01.2022 16:37
        +2

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

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


  1. prorockov
    18.01.2022 20:48

    Как рубить дрова?

    1) берём топор

    2) рубим


    1. geniot Автор
      18.01.2022 20:53
      +1

      Этот пост идёт с пометкой tutorial. Я надеюсь, информация сэкономит время тем, кто захочет повторить всё описанное. У меня ушло прилично времени, чтобы найти оптимальные для себя решения. Например, логин в Github, редеплой с помощью сервиса, jenkins sudoer. Ведь можно тысячью разными способами это организовать. Я, собственно, многие из них и перепробовал, прежде чем прийти к таким решениям.


  1. Coocos
    18.01.2022 21:37

    Почему не сделали по хуку без ожидания 15 минут?


    1. geniot Автор
      18.01.2022 22:47

      Отличный вопрос. Я рассматривал этот вариант. Тут может быть Github web hook, но он мне не подходит, потому что у меня Jenkins в локальной сети, и я не планирую запрашивать внешний ip адрес. И второй вариант (который, видимо, вы имеете ввиду) - локальный хук. Здесь я, во-первых, ленюсь всё это настраивать, во-вторых, предвижу кучу проблем: - пересяду на другой комп, забуду, что хуки не настроены, - jenkins будет не доступен по какой-нибудь причине, хук сфейлится, но легче от этого мне не станет, - я пользуюсь Intellij IDEA, там свой Git плагин, как он интегрируется, пока не понятно. Короче: сложно всё это. Я начинал вообще с того, что установил и настроил TeamCity. А потом наткнулся на проблему с билдом Angular проекта и пришлось всё свернуть, перейти на старый добрый Jenkins.