Как стать автором
Обновить
1554.49
Timeweb Cloud
То самое облако

Деплой .NET приложений для самых маленьких. Часть 1. Jenkins

Уровень сложностиСредний
Время на прочтение7 мин
Количество просмотров4.2K

В прошлой статье мы рассмотрели способы запуска наших проектов на удаленном VPS. Для этого мы арендовали хостинг, создали шаблонное приложение, перенесли его на хостинг через простое копирование через ssh и через git clone, запустили через dotnet run / dotnet publish, а также развернули приложение в докере.
И, самым лучшим описанием всего это процесса является комментарий nronie: @nronnie

Устанавливать средства разработки на (по сути боевой) сервер? Так нельзя.

Действительно, такой подход сложно назвать правильным даже для учебных целей, уж тем более его вряд ли можно назвать хорошим для реальных рабочих проектов. Единственной целью такого подхода было продемонстрировать для начинающих разработчиков, что нет никакой магии и сложности в деплое. При любом выбранном способе публикации вашего приложения, оно должно пройти одни и те же шаги – сборка, перенос на какой-либо хостинг, настройка окружения и запуск. Как именно вы будете собирать и переносить .NET приложение и как настраивать – решать исключительно Вам, но для каких-то простых приложений особой разницы нет в выборе обратного прокси и способе публикации.

Однако трудно не согласиться, что практика копирования всего кода на хостинг и сборка прямо на нем является откровенно плохой практикой по целому ряду причин:

  1. Это не безопасно. Если вдруг будет скомпрометированы данные для доступа, то масштаб последствий может быть ужасающий. Ну и не безопасно с точки зрения самого кода как продукта.

  2. Это накладно по ресурсам. Да, сейчас мы собираем примитивнейшее приложение, для чего не требуется очень много ресурсов, к тому же и на хостинге ничего нет. Но если говорить за промышленную разработку и боевые сервера, то там как правило может крутиться несколько десятков различных приложений, и сборка какого-то увесистого монстра может привезти к нехватке имеющихся ресурсов и уронить сервер.

Естественно, что для деплоя наших приложений уже существует немалый набор инструментов, упрощающих рутинный процесс. Во-первых, у каждого репозитория есть свои инструменты для деплоя. У гитхаба это GitHub Actions, у гитлаба есть свои инструменты для сборки и деплоя, есть CI/CD и у Bitbucket. Во-вторых, есть замечательный Jenkins, есть условно-бесплатный TeamCity. В прочем, есть еще несколько других и перечислять всех их смысла нет, можете для ознакомления глянуть список здесь.

Возможно, возникает логичный вопрос: какой из них выбрать? Ответ прост: для нашего простого приложения разницы никакой нет, а в коммерческой разработке скорее всего уже все украдено выбрано до нас. Поэтому предлагаю рассмотреть два из них в разных сценариях. Через Jenkins, как более популярный, развернем приложение через агента на нашем VPS и напишем простой скрипт для автоматизации процесса. В следующей статье через Github Actions настроим сборку приложения в самом репозитории, а потом рассмотрим варианты доставки артефакта до хостинга.

Jenkins
Для начала нам нужно установить Jenkins на компьютер, на котором мы планируем подтягивать код из репозитория и проводить первоначальную сборку. В принципе, благодаря докеру, можно и не устанавливать, поэтому идем в документацию и выбираем подходящий вам вариант. Я воспользуюсь докером как универсальным способом. Можно делать по гайду, я запускаю следующей командой:

docker run -‑name jenkins‑web‑docker ‑d ‑p 8080:8080 ‑p 50 000:50 000 ‑v jenkins_home:/var/jenkins_home jenkins/jenkins:lts

После этого нам становится доступна вебморда по адресу: http://localhost:8080/

Так как мы запустили в докере, то для получения пароля нужно подключиться к контейнеру и вывести его в консоль

docker exec -it jenkins-web-docker bash 
cat /var/jenkins_home/secrets/initialAdminPassword

