Автоматизация деплоя Django-приложения в маленькой команде: пошаговый гайд

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

Развёртывание ПО, или деплой (deploy) — этап в разработке, в Devops в целом, это действия, которые делают ПО готовым к использованию. Если вы умеете в грамотный деплой, масштабирование и управление конвейерами (CI/CD), то ваш софт будет конкурентоспособным.

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

Мы в digital-агентстве Stik успешно используем GitLab CI и Docker для развёртывания ПО в разных средах. Для чего нужны эти инструменты?

GitLab CI позволяет автоматизировать процессы сборки и доставки ПО. Docker — упаковать приложение и его зависимости в контейнеры, что упрощает развёртывание и масштабирование в разных средах. Используя их, вы сократите затраты на найм и оптимизируете деплой.

В этой статье расскажу о нашем опыте и покажу примеры настройки конвейеров CI/CD, как ими управлять с помощью GitLab CI и Docker. А также дам рекомендации, как масштабировать развертывание.

Вводные данные

  1. Бэкенд на Python и фреймворков Django

  2. Облачный сервер

  3. База данных PostgreSQL

  4. Отсутствие DevOps инженера и админов в команде, но желание не деплоить руками

Если вы не знаете, что такое CI/CD, можете почитать здесь.

Конфигурация Backend приложения (настройка контейнера, приложения, переменных окружения)

Для начала необходимо провести базовые конфигурации наших настроек на основе .env файла. Для этого мы используем пакеты python-decouple и dj-database-url

Достаем переменные в нашем settings.py файле:

from dj_database_url import parse as db_url
from decouple import config

DEBUG = config("DEBUG", default=False, cast=bool)
SECRET_KEY = config("SECRET_KEY", default="")

DATABASES = {
    "default": dict(
        config("DATABASE_URL", cast=db_url),
    )
}

Создаём файл с конфигурацией окружения .env проекта в корне с содержимым:

DEBUG=true
SECRET_KEY=some_key
DATABASE_URL=postgresql://dev:dev@localhost:5432/supershtab

После нам необходимо настроить наше окружение для сервера. Для этого создадим в папке файл _CI/envs/dev.env , каждое окружение мы будем рассматривать как нашу ветку.

Скопируем пока туда содержимое нашего .env файла в корне, а после заменим на последнем шаге.

Далее устанавливаем нужные пакеты для запуска приложения это:

uwsgi
psycopg2-binary

Также для запуска можно использовать gunicorn. Вообще это не так важно, какой вариант выбрать, все они работают одинаков. Для примера рассмотрим uwsgi.

После установки пакетов конфигурируем наш uwsgi, создав файл uwsgi.ini файл в корне проекта.

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

[uwsgi]
# Django-related settings
# Django's wsgi file


# process-related settings
# master
master             = true
module             = common.wsgi
enable-threads     = true
die-on-term        = true
single-interpreter = true
strict             = true
need-app           = true

# the socket (use the full path to be safe
http               = 0.0.0.0:9000

# clear environment on exit
vacuum             = true

# respawn processes taking more than 60 seconds
harakiri           = 60

stats = /tmp/stats.socket

cheaper-algo = busyness
processes = 24                      ; Maximum number of workers allowed
threads = 2
cheaper = 8                          ; Minimum number of workers allowed
cheaper-initial = 16                 ; Workers created at startup
cheaper-overload = 1                 ; Length of a cycle in seconds
cheaper-step = 4                    ; How many workers to spawn at a time

cheaper-busyness-multiplier = 30     ; How many cycles to wait before killing workers
cheaper-busyness-min = 20            ; Below this threshold, kill workers (if stable for multiplier cycles)
cheaper-busyness-max = 70            ; Above this threshold, spawn new workers
cheaper-busyness-backlog-alert = 16  ; Spawn emergency workers if more than this many requests are waiting in the queue
cheaper-busyness-backlog-step = 2    ; How many emergency workers to create if there are too many requests in the queue

max-requests = 1000                  ; Restart workers after this many requests
max-worker-lifetime = 1800           ; Restart workers after this many seconds
reload-on-rss = 1024                  ; Restart workers after this much resident memory
worker-reload-mercy = 60             ; How long to wait before forcefully killing workers

Далее мы можем запустить и проверить корректность конфигурации нашего приложения через команду:

uwsgi --ini uwsgi.ini

После запуска приложения в терминале мы должны увидеть, как выглядят логи запуска нашего uWSGi приложения и спавнинг воркеров. Далее проверим доступность по :9000 порту

При стандартной конфигурации мы должны увидеть стартовую страницу Django приложения. Если вы видите этот экран, то всё окей, и мы можем продолжать далее. Если нет, то копайте лог, ошибки могут быть разные и все предсказать нереально.

Следующий шаг — написание нашего Docker контейнера, который и будет использоваться на наших серверах для запуска.

