Всем привет! Хочу рассказать о своём опыте настройки домашнего билд-тест-сервера.
Допустим, у вас есть хобби-проект, и вы работаете на нём в одиночку. Или вам просто нечем заняться, и вы решили поиграться с 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 начинает деплоить результат билда:
останавливает локально (для билд-сервера) запущенный lametric.service
удаляет старое приложение lametric.jar
копирует новое (только что собранное) с переименованием
запускает 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
Тестируем:
меняем тестовое приложение (например, 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";
}
}
commit, push
Ждём 15 минут (или запускаем job ручками), заходим на http://192.168.2.11/lametric/hello Убеждаемся, что всё работает (изменения видны).
Далее: добавление Angular, деплой в prod, настройка автоматического тестирования.
Расскажу об этом в следующей статье.