Развертывание Django на AWS с помощью Docker и Let's Encrypt
В этом руководстве мы развернем приложение Django на AWS EC2 с помощью Docker. Приложение будет работать за HTTPS-прокси Nginx, использующим SSL-сертификаты Let's Encrypt. Мы будем использовать AWS RDS для обслуживания базы данных Postgres, а также AWS ECR для хранения и управления образами Docker.
Django на Docker Series:
Цели
К концу этого учебного курса вы сможете:
- Установите новый экземпляр EC2
- Установите Docker на экземпляр EC2
- Настройка и использование Elastic IP-адреса
- Настройка роли IAM
- Использование реестра образов Amazon Elastic Container Registry (ECR) для хранения построенных образов
- Настройте AWS RDS для сохранения данных
- Настройте группу безопасности AWS Security Group
- Развертывание Django на AWS EC2 с помощью Docker
- Запустите приложение Django за HTTPS-прокси Nginx с SSL-сертификатами Let's Encrypt
Пререквизиты
Этот пост развивает статьи Dockerizing Django with Postgres, Gunicorn, and Nginx и Securing a Containerized Django Application with Let's Encrypt.
Предполагается, что вы можете:
- Контейнеризация приложения Django вместе с Postgres, Nginx и Gunicorn.
- Защита контейнеризированного приложения Django, работающего за HTTPS-прокси Nginx, с помощью SSL-сертификатов Let's Encrypt.
- Используйте SSH для подключения к удаленному серверу и SCP для копирования файлов на сервер.
AWS EC2
Сначала создайте учетную запись AWS, если у вас ее еще нет.
Далее перейдите в консоль EC2 и нажмите Launch instance:
Введите имя экземпляра и выберите Ubuntu Server 22.04 LTS (HVM) в качестве образа сервера (AMI):
Оставайтесь с экземпляром t2.micro:
Создайте новую пару ключей для доступа к вашему экземпляру по SSH, нажав на кнопку "Create new key pair". При настройке группы безопасности выберите "Создать новую группу безопасности" и выберите "Разрешить HTTPS-трафик из Интернета" и "Разрешить HTTP-трафик из Интернета":
Эти правила необходимы для выдачи сертификатов и доступа к приложению.
Пара ключей будет автоматически загружена после создания.
Правила группы безопасности inbound используются для ограничения доступа к вашему экземпляру из Интернета. Если у вас нет дополнительных требований к безопасности, то для экземпляров, на которых размещены веб-приложения, скорее всего, нужно разрешить HTTP- и HTTPS-трафик из любой точки мира. Для подключения к экземпляру для настройки и развертывания необходимо разрешить SSH. В производственных условиях ограничьте количество IP-адресов, с которых разрешен SSH.
Нажмите Launch. На следующем экране нажмите View All Instances.
На раскрутку экземпляра уйдет несколько минут.
Настроить экземпляр EC2
В этом разделе мы установим Docker на экземпляр, добавим Elastic IP и настроим роль IAM.
Установите Docker
Вернитесь в консоль EC2, выберите только что созданный экземпляр и возьмите публичный IP-адрес:
Подключитесь к своему экземпляру EC2, используя ключ .pem
, который мы загрузили на шаге "AWS EC2".
$ ssh -i /path/to/your/djangoletsencrypt.pem ubuntu@public-ip-or-domain-of-ec2-instance
Ваш
.pem
, вероятно, был загружен по пути типа ~/Downloads/your-key-pair-name.pem. Если вы не уверены, где его хранить, переместите его в каталог "~/.ssh". Возможно, придется также изменить права доступа - например,chmod 400 -i /path/to/your/your-key-pair-name.pem
.
Начните с установки последней версии Docker и версии 1.29.2 Docker Compose:
$ sudo apt update
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
$ sudo apt update
$ sudo apt install docker-ce
$ sudo usermod -aG docker ${USER}
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker -v
Docker version 20.10.8, build 3967b7d
$ docker-compose -v
docker-compose version 1.29.2, build 5becea4c
Install AWS CLI
Сначала установите unzip:
$ sudo apt install unzip
Скачать AWS CLI ZIP:
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
Распакуйте его содержимое:
$ unzip awscliv2.zip
Установите AWS CLI:
$ sudo ./aws/install
Проверить установку:
$ aws --version
Elastic IP
По умолчанию экземпляры получают новый публичный IP-адрес при каждом запуске и перезапуске.
Elastic IP позволяет выделять статические IP-адреса для инстансов EC2, в результате чего IP-адрес остается неизменным и может быть переадресован между инстансами. Рекомендуется использовать его для производственных установок.
Перейдите в раздел Elastic IPs и нажмите Allocate Elastic IP address:
Затем нажмите Allocate:
Выделите только что созданный эластичный IP-адрес, нажмите кнопку Actions выпадающего списка -> Associate this Elastic IP address:
Выделите свой экземпляр и нажмите Associate:
IAM Role
В процессе развертывания мы будем извлекать образы Docker из AWS ECR на наш экземпляр EC2. Поскольку мы не будем предоставлять публичный доступ к Docker-образам на ECR, необходимо создать роль IAM с правами на извлечение Docker-образов из ECR и прикрепить ее к экземпляру EC2.
Перейдите в консоль IAM.
Нажмите Roles в левой боковой панели, а затем Create role:
Выберите AWS Service и EC2, затем нажмите Next:
Введите container
в поле поиска, выберите политику AmazonEC2ContainerRegistryPowerUser и нажмите Next:
Нажмите Следующий: Review. Используйте django-ec2
для имени и нажмите Create role:
Теперь необходимо прикрепить новую роль к экземпляру EC2.
Войдя в консоль EC2, нажмите Instances, а затем выберите свой экземпляр. Нажмите на выпадающий список Actions -> Instance settings -> Attach/Replace IAM Role:
Выберите роль django-ec2, а затем нажмите Update IAM role.
Добавить DNS-запись
Добавьте в DNS запись A для используемого домена, указывающую на публичный IP-адрес экземпляра EC2.
Это Elastic IP, который вы привязали к своему экземпляру.
AWS ECR
Amazon Elastic Container Registry (ECR) - это полностью управляемый реестр образов Docker, который упрощает разработчикам хранение и управление образами. Доступ к частным образам осуществляется с помощью пользователей и ролей IAM.
Перейдите на консоль ECR. Нажмите Repositories в боковой панели и затем Create repository:
Задайте имя django-ec2
и нажмите Create repository:
AWS RDS
Теперь мы можем настроить базу данных RDS Postgres.
Хотя вы можете запустить свою собственную базу данных Postgres в контейнере, поскольку базы данных являются критическими сервисами, добавление дополнительных слоев, таких как Docker, добавляет ненужный риск в производстве. Для упрощения таких задач, как обновление мелких версий, регулярное резервное копирование и масштабирование, рекомендуется использовать управляемый сервис. Итак, мы будем использовать RDS.
Перейдите в консоль RDS. Щелкните на Создать базу данных:
Выберите последнюю версию Postgres с шаблоном Free tier:
В разделе Настройки установите:
- Идентификатор экземпляра БД:
djangoec2
- Имя пользователя мастера:
webapp
- Выберите Автогенерация пароля
Оставайтесь с настройками по умолчанию для:
- Размер экземпляра БД
- Хранилище
- Доступность и долговечность
Перейдите к разделу Connectivity и установите следующее:
- Виртуальное частное облако (VPC): Default
- Группа подсетей: по умолчанию
- Публичный доступ: Нет
- Группа безопасности VPC:
django-ec2
- Порт базы данных: 5432
В разделе Мониторинг отключить Мониторинг производительности:
Оставить аутентификацию базы данных в прежнем виде.
Откройте Дополнительная конфигурация и измените Имя начальной базы данных на djangoec2
:
Оставить остальные настройки без изменений.
Наконец, нажмите Создать базу данных.
Нажмите на View credentials details, чтобы увидеть сгенерированный пароль для пользователя webapp:
Храните этот пароль в надежном месте. Вскоре его нужно будет сообщить приложению Django.
На раскрутку экземпляра уйдет несколько минут. После этого щелкните на идентификаторе созданной базы данных, чтобы увидеть ее детали. Обратите внимание на конечную точку базы данных; вам нужно будет задать ее в вашем Django-приложении.
AWS Security Group
В консоли EC2 нажмите Security Groups в боковой панели. Найдите и щелкните на идентификаторе группы django-ec2, чтобы отредактировать ее данные.
Нажмите на Редактировать правила входящих сообщений:
Добавьте входящее правило, которое будет разрешать соединения Postgres с экземплярами, входящими в данную группу безопасности. Для этого необходимо:
- нажмите на Add rule
- выберите PostgreSQL для типа правила
- выберите Custom для источника правила
- щелкните в поле поиска и выберите django-ec2 Security Group
- нажмите на Сохранить правила
Для ограничения доступа к базе данных разрешены соединения только с экземплярами, входящими в одну и ту же группу безопасности. Наше приложение может подключаться, поскольку для экземпляров RDS и EC2 установлена одна и та же группа безопасности django-ec2. Поэтому экземпляры, находящиеся в других группах безопасности, подключаться не могут.
Project Config
Теперь, когда инфраструктура AWS настроена, нам необходимо локально сконфигурировать наш проект Django перед его развертыванием.
Сначала клонируйте содержимое репозитория проекта на GitHub:
$ git clone https://github.com/testdrivenio/django-on-docker-letsencrypt django-on-docker-letsencrypt-aws
$ cd django-on-docker-letsencrypt-aws
Этот репозиторий содержит все необходимое для развертывания Dockerized Django с сертификатами Let's Encrypt HTTPS.
Docker Compose
При первом развертывании приложения необходимо выполнить следующие два шага, чтобы избежать проблем с сертификатами:
- Начните с выпуска сертификатов в среде Let's Encrypt staging
- Затем, когда все будет работать как надо, переключитесь на производственную среду Let's Encrypt
Более подробно об ограничениях Let's Encrypt в производственных средах можно прочитать в разделе Let's Encrypt из предыдущей статьи Securing a Containerized Django Application with Let's Encrypt.
Для тестирования обновите файл docker-compose.staging.yml. следующим образом:
version: '3.8'
services:
web:
build:
context: ./app
dockerfile: Dockerfile.prod
image: <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:web
command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
expose:
- 8000
env_file:
- ./.env.staging
nginx-proxy:
container_name: nginx-proxy
build: nginx
image: <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:nginx-proxy
restart: always
ports:
- 443:443
- 80:80
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- /var/run/docker.sock:/tmp/docker.sock:ro
depends_on:
- web
acme-companion:
image: nginxproxy/acme-companion
env_file:
- ./.env.staging.proxy-companion
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- acme:/etc/acme.sh
depends_on:
- nginx-proxy
volumes:
static_volume:
media_volume:
certs:
html:
vhost:
acme:
Для сервисов web
и nginx-proxy
обновите свойства image
, чтобы использовать изображения из ECR (которые мы добавим в ближайшее время).
Примеры:
image: 123456789.dkr.ecr.us-east-1.amazonaws.com/django-ec2:web
image: 123456789.dkr.ecr.us-east-1.amazonaws.com/django-ec2:nginx-proxy
Значения состоят из URL хранилища (123456789.dkr.ecr.us-east-1.amazonaws.com
), а также имени изображения (django-ec2
) и тегов (web
и nginx-proxy
).
Для простоты мы используем один реестр для хранения обоих образов. Мы использовали символы
web
иnginx-proxy
, чтобы различать их. В идеале следует использовать два реестра: один дляweb
и один дляnginx-proxy
. Обновите это самостоятельно, если хотите.
Кроме свойств image
, мы также удалили службу db
(и связанный с ней том), поскольку мы используем RDS, а не управляем Postgres в контейнере.
Окружающая среда
Пришло время настроить файлы окружения для контейнеров web
и acme-companion
.
Сначала добавьте файл .env.staging для контейнера web
:
DEBUG=0
SECRET_KEY=change_me
DJANGO_ALLOWED_HOSTS=<YOUR_DOMAIN.COM>
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=djangoec2
SQL_USER=webapp
SQL_PASSWORD=<PASSWORD-FROM-AWS-RDS>
SQL_HOST=<DATABASE-ENDPOINT-FROM-AWS-RDS>
SQL_PORT=5432
DATABASE=postgres
VIRTUAL_HOST=<YOUR_DOMAIN.COM>
VIRTUAL_PORT=8000
LETSENCRYPT_HOST=<YOUR_DOMAIN.COM>
CSRF_TRUSTED_ORIGINS=https://<YOUR_DOMAIN.COM>
Примечания:
- Измените
<YOUR_DOMAIN.COM>
на ваш реальный домен. - Измените
SQL_PASSWORD
иSQL_HOST
так, чтобы они соответствовали созданным в разделе RDS. - Измените
SECRET_KEY
на некоторую длинную случайную строку. - Контейнер
VIRTUAL_HOST
иVIRTUAL_PORT
необходимыnginx-proxy
для автоматического создания конфигурации обратного прокси. LETSENCRYPT_HOST
нужен для того, чтобыnginx-proxy-companion
мог выпустить сертификат Let's Encrypt для вашего домена.
Для целей тестирования/отладки вы можете использовать
*
дляDJANGO_ALLOWED_HOSTS
при первом развертывании, чтобы упростить работу. Только не забудьте ограничить количество разрешенных хостов после завершения тестирования.
Во-вторых, добавьте файл .env.staging.proxy-companion, обязательно обновив значение DEFAULT_EMAIL
:
DEFAULT_EMAIL=youremail@yourdomain.com
ACME_CA_URI=https://acme-staging-v02.api.letsencrypt.org/directory
NGINX_PROXY_CONTAINER=nginx-proxy
Просмотрите раздел ACME companion service из статьи Securing a Containerized Django Application with Let's Encrypt, чтобы узнать больше об упомянутых выше переменных окружения.
Build and Push Docker Images
Теперь мы готовы к сборке образов Docker:
$ docker-compose -f docker-compose.staging.yml build
На сборку может потребоваться несколько минут. После этого мы готовы вывести изображения на ECR.
Сначала, предполагая, что у вас установлен awscli и заданы учетные данные AWS, войдите в Docker-репозиторий ECR:
$ aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com
# aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
Вы должны увидеть:
Login Succeeded
Затем переведите изображения в режим ECR:
$ docker-compose -f docker-compose.staging.yml push
Откройте свой репозиторий django-ec2 ECR, чтобы увидеть выложенные изображения:
Запуск контейнеров
Все готово к развертыванию.
Пришло время перейти к вашему экземпляру EC2.
Предполагая, что на вашем экземпляре создан каталог проекта, например /home/ubuntu/django-on-docker, скопируйте в него файлы и папки с помощью SCP:
$ scp -i /path/to/your/djangoletsencrypt.pem \
-r $(pwd)/{app,nginx,.env.staging,.env.staging.proxy-companion,docker-compose.staging.yml} \
ubuntu@public-ip-or-domain-of-ec2-instance:/path/to/django-on-docker
Далее подключитесь к своему экземпляру по SSH и перейдите в каталог проекта:
$ ssh -i /path/to/your/djangoletsencrypt.pem ubuntu@public-ip-or-domain-of-ec2-instance
$ cd /path/to/django-on-docker
Вход в Docker-репозиторий ECR.
$ aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com
Вытащить изображения:
$ docker pull <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:web
$ docker pull <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:nginx-proxy
После этого можно приступать к раскрутке контейнеров:
$ docker-compose -f docker-compose.staging.yml up -d
После того как контейнеры будут запущены, перейдите в браузере на свой домен. Вы должны увидеть что-то вроде:
Это ожидаемо. Это окно отображается потому, что сертификат был выпущен из постановочной среды.
Как узнать, все ли работает?
Нажмите на кнопку "Дополнительно" и затем на кнопку "Продолжить". Теперь вы должны увидеть свое приложение. Загрузите изображение, а затем убедитесь, что его можно просмотреть по адресу https://yourdomain.com/media/IMAGE_FILE_NAME
.
Выдача сертификата на продукцию
Теперь, когда все работает как надо, мы можем переключиться на производственную среду Let's Encrypt.
Свернуть существующие контейнеры и выйти из экземпляра:
$ docker-compose -f docker-compose.staging.yml down -v
$ exit
На локальной машине откройте docker-compose.prod.yml и внесите те же изменения, что и для staging-версии:
- Обновите свойства
ìmage
, чтобы они соответствовали вашим AWS ECR URL для сервисовẁeb
иnginx-proxy
- Удалите службу
db
вместе с соответствующим томом
version: '3.8'
services:
web:
build:
context: ./app
dockerfile: Dockerfile.prod
image: 046505967931.dkr.ecr.us-east-1.amazonaws.com/django-ec2:web
command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
expose:
- 8000
env_file:
- ./.env.prod
nginx-proxy:
container_name: nginx-proxy
build: nginx
image: 046505967931.dkr.ecr.us-east-1.amazonaws.com/django-ec2:nginx-proxy
restart: always
ports:
- 443:443
- 80:80
volumes:
- static_volume:/home/app/web/staticfiles
- media_volume:/home/app/web/mediafiles
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- /var/run/docker.sock:/tmp/docker.sock:ro
depends_on:
- web
acme-companion:
image: nginxproxy/acme-companion
env_file:
- ./.env.prod.proxy-companion
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- certs:/etc/nginx/certs
- html:/usr/share/nginx/html
- vhost:/etc/nginx/vhost.d
- acme:/etc/acme.sh
depends_on:
- nginx-proxy
volumes:
static_volume:
media_volume:
certs:
html:
vhost:
acme:
Далее создайте файл .env.prod, продублировав в нем файл .env.staging. Вносить в него какие-либо изменения не нужно.
Наконец, добавляем файл .env.prod.proxy-companion:
DEFAULT_EMAIL=youremail@yourdomain.com
NGINX_PROXY_CONTAINER=nginx-proxy
Снова постройте и выведите изображения на экран:
$ docker-compose -f docker-compose.prod.yml build
$ aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com
$ docker-compose -f docker-compose.prod.yml push
Скопируйте новые файлы и папки на свой экземпляр с помощью SCP:
$ scp -i /path/to/your/djangoletsencrypt.pem \
$(pwd)/{.env.prod,.env.prod.proxy-companion,docker-compose.prod.yml} \
ubuntu@public-ip-or-domain-of-ec2-instance:/path/to/django-on-docker
Как и прежде, подключитесь к своему экземпляру по SSH и перейдите в каталог проекта:
$ ssh -i /path/to/your/djangoletsencrypt.pem ubuntu@public-ip-or-domain-of-ec2-instance
$ cd /path/to/django-on-docker
Снова войдите в свой Docker-репозиторий ECR:
$ aws ecr get-login-password --region <aws-region> | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com
Вытащить изображения:
$ docker pull <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:web
$ docker pull <aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/django-ec2:nginx-proxy
И, наконец, раскручиваем контейнеры:
$ docker-compose -f docker-compose.prod.yml up -d
Снова перейдите к своему домену. Предупреждение больше не должно появляться.
Поздравляем! Теперь вы используете производственный сертификат Let's Encrypt для своего Django-приложения, работающего на AWS EC2.
Хотите увидеть процесс создания сертификата в действии, посмотрите журналы:
$ docker-compose -f docker-compose.prod.yml logs acme-companion
Заключение
В этом руководстве вы развернули контейнерное приложение Django на EC2. Приложение работает за HTTPS-прокси Nginx с SSL-сертификатами Let's Encrypt. Также использовался экземпляр RDS Postgres, а образы Docker хранились на ECR.
Что дальше?
Вернуться на верх