Подключение Django на Ubuntu EC2 к AWS RDS MySQL: pymysql пытается подключиться к localhost вместо иностранного сервера
Я нахожусь в процессе развертывания проекта Django на Ubuntu EC2. Он должен подключаться к серверу MySQL на AWS RDS. Проект отлично работает на встроенном сервере разработки Django, который запускается с помощью runserver
, и он также правильно подключается к экземпляру RDS. Однако, если я пытаюсь запустить его в продакшене, он бросает 500 Internal Server Error
, что создает следующий вывод в журнале ошибок:
mod_wsgi (pid=18656): Exception occurred processing WSGI script '/home/ubuntu/mysite/mysite/wsgi.py'.
Traceback (most recent call last):
File "/home/ubuntu/mysite/mysite_venv/lib/python3.8/site-packages/pymysql/connections.py", line 613, in connect
sock = socket.create_connection(
File "/usr/lib/python3.8/socket.py", line 808, in create_connection
raise err
File "/usr/lib/python3.8/socket.py", line 796, in create_connection
sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused
During handling of the above exception, another exception occurred:
...
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
Эта ошибка сокета возникает как в моих пользовательских представлениях, так и в представлении mydomain.com/admin. Соответствующий фрагмент views.py
выглядит следующим образом:
# get environment variables
db_host = os.environ.get('DB_HOST')
db_user = os.environ.get('DB_USER')
db_pass = os.environ.get('DB_PASS')
# connect to db
conn = pymysql.connect(host=db_host,
user=db_user,
password=db_pass)
c = conn.cursor()
c.execute('''USE mysitedatabase''')
Файл в целом выполняется правильно, операторы журнала, которые я вставил в качестве теста перед подключением к базе данных, создаются без ошибок.
Я уже пробовал удалить все правила входящего трафика из моей группы безопасности RDS, установив ее на весь трафик IPv4 с помощью 0.0.0.0/0
, но ошибка сохраняется.
Я предполагаю, что ошибка сокета и ошибка localhost связаны, поскольку я не думаю, что имеет смысл подключаться через unix-сокет, не находясь на localhost. Как мне остановить pymysql от попыток подключения на localhost, когда он должен подключиться к RDS?
Я использую Ubuntu 20.04, Django 4.0, Apache/2.4.41 (Ubuntu) и Python 3.8.
Чтобы дать больше контекста, вот мой settings.py
:
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = "x"
DEBUG = False
ALLOWED_HOSTS = ["ec2-53653-335-compute-1.amazonaws.com", "mydomain.com", "22.22.222.222" (IP of server, redacted)]
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mysite.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'mysite.wsgi.application'
...
Вот мой /etc/apache2/sites-enabled/000-default.conf
:
<VirtualHost *:80>
Servername mydomain.com
WSGIScriptAlias / /home/ubuntu/mysite/mysite/wsgi.py
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
WSGIDaemonProcess mysite python-path=/home/ubuntu/mysite python-home=/home/ubuntu/mysite/mysite_venv
WSGIProcessGroup mysite
ErrorLog /home/ubuntu/mysite/mysite/error.log
CustomLog /home/ubuntu/mysite/mysite/access.log combined
<Directory /home/ubuntu/mysite/mysite>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
</VirtualHost>
А вот мой /etc/apache2/apache2.conf
:
DefaultRuntimeDir ${APACHE_RUN_DIR}
PidFile ${APACHE_PID_FILE}
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
HostnameLookups Off
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
Include ports.conf
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
<Directory /usr/share>
AllowOverride None
Require all granted
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<Directory /home/ubuntu/mysite>
AllowOverride None
Require all granted
</Directory>
<Directory /home/ubuntu/mysite/polls>
AllowOverride None
Require all granted
</Directory>
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
AccessFileName .htaccess
<FilesMatch "^\.ht">
Require all denied
</FilesMatch>
Переменные окружения не были настроены в продакшене, спасибо @Mark B за указание правильного направления. Есть несколько способов решения этой проблемы. Я решил пойти простым путем и добавить учетные данные базы данных в settings.py
следующим образом:
DB_HOST = 'your_rds_endpoint'
DB_PASS = 'your_secure_password'
DB_USER = 'your_username'
Тогда просто импортируйте settings.py
в views.py
и получите доступ к нужной вам переменной. В views.py
:
from django.conf import settings as my_settings
db_host = my_settings.DB_HOST
db_user = my_settings.DB_USER
db_pass = my_settings.DB_PASS
Однако, это не лучший способ сделать это, потому что, насколько я смог найти, вы должны ссылаться на файл настроек как можно реже из-за соображений безопасности.
Лучшими альтернативами для решения этой проблемы, вероятно, являются django-environ (который, вопреки интуиции, работает и вне Django) и python-decouple.
Вероятно, вы столкнулись с тем, что у RDS есть адрес хоста, который вам нужно указать на изображении.
Возможная причина того, что он работает в вашей среде разработки (предполагая, что она локальная), заключается в том, что вы установили
db_host = localhost
попробуйте установить
DB_HOST = rds_address
DB_USER = rds_access_username
DB_PASS = rds_access_password