![Django База [2023]: Деплой Django на VPS в Docker контейнере с SSL 💻 #50](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fthumbnails%2F2023%2F04%2F11%2Fimage-a912168029.png&w=3840&q=90)
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.
![Характеристики VPS: 1 CPU / 2 RAM / 15 GB SSD](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F0e5de5a222-uploaded-image.jpg&w=3840&q=75)
Далее пишем конфигурационный файл:
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. В них после успешной настройки будут храниться сертификаты.
![На данный момент структура проекта выглядит следующим образом](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2Fffdb0bfaba-uploaded-image.jpg&w=3840&q=75)
Работа с сервером
Далее нам необходимо подключиться к нашему VPS. На Windows это делается через терминал, я ввожу следующую команду: ssh root@ip_адрес_или_домен
. В моем случае это ssh root@81.200.151.210
. Подключаемся, вводим пароль от сервера.
Устанавливаем Docker и Docker Compose v2. Урок по установке Docker на Ubuntu 22.04, урок по установке Docker Compose v2.
![Установил Docker](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2Fb9105ddb33-uploaded-image.jpg&w=3840&q=75)
![Установил Docker Compose](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F7c194e5a99-uploaded-image.jpg&w=3840&q=75)
Далее загружаем наш проект на сервер. Знатоки могут воспользоваться git клонированием, ну или традиционным способом копирования папок на сервер. Для экономии времени я закину проект на git и клонирую его на сервер в папку /home/.
![Должно получиться вот так. Я скопировал проект на сервер с помощью git](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F1e26e42ead-uploaded-image.jpg&w=3840&q=75)
Переходим в консоль и в нашу папку с данным проектом: 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
. Скорее всего, какая-то проблема внутри сервера при запросе доступа к пакетам. При повторном запуске команды все начало работать.
![Сбилженный проект Django](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F502c75cc50-uploaded-image.jpg&w=3840&q=75)
Получение 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
, подтянутся зависимые другие контейнеры. Ничего. Важно получить подобное сообщение:
![Сообщения от Nginx и Certbot](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2Ffbf4653d02-uploaded-image.jpg&w=3840&q=75)
![Сообщения от Nginx и Certbot](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2Ffb47fb1ec4-uploaded-image.jpg&w=3840&q=75)
Поздравляю, сертификаты успешно получены на доменное имя. Successfully received certificate.
![Сохраненные данные сертификата](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2Fea15eda0a0-uploaded-image.jpg&w=3840&q=75)
Теперь останавливаем наш контейнер: docker compose stop
.
![Останавливаем контейнеры](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F89ab62e9f0-uploaded-image.jpg&w=3840&q=75)
Закомментируем команду certonly --webroot --webroot-path=/var/www/certbot/ --email email@mail.com --agree-tos --no-eff-email -d example.com -d www.example.com
.
![Закомментируем, чтобы команда не запускалась постоянно](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F2aa56ca16a-uploaded-image.jpg&w=3840&q=75)
Теперь можно раскомментировать конфигурацию 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
.
![Запуск контейнеров](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F28df9fef90-uploaded-image.jpg&w=3840&q=75)
![Запуск контейнеров](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2Fe612db2157-uploaded-image.jpg&w=3840&q=75)
![Запуск контейнеров](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F035f20a27a-uploaded-image.jpg&w=3840&q=75)
Проверяем работу сайта
Перейдя на свое доменное имя, в моем случае это flowmods.net, я могу увидеть свой сайт
![Рабочий SSL и наш рабочий Django проект](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F9a4e4c0568-uploaded-image.jpg&w=3840&q=75)
Если есть желание, вы можете восстановить данные из дампа, если его сохраняли. Подробнее в этом уроке, пункт: Сохранение данных для переноса БД из SQLITE в PostgreSQL в Django.
Я создам суперпользователя и попробую протестировать celery и добавить статью. Войти в консоль Django в Docker можно с помощью следующей команды: docker exec -it django sh
, а после команда python manage.py createsuperuser
.
![Процесс создания](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2Fa40b8e02a6-uploaded-image.jpg&w=3840&q=75)
Войдем под админом.
![Админка](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F5173f79571-uploaded-image.jpg&w=3840&q=75)
Добавлю тестовую статью.
![Тестовая статья](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2Fc2feabf45e-uploaded-image.jpg&w=3840&q=75)
Не забываем изменить example.com на свой домен.
![Меняем example.com на свой домен](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F837e8507c9-uploaded-image.jpg&w=3840&q=75)
Регистрируем аккаунт.
![Регистрация аккаунта](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F2700f9c52b-uploaded-image.jpg&w=3840&q=75)
Получаем письмо подтверждения.
![Письмо подтверждения](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F40978aac96-uploaded-image.jpg&w=3840&q=75)
![Подтверждаем аккаунта](/_next/image?url=https%3A%2F%2Fproghunter.ru%2Fmedia%2Fimages%2Fuploads%2F2023%2F04%2F11%2F2d086e8afc-uploaded-image.jpg&w=3840&q=75)
Отлично, надеюсь у Вас всё получилось! И эти знания пошли Вам на пользу!
Все вопросы можете задавать на нашем telegram канале: @ProgHub