Создаём папку _CI в корне проекта и кладём туда наш Dockerfile.

Выглядит он таким образом:

FROM python:3.11-slim as base
RUN apt-get update -y && apt-get install -y gettext  \
    build-essential  \
    libssl-dev  \
    git  \
    python3-dev  \
    gcc  \
    libpq-dev \
    libffi-dev

# Устанавливаем локали для системы
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y locales
RUN sed -i -e 's/# ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/' /etc/locale.gen && \
     dpkg-reconfigure --frontend=noninteractive locales && \
     update-locale LANG=ru_RU.UTF-8


# копируем и устанавливаем зависимости
COPY requirements.txt /app/requirements.txt
RUN pip install -r /app/requirements.txt

# копируем наш проект и устанавливаем рабочую папку
COPY . /app
WORKDIR /app

ENV PYTHONUNBUFFERED 1

# запускаем контейнер
ENTRYPOINT ["/bin/bash", "/app/_CI/runserver.sh"]

Далее нам необходимо написать runserver.sh файл, где мы и будем запускать наше приложение. Данный файл также должен находится в CI/runserver.sh в проекте. В данном файле есть переменные $ENVIRONMENT и $DATABASE_URL. Мы к ним ещё вернемся в последующих шагах.

#!/bin/bash

# Копируем файлы переменной окружения
cp -R /app/_CI/envs/$ENVIRONMENT.env   /app/.env

echo "
DATABASE_URL=$DATABASE_URL
" >> /app/.env

# Экспортируем переменные окружения из файла
source /app/.env

cd /app

# Миграции и статика
python /app/manage.py collectstatic --no-input
python /app/manage.py migrate --no-input

# запуск
uwsgi --ini uwsgi.ini

Проверяем сборку нашего приложения командой:

docker build -t backend .

В ходе билда должны:

  • скачаться родительский image;

  • установиться системные пакеты;

  • сгенерироваться локали;

  • установиться пакеты из PyPi.

Далее мы можем запустить наш бэкенд, тут нам и понадобятся наши переменные:

docker run --restart=always --name=backend --env ENVIRONMENT=local --env DATABASE_URL=postgresql://dev:dev@localhost:5432/db_name -d backend -p 9000:9000

После чего нам необходимо проверить, всё ли хорошо с нашим контейнером, его логи сборки и запуска можно посмотреть через команду docker logs backend и зайти на порт 9000.

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

Конфигурация сервера и настройка смежных контейнеров

Далее необходимо подготовить сервер под развёртку нашего приложения и установить компоненты системы:

  1. Nginx

  2. Docker

  3. PostgreSQL in Docker

Существует множество статей и способов установить данные компоненты, мы не будем останавливаться на них. Вы можете установить их самостоятельно:

  1. Как установить Docker

  2. Как развернуть PostgreSQL в докере

Nginx можно поставить простой командой sudo apt install nginx

Конфигурация Gitlab CI/CD

После того как мы запушили наш проект в репозиторий в ветку master (будем считать, что он у нас был пустой и это одна ветка), нам необходимо создать ветку dev от ветки master.

После чего необходимо перейти к защите веток от нежелательных прямых вливаний. Сделать это можно в Gitlab в разделе Settings → Repository → Protected Branches.

Также это необходимо сделать, чтобы Gitlab Runner в последствии мог в этих ветках использовать защищённые переменные окружения.

Пример настройки защиты веток:

Далее нам необходимо создать переменные окружения, как и описывал выше это будет:

DATABASE_URL для примера, остальные переменные вы можете задать самостоятельно.

Для создания переменной нам необходимо будет перейти в раздел Settings → CI/CD →Variables и задать переменную

Здесь важно, чтобы у вас появилась возможность экранировать переменную в код через символ $ .

После сохранения необходимо перейти к автоматизации всего этого процесса, ручного труда нам и так хватает

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


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

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

Привет, Хабр! Продолжаю серию статей про разработку telegram-ботов на библиотеке aiogram и языке программирования Python. Хочется отметить, что статья не является документацией или учебником. Я просто...
Хабр, привет. Меня зовут Рамиль Шайбаков, я фронтенд-разработчик в СберЗдоровье. Последние несколько лет я часто собеседую кандидатов на позицию frontend-разработчика в нашу компанию и заметил одну за...
Все говорят, что для защиты сети нужно применять межсетевые экраны, но никто не говорит, как это нужно делать. Что ж, исправим ситуацию, рассмотрим типовые сценарии применения межсетевых экранов и...
Для хранения тестовых данных обычно требуется такой тип данных, который:• допускает объявление нескольких свойств;• имеет минимальное поведение или вообще его не имеет;• позволяет легко с...
Кризис из-за пандемии COVID-19 показал, что ситуацией владеет тот, кто проявляет наибольшую гибкость. Мы решили воспользоваться моментом и проверить на прочность историю, которую давно ра...