ProIT: медіа для профі в IT
8 хв

Django + Docker: безболісний деплой проєкту будь-де

author avatar Дмитро Лузановський
Python Software Developer в Workconsult Ukraine

Цей допис розрахований на юних(та не дуже:) падаванів на шляху до становлення джедаєм в розробці web-додатків за допомогою Django.

Поговоримо про те, коли, рано чи пізно, ви зіткнетеся з питанням - йой, а як же його задеплоїти на сервері, в хмарі, локально, на компі бабусі (потрібне підкреслити)? Що потрібно зробити, щоб було достатньо однієі команди для безболісного деплою?

Правильно! Docker!

Docker - це магія для розробників, яка допомагає запускати наші програми в спеціальних контейнерах. Контейнери допомагають зробити наш додаток "переносимим", а це означає, що ми можемо запускати його на будь-якому комп'ютері або хмарному сервері, і не хвилюватись, чи все правильно налаштовано.

Мабуть, ви чули про Docker, але могли стикнутись з невдалими спробами його використання. Не хвилюйтесь, я сам декілька разів попадав у такі ж халепи. Тому я зібрав свій досвід у цій статті, щоб поділитись з вами найкращим рішенням, яке спростить ваш процес деплою.

Ми покроково розглянемо, як поєднати силу Django та Docker для безболісного деплою вашого проекту. Не треба витрачати час на нудні пошуки гайдів - давайте разом розберемося, як зробити ваше деплоїнг життя простішим і веселішим!

Щоб почати нашу пригоду з Docker, зверніться до офіційної документації Docker для його встановлення. Розглядати це тут ми не будемо.

Отже,

Підготовка Django до деплою

Virtual environment (не pyvenv єдиним!)

В своїй повсякденній практиці я використовую Poetry - прекрасна альтернатива pip.

Що ж це за звір такий та як він працює?

Poetry - це потужний інструмент для управління залежностями та віртуальним середовищем у вашому проекті. Він дозволяє створювати ізольовані середовища для вашого додатку, де можна встановлювати і оновлювати залежності без впливу на інші проекти або систему в цілому.

Чому Poetry, а не звичайний pyvenv або pip? Він має багато переваг, включаючи зручний синтаксис для встановлення пакетів, автоматичне створення та оновлення файлу pyproject.toml, а також підтримку requirements.txt. Poetry робить управління залежностями більш зрозумілим та зручним.

З Poetry все надзвичайно просто. По-перше, встановіть Poetry у вашій системі (детальні інструкції можна знайти на офіційному сайті Poetry). Потім, у папці вашого Django проекту, виконайте команду poetry init, яка допоможе створити новий проект або використати існуючий. Виберіть свої налаштування та введіть необхідну інформацію про ваш проект.

Додайте необхідні залежності

Після того, як ми ознайомилися з Poetry та його простотою управління залежностями, давайте перейдемо до кількох додаткових кроків, щоб наш Django проект був готовий до деплою.

Для запуску проекту та деплою ми будемо використовувати gunicorn - потужний WSGI-сервер (Web Server Gateway Interface). Це забезпечить швидкий та надійний запуск нашого Django додатку.

poetry add gunicorn

Тепер, для того щоб наш Django проект міг працювати з базою даних Postgresql, потрібно встановити залежність psycopg2:

poetry add psycopg2-binary

Після виконання цих команд в директорії вашого проекту з'являться два файли:

  • pyproject.toml це основний файл конфігурації вашого проекту, де містяться всі залежності та налаштування.

  • poetry.lock це файл з замкнутими версіями залежностей, які гарантують стабільність у вашому проекті. Ви можете ігнорувати цей файл у системі контролю версій, оскільки Poetry буде автоматично виправляти його при встановленні залежностей.

Завдяки Poetry, ми зберегли наші залежності у віртуальному середовищі, що робить наш Django проект більш незалежним та структурованим.

