Назад в блог

Создание приложения Django и Gunicorn с использованием Docker на Ubuntu

Создание приложения Django и Gunicorn с использованием Docker на Ubuntu

Django — это высокоуровневый веб-фреймворк с открытым исходным кодом Python, который помогает быстро создавать веб-приложения. Он способствует быстрой разработке и чистому, прагматичному дизайну благодаря следованию архитектурному шаблону «модель-шаблон-представление» (model–template–views). Из коробки фреймворк поставляется с необходимыми компонентами современного приложения, такими как аутентификация пользователей, система кэширования, объектно-реляционное отображение (ORM), диспетчер URL, система шаблонов, и настраиваемый административный интерфейс.

Gunicorn ‘Green Unicorn’ — это HTTP-сервер WSGI для Python, предназначенный для UNIX-систем. Сервер Gunicorn совместим с различными веб-фреймворками, обеспечивает отличную производительность и нетребователен к ресурсам сервера. Docker — это платформа контейнеризации с открытым исходным кодом, которая существует уже довольно давно, делая разработку приложений быстрой, эффективной и предсказуемой.

In this tutorial, you получите навыки разработки и развертывания масштабируемых контейнеризированных веб-приложений Django. Мы будем использовать приложение Django Polls, созданное на основе вводных руководств по началу работы с Django. На момент написания этого руководства мы основывались на Django 3.2, поддерживаемом Python 3.6 или более поздней версии. Мы развернем приложение в виде контейнера с помощью Docker и будем обслуживать его с помощью сервера Gunicorn. Конечно, перед развертыванием приложения Django в контейнере вам придется внести некоторые изменения в код проекта для обработки таких вещей, как логирование в стандартные потоки вывода и работа с переменными окружения. Статические файлы, такие как CSS, JavaScript и изображения, можно перенести в службы объектного хранилища, чтобы упростить управление файлами из одного места в мультиконтейнерной среде.

Мы покажем вам, как реализовать эти изменения на основе четко изложенной twelve-factor методологии для создания масштабируемых веб-приложений. После завершения модификаций вы создадите Docker-образ приложения и развернете контейнеризированное приложение с помощью Docker. Мы рекомендуем вам следовать шагам, описанным в руководстве, чтобы полностью во всем разобраться.

Предварительные требования

Поскольку это практическое руководство, мы рекомендуем вам подготовить следующую конфигурацию, чтобы вам было проще следовать инструкциям:

  • Сервер Ubuntu 20.04. Вы можете выполнить шаги с 1 по 4 этого пошагового руководства, которое поможет вам настроить сервер Ubuntu на CloudSigma.

  • Обязательно добавьте пользователя с привилегиями sudo на обоих узлах, которые мы будем использовать для запуска команд, как описано в руководстве выше.

  • Установите Docker на сервер. Вы можете выполнить шаги 1, 2 и 3 нашего руководства по установке и работе с Docker. Не забудьте добавить созданного выше пользователя sudo в группу Docker.

  • Совместимое объектное хранилище. Django поддерживает несколько сервисов хранения данных, перечисленных в документации django-storages. Вы можете выбрать наиболее предпочтительный вариант и следовать документации для его настройки. В этом руководстве мы будем использовать MinIO, которое является S3-совместимым облачным хранилищем.

  • Экземпляр базы данных SQL. Django поддерживает несколько баз данных SQL, которые вы можете свободно выбирать. В этом руководстве мы будем использовать PostgreSQL. База данных PostgreSQL не будет развернута внутри контейнера. Мы настроим отдельный сервер Ubuntu для размещения экземпляра PostgreSQL, чтобы обеспечить мультиконтейнерную структуру, а также постоянство данных. Вы можете создать еще один экземпляр Ubuntu 20.04 и следовать этому руководству, чтобы настроить экземпляр базы данных PostgreSQL на Ubuntu. Не забудьте добавить роль в базе данных PostgreSQL для вашего пользователя sudo, как описано в шагах 2 и 3. Эта роль позволит вам подключаться к базе данных с других серверов, на которых размещены ваши контейнеры.

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

Шаг 1. Настройка экземпляра базы данных PostgreSQL

В этом разделе мы изменим конфигурации Postgres на сервере Ubuntu, на котором запущен экземпляр Postgres. Это позволит разрешить подключения с внешнего IP-адреса. После подключения мы сможем создать базу данных и роль пользователя, предназначенные специально для развертываемого нами приложения Django Polls.

Во-первых, если вы настроили свою среду в соответствии с Предварительными требованиями, у вас должна быть роль в базе данных PostgreSQL для вашего пользователя sudo. Далее нам нужно установить пароль для этой роли. Находясь на сервере, на котором запущен PostgreSQL, войдите в терминал Postgres с помощью следующей команды:

Находясь в терминале Postgres, выполните команду \password для изменения пароля пользователя. Синтаксис для команды \password выглядит следующим образом: \password <username>. В нашем случае команда:

Введите пароль и подтвердите его. Сохраните этот пароль в надежном месте, так как позже вы будете использовать его для аутентификации с другого сервера Ubuntu. После этого введите exit и нажмите Enter, чтобы выйти из терминала Postgres.

Если вы включили брандмауэр (ufw) на сервере PostgreSQL, вам нужно будет разрешить трафик на порт Postgres по умолчанию 5432. Вы можете ограничить трафик, разрешив его только с определенного IP-адреса вашего другого сервера Ubuntu, на котором будет запущен контейнер Docker. Выполните следующую команду, чтобы добавить правило ufw, заменив IP-адрес в выделенном месте:

Это гарантирует, что только ваш сервер сможет подключиться к экземпляру PostgreSQL. Хотя это разрешает трафик через брандмауэр, вам также необходимо изменить конфигурационные файлы PostgreSQL, чтобы разрешить подключение с удаленного IP-адреса. По умолчанию конфигурация разрешает подключение только с localhost. Конфигурационные файлы PostgreSQL находятся в каталоге /etc/postgresql/12/main directory. 12, в данном случае — это версия PostgreSQL, которую мы установили для этого руководства. Возможно, вы установили другую версию. Таким образом, вы можете перейти в каталог /etc/postgresql/ и вывести список содержимого, чтобы узнать номер версии установленного вами PostgreSQL.

Используйте nano для изменения конфигурационного файла:

Найдите строку ниже, раскомментируйте ее и настройте ее так, чтобы разрешить подключения со всех IP-адресов:

Сохраните и закройте файл. Затем вам также нужно отредактировать файл pg_hba.conf , он находится в том же каталоге, что и postgresql.conf. Файл pg_hba.conf позволяет определить, с каких компьютеров вы можете подключаться к экземпляру PostgreSQL, а также метод аутентификации. Откройте файл с помощью nano:

Пожалуйста, прочитайте комментарии в этом файле, чтобы понять ключевые слова. Раздел, который мы ищем, выглядит следующим образом:

Building a Django and Gunicorn Application with Docker on Ubuntu 1

Мы сосредоточимся на второй строке, после раскомментирования она должна выглядеть следующим образом:

Пожалуйста, замените выделенную часть IP-адресом вашего сервера Ubuntu, чтобы разрешить ему подключение к экземпляру PostgreSQL. Сохраните файл, когда будете готовы. Перезапустите базу данных PostgreSQL, чтобы изменения вступили в силу:

Наш другой сервер Ubuntu с указанным IP-адресом должен иметь возможность подключиться к экземпляру Postgres.

Шаг 2. Подключение к экземпляру сервера PostgreSQL и создание базы данных и пользователя

На этом шаге мы постараемся убедиться, что экземпляр Ubuntu, обслуживающий наш контейнер Docker, может подключиться к другому серверу, на котором запущен экземпляр PostgreSQL. Войдите в экземпляр Ubuntu, на котором установлен Docker, и установите пакет postgresql-client на хост-машине Ubuntu (пока не внутри контейнера).

Как обычно, сначала обновите пакет apt, а затем установите пакет с помощью следующих команд:

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

Параметры подключения соответствуют следующему синтаксису:

В этой команде username — это пользователь/роль, которую вы добавили в свою базу данных PostgreSQL. host — это IP-адрес инстанса Ubuntu, на котором запущена ваша база данных PostgreSQL. port — это порт по умолчанию, на котором Postgres ожидает входящие подключения, т. е. 5432. Вместо database мы будем использовать базу данных по умолчанию с именем postgres, которая поставляется вместе с установкой PostgreSQL. Замените значения в выделенных частях соответствующим образом и нажмите Enter. При появлении запроса введите установленный вами пароль. Это позволит вам войти в консоль Postgres, где вы сможете управлять базой данных.

Вы успешно подключились к экземпляру PostgreSQL. Теперь вы можете создать базу данных для приложения опросов Django. Давайте назовем ее django_polls:

Убедитесь, что ваша инструкция заканчивается точкой с запятой, чтобы избежать ошибок. Затем переключитесь на базу данных django_polls с помощью команды:

Затем создайте пользователя базы данных специально для этого проекта. Давайте назовем пользователя django_user:

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

Установите кодировку по умолчанию, которую ожидает Django, как UTF-8:

Затем установите схему изоляции транзакций по умолчанию на « read committed», которая блокирует чтение из незафиксированных транзакций:

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

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

Выйдите из консоли PostgreSQL, когда будете готовы:

Это все для данного шага. Как только вы правильно настроите свое приложение Django, оно сможет управлять вашей базой данных.

Шаг 3. Получение приложения из репозитория Git и определение зависимостей

На этом шаге мы клонируем репозиторий приложения Django-polls. Этот репозиторий содержит код для Django’s руководства по написанию вашего первого приложения Django.

Войдите на сервер Ubuntu, на котором запущен Docker, создайте каталог с именем django_project и перейдите в него:

Затем клонируйте репозиторий в этот каталог с помощью следующей команды:

Перейдите в каталог и выведите список содержимого:

Выведите список содержимого каталога:

