Подключение 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

RDS Instance

Вернуться на верх