Отже, virtual environment готовий до деплою.

Сконфігуруйте settings.py та urls.py під "продакшн" та "local development"

Перш за все створіть .env файл для визначення змінних оточнення (кредли БД, хости і так далі)

#.env
DB_PORT=5432
DB_HOST=projectname_postgres # ім'я хоcту в мережі Docker
POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres

Далі треба створити файл local_settings.py та покласти його в теку ядра проекту (там, де settings.py)

# local_settings.py
from .settings import * 
# Додаємо '127.0.0.1' до ALLOWED_HOSTS, щоб Django міг обслуговувати запити з localhost
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
# Використовуємо SQLite базу даних для локального розробки
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
# Додаємо дебаг-режим для локальної розробки
DEBUG = True

Редагуємо основний файл налаштувань settings.py:

# settings.py
import os
...
# Вказуємо реальні хости доменів, які Django може обслуговувати на продакшн сервері
ALLOWED_HOSTS = ['example.com', 'www.example.com']

# Використовуємо PostgreSQL базу даних для продакшн сервера
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('POSTGRES_DB'),
        'USER': os.environ.get('POSTGRES_USER'),
        'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),
        'HOST': os.environ.get('DB_HOST'),  # або адреса вашого PostgreSQL сервера
        'PORT': os.environ.get('DB_POST'),  # порт (залиште порожнім, якщо використовуєте дефолтний порт)
    }
}

# Вимикаємо дебаг-режим для продакшн сервера
DEBUG = False
# Описуємо налаштування для статики та медіа
STATIC_ROOT = BASE_DIR / 'static'
STATIC_URL = '/static/'
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'

Також потрібно доповнити корний файл urls.py для фетчінгу статики та медіа якщо дебаг режим ввимкнений:

# urls.py
from django.contrib.staticfiles.views import serve
from django.urls import path, include
...

static_and_media_urls = [
    path('static/<path:path>', serve, {'document_root': settings.STATIC_ROOT}),
    path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
]
urlpatterns = [...]
urlpatterns + static_and_media_urls

Все! Ваш Django-проект готовий до деплою!

Готуємо Docker

Тепер, коли ми підготували наш Django проект та налаштували віртуальне середовище за допомогою Poetry, настав час перейти до Docker. Ця чарівна технологія дозволить нам упакувати наш додаток та всі його залежності у контейнери, що забезпечить легке та безпечне деплою на будь-якому сервері або хмарній платформі.

Створимо дві директорії в корневій директорії проекту:

/backend
/nginx

В директорії backend створюємо два файли: DockerFile та docker-entrypoint.sh

DockerFile:

FROM python:3.10-buster # обираємо версію Python на якій буде працювати проект

# описуємо вірт.оточення:
ENV PYTHONBUFFERED=1 \
    POETRY_VERSION=1.4.2 \
    POETRY_VIRTUALENVS_CREATE="false"

RUN pip install "poetry==$POETRY_VERISON" # встановлюємо Poetry

# Вказуємо робочу теку:
WORKDIR /app

# копіюємо файли залежностей та баш-скрипт в корінь контейнера:
COPY pyproject.toml poetry.lock docker-entrypoint.sh ./

# встановлюємо залежності
RUN poetry install --no-interaction --no-ansi --no-dev

# копіюємо проект в робочу теку:
COPY project /app
# вказуємо порт
EXPOSE 8000
# даємо права на виконання ентріпоінту
RUN chmod +x docker-entrypoint.sh
ENTRYPOINT ["./docker-entrypoint.sh"]

docker-entrypoint

#!/bin/sh
set -e
until cd /app
do
    echo "Wait for server volume..."
done

# робимо міграції перед запуском wsgi сервера
until python manage.py migrate
do
    echo "Waiting for postgres ready..."
done

# збираємо статику
python manage.py collectstatic

# та запускаємо wsgi сервер за допомогою gunicorn
gunicorn project.wsgi:application --bind 8000 --workers 4 --threads 4 # project - це ім'я вашого проекту

Django ми вже остаточно підготували до деплою, тому тепер перейдемо до конфігурації контейнера

NGINX

В теці nginx створюємо: DockerFile

FROM nginx:stable-alpine
CMD ["nginx", "-g", "daemon off;"]
# копіюємо файли ssl сертифікатів, попередньо замовивши-створивши їх для свого домену
COPY nginx/ca.crt /etc/nginx/ssl/ca.crt
COPY nginx/your-domain.com.crt /etc/nginx/ssl/your-domain.com.crt
COPY nginx/your-domain.com.key /etc/nginx/ssl/your-domain.com.key

conf.d

Файл конфігурації nginx

server {
    listen 80;
    server_name your-domain.com www.your-domain.com;
    rewrite ^ https://$server_name$request_uri? permanent;
}

server {
    listen 443 ssl;
    server_name your-domain.com www.your-domain.com;
    server_tokens off;
    ssl_certificate /etc/nginx/ssl/your-domain.com.crt;
    ssl_certificate_key /etc/nginx/ssl/your-domain.com.key;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    keepalive_timeout 70;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_trusted_certificate /etc/nginx/ssl/ca.crt;
    access_log /var/log/nginx/your-domain.com.access.log;
    error_log /var/log/nginx/your-domain.com.su.error.log;

    location /admin {
        try_files $uri @proxy_api;
    }

    location @proxy_api {
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Url-Scheme $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass   http://project_django_1:8000;
    }

    location /static { alias /app/static; }

    location /media { alias /app/media; }

    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://project_django_1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
    }
}

Фух, так?...:)

Вже майже все!

Тепер нам залишилося сворити найважливіший файл - docker-compose.yaml

Що ж це за файл такий і нащо він потрібен?

docker-compose.yaml - це файл конфігурації для Docker Compose, інструмента, що дозволяє вам описувати та запускати багатоконтейнерні додатки з легкістю. У цьому файлі ви можете визначити усі контейнери, що складають ваш додаток, а також налаштування для кожного контейнера. Це дозволяє вам встановити всі контейнери за одну команду та легко керувати їх взаємодією.

Чому він потрібен?

docker-compose.yaml забезпечує простий та стандартизований спосіб описувати ваш додаток та його середовище. Завдяки цьому файлу, інші розробники можуть легко розгортати ваш додаток на своїх комп'ютерах або серверах без необхідності докладного дослідження і налаштування.

Також docker-compose.yaml дозволяє зберігати всі налаштування проекту в одному місці, що спрощує та зберігає наш процес деплою організованим та структурованим.

Створення docker-compose.yaml

Давайте створимо docker-compose.yaml для нашого Django проекту. Відкрийте текстовий редактор та створіть новий файл з назвою docker-compose.yaml у кореневій папці вашого проекту. Додайте такий зміст:

version: '3'

services:
  postgres:
    restart: unless-stopped
    image: postgres:13.1-alpine
    env_file:
      - ./.env
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    networks:
      - project_network

  django:
    restart: unless-stopped
    build:
      context: .
      dockerfile: Dockerfile
    env_file:
      - ./.env
    volumes:
      - static_volume:/app/static
      - media_volume:/app/media
    networks:
      - project_network
    depends_on:
      - postgres

  nginx:
    restart: unless-stopped
    build:
      context: .
      dockerfile: ./nginx/DockerFile
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d/default.conf
      - static_volume:/app/static
      - media_volume:/app/media
    networks:
      - project_network
    depends_on:
      - django

volumes:
  static_volume:
  media_volume:
  postgres_data:

networks:
  project_network:
driver: bridge

Опис сервісів

Опис кожного сервісу:

