Настройка конвейерной сборки Java-проектов в GitLab

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

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

Мы пройдем полный процесс создания пайплайна для сборки и деплоя при помощи GitLab и сопутствующего ПО. Все операции мы проделаем на одном компьютере, хотя ничто не должно вам помешать сразу или в дальнейшем масштабировать полученное решение на один или несколько серверов. Для экспериментов лучше иметь достаточно современный компьютер с количеством оперативной памяти не менее 16 гигабайт, производительным процессором и хорошим интернет-каналом. 

Предполагается, что у вас уже установлены Docker и ssh-сервер и вы немного умеете со всем этим обращаться. 

Создаем локальный реестр образов 

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

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

Создадим каталог с докер-проектом: 

mkdir docker-registry 
cd docker-registry 
mkdir auth 

Для генерации пароля потребуется установить в систему пакет apache2-utils. Это можно сделать родным пакетным менеджером, если у вас Linux, или brew в MacOS и chocolately для Windows. В результате у нас должна начать срабатывать такая команда: 

htpasswd -bnB user password > auth/htpasswd 

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

/etc/docker/daemon.json

{ "insecure-registries":["172.17.0.1:5000"] } 

172.17.0.1 — стандартный ip-адрес гейтвея, который виден как с хостовой машины, так и из рантаймов докер-образов. 

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

sudo systemctl restart docker 

Теперь мы готовы запустить сервис реестра докер-образов: 

docker container run -d -p 5000:5000 --name registry -v "$(pwd)"/auth:/auth -e REGISTRY_AUTH=htpasswd -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd  registry 

Если что-то пошло не так, останавливайте контейнер, удаляйте:  

docker stop registry 
docker rm registry 

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

docker update --restart unless-stopped registry 

Устанавливаем GitLab  

Вместо разворачивания локального гитлаба можно использовать уже существующий корпоративный или воспользоваться сервисом gitlab.com или его аналогами и этот шаг пропустить. 

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

sudo echo "export GITLAB_HOME=/srv/gitlab" >> ~/.bash_profile 

Обратите внимание, при выполнении команд из-под sudo эта переменная может оказаться еще не прочитанной. Если значение не задано, то будет использоваться путь /data/gitlab. 

  docker run --detach --hostname 172.17.0.1 --env GITLAB_OMNIBUS_CONFIG="external_url 'http://172.17.0.1'" --publish 443:443 --publish 80:80 --publish 22:22 --name gitlab --restart always --volume $GITLAB_HOME/config:/etc/gitlab --volume $GITLAB_HOME/logs:/var/log/gitlab --volume $GITLAB_HOME/data:/var/opt/gitlab --shm-size 256m gitlab/gitlab-ce:latest 

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

sudo docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password 

Используйте пароль для входа сразу после запуска сервиса, открыв в браузере http://172.17.0.1 

Создаем проект и добавляем ssh-ключ 

Сгенерируем новый ключ, если у вас его еще нет: 

ssh-keygen 

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

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

Если с пушем с аутентификацией по ключу возникают сложности, в поисках ответа на вопрос «что же пошло не так?» можно покопаться в выводе команды: 

ssh -vvvT git@172.17.0.1 

Устанавливаем демона-сборщика 

Запустим контейнер сборщика: 

docker run -d --name gitlab-runner --restart always -v /data/gitlab-runner/config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest 

Добавим наш сборщик в секции CI/CD левой панели, перейдя в интерфейсе по ссылкам: 

[Левая панель] CI/CD -> Runners [Развернуть] -> New project runner 

указать имя, тег например jmix: 

docker run --rm -it -v /srv/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register register -n --url "http://172.17.0.1" --registration-token glrt-KyCeiUQQVodooFk7m6wj --executor docker --description "My Docker Runner" --docker-image "docker:24.0.5" --docker-privileged  

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

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

docker logs gitlab-runner 

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

Генерируем ssh-ключи для сборщика 

ssh-keygen -t ed25519 -C "gitlab-runner" -f ~/.ssh/id_gitlab-runner 

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

Добавляем приватный ключ в гитлаб как переменную SSH_PRIVATE_KEY с File Type в CI/CD. На конце обязательно надо добавить пустую строку, без этого сборка будет заваливаться на этапе установки ключа в контейнер. 

Увидеть ключ можно с помощью команды: 

cat ~/.ssh/id_gitlab-runner 

Публичный ключ мы пропишем в ~/.ssh/authorized_keys на хостовом сервере, чтобы ранер мог на него заливать файлы и выполнять команды. 

Добавляем сборочную конфигурацию 

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

Для разработки на Jmix вам нужно установить JDK 17 или 21. Мы возьмем специально подготовленный для обучения проект jmix-onboarding, в котором уже есть небольшая модель, UI и тестовые данные: https://github.com/jmix-framework/jmix-onboarding-2.git 

Наш ранее созданный репозиторий в гитлабе назывался testjmixci, а сам проект называется jmix-onboarding, вы можете создать новый репозиторий с таким же названием, но тогда учитывайте это в дальнейшем. Также вы можете создать новый проект с названием testjmixci в среде разработки IntelliJ IDEA с плагином Jmix, он будет сразу готов к сборке и запуску, но в примерах кода заменяйте jmix-onboarding на testjmixci.

