Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Сборка проекта Продакшен и Деплой

Deploy

Деплой на PaaS (Heroku, Render) дает нам представление того, как организовать его самостоятельно, на свой сервер. В общем случае, процесс выглядит так:

  1. Клонирование репозитория
  2. Сборка проекта. В нашем случае установка зависимостей
  3. Доставка на сервера. У нас пока один сервер
  4. Остановка старой версии и запуск новой

С точки зрения 12 факторов, важно разделять процесс сборки и релиза. Представьте что у нас 10 машин. Если мы начнем клонировать репозиторий на каждую из машин и выполнять там сборку, то получим множество неудобств:

  1. Если сборка пройдет неуспешно, то мы просто потратим ресурсы серверов впустую.
  2. Скачивание зависимостей это трафик, который стоит денег. Умножаем вес зависимостей на количество серверов.
  3. В случае отката на предыдущую версию, придется долго ждать пока заново выполнится сборка. Проблема даже с одним сервером.

Сборка, обычно, выполняется отдельно от релиза. Чаще всего ей занимается CI, который, после выполнения всех проверок, формирует какой-то артефакт: пакет под операционную систему (например deb), архив, Docker-образ. Последний стал стандартом де-факто. Все так или иначе переехали на Docker.

Разберем, как организовать сборку через CI, для любого проекта на примере devops-example-app

Сборка

Первым делом, нужно создать Dockerfile и добавить туда все шаги для подготовки приложения. Обычно это делается так, гуглится статья (официальные примеры), в которой упаковывается приложение на том же фреймворке и дальше методом проб и ошибок этот процесс повторяется, до тех пор, пока локально получится собрать образ, запустить его и увидеть готовое приложение. Для нашего приложения Dockerfile выглядит так:

FROM node:17

# Куда складываем файлы проекта
WORKDIR /app

# Копирует package.json и package-lock.json
COPY package*.json ./

# Установка зависимостей происходит до копирования файлов проекта
# Так как это позволяет реже сбрасывать этот слой (только при изменении файлов package*)
RUN npm ci

COPY . .

# Старт сервера описывается в scripts внутри package.json
CMD ["npm", "start"]

Когда Dockerfile готов, а образ собирается и запускается, пора создать аккаунт на Docker Hub. Внутри добавляется репозиторий для хранения нашего Docker-образа, и, наконец, выполняется docker push. Так мы получаем образ, готовый для деплоя.

Docker Hub Репозиторий

Остается последний шаг - автоматизация сборки с помощью Github Actions. Причем она будет практически идентичная для всех проектов, которые собираются с помощью Docker независимо от стека.

# Отличается от docker-example-app, так как он собирается через Docker Compose
# https://github.com/docker/build-push-action
name: main

on:
  push:
    branches:
      - 'main'

env: # тег, под которым будет храниться временный образ-кеш
  TEST_TAG: hexletcomponents/devops-example-app:test

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      # Клонируем репозиторий
      - uses: actions/checkout@v2

      # Ниже список шагов из документации build-push-action
      - uses: docker/setup-qemu-action@v1
      - uses: docker/setup-buildx-action@v1
      - uses: docker/login-action@v1
        # Эти секреты нужно добавить самостоятельно
        # Получить https://docs.docker.com/docker-hub/access-tokens/
        # Добавить https://docs.github.com/en/actions/security-guides/encrypted-secrets
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      # Сборка и сразу кеширование, чтобы повторный запуск был быстрее
      - uses: docker/build-push-action@v2
        with:
          context: .
          load: true
          tags: ${{ env.TEST_TAG }}

      # Запуск тестов, команда запуска зависит от стека
      # Лучше ее спрятать за make
      - run: docker run --rm ${{ env.TEST_TAG }} npm test
      # Здесь же нужно настроить запуск линтера

      # Заливаем протестированный образ на Docker Hub
      # https://github.com/docker/build-push-action/blob/master/docs/advanced/test-before-push.md
      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          context: .
          push: true
          tags: hexletcomponents/devops-example-app:latest

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

Такой workflow запускается не на коммиты, а на создание тега в git. Этот же тег затем используется и для Docker-образа. Ниже пример обновленного workflow, который нам подходит:

name: release

on:
  create:
    tags:
      - v* # только теги начинающиеся с v: v1, v2, v5

env:
  APP_IMAGE_NAME: hexletcomponents/devops-example-app

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      # Скачиваем образ
      - run: docker pull ${{ env.APP_IMAGE_NAME }}:latest
      # Тегируем, тег в образе совпадает с тегом git-репозитория
      # github.ref_name - в данном случае имя тега
      - run: docker tag ${{ env.APP_IMAGE_NAME }}:latest ${{ env.APP_IMAGE_NAME }}:${{ github.ref_name }}

      - uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      # Заливаем новый тег
      - run: docker push ${{ env.APP_IMAGE_NAME }}:${{ github.ref_name }}

Запуск проверок для latest не нужен, так как он уже был проверен во время сборки по коммиту. Это сэкономит время на сборку.

В этой системе есть один момент, который нужно учитывать. Создавать тег можно только тогда, когда выполнится сборка latest. Иначе этот воркфлоу сделает тег из старого образа. Обойти это ограничение можно автоматическим созданием тега во время пуша в git-репозиторий.


Самостоятельная работа

  1. Зарегистрируйтесь на Docker Hub

  2. Склонируйте репозиторий devops-example-app, выполните сборку и залейте образ в ваш аккаунт на Docker Hub


Дополнительные материалы

  1. Make для автоматизации

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»