Как настроить GitHub Actions и не заплакать: пошаговая инструкция

Как настроить GitHub Actions и не заплакать: пошаговая инструкция

Привет всем! Меня зовут Виталий, я фронтендер в Mish. Решил недавно освоить полноценный автоматический деплой проекта, чтобы все работало само. Расскажу и вам, что из этого получилось.

В статье буду разговаривать о деплое только фронтенда. Про деплой бэкенда расскажу в следующем материале.

Выбираем платформу

Настраивать деплой файлов и переносить их на сервер в определенную папку — как-то несовременно. Поэтому взгляд упал на Docker.

Дальше встал вопрос хостинга. Можно было воспользоваться AWS, но хотелось построить весь процесс с нуля. Поэтому выбрал сторонний сервис, чтобы и домен, и все остальное было свое.

Так как до этого проект лежал на хостинге Sprinthost, а недавно они добавили виртуальные боксы, решил купить его и там же настроить деплой. Не хотелось платить много денег за мощности, поэтому решил собирать приложение за счет GitHub, раз уж они такие добрые.

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

Начинаем процесс

Раз мы будем использовать докер, то грех не использовать Docker Hub. Вот как это будет выглядеть:

Как настроить GitHub Actions и не заплакать: пошаговая инструкция

Если описать эту историю словами, то выглядеть будет так:

  • Собираем на Git новый image приложения.
  • Закидываем его в репозиторий Docker Hub.
  • Обновляем контейнер.
  • Наслаждаемся победой.

Для начала нам нужно будет определиться с Linux, который будем использовать. Так как Sprinthost предоставляет готовые сборки, выберем Ubuntu + Docker + Pointer. Но если захотите иначе, все это сработает и на CentOS.

Подготавливаем окружение

Создадим Dockerfile для нашего фронтенда:

# syntax=docker/dockerfile:1 FROM node:16.10.0-alpine as build RUN apk add --no-cache python2 g++ make WORKDIR /sbh-fe-ts COPY package*.json . RUN yarn install COPY . . RUN yarn run build FROM nginx:1.19 COPY ./nginx/nginx.conf /etc/nginx/nginx.conf COPY --from=build /sbh-fe-ts/build /usr/share/nginx/html

Если вы новичок, то можете еще не знать, что такое nginx.conf и зачем он нужен. Это конфигурационный файл nginx — сервера, который будет отображать нашу страницу по заданному домену. Его задача — лежать внутри проекта. Для этого создаем в корне папку nginx и в нее кладем .CONFIG файл:

http { server { listen 80; server_name {your domain name}; root /usr/share/nginx/html; index index.html index.htm; location / { try_files $uri $uri/ /index.html; } } }

Подготавливаем репозиторий Docker Hub

Репозитории в Docker Hub есть приватные и публичные. Использовать можно любой. Я не хотел тратить бесплатный приватный слот, поэтому сделал его публичным. Вы можете сделать так, как удобно вам.

Заходим на Docker Hub, регистрируемся, запоминаем пароль и логин — он понадобится в будущем. Теперь создаем репозиторий:

Как настроить GitHub Actions и не заплакать: пошаговая инструкция

Нажимаем на синюю кнопку, после чего указываем тип репозитория и его название:

Как настроить GitHub Actions и не заплакать: пошаговая инструкция

Теперь докер подсказывает справа, как привязать репозиторий. Но можно поступить проще. Идем в консоль сервера и начинаем процесс логина. Для этого не нужны никакие SSH-ключи, достаточно логина и пароля:

Как настроить GitHub Actions и не заплакать: пошаговая инструкция

Если все прошло успешно, вы увидите надпись login succeeded.

Настраиваем сервис

Как помним из схемы, сначала нужно подтянуть все обновления image из репозитория, потом обновить контейнер. Для этого проще всего создать собственный скрипт, чтобы запускать его потом из-под GitHub Actions.

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

sudo adduser frontend sudo groupadd docker sudo usermod -aG docker frontend sudo usermod -aG sudo frontend su frontend

Теперь создаем новый файл под названием deploy.sh и пропишем в него следующие команды:

#!/bin/bash echo "Stop container" docker stop frontend docker rm frontend docker image rm {dockerhub username}/{dockerhub repo name} echo "Pull image" docker pull {dockerhub username}/{dockerhub repo name} echo "Start frontend container" docker run -p 80:80 --name frontend -d {dockerhub username}/{dockerhub repo name} echo "Finish deploying!"