В качестве альтернативы подойдет любой проект на Spring Boot.  

В build.gradle надо добавить конфигурацию для публикации докер-образа: 

bootBuildImage { 
    imageName = "172.17.0.1:5000/jmix-onboarding:0.0.1-SNAPSHOT" 
    publish = true 
    docker { 
        publishRegistry { 
            url = "https://172.17.0.1:5000/" 
            username = "user" 
            password = "password" 
        } 
    } 
} 

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

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

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

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

[Левая панель] Build -> Pipeline editor 

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

default: 
  image: docker:24.0.5 
  services: 
    - name: docker:dind 
       command: ["--insecure-registry=172.17.0.1:5000"] 
  before_script: 
    - docker info 
  stages:  
    - deploy  
  deploy:  
    tags:  
      - jmix  
    stage: deploy  
    variables:  
        DOCKER_HOST: tcp://docker:2375 
        DOCKER_TLS_CERTDIR: "" 
        GIT_STRATEGY: none  
    before_script:  
      - apk add git  
      - apk add openjdk17  
      - apk add nodejs npm  
      - apk add openssh-client  
      - eval $(ssh-agent -s)  
      - chmod 400 "$SSH_PRIVATE_KEY"  
      - ssh-add "$SSH_PRIVATE_KEY"  
      - mkdir -p ~/.ssh  
      - chmod 700 ~/.ssh  
      - CLONE_DIR="$BUILD_DIR/$CI_PROJECT_PATH"  
      - cd $BUILD_DIR  
      - rm -rf $CLONE_DIR  
      - mkdir -p $CLONE_DIR  
      - git -c core.sshCommand='ssh -o StrictHostKeyChecking=no' clone ssh://git@172.17.0.1:22/root/testjmixci.git $CLONE_DIR  
      - cd $CLONE_DIR  
      - git checkout main  
    only:  
      refs:  
        - main  
    allow_failure: true  
    script:  
      - ./gradlew -Pvaadin.productionMode=true bootBuildImage 

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

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

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

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

Альтернативные способы сборки docker-образа 

Если вам не подходит Paketo Buildpacks, которые использует инфраструктура Spring Boot для сборки образов, можно сделать простой docker-файл для самозапускающегося jar-ника примерно такого содержания: 

FROM openjdk:17 
EXPOSE 8080 
ARG JAR=jmix-onboarding-0.0.1-SNAPSHOT.jar 
COPY build/libs/$JAR /app.jar 
ENTRYPOINT ["java","-jar","/app.jar"] 

Если сразу вы угадали с именем файла, назвав его Dockerfile, cобираться он будет простыми: 

docker build . 

Также перспективным способом собирать контейнерные образы является инструментарий от Google под названием Jib. Интересен он в первую очередь тем, что не требует наличия докер-демона для сборки и публикации, что в нашем случае значит и использования Docker-in-Docker. 

Публикация его тоже будет производиться низкоуровнево, сначала надо авторизоваться в реестре: 

docker login -u user -p password https://172.17.0.1:5000 

и затем можно пушить образ: 

docker push 172.17.0.1:5000/jmix-onboarding:0.0.1-SNAPSHOT 

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

Проверка результатов сборки  

В терминале хостовой машины авторизуемся клиентом 

docker login -u user -p password https://172.17.0.1:5000 

и проверим наличие образов с помощью команд: 

docker pull 172.17.0.1:5000/jmix-onboarding 
docker images | grep jmix-onboarding 

Деплой сервиса 

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

- ssh -o StrictHostKeyChecking=no $username@172.17.0.1 -p 2222 'docker stop jmix-onboarding ; docker rm jmix-onboarding ; docker pull 172.17.0.1:5000/jmix-onboarding:0.0.1-SNAPSHOT ; docker run -d --restart=always -p 8080:8080 --name=jmix-onboarding 172.17.0.1:5000/jmix-onboarding:0.0.1-SNAPSHOT' 

где вместо $username надо использовать свой логин в системе

Чтобы избежать конфликтов портов с гитлабовскими протоколами я перенастроил свой sshd на порт 2222. Вместо user@ вам следует использовать свою учетку. 

Если все хорошо, после успешно отработавшего пайплайна, выполнение команды  

docker ps 

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

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

Источник: https://habr.com/ru/companies/haulmont/articles/810151/


Интересные статьи

Интересные статьи

В прошлой статье мы рассказывали как защитить почтовый сервер Carbonio от входящего спама и писем со зловредным ПО. Входящий спам может доставить много головной боли системным администраторам, но саму...
В этом посте раскрываются основы интеграции СУБД CockroachDB с Active Directory. AD — коммерческий побратим Kerberos, предоставляемый компанией Microsoft. Сегодня поговорим про интеграцию Cockroa...
На новом проекте, на котором я работаю в качестве PHP Tech Lead. Команда столкнулась с вопросом наведения разного рода.
Сразу хочу сказать, что не являюсь экспертом в K8S, однако имел опыт с развертыванием продуктовых установок DC/OS (экосистеме, основанной на Apache Mesos). Долгое время K...
Можно бесконечно холиварить о том, является ли GitLab хорошим продуктом. Лучше посмотреть на цифры: по итогам раунда инвестирования оценка GitLab составила 2,7 млрд долларов, в то время как преды...