Всем привет! Хочу рассказать о своём опыте настройки домашнего билд-тест-сервера.
Допустим, у вас есть хобби-проект, и вы работаете на нём в одиночку. Или вам просто нечем заняться, и вы решили поиграться с 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, настройка автоматического тестирования.
Расскажу об этом в следующей статье.
Комментарии (7)
prorockov
18.01.2022 20:48Как рубить дрова?
1) берём топор
2) рубим
geniot Автор
18.01.2022 20:53+1Этот пост идёт с пометкой tutorial. Я надеюсь, информация сэкономит время тем, кто захочет повторить всё описанное. У меня ушло прилично времени, чтобы найти оптимальные для себя решения. Например, логин в Github, редеплой с помощью сервиса, jenkins sudoer. Ведь можно тысячью разными способами это организовать. Я, собственно, многие из них и перепробовал, прежде чем прийти к таким решениям.
Coocos
18.01.2022 21:37Почему не сделали по хуку без ожидания 15 минут?
geniot Автор
18.01.2022 22:47Отличный вопрос. Я рассматривал этот вариант. Тут может быть Github web hook, но он мне не подходит, потому что у меня Jenkins в локальной сети, и я не планирую запрашивать внешний ip адрес. И второй вариант (который, видимо, вы имеете ввиду) - локальный хук. Здесь я, во-первых, ленюсь всё это настраивать, во-вторых, предвижу кучу проблем: - пересяду на другой комп, забуду, что хуки не настроены, - jenkins будет не доступен по какой-нибудь причине, хук сфейлится, но легче от этого мне не станет, - я пользуюсь Intellij IDEA, там свой Git плагин, как он интегрируется, пока не понятно. Короче: сложно всё это. Я начинал вообще с того, что установил и настроил TeamCity. А потом наткнулся на проблему с билдом Angular проекта и пришлось всё свернуть, перейти на старый добрый Jenkins.
puyol_dev2
Ещё один пост на тему очумелые ручки. Практическая ценность -> 0
brnovk
Ну почему же. В последнее время с облачными 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/
Подобные сбои в облаках всё чаще и чаще.
tzlom
Да хотя бы потому что разворачивать это не докером - искать приключений на следующем обновлении системы.
Ну и ценность материала стремится к нулю т.к. данный вопрос легко гуглится с более детальными объяснениями.