Входим под именем администратора, пропускаем раздел с плагинами и переходим к дашборду.

Нам нужно сделать пайплайн – описать для Jenkins сценарий работы с нашим кодом, который должен ответить на ряд вопросов:

  1. Где брать исходный код.

  2. Что с ним делать.

  3. Как его запускать.

Более подробно все это расписано в документации. В нашем случае нам нужно брать код в репозитории Github, для этого нужен плагин. Ставим плагин, читаем инструкцию к плагину как настроить подключение и аутентификацию, там все подробно описано. Если кратко, то достаточно создать персональный токен с правами в настройках гитхаба с нужными параметрами, затем создаем новые креденшалы в Jenkins, где выбираем тип “Username with Password”, указываем учетку нашего гитхаба и сохраняем наш токен из кабинета как пароль.

Добавляем токен от GitHub
Добавляем токен от GitHub

Теперь можем добавить их в плагине GitHub, создав Github Server, и проверить соединение, нажав на Test connection. Если все верно, то должно быть что-то наподобие.

Теперь Jenkins имеет доступ к нашему репозиторию, а значит мы можем начать строить свой пайплайн.

И перед тем, как строить пайплайн, есть еще один нюанс. Jenkins работает на нашем локальном компьютере, а разворачивать приложение нам надо на внешнем сервере. Значит, нам нужен агент, который будет это все делать на хостинге, а управлять мы им сможем с нашего компьютера. Как это сделать рассмотрено в документации. Опять же, если кратко, то нам надо сгенерировать пару SSH ключей, приватный добавить в Jenkins, публичный в хранилище хостинга.

sudo adduser jenkins 
sudo usermod -aG 
sudo jenkins su – jenkins 
ssh-keygen -t rsa -b 4096 
cat /home/jenkins/.ssh/id_rsa

Данный ключ копируем и добавляем в хранилище.

Также на хостинге нужно установить Java

sudo apt update 
sudo apt install openjdk-11-jre -y

Теперь мы можем добавить нового агента и протестировать. Заходим в Manage Jenkins и выбираем Set up agent и создаем новую ноду.

Нам нужно подключаться по SSH, но в разделе Launch Methods у нас на выбор только Launch agent by connecting it to the controller. Значит, нам нужен плагин для работы через SSH. Устанавливаем SSH Build Agents плагин. После этого при создании появится соответствующий пункт Launch agents via SSH, указываем адрес нашего VPS, и в Credentials выбираем Jenkins (в него мы ранее сохранили приватный ключ).

Сохраняем, запускаем агента и… и видим в логах что-то типа следующего:

Key exchange was not finished, connection is closed. 
SSH Connection failed with IOException: 
"Key exchange was not finished, connection is closed.", retrying in 15 seconds. 
There are 7 more retries left. /var/jenkins_home/.ssh/known_hosts [SSH] No Known Hosts file was found at /var/jenkins_home/.ssh/known_hosts. 
Please ensure one is created at this path and that Jenkins can read it.

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

su - jenkins 
mkdir -p /home/jenkins/.ssh 
touch /home/jenkins/.ssh/authorized_keys 
chmod 700 /home/jenkins/.ssh 
chmod 600 /home/jenkins/.ssh/authorized_keys 
ssh-keygen -y -f /home/jenkins/.ssh/id_rsa

Где /home/jenkins/.ssh/id_rsa – путь до приватного ключа. Вторым шагом подключимся к контейнеру с докером и добавим IP нашего VPS в known_hosts.

docker exec -it jenkins-web-docker bash 
mkdir -p /var/jenkins_home/.ssh 
touch /var/jenkins_home/.ssh/known_hosts 
ssh-keyscan -H <IP_ADRESS>  >> /var/jenkins_home/.ssh/known_hosts

И после этого перезапускаем агента в дашборде и проверяем логи. Должно быть все как надо:
Agent successfully connected and online