В данном случае я назвал пользователя frontend — для прозрачного распределения обязанностей — и выдал ему права доступа к контейнеру Docker, чтобы не было ошибок. Нужно учитывать, что подобный деплой сделает недоступным сервис во время остановки и запуска контейнера. Чтобы всего этого избежать, можно использовать дополнительные возможности, о которых я расскажу в следующих материалах.

Создаем контейнер

Теперь придется немного поработать руками. Собираем локально image и закидываем его на Docker Hub через консоль или Docker Desktop. Первый вариант удобнее:

docker build . -t {docker username}/{docker repo name} docker push {docker username}/{docker repo name}

Далее переходим в контейнер на сервере и обновляем image:

docker pull {docker username}/{docker repo name}

Теперь создадим контейнер, который будем обновлять. Заодно проверим, как все работает:

docker run -p 80:80 --name frontend -d {docker username}/{dockerhub repo name}

У меня это выглядит вот так:

docker build . -t rstpgod/schoolsolver docker push rstpgod/schoolsolver

Переходим в консоль сервера и проверяем, все ли работает:

docker pull rstpgod/schoolsolver docker run -p 80:80 --name frontend -d rstpgod/schoolsolver

Результат перед вами:

<p>Все запустилось с первого раза. Главное — не забудьте выключить nginx, если он запущен на сервере, чтобы не было конфликтов. Это можно сделать такой командой:</p>

Все запустилось с первого раза. Главное — не забудьте выключить nginx, если он запущен на сервере, чтобы не было конфликтов. Это можно сделать такой командой:

sudo systemctl stop nginx

Мы на середине пути — что имеем к этому этапу:

  • Сборку приложения в докере
  • Репозиторий на Dockerhub
  • Работающий контейнер на сервере

Автоматизируем процесс

Для этого используем GitHub Actions.

Заходим в наш репозиторий и задаем все секретные ключи:

  • Docker username
  • Docker password
  • Docker Repo Name
  • Server ip
  • Server port (default 22)
  • Server user login
  • Server user password
Как настроить GitHub Actions и не заплакать: пошаговая инструкция

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

Теперь можем переходить к настройке самого GitHub Action. Идем во вкладку и выбираем Manual Workflow. Задаем туда этот код:

Как настроить GitHub Actions и не заплакать: пошаговая инструкция
name: Publish to server on: push: branches: [ "master" ] jobs: push_to_registry: name: Push Docker image to Docker Hub runs-on: ubuntu-latest steps: - name: Check out the repo uses: actions/checkout@v3 - name: Log in to Docker Hub uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 with: images: ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO_NAME }} tags: latest labels: latest - name: Build and push Docker image uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} server_update: needs: push_to_registry name: Update server buy ssh runs-on: ubuntu-latest steps: - name: Connect and run script uses: appleboy/ssh-action@master with: host: ${{ secrets.SERVER_HOST }} port: ${{ secrets.SERVER_PORT }} username: ${{ secrets.USERNAME }} password: ${{ secrets.PASSWORD }} script_stop: true script: bash deploy.sh

Первая часть job запускает стандартный workflow гита:, он собирает image и пушит его в репозиторий. Здесь же можно настроить различные метки, например, если хотите хранить больше одного image на сервере.

В server_update подключаемся к нашему серверу и запускаем написанный скрипт, чтобы обновить контейнер с приложением. Также указываем script stop, если выпала какая то ошибка на сервере.

Еще указан параметр needs. Он нужен, чтобы программа дождалась предыдущего шага и только потом начала что-то творить на сервере.

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

name: Docker Image CI on: pull_request: branches: [ "master" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build the Docker image run: docker build . -t pull_request:$(date +%s)

Так мы защищаем главную ветку и триггерим сборку только в тот момент, когда кто-то вносит изменения в основной код. Как только мы объединили две ветки, запускается верхний workflow, который обновляет версию на сервере.

Теперь все готово — можем поаплодировать и себе и погладить по голове. Пользуйтесь моими советами и алгоритмами, чтобы у вас все было, но вам за это ничего не было.

99
1 комментарий

Крутяк! Спасибо!

Ответить