我正在使用
Django Rest Framework
创建一个应用程序,并正在努力解决使用 Djoser
发送电子邮件时出现的奇怪问题。当我尝试在生产环境中的 Docker
容器内的主机上重现此错误时,我没有任何错误,所有电子邮件均已成功发送,但是当我尝试在 Docker
下的生产服务器上执行此操作时,我得到 OsError
,这是完整的回溯:
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/contextlib.py", line 81, in inner
return func(*args, **kwds)
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
return view_func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/rest_framework/viewsets.py", line 125, in view
return self.dispatch(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/rest_framework/mixins.py", line 19, in create
self.perform_create(serializer)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/djoser/views.py", line 140, in perform_create
settings.EMAIL.activation(self.request, context).send(to)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/templated_mail/mail.py", line 78, in send
super(BaseEmailMessage, self).send(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/django/core/mail/message.py", line 298, in send
return self.get_connection(fail_silently).send_messages([self])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/django/core/mail/backends/smtp.py", line 127, in send_messages
new_conn_created = self.open()
^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/django/core/mail/backends/smtp.py", line 85, in open
self.connection = self.connection_class(
File "/usr/local/lib/python3.12/smtplib.py", line 255, in __init__
(code, msg) = self.connect(host, port)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/smtplib.py", line 341, in connect
self.sock = self._get_socket(host, port, self.timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/smtplib.py", line 312, in _get_socket
return socket.create_connection((host, port), timeout,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/socket.py", line 852, in create_connection
raise exceptions[0]
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/socket.py", line 837, in create_connection
sock.connect(sa)
^^^^^^^^^^^^^^^^
Exception Type: OSError at /api/auth/users/
Exception Value: [Errno 99] Address not available
Raised during: djoser.views.UserViewSet
Request information:
这是我的
docker-compose.yaml
:
version: '3.8'
services:
# PostgreSQL
db:
image: postgres:12.18-alpine
restart: always
networks:
- backend-network
env_file:
- .env
expose:
- '5432'
volumes:
- db_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 3s
retries: 3
# BackEnd
backend:
build:
context: .
target: prod
networks:
- backend-network
ports:
- '8000:8000'
env_file:
- .env
volumes:
- .:/backend
restart: always
depends_on:
db:
condition: service_healthy
networks:
backend-network:
driver: bridge
volumes:
db_data:
SMTP 服务器设置:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = env("EMAIL_HOST")
EMAIL_USE_TLS = True
EMAIL_PORT = env("EMAIL_PORT")
EMAIL_HOST_USER = env("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD")
我确信我使用的
SMTP
服务器没有问题,因为在我的计算机上它工作正常,电子邮件已发送。此外,当我尝试从生产服务器上的 SMTP
容器 ping Docker
服务器时,我收到了一些从中收到的字节。另外,我认为这个问题与Djoser
无关。
我好几天都无法解决问题,也不知道出了什么问题。 我将非常感谢任何帮助
错误消息
[Errno 99] Address not available
表示在 Docker 容器内运行的 Django 应用程序无法与您配置的 SMTP 服务器建立网络连接。虽然服务器似乎可以从您的开发计算机访问,并且可能可以从容器 ping 通,但可能存在一些配置差异,导致 Docker 内出现问题。
一些潜在的解决方案:
验证 Docker 网络: 确保您的
backend
中的 docker-compose.yaml
服务可以访问 SMTP 服务器所在的网络。默认情况下,Docker 在同一 docker-compose.yaml
文件中为服务创建桥接网络。如果 SMTP 服务器位于单独的网络上,您可能需要配置自定义网络或使用主机名解析技术。
检查 DNS 解析:在 Docker 容器内,尝试使用
ping <SMTP_server_hostname>.
解析 SMTP 服务器的主机名。如果主机名未解析为 IP 地址,则您的容器可能使用与开发计算机不同的 DNS 服务器。您可以尝试在 docker-compose.yaml
中为后端服务指定自定义 DNS 服务器。
验证环境变量:仔细检查
.env
文件是否已正确安装在后端服务的docker-compose.yaml
中。确保环境变量(EMAIL_HOST
、EMAIL_PORT
等)在 .env
文件中正确设置,并且可以在容器内访问。您可以在 Django 应用程序中使用 print(os.environ.get('EMAIL_HOST'))
来验证它们。
手动检查 SMTP 连接: 不要依赖 Djoser 发送电子邮件,而是尝试显式创建电子邮件对象并使用 Django 的
EmailMessage
模块中的 django.core.mail
发送它。这将有助于确定问题是否出在 Djoser 或底层 SMTP 连接上。
网络安全组(如果适用):如果您的生产服务器使用安全组来限制网络流量,请确保 Docker 容器的子网或 SMTP 服务器使用的端口(通常为 TLS 的端口 587)允许出站流量IP地址。