Локальная разработка Docker и Django: минимальное пошаговое руководство
Зачем запускать Django внутри Docker локально? Разве у вас уже недостаточно движущихся частей, необходимых для работы?
Я пытаюсь ответить на этот вопрос здесь. Посмотрите, относится ли это к вашему варианту использования. Этот пост посвящен тому, как это сделать.
В конце этого поста у вас будет:
- Настройте Docker локально на своем устройстве разработки.
- Запустите Django в контейнере Docker на том же компьютере.
- Поставили точку останова и отладили код!
Pre-reqs
- Докер установлен локально. Для проверки концепции я использовал Docker Desktop.
Минимальная установка Docker
Наша минимальная установка docker будет такой:
- запустить реляционную базу данных: Postgres
- непосредственно запустить команду
runserver
, что следует делать в целях отладки
Наша минимальная установка не docker:
- запустите веб-сервер, например Nginx
- запустите
gunicorn
илиuwsgi
как “клей" между фреймворком (кодом Django) и веб-сервером
Поскольку целью является локальная разработка с помощью Docker, ни то, ни другое не нужно.
Минимальное понимание Docker
Если некоторые концепции Docker все еще непонятны вам, не волнуйтесь. Мне самому постоянно приходится искать что-то новое.
Когда я начинал, мне очень помогла эта статья: 6 основ Docker, которые вы должны полностью усвоить, приступая к работе. В этой статье объясняются взаимосвязь и основные различия между:
- Containers
- Images
- Dockerfiles
- Volumes
- Port Forwarding
- Docker compose
Спасение, если вы запутались в шквале нового жаргона Docker. Для меня это было так. В этом посте мы будем настраивать:
- один
Dockerfile
, - один файл Docker compose, или
docker-compose.yml
Установите проект Django локально
Dockerfile
Добавьте Dockerfile
в корень вашего проекта Django с таким содержимым:
|
|
Давайте разложим этот Dockerfile
.
Строка 1 выбирает образ: FROM python:3
указывает Docker начать с изображения python:3
. Часто можно увидеть “alpine” версию для образов Python. Alpine Linux намного меньше, чем большинство базовых образов дистрибутива, и в целом приводит к более тонким образам. Подробнее об образах Python и их вариантах можно прочитать здесь.
В строке 2 переменной среды PYTHONUNBUFFERED
присваивается значение 1
. Что это? Обычно, если у вас есть процесс, передающий данные в ваше приложение, терминал может буферизовать данные. Терминал хранит данные в резервуаре до тех пор, пока не будет достигнут предел размера или определенный символ (обычно новая строка или конец строки). В этот момент он сразу выгружает весь блок данных в ваше приложение. То же самое для выходных данных и данных об ошибках (stdout
и stderr
). Эта опция предписывает терминалу не использовать буферизацию. Подробнее об этой опции здесь.
Оставшийся набор инструкций в строках 3-7:
- создает каталог
/code
на корневом уровне - копирует в него
requirements.txt
- устанавливает пакеты python (для начала в контейнере нет необходимости в virtualenv)
- копирует в него полный каталог проекта
Так что Dockerfile
выше:
- выбирает для нас базовое изображение,
- настраивает его, чтобы мы могли запускать что-то поверх него, устанавливая необходимые пакеты и копируя код нашего проекта Django.
Приятно!
Вопрос: Так как же нам запустить этот контейнер?
В: Мы будем использовать docker-compose
.
О: Но контейнер выше работает только с Django. Разве нам не нужен контейнер для Postgres?
О: Нам не нужно настраивать контейнер Postgres, поскольку Docker предоставляет образ Docker для Postgres, который мы можем просто запустить. Затем мы войдем в него и настроим его, как если бы он работал локально.
Вопрос: Должны ли мы написать сценарий оболочки и выполнить процесс docker на нашей локальной машине для обоих контейнеров?
A: Нет. Docker предоставляет docker-compose
, а не полагается на сценарии оболочки.
docker-compose
Большим преимуществом файла docker-compose.yml
является то, что он очень удобен для чтения.
Добавьте этот файл docker-compose.yml
в каталог вашего проекта Django:
|
|
У нас есть две “службы”, db
и web
.
Служба db
запускает процесс Postgres внутри контейнера, который использует образ postgres
.
POSTGRES_DB
, POSTGRES_USER
и POSTGRES_PASSWORD
жестко запрограммированы в docker-compose.yml
. Однако вы можете настроить их для использования переменных среды с помощью .env
или .envrc
файл. Это ИМХО не стоит усилий, если вы делаете это только для локального тестирования.
Служба web
запускает процесс manage.py runserver
внутри контейнера с подходящим названием django_web
.
Инструкция build: .
указывает Docker compose использовать Dockerfile
, расположенный в этом же каталоге, для запуска службы web
. Он будет запускать службу “внутри” контейнер django_web
. Документы по сборке
здесь.
Команда command
запускает команду Django runserver
и предоставляет ее порту контейнера 8000
.
container_name
- это пользовательское имя, которое вы можете добавить для ясности. Мы увидим его эффект при запуске в следующем разделе.
environment
позволяет повторно использовать переменные среды с хост-компьютера. Подробнее об управлении переменными среды для вашего проекта Django. В этом случае переменная среды DATABASE_URL
используется повторно.
volumes
используется для “монтирования” хостовые пути. Основное употребление в нашем контексте — “поделиться” код на нашем компьютере с кодом в сервис-контейнере django_web
. Документы здесь.
В заключение:
- Строки 18–19 сопоставляют порт хост-компьютера
8000
с портом контейнера8000
. - Строки 20–21 обеспечивают зависимость контейнера
web
от контейнераdb
.
Достаточно объяснений! Давайте запускать!
Заставьте его работать на вашем локальном Docker-контейнере
Убедитесь, что ваш рабочий стол Docker запущен.
Запустите docker ps
, чтобы получить список контейнеров. Если вы не запускали никаких других контейнеров, вы не должны увидеть ни одного. Вывод должен быть следующим:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Выполните следующую команду для запуска двух контейнеров в соответствии с вашим docker-compose.yml
:
docker-compose up
Вывод терминала должен заканчиваться обычным выводом команды Django runserver
:
web_1 | Watching for file changes with StatReloader
web_1 | Performing system checks...
web_1 |
web_1 | System check identified no issues (0 silenced).
web_1 | June 06, 2020 - 10:24:43
web_1 | Django version 3.0.6, using settings 'djangotest.settings'
web_1 | Starting development server at http://0.0.0.0:8000/
web_1 | Quit the server with CONTROL-C.
Запуск docker ps
теперь должен показать два контейнера (прокрутите страницу вправо, чтобы увидеть полный вывод):
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
91f53455ce25 django_web "python manage.py ru…" 59 seconds ago Up 58 seconds 0.0.0.0:8000->8000/tcp django_web
45b3091c0c62 postgres "docker-entrypoint.s…" 59 seconds ago Up 58 seconds 5432/tcp djangotest_db_1
Обратите внимание, что вывод приведенного выше NAMES
зависит от имени каталога проекта. Например, поскольку у контейнера службы db
нет имени, результирующее имя контейнера будет djangtest_db1
, поскольку каталог проекта — djangotest
.
В другом окне терминала/вкладке войдите в db
docker-контейнер:
docker-compose exec db sh
После входа в систему откройте psql
как пользователь postgres
:
su - postgres -c psql
И создайте базу данных:
CREATE DATABASE djangodb OWNER postgres;
Закройте как psql
, так и док-контейнер db
.
Введите веб-контейнер:
docker-compose exec web sh
После входа в систему выполните следующие действия для применения миграций базы данных и создания суперпользователя:
./manage.py migrate
./manage.py createsuperuser
Обновите сайт по адресу http://localhost:8000/admin/
и войдите в систему под именем суперпользователя, которого вы только что создали.
Вы можете выйти из контейнера web
.
Остановите предыдущую web
службу, прежде чем продолжить.
Выполните команду для остановки процесса, как вы бы сделали это с локальным процессом Django runserver
.
После этого запуск docker ps
должен отображать только запущенную службу db
.
Время отладки!
Обновите код и поставьте точку останова в одном из представлений. Я использовал отладчик IPython ipdb.
Создайте контейнер web
После изменения кода выполните следующие действия, чтобы перестроить контейнер web
, включая зависимости:
docker-compose up -d --no-deps --build web
Давайте разберем приведенную выше команду docker-compose up
:< /p>
-d
или--detach
означает “Отдельный режим”; для запуска контейнеров в фоновом режиме--no-deps
указываетdocker-compose up
не запускать связанные службы--build
указываетdocker-compose up
собрать все необходимые образы перед запуском контейнеровweb
— это сервис, для которого я запускаюdocker-compose up
Отладка!
Для отладки с помощью ipdb
используйте docker-compose run
, документы здесь.
Флаг --service-ports web
заставляет службу web
предоставлять необходимые порты для отладки:
docker-compose run --service-ports web
Если вы запустите docker ps
, вы должны увидеть, что ваша служба web
снова запущена и работает.
Найдите URL-адрес, который остановит выполнение в точке останова. Поскольку я установил точку останова на главной странице, я вижу вывод терминала ниже:
System check identified no issues (0 silenced).
June 06, 2020 - 10:51:15
Django version 3.0.6, using settings 'djangotest.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.
> /code/items/views.py(15)get_context_data()
13 context = super().get_context_data(**kwargs)
14 import ipdb; ipdb.set_trace()
---> 15 return context
ipdb> self.request
<WSGIRequest: GET '/'>
Обратите внимание, что /code/items/views.py
- это расположение модуля на контейнере, а не на вашем локальном dev-боксе.
Это значит... вот оно, вы отлаживаете код, запущенный в вашем сервисе Docker!
Обновление после прочтения "Django для профессионалов"
Чтобы мои знания о Django оставались “актуальными” Читаю книги время от времени. Я узнаю об авторе Уилле Винсенте из подкаста DjangoChat.
Имея опыт работы с Django, я выбрал Django для профессионалов.
Освежает то, что мы начинаем непосредственно с Docker. В первой главе подробно рассказывается о том, что такое Docker. И как сделать так, чтобы ваше приложение Django запускалось на нем с "Hello World" get go.
Я узнал некоторые вещи, которые не знал о Docker, даже после написания этого поста. Например:
ENV PYTHONDONTWRITEBYTECODE 1
предотвращает создание Python файлов pyc
.
Единственное предостережение: я не использую pipenv. Я спросил на Reddit о том, чтобы pipenv обрабатывал только производственные требования. Реакция в то время была неутешительной. Мне нравится иметь более точный контроль над тем, какие пакеты запускаются локально в моей родной среде разработки. И какие пакеты запускать в другом месте, например staging/prod.
Но это изменится. Почему? Со временем я хочу перейти на локальный запуск вещей и в Docker. Сейчас я еще не дошел до этого. YMMV.
Django для профессионалов использует pipenv
. Его функция детерминированной сборки действительно привлекательна:
Преимущество файла блокировки в том, что это приводит к детерминированной сборке: сколько бы раз вы ни устанавливали пакеты программ, результат будет один и тот же. Без файла блокировки, который "фиксирует" зависимости и их порядок, это не обязательно так. Это означает, что у двух членов команды, устанавливающих один и тот же список программных пакетов, сборки могут немного отличаться.
Но это не должно помешать вам перевести это на любой инструмент управления пакетами, который вы используете.
Еще один урок был усвоен, когда автор попросил, чтобы те части ваших файлов, которые часто меняются, были последними:
Таким образом, нам придется регенерировать только эту часть образа при изменении, а не переустанавливать все при каждом изменении
У меня есть одно замечание по поводу пакета psycopg2-binary
по умолчанию. Я хотел бы иметь psycopg2
в производстве. Потому что согласно этому:
Пакет psycopg2-binary предназначен для новичков, чтобы начать работать с Python и PostgreSQL без необходимости соответствовать требованиям сборки.
Если вы являетесь сопровождающим опубликованного пакета, зависящего от psycopg2, вам не следует использовать psycopg2-binary в качестве зависимости от модуля. Для производственного использования вам рекомендуется использовать исходный дистрибутив.
Я не уверен, относится ли это ко всем приложениям или только к издателям пакетов. Я связался с автором по этому поводу. Как только я получу ответ, я сообщу об этом.
Обновление 2020-01-08: Ответ Уилла Винсента по этому поводу:
По поводу
psycopg2-binary
это скорее причуда Pipenv. Это что-то в текучке. Я думаю, что просто pip не использует двоичный формат - это нормально.
Так что, если в вашей ситуации все просто, просто выберите pyscopg2
.
Заключение
Этот учебник призван помочь вам начать и показать основы. Если вы захотите погрузиться глубже, я снабдил вас указателями.
Эта статья не ставит своей целью показать вам все, что можно сделать с помощью Docker. Это далеко не так.
Ландшафт "tech ops" постоянно меняется. И я уверен, что многие команды (или их аргументы) быстро устареют.
Я обратился к этому gist пользователя Github katylava для создания "структуры docker" для этого проекта Django. Это очень помогло.
Вернуться на верх