Теперь дело осталось за малым, за тем, что мы и хотели сделать изначально – настроить пайплан для сборки и выкатки нашего приложения. И для создания пайплайна нужно поставить необходимый плагин Pipeline.Теперь мы можем в дашборде добавить новый элемент и выбрать Pipeline, в нем используя плагин для гитхаба подтянуть код из репозитория и использовать созданного агента на прошлом этапе. Жмем New Item, указываем имя и выбираем Pipeline. .

Ставим галочку Discard old builds, ставим галочку Github projects. Указываем путь до репозитория нашего приложения. Ставим галочку This project is parameterized и добавляем два параметра - Credentials Parameter, где выбираем в списке Default value наш токен, который мы добавили в самом начале, также отмечаем Required. И добавляем String Parameter со значениями git_repo и ссылкой на наш репозиторий.

Конфигурация в картинках

Все что нам осталось – это создать Jenkinsfile, где мы доступно должны объяснить Jenkins что делать с нашим кодом. Для этого используется язык Groovy, самые типовые варианты и примеры разобраны в документации. Приведу пример своего, которого вполне достаточно для запуска такого простого приложения.

env.NAME = 'simpleapp';
env.PORT = '5144';
env.PATH_TO_DOCKERFILE = 'dockerfile';
env.GIT_BRANCH = 'master'
env.DEPLOY_TO = 'vps';

node(env.DEPLOY_TO) {
    stage('Checkout git repo') {
      git branch: env.GIT_BRANCH, credentialsId:params.github_token,  url: params.git_repo
    }
    stage('build'){
        sh(script: "docker build . --rm -t $NAME:latest -f $PATH_TO_DOCKERFILE", returnStdout: true)
    }
    stage('remove existing containers'){
        sh '''RELEASECONTAINERS=$(docker ps -qa --filter "name=$NAME$")
        [ ! -z $RELEASECONTAINERS ] && docker stop $RELEASECONTAINERS && docker rm $RELEASECONTAINERS || echo No containers found!'''
    }
    stage('publish') {
        sh '''docker run -d -u root -p $PORT:8080 \
        --name $NAME \
        -e "TZ=Europe/Moscow" \
        --restart=always \
        $NAME:latest '''
    }
    stage('cleanup old docker images') {
        sh '''docker image prune -f'''
    }
}

Как видно по нему, это самый обычный скрипт, где мы можем задать определенные переменные (имя, порт, репозиторий), далее указываем ноду vps, затем прописываем необходимые этапы. В моем случае их всего 4 – билд докер образа, удаление прошлого контейнера, развертывание нового с необходимым параметрами (порт, имя, тайм зона, флаг на перезапуск в случае падения), и удаление старых образов для экономии места.

Понятное дело, что этапов может быть больше, могут быть какие-то промежуточные шаги, проверки, тестирование и т.д., все по вашему вкусу и потребностям. Также стоит уточнить, что Jenkins – мощный инструмент за счет огромного количества плагинов и позволяет строить сложные CI/CD, управлять правами доступа к репозиториям и пайплайнам для разных пользователей, работать с разными агентами, разграничивать креденшалы и много чего еще, в чем, собственно, и заключается его ценность. Поэтому конкретно в нашем примере это выглядит немного как забивание гвоздей микроскопом, но, с другой стороны, научившись таким способом разворачивать приложение, проще будет на реальных проектах с ним работать.


В следующей части мы рассмотрим выкатку приложения через другой популярный инструмент GitHub Actions, который позволяет собирать приложения, не покидая репозиторий, предоcтавляя бесплатно 2000 минут. Если есть вопросы и пожелания, какие темы стоит рассмотреть — оставляйте их в комментариях. Можете подписаться на мой телеграм, чтобы быть в курсе планов выхода следующих статей.


Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале 

Жамкнуть

Теги:
Хабы:
+26
Комментарии8

Публикации

Информация

Сайт
timeweb.cloud
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия
Представитель
Timeweb Cloud