Django База [2023]: Деплой Django на VPS в Docker контейнере с SSL 💻 #50
В данной статье мы подробно рассмотрим процесс развертывания проекта Django 4.1 на виртуальном сервере VPS с использованием технологий Docker, Certbot для настройки SSL-сертификатов и Gunicorn в качестве веб-сервера.
Если вы хотите выразить благодарность автору сайта, статей и курса по Django, вы можете сделать это по ссылке ниже:
Исходить мы будем из данных прошлого урока по докеризации проекта.
Настройка env.prod
В прошлом уроке мы настроили env.dev, теперь нам необходимо создать env.prod с настоящими данными и выключить дебаг режим Django.
Файл env.prod создаем также в папке docker/env/env.prod, как в прошлом уроке.
SECRET_KEY=<djangokey>
DEBUG=0
ALLOWED_HOSTS=<IPAddressOrDomen>
CSRF_TRUSTED_ORIGINS=<IPAddressOrDomen>
POSTGRES_DB=<dbName>
POSTGRES_USER=<dbUser>
POSTGRES_PASSWORD=<dbPassword>
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
REDIS_LOCATION=redis://redis:6379/1
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0
RECAPTCHA_PUBLIC_KEY=<recaptchaPublicKey>
RECAPTCHA_PRIVATE_KEY=<recaptchaPrivateKey>
EMAIL_HOST=<emalHost>
EMAIL_PORT=<emailPort>
EMAIL_USE_TLS=1
EMAIL_HOST_USER=<email>
EMAIL_HOST_PASSWORD=<emailPassword>
В env.prod мы добавляем домен сайта, если у нас есть и он припаркован к VPS, либо IP адрес VPS сервера, если у вас ещё нет домена и вы тестируете сайт публично. Debug режим Django отключаем, вместо ошибок будет выходить страница 500 - ошибка сервера.
Меняем настройки в settings.py
Нам необходимо изменить настройки, а именно строку environ.Env.read_env(env_file=Path('./docker/env/.env.dev'))
которую мы прописали в прошлом уроке на environ.Env.read_env(env_file=Path('./docker/env/.env.prod'))
.
Дополняем requirements.txt
В прошлом уроке мы создали requirements.txt, с помощью команды: pip freeze > requirements.txt
. Заходим в этот файл и в самый низ добавляем модуль gunicorn, версию можно не указывать, установится самая последняя доступная версия.
amqp==5.1.1
asgiref==3.6.0
async-timeout==4.0.2
billiard==3.6.4.0
celery==5.2.7
click==8.1.3
click-didyoumean==0.3.0
click-plugins==1.1.1
click-repl==0.2.0
colorama==0.4.6
Django==4.1.5
django-ckeditor-5==0.2.4
django-cleanup==7.0.0
django-environ==0.10.0
django-js-asset==2.0.0
django-mptt==0.14.0
django-recaptcha==3.0.0
django-taggit==3.1.0
django_debug_toolbar==3.8.1
kombu==5.2.4
lxml==4.9.2
Pillow==9.4.0
prompt-toolkit==3.0.38
psycopg2==2.9.5
pytils==0.4.1
pytz==2022.7.1
redis==4.5.1
six==1.16.0
sqlparse==0.4.3
tzdata==2022.7
vine==5.0.0
wcwidth==0.2.6
gunicorn
Примечание (необязательно): рекомендую удалить лишнее из зависимостей, чтобы при билдинге не возникло ошибок. Обычно я оставляю список зависимостей в котором библиотеки, установленные мной вручную. Остальные зависимости установятся уже сами, как дополнения к основным указанным зависимостям.
celery==5.2.7
Django==4.1.5
django-ckeditor-5==0.2.4
django-cleanup==7.0.0
django-environ==0.10.0
django-mptt==0.14.0
django-recaptcha==3.0.0
django-taggit==3.1.0
django_debug_toolbar==3.8.1
Pillow==9.4.0
psycopg2==2.9.5
pytils==0.4.1
redis==4.5.1
gunicorn
Создание docker-compose.yml
Далее создадим файл docker-compose.yml в корне проекта, он будет немного отличаться от того docker-compose.dev.yml, который мы делали в прошлом уроке, а именно, мы добавим работу с gunicorn в Django, а также добавим образ Certbot'a для SSL.
version: '3.8'
volumes:
pgdata:
static:
media:
services:
django:
build:
context: .
ports:
- '8000:8000'
container_name: django
env_file:
- docker/env/.env.prod
volumes:
- ./:/app
- static:/app/static
- media:/app/media
depends_on:
- postgres
- redis
command: sh -c "python manage.py collectstatic --no-input &&
python manage.py makemigrations &&
python manage.py migrate &&
gunicorn --workers=4 --reload --max-requests=1000 backend.wsgi -b 0.0.0.0:8000""
nginx:
container_name: nginx
working_dir: /app
image: nginx:stable-alpine
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- static:/app/static
- media:/app/media
- ./docker/nginx/prod/:/etc/nginx/conf.d:ro
- ./docker/certbot/conf:/etc/letsencrypt:ro
- ./docker/certbot/www:/var/www/certbot:ro
links:
- django
depends_on:
- django
postgres:
image: postgres:alpine
container_name: postgres
restart: always
env_file:
- docker/env/.env.prod
volumes:
- pgdata:/var/lib/postgresql/data/
redis:
image: redis:alpine
container_name: redis
env_file:
- docker/env/.env.prod
expose:
- 6379
volumes:
- ./docker/redis/data:/data
celery-worker:
build: .
container_name: celery-worker
restart: always
env_file:
- docker/env/.env.prod
volumes:
- ./:/app
- media:/app/media
command: celery -A backend worker --loglevel=info --logfile=./docker/logs/celery-worker.log
depends_on:
- redis
celery-beat:
build: .
container_name: celery-beat
env_file:
- docker/env/.env.prod
depends_on:
- redis
command: celery -A backend beat --loglevel=info --logfile=./docker/logs/celery-beat.log
volumes:
- media:/app/media
- ./:/app
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./docker/certbot/conf:/etc/letsencrypt:rw
- ./docker/certbot/www:/var/www/certbot:rw
command: certonly --webroot --webroot-path=/var/www/certbot/ --email email@mail.com --agree-tos --no-eff-email -d example.com -d www.example.com
depends_on:
- nginx
В этом файле я добавил образ certbot, добавил порт 443 для работы с SSL, а также поменял в нужных местах env.dev на env.prod, добавил работу gunicorn в Django, и поменял volumes в nginx для работы с SSL и конфигом продакшена.
Также добавил команду, которую необходимо выполнить первый раз при запуске Certbot с Nginx. Команда certonly --webroot --webroot-path=/var/www/certbot/ --email email@mail.com --agree-tos --no-eff-email -d example.com -d www.example.com
, вместо example.com
свой домен или ip адрес, вместо email@mail.com
свой email.
Создаем конфигурации nginx
В папке docker/nginx/prod создаем конфиг django.conf для nginx, который будет использоваться в версии на деплое.
Заведомо до создания конфигурации у нас должен быть сервер, чтобы мы знали IP адрес и домен. Для урока я купил VPS, а домен у меня уже был.
Лично я использую для своих проектов VPS от Timeweb, используя Ubuntu 22.04. И для урока тестовый сервер имеет IP адрес: 81.200.151.210.
Далее пишем конфигурационный файл:
upstream django {
server django:8000;
}
server {
listen 80;
listen [::]:80;
server_name доменное_имя;
server_tokens off;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# server {
# listen 443 default_server ssl http2;
# listen [::]:443 ssl http2;
# server_name доменное_имя;
# server_tokens off;
# ssl_certificate /etc/letsencrypt/live/ваше_доменное_имя/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/ваше_доменное_имя/privkey.pem;
# client_max_body_size 20M;
# charset utf-8;
# gzip on;
# gzip_disable "msie6";
# gzip_min_length 1000;
# gzip_vary on;
# gzip_proxied expired no-cache no-store private auth;
# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
# location / {
# 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://django;
# }
# location /static/ {
# alias /app/static/;
# expires 15d;
# }
# location /media/ {
# alias /app/media/;
# expires 7d;
# }
# if ($http_host !~ "^доменное_имя$"){
# rewrite ^(.*)$ https://доменное_имя$1 redirect;
# }
# }
Со строки 21 до 63 конфигурация на данный момент закомментирована. Это необходимо, чтобы мы могли сначала запустить Nginx и Certbot без конфигурации для сервера Django и порта 443. Как вы знаете, Nginx будет выдавать ошибку без SSL-сертификата. Поэтому мы запускаем Nginx только для получения сертификата в первый раз. Я покажу вам, как это выглядит далее в уроке. Не волнуйтесь, мы снова раскомментируем нашу конфигурацию, когда получим сертификат.
Создаем необходимые папки для Certbot
Далее в папке docker мы создадим папку certbot, а в ней две папки: conf и www. В них после успешной настройки будут храниться сертификаты.
Работа с сервером
Далее нам необходимо подключиться к нашему VPS. На Windows это делается через терминал, я ввожу следующую команду: ssh root@ip_адрес_или_домен
. В моем случае это ssh root@81.200.151.210
. Подключаемся, вводим пароль от сервера.
Устанавливаем Docker и Docker Compose v2. Урок по установке Docker на Ubuntu 22.04, урок по установке Docker Compose v2.
Далее загружаем наш проект на сервер. Знатоки могут воспользоваться git клонированием, ну или традиционным способом копирования папок на сервер. Для экономии времени я закину проект на git и клонирую его на сервер в папку /home/.
Переходим в консоль и в нашу папку с данным проектом: cd ../home/Название_проекта
Работа с Docker
Далее билдим проект на нашем VPS. Вводим следующую команду находясь в папке проекта: docker compose build
. При первом запуске лично у меня возникла ошибка: failed to solve: process "/bin/sh -c apk add --virtual .build-deps gcc python3-dev musl-dev postgresql-dev" did not complete successfully: exit code: 4
. Скорее всего, какая-то проблема внутри сервера при запросе доступа к пакетам. При повторном запуске команды все начало работать.
Получение SSL
Теперь нам необходимо получить SSL, я уже попробовал получить SSL на IP Адрес, к сожалению, Certbot выдает ошибку: Requested name 81.200.151.210 is an IP address. The Let's Encrypt certificate authority will not issue certificates for a bare IP address.
. Поэтому придется использовать домен. В принципе, на деплое без домена не может быть.
Далее запускаем Docker с командой: docker compose up nginx certbot
, подтянутся зависимые другие контейнеры. Ничего. Важно получить подобное сообщение:
Поздравляю, сертификаты успешно получены на доменное имя. Successfully received certificate.
Теперь останавливаем наш контейнер: docker compose stop
.
Закомментируем команду certonly --webroot --webroot-path=/var/www/certbot/ --email email@mail.com --agree-tos --no-eff-email -d example.com -d www.example.com
.
Теперь можно раскомментировать конфигурацию nginx: django.conf
upstream django {
server django:8000;
}
server {
listen 80;
listen [::]:80;
server_name доменное_имя;
server_tokens off;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 default_server ssl http2;
listen [::]:443 ssl http2;
server_name доменное_имя;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/ваше_доменное_имя/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ваше_доменное_имя/privkey.pem;
client_max_body_size 20M;
charset utf-8;
gzip on;
gzip_disable "msie6";
gzip_min_length 1000;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
location / {
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://django;
}
location /static/ {
alias /app/static/;
expires 15d;
}
location /media/ {
alias /app/media/;
expires 7d;
}
if ($http_host !~ "^доменное_имя$"){
rewrite ^(.*)$ https://доменное_имя$1 redirect;
}
}
Эти обновленные файлы мы должны поместить на сервер.
Запускаем Django в Docker
Наконец мы можем запустить наш Django, введя команду: docker compose up
.
Проверяем работу сайта
Перейдя на свое доменное имя, в моем случае это flowmods.net, я могу увидеть свой сайт
Если есть желание, вы можете восстановить данные из дампа, если его сохраняли. Подробнее в этом уроке, пункт: Сохранение данных для переноса БД из SQLITE в PostgreSQL в Django.
Я создам суперпользователя и попробую протестировать celery и добавить статью. Войти в консоль Django в Docker можно с помощью следующей команды: docker exec -it django sh
, а после команда python manage.py createsuperuser
.
Войдем под админом.
Добавлю тестовую статью.
Не забываем изменить example.com на свой домен.
Регистрируем аккаунт.
Получаем письмо подтверждения.
Отлично, надеюсь у Вас всё получилось! И эти знания пошли Вам на пользу!
Все вопросы можете задавать на нашем telegram канале: @ProgHub