Building a Django and Gunicorn Application with Docker on Ubuntu 2

Обратите внимание на следующие элементы:

  • manage.py: этот файл является точкой входа в утилиту командной строки, которую Django предоставляет для управления вашим приложением.

  • mysite: каталог с областью действия проекта Django и настройками кода.

  • polls: каталог, содержащий polls код приложения.

  • templates: содержит пользовательские файлы шаблонов для страниц администратора.

Чтобы узнать больше о том, как мы на самом деле создали проект, ознакомьтесь с разделом Writing your first Django app в официальной документации. Внутри django-polls директории мы хотим, чтобы наши зависимости Python были определены в текстовом файле. Мы назовем его requirements.txt. Откройте файл в предпочитаемом вами редакторе:

Вставьте следующие строки внутрь файла, чтобы объявить зависимости:

В этом файле мы определили зависимости Python с их точными версиями, которые должны быть установлены при сборке приложения. Некоторые из них включают Django, django-storages для взаимодействия с бакетами объектного хранилища, psycopg2 адаптер для PostgreSQL, gunicorn WSGI-сервер и другие дополнительные зависимости. Сохраните и закройте файл, когда закончите.

Шаг 4: Настройка переменных окружения для приложения Django

Методология twelve-factor app рекомендует извлекать жестко закодированные конфигурации из кодовой базы вашего приложения. Таким образом, вы получаете свободу изменять поведение приложения во время выполнения, изменяя переменные окружения без изменения кодовой базы. Docker работает с такой конфигурацией, поэтому мы изменим файл настроек для работы с переменными окружения. Kubernetes также работает с этой конфигурацией. Мы опубликуем еще одно руководство по развертыванию с помощью Kubernetes в блоге CloudSigma.

Файл settings.py является основным файлом настроек для проекта Django. Это модуль Python, который использует встроенные структуры данных для настройки приложения. Для нашего приложения этот файл находится по адресу django-polls/mysite/settings.py. Большинство его значений жестко закодированы. Это потребует от вас изменения конфигурационного файла в кодовой базе, если вы измените поведение приложения. Мы хотим это изменить. К счастью, Python предлагает функцию getenv в модуле os модуле. Мы можем использовать ее, чтобы настроить Django для чтения параметров конфигурации из локальных переменных окружения.

Давайте продолжим, изменив файл django-polls/mysite/settings.py, чтобы заменить жестко закодированные значения переменных, которые мы хотим обновлять во время выполнения с помощью вызова os.getenv. Эта функция считывает значение, заданное в указанном имени переменной окружения. При желании вы можете передать второй параметр, который является значением по умолчанию, используемым, если переменная окружения не задана.

Вот пример:

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

Вот еще один пример с опцией по умолчанию:

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

Теперь, когда вы знаете важность переменных окружения, откройте файл django_project/django-polls/settings.py в вашем редакторе. Сначала импортируйте модуль os, добавив эту строку в начало файла settings.py file:

Затем найдите эти переменные и обновите их следующим образом:

В настройке ALLOWED_HOSTS мы указываем, что значение должно быть получено из DJANGO_ALLOWED_HOSTS переменной окружения и разделено в список Python с использованием запятой ( ,) в качестве разделителя. Если переменная отсутствует, ALLOWED_HOSTS устанавливается в 127.0.0.1.

Затем прокрутите файл и найдите раздел DATABASES, настройте его так, чтобы он также читал из переменных окружения:

Обратите внимание, что мы добавили модуль json.loads. Вам также следует добавить импорт этого модуля в верхней части файла settings.py:

Функция json.loads десериализует объект JSON, переданный в DATABASES['default']['OPTIONS'] из переменной окружения DB_OPTIONS. Указание этого параметра позволяет нам передавать произвольную структуру данных для определения конфигурации базы данных. Движок базы данных включает в себя набор применимых к нему допустимых параметров. Параметр JSON дает нам гибкость для кодирования объекта JSON с соответствующими параметрами для используемого в данный момент движка базы данных.

Параметр DATABASES['default']['NAME'] указывает имя базы данных в настроенной нами системе управления реляционными базами данных. В случае использования базы данных SQLite вам следует указать путь к файлу базы данных.

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

Шаг 5. Работа с внешними службами объектного хранилища

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

Концепция, которую мы пытаемся объяснить выше, заключается в использовании облачных служб объектного хранилища или Simple Storage Services (S3). У Django есть пакет под названием django-storages, которая позволяет работать с удаленными бэкендами хранения. Django-storages работают с большинством S3-совместимых сервисов объектного хранения, таких как FTP, SFTP, Amazon AWS S3, Google Cloud Storage, Dropbox и Azure Storage, помимо прочих. В этом руководстве мы будем использовать MinIO. Вы можете использовать любые другие S3-совместимые сервисы объектного хранения. MinIO предлагает высокопроизводительное S3-совместимое объектное хранилище. С помощью MinIO вы можете создать S3-совместимую инфраструктуру данных в любом облаке.

Мы покажем вам, как настроить службу хранения MinIO на платформе CloudSigma. Пожалуйста, выполните следующие шаги:

  • Начните с создания учетной записи на CloudSigma. Если у вас возникнут проблемы при создании хранилища MinIO, свяжитесь с бесплатной круглосуточной службой поддержки CloudSigma в чате, и они помогут вам.

  • Добавьте платежную информацию.

  • Затем запросите публично доступный бакет отсюда: https://blog.cloudsigma.com/xxxx. Вам нужно будет связаться с поддержкой в чате, чтобы получить учетные данные для доступа к аккаунту.

  • После создания среды объектного хранения MinIO вам будут предоставлены учетные данные и другие инструкции для доступа к ней. Учетные данные должны включать ваш MINI_ACCESS_KEY, MINIO_SECRET_KEY, а также MINIO_URL. Вы будете использовать эти ключи в инструкциях ниже.

Давайте внесем еще несколько изменений в файл mysite/settings.py, который мы изменяли на предыдущем шаге. В этом файле добавьте приложение storages в список Django INSTALLED_APPS:

INSTALLED_APPS

Приложение storages устанавливается через django-storages, как определено в requirements.txt. Прокрутите файл до конца и замените переменную STATIC_URL следующим фрагментом кода:

Обратите внимание, что некоторые конфигурационные переменные жестко закодированы:

  • STATICFILES_STORAGE: определяет бэкенд хранения, который Django будет использовать для обработки статических файлов. В нашем руководстве мы используем хранилище MinIO, но вы можете использовать любой S3-совместимый бэкенд, как описано в документации Django Storages.

  • AWS_S3_OBJECT_PARAMETERS: определяет заголовки управления кэшированием (cache-control).

  • AWS_LOCATION: мы используем это для указания директории внутри бакета хранилища, где будут храниться все статические файлы. Вы можете выбрать другое имя.

  • AWS_DEFAULT_ACL: устанавливает список контроля доступа (ACL) для статических файлов. Установка значения в ‘ public-Read’ сделает файлы доступными для всех публичных пользователей.

  • STATIC_URL: Django использует базовый URL, заданный в этой переменной, для генерации URL-адресов статических файлов. Базовый URL в данном случае получается путем объединения URL-адреса конечной точки и поддиректории статических файлов.

  • STATIC_ROOT: определяет, где собирать статические файлы локально перед их копированием в удаленное объектное хранилище.

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

  • AWS_STORAGE_BUCKET_NAME: определяет имя бакета хранилища, в который Django будет загружать статические файлы.

  • AWS_S3_ENDPOINT_URL: определяет URL-адрес конечной точки, используемый для доступа к сервису объектного хранилища. Это будет URL-адрес, сопоставленный с сервером, на котором размещена ваша служба MinIO.

Сохраните и закройте файл после завершения редактирования.

Как только вы настроите эти параметры и установите объявленные зависимости Python, вы сможете запустить команду Django manage.py collectstatic в любое время, чтобы собрать статические файлы вашего проекта и загрузить их в удаленное объектное хранилище:

Однако мы еще не настроили файл env с конфигурациями, поэтому, скорее всего, произойдет ошибка.

При запуске команды копирование ваших статических файлов в облачное хранилище MinIO занимает некоторое время в зависимости от их размера и скорости вашего интернета.

На этом шаге все. Давайте посмотрим, как мы можем настроить отправку логов Django в Docker Engine, чтобы вы могли просматривать их с помощью команды docker logs на следующем шаге.

Шаг 6. Настройка логирования в приложении Django

В режиме отладки, когда параметр DEBUG установлен в значение True, Django выводит логи в стандартный поток вывода и стандартный поток ошибок. Информация логов обычно отображается в терминале, из которого вы запустили сервер разработки HTTP.

В рабочей среде (production) вы, скорее всего, используете другой HTTP-сервер, а параметр DEBUG установлен в значение False. В этом случае Django будет использовать другой метод логирования. Django отправляет логи с приоритетом ERROR или CRITICAL на указанный вами адрес электронной почты администратора. Это отлично работает во многих ситуациях.

В контейнеризированных средах и средах Kubernetes настоятельно рекомендуется выводить логи в стандартный поток вывода и стандартный поток ошибок. Сообщения логов собираются в одном каталоге файловой системы ноды и легко доступны с помощью команд kubectl and docker . Благодаря централизованной точке логирования в файловой системе ноды операционная группа может легко запускать процессы на каждой ноде для отслеживания и пересылки логов. Следовательно, мы должны настроить наше приложение для записи логов в эту стандартную конфигурацию.

Вы будете рады узнать, что Django использует легко настраиваемый модуль logging из стандартной библиотеки Python. Это позволяет вам определить словарь, который передается в logging.config.dictConfig для определения желаемых выводов и форматирования. Вот отличная статья о Django Logging, The Right Way, которая поможет вам освоить методы логирования в Django.

Откройте файл django-polls/mysite/settings.py  в вашем редакторе. Добавьте импорт библиотеки Python logging.config в самом начале файла:

На данный момент, с учетом всех добавленных нами импортов, раздел импорта в settings.py должен выглядеть следующим образом:

Building a Django and Gunicorn Application with Docker on Ubuntu 3

Библиотека logging.config принимает словарь новой конфигурации логирования через функцию dictConfig для переопределения стандартного поведения логирования Django.

Прокрутите файл до самого низа и добавьте следующий фрагмент кода конфигурации логирования:

LOGGING_CONFIG устанавливается в None, чтобы отключить/очистить стандартные конфигурации логирования, которые определяет Django. LOGLEVEL задается переменной окружения DJANGO_LOGLEVEL. Однако, если она не существует, мы хотим установить ее в ‘ info’.

Модуль logging.config, который мы импортировали в самом начале, предоставляет функцию dictConfig, которая используется для настройки нового словаря конфигурации. Словарь определяет форматирование текста с помощью ключа formatters. Вывод настраивается с помощью ключа handlers, и, наконец, ключ loggers определяет, какое сообщение должно отправляться в какой обработчик.

После того как эти настройки будут определены, Docker сделает логи доступными через команду docker logs. Аналогично, в другом руководстве, которое мы подготовим для Kubernetes, вы сможете просматривать логи с помощью команды kubectl logs. Теперь давайте начнем процесс контейнеризации на следующем шаге.

Шаг 7: Определение Dockerfile приложения

На этом шаге мы определим конфигурацию для запуска образа контейнера, в котором будет работать приложение Django, обслуживаемое WSGI-сервером Gunicorn. Мы определим среду выполнения для сборки образа контейнера, установим приложение и его зависимости, а также выполним некоторые финальные настройки.

  • Родительский образ для приложения Django

Выбор базового образа, на котором будет основан ваш контейнер, — это самое первое решение, которое вы примете при работе с контейнеризированным развертыванием. Конечно, у вас есть возможность собирать образы контейнеров с нуля (SCRATCH), то есть из пустой файловой системы, или основывать их на существующем образе контейнера. Поскольку мы не хотим изобретать велосипед, мы будем собирать наш образ на основе базового. Существует множество образов контейнеров с открытым исходным кодом, доступных в официальном репозитории образов контейнеров Docker. Если только вы не собираете свой образ с нуля, настоятельно рекомендуется использовать образ из официального Docker Hub. Это связано с тем, что Docker проверяет образы на соответствие лучшим практикам, а также обеспечивает регулярные обновления и исправления безопасности.

Поскольку Django — это фреймворк Python, мы воспользуемся образом со стандартным окружением Python, в котором уже установлены необходимые нам инструменты и библиотеки. На официальной странице образов Python на Docker Hub, вы можете найти образ на базе Python для различных его версий.

Из наших различных руководств по Docker, вы заметите, что мы используем образы на базе Alpine Linux. Alpine Linux предлагает надежную, но легковесную операционную систему для запуска контейнеризированных приложений. Хотя ее файловая система невелика, она расширяема и поставляется с полноценной системой управления пакетами с возможностью добавления новых функций.

При выборе базового образа на Docker Hub вы можете заметить несколько тегов, доступных для каждого образа. Что касается Python, у нас есть 3-alpine, который указывает на образ последней версии Python 3 на базе последней версии Alpine. Это означает, что если ваш проект работает со старой версией образа, он может сломаться, когда мейнтейнеры образа Docker выпустят обновление. Чтобы избежать подобных сценариев в будущем, всегда рекомендуется выбирать наиболее конкретные теги для образа, который вы хотите использовать.

В этом руководстве мы будем использовать 3.8.12-alpine3.15 образ в качестве базового образа для нашего приложения Django. Этот конкретный тег будет указан в Dockerfile с использованием инструкции FROM. Dockerfile будет находиться в основном каталоге проекта: django_project.

Начните с перехода из каталога Django-polls обратно в каталог django_project directory:

Оказавшись в этом каталоге, используйте ваш любимый редактор, чтобы открыть файл с именем Dockerfile :

Затем вставьте следующую строку, чтобы задать базу вашего образа:

Ключевое слово FROM  определяет начальную точку для кастомного образа Docker. Определив это, мы можем продолжить добавлять инструкции для настройки приложений. Эти инструкции установят необходимые зависимости, скопируют файлы приложения и настроят среду выполнения.

Добавьте следующий фрагмент кода внутрь Dockerfile:

В этом фрагменте кода мы указываем Docker скопировать файл requirements.txt  в /app/requirements.txt, чтобы гарантировать доступность зависимостей приложения в файловой системе образа. Требования включают все пакеты Python, необходимые для запуска приложения. Зависимости копируются в первую очередь, чтобы Docker мог кэшировать слой образа. Это связано с тем, что Docker кэширует каждый шаг в Dockerfile. Первая сборка образа обычно занимает больше времени. Docker загрузит зависимости, а затем кэширует их. Если файл requirements.txt не меняется, Docker будет выполнять сборку из кэша, что ускорит последующие сборки.