postgres: Цей сервіс використовує образ PostgreSQL версії 13.1, з базовим образом Alpine Linux. Він має зазначені параметри restart: unless-stopped для автоматичного перезапуску сервісу, якщо він припинить роботу (крім явного зупинення). env_file вказує на файл .env, де містяться змінні оточення для контейнера. Використовується том postgres_data, щоб зберігати дані PostgreSQL між рестартами контейнера. django: Цей сервіс збирається з використанням Dockerfile, що знаходиться в поточній директорії (context: .) і має назву Dockerfile. Також використовує файл .env для змінних оточення. Використовує томи static_volume і media_volume для зберігання статичних та медіафайлів між рестартами контейнера. Залежить від сервісу postgres, щоб мати доступ до бази даних. nginx: Цей сервіс будується з використанням Dockerfile, що знаходиться в папці ./nginx. Він перенаправляє порти 80 та 443 з контейнера на відповідні порти на локальній машині. Монтує файли конфігурації Nginx з локальної папки ./nginx/prod/conf.d. Використовує томи static_volume і media_volume для доступу до статичних і медіафайлів. Залежить від сервісу django, щоб мати доступ до вашого Django додатку через Gunicorn.

Також, у файлі є визначені томи (volumes) і мережа (networks) для спільного використання даних та мережі між контейнерами.

Запуск за допомогою Docker Compose

Для запуску проекту за допомогою Docker Compose відкрийте командний рядок у кореневій папці вашого проекту та виконайте таку команду:

docker-compose up -d --buid

Тепер ваш Django додаток та база даних PostgreSQL будуть запущені, і ваш проект буде доступний за адресою https://your-domain.com.

Готово!

Тепер у нас є повністю підготовлений Django проект, готовий до деплою з використанням Docker та Docker Compose. Ви можете деплоїти його на будь-якому сервері або хмарній платформі з легкістю.

Дякую, що приєдналися до нашої пригоди у розробці web-додатків з Django та Docker. Бажаю вам успіхів у вашому джедайському шляху!

P.S. Корисні команди Docker та Docker-compose

Команди Docker:

  • docker build: Збирає Docker образ з Dockerfile. Приклад: docker build -t my_image_name:latest.
  • docker run: Запускає контейнер із вибраним образом. Приклад: docker run -d -p 8000:80 my_image_name:latest
  • docker ps: Показує список активних контейнерів. Приклад: docker ps
  • docker stop: Зупиняє активний контейнер. Приклад: docker stop container_id
  • docker rm: Видаляє зупинений контейнер. Приклад: docker rm container_id
  • docker images: Показує список доступних Docker образів. Приклад: docker images
  • docker rmi: Видаляє Docker образ. Приклад: docker rmi image_id
  • docker exec: Виконує команду всередині контейнера. Приклад: `docker exec -it container_id command
  • docker logs: Переглядає логи контейнера. Приклад: docker logs container_id

Команди Docker Compose:

  • docker-compose up: Піднімає всі контейнери зі складу docker-compose.yaml. Приклад: docker-compose up -d (запуск в фоновому режимі)
  • docker-compose down: Зупиняє та видаляє всі контейнери, створені за допомогою docker-compose up. Приклад: docker-compose down
  • docker-compose ps: Показує статус контейнерів, визначених у docker-compose.yaml. Приклад: docker-compose ps
  • docker-compose logs: Переглядає логи всіх контейнерів з docker-compose.yaml. Приклад: docker-compose logs
  • docker-compose exec: Виконує команду всередині контейнера, визначеного в docker-compose.yaml. Приклад: docker-compose exec service_name command
  • docker-compose build: Збирає образи для всіх контейнерів, визначених у docker-compose.yaml. Приклад: docker-compose build
  • docker-compose rm: Видаляє зупинені контейнери, визначені у docker-compose.yaml. Приклад: docker-compose rm
Приєднатися до company logo
Продовжуючи, ти погоджуєшся з умовами Публічної оферти та Політикою конфіденційності.