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

от автора

В прошлой статье мы рассмотрели чрезвычайно популярный инструмент для выкатки приложений Jenkins. Мы подружили его через плагины с SSH, с GitHub, построили простой пайплайн с помощью Groovy. И вроде все здорово, все работает как должно, но все равно есть ощущение, что можно сделать лучше. И действительно, наш процесс можно улучшить, перестав проводить сборку на VPS.

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

Источник изображения: Medium

Источник изображения: Medium

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

Для начала конечно стоит изучить документацию и рассмотреть приведенные примеры, посмотреть раздел быстрого старта.

Из него становится ясно, что для создания собственного процесса нам нужно создать конфигурационный файл с расширением .yml внутри нашего репозитория в каталоге .github/workflows . Что указывать в этом файле? Это подробно рассмотрено также в документации, в соответствующем разделе.

На самом деле, если язык Groovy, используемый в Jenkins может вызывать сложность в понимании, то здесь используется YAML, и если вы хоть раз составляли какие-то конфигурационные файлы или хотя бы делали docker-compose, то у Вас не должно возникнуть с ним никаких трудностей. Поэтому, дабы не распыляться в пустую, перейдем к сути.

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

  • сборка приложения и подготовка артефакта;

  • перенос артефакт на хостинг;

  • правила деплоя доставленного артефакта.

И перед тем, как мы приступим к созданию собственного сценария, нужно уделить внимание ключам. Мы уже создавали ключ в прошлой статье и размещали его в панели Jenkins, чтобы была связь с агентом, а также достаточно прав для запуска приложения. Чтобы безопасно хранить чувствительные данные, в GitHubActions есть специальный раздел настроек в репозитории «Actions secrets and variables». Подробнее можно изучить в документации, но простыми словами – это обычное хранилище ключ-значение, где мы добавляем в хранилище какое-либо значение, а затем можем обратиться к нему по ключу.

Добавили 3 ключа, которые пригодятся в дальнейшем - адрес хостинга, ключ и пользователь

Добавили 3 ключа, которые пригодятся в дальнейшем — адрес хостинга, ключ и пользователь

Начнем с общей части, описательной:

name: SimpleApp Deploy  on:   push:   branches: [ "master" ]

Мы создаем правило – начать деплой, если в ветку master произошел push. Одно простое правило, которое будет являться триггером. Если проект разрастается и в ветке “master” не всегда рабочая версия приложения (или не всегда релизная), то можно добавить дополнительное условие. Например, проверить содержит ли коммит значение [release].

jobs:   deploy:     if: contains(github.event.head_commit.message, '[release]') 

Можно завезти отдельные теги c версиями и проверять их.

on:   push:     tags:       - v1.**

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

❯ Шаг 1. Сборка приложения

Тут все как и прежде – создание docker образа. Единственное, что добавляется, это упаковка его в tar архив.

- uses: actions/checkout@v4     - name: Set up Docker Buildx       uses: docker/setup-buildx-action@v2      - name: Build Docker image       run: docker build -t simpleapp .          - name: Save Docker image as tarball       run: docker save simpleapp -o simpleapp.tar      - name: Verify tarball exists       run: ls -lh simpleapp.tar            - name: Change file permissions       run: chmod 644 simpleapp.tar 

❯ Шаг 2. Упакованный архив копируем на хостинг

- name: SSH to VPS and remove existing tarball       uses: appleboy/ssh-action@v0.1.7       with:         host: ${{ secrets.VPS_HOST }}         username: ${{ secrets.VPS_USER }}         key: ${{ secrets.VPS_KEY }}         script: |           if [ -f /home/${{ secrets.VPS_USER }}/simpleapp.tar ]; then             rm /home/${{ secrets.VPS_USER }}/simpleapp.tar           fi      - name: Copy Docker image to VPS       uses: appleboy/scp-action@v0.1.7       with:         host: ${{ secrets.VPS_HOST }}         username: ${{ secrets.VPS_USER }}         key: ${{ secrets.VPS_KEY }}         source: simpleapp.tar         target: /home/${{ secrets.VPS_USER }}/ 

❯ Шаг 3. Подключаемся по ssh к хостингу и разворачиваем наше приложение с необходимым параметрами

- name: SSH to VPS and deploy       uses: appleboy/ssh-action@v0.1.7       with:         host: ${{ secrets.VPS_HOST }}         username: ${{ secrets.VPS_USER }}         key: ${{ secrets.VPS_KEY }}         script: |           docker load -i /home/${{ secrets.VPS_USER }}/simpleapp.tar           if [ "$(docker ps -q -f name=simpleapp)" ]; then             docker stop simpleapp           fi           if [ "$(docker ps -a -q -f name=simpleapp)" ]; then             docker rm simpleapp           fi           docker run --name simpleapp -p 5144:8080 -d simpleapp

Сохраняем все в файл и пушим в наш репозиторий, тем самым вызывая триггер.

Успешный деплой