Следующий шаг содержит инструкцию RUN, которая выполняет список команд Linux, объединенных оператором Linux &&. Эти команды выполняют следующее:

  • Используют инструмент управления пакетами Alpine apk для установки файлов разработки PostgreSQL и базовых зависимостей сборки.

  • Создают виртуальное окружение Python.

  • Устанавливают зависимости Python, определенные в файле requirements.txt с помощью pip.

  • Компилируют необходимые пакеты среды выполнения, анализируя требования установленных пакетов Python.

  • Удаляют любые зависимости сборки, которые больше не нужны.

Причина объединения команд на шаге RUN заключается в уменьшении количества слоев образа. Docker создает новый слой образа поверх существующей файловой системы каждый раз, когда встречает ADD, COPY или RUN инструкция в Dockerfile. Объединение команд, где это применимо, минимизирует количество создаваемых слоев образа.

Элементы, добавленные в слои образа, не могут быть удалены в последующем слое. Вам необходимо объявить инструкции по удалению ненужных элементов перед переходом к следующей инструкции. Это необходимо для уменьшения размера образа. Вы должны заметить, что мы добавили apk del команду в конце RUN команды. Это было сделано для удаления зависимостей сборки после того, как мы использовали их для сборки пакетов приложения.

Далее у нас есть еще одна инструкция ADD , которую мы используем для копирования кода приложения в директорию /app . Затем мы будем использовать инструкцию WORKDIR , чтобы установить рабочую директорию образа на /app , в которой теперь находится код приложения.

Далее у нас есть инструкции ENV , которые мы используем для установки двух переменных окружения, которые образ сделает доступными для запущенных контейнеров. Сначала мы устанавливаем переменную VIRTUAL_ENV в значение /env . Во-вторых, мы настраиваем переменную PATH , чтобы она включала директорию /env/bin . В этих двух строках мы подключаем скрипт /env/bin/activate script, с помощью которого мы активируем виртуальное окружение в среде Linux. Вы можете прочитать подробнее о работе с виртуальными окружениями в Python в других операционных системах . Последняя инструкция — это команда EXPOSE , которая задает порт, 8000 на котором контейнер будет слушать во время работы.

К этому моменту ваш Dockerfile почти готов, за исключением команды по умолчанию, которая будет запускаться при старте контейнеров. Давайте определим ее в следующем разделе.

  • Понимание команды по умолчанию для Docker-образа

При запуске Docker-контейнера вы можете указать команду для выполнения. Однако, если вы не укажете команду, команда по умолчанию Docker-образа определит, что произойдет при запуске контейнера. Мы используем инструкции ENTRYPOINT или CMD по отдельности или вместе, чтобы определить команду по умолчанию в Dockerfile.

Если вы решите определить как ENTRYPOINT , так и CMD , то в инструкции ENTRYPOINT вы определяете исполняемый файл, который будет запущен контейнером. В инструкции CMD определите список аргументов по умолчанию для исполняемой команды. Вы можете переопределить список аргументов по умолчанию, добавив альтернативные аргументы в командной строке при запуске контейнера в формате:

Этот формат не позволяет разработчикам легко переопределять команду ENTRYPOINT . Команда ENTRYPOINT определяется для вызова скрипта, который настроит окружение и выполнит различные действия на основе предоставленного списка аргументов.

Вы можете использовать только инструкцию ENTRYPOINT для настройки исполняемого файла контейнера. Однако этот формат не позволяет определить список аргументов по умолчанию. Вы можете передать аргументы при запуске контейнера с помощью команды docker run .

Если вы решите использовать только CMD , Docker интерпретирует это как команду и список аргументов по умолчанию, которые вы можете переопределить во время выполнения. Вы можете найти дополнительную информацию в официальной справочной документации по Dockerfile.

Давайте посмотрим, как мы можем применить полученную информацию о командах по умолчанию к нашему примеру контейнера. Мы хотим по умолчанию запускать приложение с помощью сервера gunicorn . Хотя список аргументов, передаваемых серверу gunicorn , не обязательно должен быть настраиваемым во время выполнения, нам нужна гибкость для запуска других команд в таких целях, как отладка или управление конфигурациями (инициализация базы данных, сбор статических файлов и т. д.). Как видите, в наших интересах использовать CMD для определения команды по умолчанию, что позволит нам переопределять ее при необходимости.

Вот несколько вариантов синтаксиса, которые вы можете использовать для определения команды CMD :

  • CMD ["command", "argument 1", "argument 2", . . . ,"argument n"]: Формат exec (рекомендуемый формат) принимает команду и список аргументов. Он выполняет команду напрямую без обработки оболочкой (shell).
  • CMD command "argument 1" "argument 2" . . . "аргумент n": Формат shell определяет команду и список аргументов. Он передает список команд оболочке для обработки. Это может быть полезно, если вы хотите подставить переменные окружения в команду, однако это не совсем предсказуемо.
  • CMD ["аргумент 1", "аргумент 2", . . . ,"аргумент n"]: Формат списка аргументов, он определяет только список аргументов по умолчанию и используется вместе с ENTRYPOINT инструкцией.

Мы будем использовать exec формат для определения нашей финальной инструкции в Dockerfile. Добавьте следующую строку в конец вашего Dockerfile:

Теперь вы можете сохранить и закрыть Dockerfile.

Когда вы запустите контейнеры с использованием этого образа, они будут выполнять gunicorn, привязанный к порту localhost 8000 с 3 воркерами, и вызывать функцию application в файле wsgi.py, расположенном в каталоге mysite . Вы можете передать другую команду, чтобы переопределить команду по умолчанию во время выполнения и запустить другой процесс вместо gunicorn. Возможно, вы захотите узнать больше о воркерах Gunicorn.

Ваш Dockerfile готов, и вы можете использовать docker build для сборки образа приложения. Вы можете использовать docker run для запуска контейнера на вашей локальной машине разработки.

  • Сборка Docker-образа

Команда docker build по умолчанию ищет Dockerfile в текущем каталоге, чтобы найти инструкции по сборке. Она также отправляет «контекст» сборки демону Docker. Контекст сборки — это набор файлов, которые должны быть доступны в процессе сборки. По умолчанию текущий каталог, в котором вы запускаете команду docker build , устанавливается в качестве контекста сборки.

Находясь в том же каталоге, где находится ваш Dockerfile, выполните команду docker build. Укажите образ и тег с помощью флага -t и задайте текущий каталог в качестве контекста сборки, используя точку ( .) в конце команды:

В этой команде мы назвали образ django-polls , а тег — v1. Обратите внимание на точку в конце команды: мы используем ее для обозначения текущего каталога в качестве контекста сборки.

Когда процесс docker build завершится, вы должны увидеть вывод, похожий на следующий:

Building a Django and Gunicorn Application with Docker on Ubuntu 4

Ваш Docker-образ готов. Если бы мы не вынесли часть конфигураций во внешние переменные окружения, вы могли бы легко запустить контейнер с помощью команды docker run. Однако, поскольку мы не настроили внешние переменные окружения, которые мы указали в файле settings.py, запуск завершится ошибкой. Давайте завершим это на следующем шаге.

Шаг 8: Настройка среды выполнения и тестирование приложения

Мы приближаемся к концу этого руководства. На этом шаге мы настроим переменные окружения в файле env . После настройки переменных в файле env мы сможем создать схему базы данных, сгенерировать и загрузить статические файлы во внешнее объектное хранилище и, наконец, протестировать приложение.

В Docker есть несколько методов, которые можно использовать для передачи переменных окружения в контейнер. В нашем случае мы хотим передать список переменных окружения через файл. Поэтому мы будем использовать метод --env-file.

Используя предпочитаемый вами редактор, создайте файл с именем env в каталоге django_project:

Вставьте следующий список переменных:

Переменные в списке — это те, которые вы определили на предыдущих шагах:

  • DJANGO_SECRET_KEY: Сгенерируйте уникальное, непредсказуемое значение, как описано в Django docs. Вы можете использовать эту команду для генерации случайной строки и присвоения её переменной:

  • DEBUG: Мы установили это значение в True, но для развертывания в рабочей среде не забудьте установить его в False , оставив его пустым.

  • DJANGO_LOGLEVEL: мы установили это значение в info, вы можете настроить его на нужный вам уровень.

  • DJANGO_ALLOWED_HOSTS: установите это значение равным IP-адресу сервера Ubuntu, на котором запущены ваши контейнеры Docker. При желании установите его в *, символ подстановки, соответствующий всем хостам, если вы находитесь в режиме разработки.

  • DB_DATABASE: если вы использовали другое имя базы данных, укажите его здесь соответствующим образом.

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

  • DB_PASSWORD: установите здесь пароль, который вы выбрали для своей базы данных.

  • DB_HOST: установите здесь хост, на котором запущен ваш экземпляр базы данных, как вы настроили на Step One.

  • DB_PORT: установите здесь порт вашей базы данных.

  • STATIC_MINIO_BUCKET_NAME: установите здесь имя бакета, которое вы создали в своем аккаунте облачного хранилища MinIO.

Сохраните и закройте файл после завершения редактирования.

Environment configurations are now ready. We need to run the container passing in arguments to override the default CMD command and create the database schema using the manage.py makemigrations and manage.py migrate commands.

Вот эта команда:

В этой команде мы запускаем образ контейнера django-polls:v1, используя флаг env-file для передачи файла переменных среды. Мы также переопределяем команду CMD по умолчанию на sh -c "python manage.py makemigrations && python manage.py migrate" При запуске этой команды для старта контейнера будет создана схема базы данных, определенная в коде приложения.

В случае успеха вы должны увидеть вывод, аналогичный приведенному ниже:

Building a Django and Gunicorn Application with Docker on Ubuntu 4

Вывод указывает на то, что схема базы данных была успешно создана.

Следующим шагом является создание администратора для приложения Django. Мы запустим контейнер и откроем интерактивную оболочку внутри него с помощью следующей команды:

Команда запускает контейнер с командной строкой оболочки, которую вы можете использовать для взаимодействия с оболочкой Python. Давайте создадим пользователя:

Следуйте подсказкам, чтобы ввести имя пользователя, адрес электронной почты, пароль, повторно ввести пароль и нажать Enter для создания пользователя. Выйдите из оболочки и остановите контейнер, нажав CTRL+D.

Далее нам нужно снова запустить контейнер, переопределив команду по умолчанию командой Django collectstatic , чтобы сгенерировать статические файлы для приложения и загрузить их в ваше облачное хранилище MinIO:

После завершения вы должны увидеть аналогичный вывод, указывающий на то, что ваш контейнер успешно подключился к хранилищу MinIO и загрузил статические файлы:

static files

Наш бакет хранилища теперь выглядит следующим образом, с директориями, созданными Django:

Building a Django and Gunicorn Application with Docker on Ubuntu 5

Наконец, теперь мы можем запустить приложение с помощью команды:

Вот вывод:

output

При выполнении вышеуказанной команды запускается команда CMD по умолчанию в вашем образе и открывается порт 8000, как определено. Теперь Ubuntu на порту 80 сопоставляется с портом 8000 контейнера django-polls:v1 .

Теперь мы можем протестировать приложение в браузере. Перейдите по публичному IP-адресу вашего сервера в браузере: http://your_server_public_ip.

Ожидайте увидеть ошибку 404 Page Not Found, так как согласно руководству Django, мы не определили маршрут для пути / :

page not found

У нас установлена переменная DEBUG со значением True, поэтому мы видим эту страницу ошибки с большим количеством важной информации. Давайте сбросим переменную DEBUG. Сначала вам нужно будет остановить запущенный контейнер с помощью CTRL+C. Затем откройте файл env :

Затем найдите переменную DEBUG и сбросьте ее или оставьте пустой. Мы оставляем ее пустой, потому что функция getenv интерпретирует False как строку, возвращая в итоге true:

Сохраните файл и снова запустите контейнер с помощью команды:

Если вы перейдете по адресу http://your_server_public_ip в своем браузере, вы должны увидеть страницу 404 по умолчанию:

not found

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

Перейдите по адресу http://your_server_public_ip/polls, чтобы увидеть главную страницу опросов (Polls):

Building a Django and Gunicorn Application with Docker on Ubuntu 6

У нас нет опросов, так как мы только что развернули приложение.

Перейдите в интерфейс администратора: http://your_server_public_ip/admin, чтобы открыть окно аутентификации администратора:

polls administration

Введите учетные данные, которые вы установили с помощью команды createsuperuser, чтобы войти в систему. Теперь вы должны находиться в интерфейсе административной панели:

site administration

Обратите внимание, что все статические файлы обслуживаются из настроенного нами внешнего хранилища. Вы можете нажать правой кнопкой мыши в окне браузера и выбрать «Просмотр кода страницы» (View Page Source):

external storage

Вы можете добавить несколько вопросов и вариантов ответов и протестировать общую производительность приложения:

questions and choices

Вернитесь на главную страницу опросов http://your_server_public_ip/polls и попробуйте проголосовать по вопросу:

django framework

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

Заключение

Вы успешно настроили веб-приложение Django для работы в контейнерной среде. Это включало адаптацию приложения для работы с внешними переменными окружения, настройку приложения на использование облачного хранилища для статических файлов и создание Dockerfile для образа контейнера. Вы можете просмотреть изменения, которые мы внесли для контейнеризации приложения, в ветке django-polls-docker репозитория GitHub django-polls .

Отсюда возможности ограничены только вашим воображением. Вы можете настроить обратный прокси-сервер Nginx между клиентами и сервером Gunicorn. Вы также можете добавить Certbot для получения TLS-сертификатов для защиты вашего сервера Nginx. Мы рекомендуем добавить HTTP-прокси для буферизации медленных клиентов и защиты вашего сервера Gunicorn от атак типа «отказ в обслуживании» (DoS).

Хотя в команде запуска Dockerfile мы определили 3 воркера, вы можете установить предпочтительное количество в зависимости от ресурсов, доступных на вашем сервере. Дополнительную информацию можно найти в официальной документации по архитектуре Gunicorn. При желании вы можете отправить созданный вами Docker-образ в Docker Hub и попробовать развернуть его в нескольких средах с установленным Docker. Если вы хотите узнать больше, следите за нашим блогом с руководствами, так как мы подготовим следующее руководство по обеспечению безопасности приложения Django с помощью Nginx и Let’s Encrypt.

Наконец, вот еще несколько ресурсов, которые помогут вам в работе с Docker:

Приятной работы!

author

Shreyas Patil

Автор · CloudSigma

Preslav Dobrev — креативный дизайнер в CloudSigma, сосредоточенный на формировании последовательного корпоративного образа с помощью традиционных и инновационных маркетинговых каналов. Он умело сочетает художественное видение со стратегическим маркетингом, создавая убедительные истории бренда.

Комментарии

Комментариев пока нет. Будьте первым.