Успешный деплой
Полная версия файла
name: Docker Image CI  on:   push:     branches: [ "master" ]  jobs:    build:      runs-on: ubuntu-latest      steps:     - uses: actions/checkout@v4     - name: Set up Docker Buildx       uses: docker/setup-buildx-action@v2      - name: Build Docker image       run: docker build -t simpleapp .          - name: Save Docker image as tarball       run: docker save simpleapp -o simpleapp.tar      - name: Verify tarball exists       run: ls -lh simpleapp.tar            - name: Change file permissions       run: chmod 644 simpleapp.tar            - name: SSH to VPS and remove existing tarball       uses: appleboy/ssh-action@v0.1.7       with:         host: ${{ secrets.VPS_HOST }}         username: ${{ secrets.VPS_USER }}         key: ${{ secrets.VPS_KEY }}         script: |           if [ -f /home/${{ secrets.VPS_USER }}/simpleapp.tar ]; then             rm /home/${{ secrets.VPS_USER }}/simpleapp.tar           fi      - name: Copy Docker image to VPS       uses: appleboy/scp-action@v0.1.7       with:         host: ${{ secrets.VPS_HOST }}         username: ${{ secrets.VPS_USER }}         key: ${{ secrets.VPS_KEY }}         source: simpleapp.tar         target: /home/${{ secrets.VPS_USER }}/      - name: SSH to VPS and deploy       uses: appleboy/ssh-action@v0.1.7       with:         host: ${{ secrets.VPS_HOST }}         username: ${{ secrets.VPS_USER }}         key: ${{ secrets.VPS_KEY }}         script: |           docker load -i /home/${{ secrets.VPS_USER }}/simpleapp.tar           if [ "$(docker ps -q -f name=simpleapp)" ]; then             docker stop simpleapp           fi           if [ "$(docker ps -a -q -f name=simpleapp)" ]; then             docker rm simpleapp           fi           docker run --name simpleapp -p 5144:8080 -d simpleapp 

Давайте пройдемся немного подробнее по каждому шагу, чтобы было понимание. Вначале мы используем actions/checkout, который предоставляет доступ в репозиторий. В случае наличии изменений в репозитории, переходим на следующий шаг – сборка контейнера, самая обычная с помощью команды docker build -t simpleapp .
После этого упакуем образ в архив .tar и даем права, и переходим к следующему этапу, копированию. Тут мы выполняем ровно два действия. Используя SSH, сначала проверяем, имеется ли уже в нашей директории файл с таким названием. Если имеется, то удаляем, так как следом, используя  scp-action, мы копируем наш архив на хостинг и переходим к следующему этапу.

Здесь также без каких либо сложностей. Используя ключи из секретов, мы снова подключаемся к VPS и загружаем наш образ в архиве. Скриптом проверяем, есть ли одноименный контейнер, и если есть, то останавливаем его и удаляем. И потом запускаем наш свежий командой docker run --name simpleapp -p 5144:8080 -d simpleapp

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

У такого подхода есть одно преимущество (особенно в текущих реалиях), и оно же является недостатком. Более популярным решением является использовать какое-то внешнее хранилище, куда обычно размещают собранный образ. На хостинг явно ничего не копируется, а лишь указывается – сходи в хранилище и возьми нужный образ, и разверни из него контейнер. Кто-то для этого использует Docker Hub, многие используют хранилище GitHub. Стоит отметить, что у них сильно ограничены возможности для бесплатного тарифа, это раз. Во-вторых, с их выкрутасами и выкрутасами не РКН, все это в любой момент может отвалиться и лично я предпочитаю, в текущей обстановке, по минимуму  завязываться на них, а посему использую самое просто копирование.

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

Недостаточно прав в токене, полученном при входе в ghcr.io

Недостаточно прав в токене, полученном при входе в ghcr.io

Поэтому идем сначала в раздел с токенами и создаем токен с достаточными правами на запись write:packages и read:packages, repo (если у Вас уже есть токен с такими правами, то можете его использовать), и добавляем его в хранилище секретов, как это делали в самом начале. Я добавил токен с именем GHCR_PAT. В итоге, файл приобрел следующий вид:

Полная версия файла
name: Docker Image CI  on:   push:     branches: [ "master" ]  jobs:    build:      runs-on: ubuntu-latest      steps:       - uses: actions/checkout@v4       - name: Set up Docker Buildx         uses: docker/setup-buildx-action@v2        - name: Log in to GitHub Container Registry         run: |           echo ${{ secrets.GHCR_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin        - name: Build Docker image         run: docker build -t ghcr.io/${{ github.repository_owner }}/simpleapp:latest .        - name: Push Docker image to GHCR         run: docker push ghcr.io/${{ github.repository_owner }}/simpleapp:latest        - name: SSH to VPS and deploy         uses: appleboy/ssh-action@v0.1.7         with:           host: ${{ secrets.VPS_HOST }}           username: ${{ secrets.VPS_USER }}           key: ${{ secrets.VPS_KEY }}           script: |             echo ${{ secrets.GHCR_PAT }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin             docker pull ghcr.io/${{ github.repository_owner }}/simpleapp:latest             if [ "$(docker ps -q -f name=simpleapp)" ]; then               docker stop simpleapp             fi             if [ "$(docker ps -a -q -f name=simpleapp)" ]; then               docker rm simpleapp             fi             docker run --name simpleapp -p 5144:8080 -d ghcr.io/${{ github.repository_owner }}/simpleapp:latest 

Результат такой же.

Больше никаких архивов и копирований, только хранилище, только хардкор.

Больше никаких архивов и копирований, только хранилище, только хардкор.

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


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

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

Перейти ↩

📚 Читайте также:


ссылка на оригинал статьи https://habr.com/ru/articles/838422/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *