[英]Django ALLOWED_HOSTS with ELB HealthCheck
我在 Elastic Beanstalk 上部署了一个 django 应用程序。 我的应用程序的 HealthCheck 一直失败,因为 ELB HealthCheck 的 IP 未包含在我的ALLOWED_HOSTS
设置变量中。
如何修改ALLOWED_HOSTS
以使 HealthCheck 通过? 我只会传入显式 IP 地址,但我相信这会发生变化,因此每当 IP 更改时,检查都会再次失败,直到我添加新 IP。
您的 EC2 实例可以查询有关其自身的元数据,包括其 IP 地址,该地址位于: http : //169.254.169.254/latest/meta-data/local-ipv4 。
您可以通过 ssh-ing 到您的 EC2 实例并运行来测试:
curl http://169.254.169.254/latest/meta-data/local-ipv4
因此,在您的配置中,您可以执行以下操作:
import requests
ALLOWED_HOSTS = ['.yourdomain.com', ]
try:
EC2_IP = requests.get('http://169.254.169.254/latest/meta-data/local-ipv4').text
ALLOWED_HOSTS.append(EC2_IP)
except requests.exceptions.RequestException:
pass
显然,如果您不想要依赖requests
,您可以用urllib
替换requests
。
这是使用 Django 中间件的另一个解决方案。
Django 的django.middleware.common.CommonMiddleware
调用request.get_host()
,它使用ALLOWED_HOSTS
验证请求。 如果您只是想检查应用程序是否正在运行,您可以创建这样的中间件。
from django.http import HttpResponse
class HealthCheckMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.path == '/health':
return HttpResponse('ok')
return self.get_response(request)
并把你的HealthCheckMiddleware
前面CommonMiddleware
在settings.py
MIDDLEWARE = [
'yourdjangoapp.middleware.HealthCheckMiddleware',
......
'django.middleware.common.CommonMiddleware',
......
]
只要您的应用程序在运行,无论任何配置如何,您的应用程序都将始终以ok
响应路径/health
。
另一个解决方案没有回答这个问题,因为它没有考虑 AWS 拥有的所有各种工具(ELB 等)。 我们最终做的(因为我们使用nginx
+ uwsgi
)是我们在用户发送请求时将标头设置为有效的内容。
如本页所述:
https://opbeat.com/blog/posts/the-new-django-allowed_hosts-with-elb-uwsgi-and-nginx/
我们把我们的nginx
配置如下:
set $my_host $host;
if ($host ~ "\d+\.\d+\.\d+\.\d+") {
set $my_host "example.com";
}
location / {
uwsgi_pass unix:///tmp/mysite.com.sock;
uwsgi_param HTTP_HOST $my_host;
include uwsgi_params;
}
这里的关键是根据您的ALLOWED_HOSTS
为$my_host
设置一个有效值。
不幸的是,没有不增加开销的“完美”解决方案。 某些配置将要求您始终将 IP 地址添加到ALLOWED_HOSTS
但此解决方案通常会以最少的ALLOWED_HOSTS
工作。
要扩展dfrdmn 提供的答案:
虽然这个答案在大多数情况下都很有效,但它有一些潜在的小问题。
首先,如果您使用的是 ELB网络负载均衡器,则此方法不适用于其 HTTP 运行状况检查,因为负载均衡器在 HTTP 主机标头中发送负载均衡器的 IP 地址。 从AWS 文档:
健康检查请求中的 HTTP 主机头包含负载均衡器节点的 IP 地址和侦听器端口,而不是目标的 IP 地址和健康检查端口。 如果您按主机标头映射传入请求,则必须确保运行状况检查与任何 HTTP 主机标头匹配。 另一种选择是在不同的端口上添加单独的 HTTP 服务,并将目标组配置为使用该端口进行健康检查。 或者,考虑使用 TCP 运行状况检查。
因此,将您的实例(目标组)IP 添加到您的ALLOWED_HOSTS
将不起作用。 如上所述,您可以使用 TCP 运行状况检查,或者您可以使用另一个答案中描述的中间件方法。
其次,由于元数据端点限制并发连接数并限制请求,在某些情况下您可能会遇到问题。
您的 Django settings.py
文件为每个进程执行,并且任何时候进程需要重新启动。 如果您的网络服务器配置为使用多个进程,例如使用gunicorn workers 时,这很重要,因为通常配置为正确利用系统 CPU 资源。
这意味着,如果有足够多的进程,您的settings.py
文件将被执行多次,向元数据端点发送许多并发请求,并且您的进程可能无法启动。 此外,在后续进程重新启动时,节流将加剧节流问题。 在某些情况下,这可能会导致您的应用程序停止运行或运行的进程比预期的少。
为了解决这个问题,你可以做一些事情:
$ export ALLOWED_HOST_EC2_PRIVATE_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
$ gunicorn -w 10 ... myapp:app
# settings.py
ALLOWED_HOSTS = ['myhost.tld', ]
if os.getenv('ALLOWED_HOST_EC2_PRIVATE_IP'):
ALLOWED_HOSTS.append(os.environ['ALLOWED_HOST_EC2_PRIVATE_IP'])
如果许多应用程序或其他服务同时使用实例的元数据,您可能仍会遇到元数据端点的限制问题。
您可以在settings.py
安全地执行此操作,因为访问此文件没有节流或速率限制。 这也避免了您的应用程序可能干扰需要实例元数据端点的其他服务。
# settings.py
import os
import json
ALLOWED_HOSTS = ['myhost.tld', ]
if os.getenv('ECS_CONTAINER_METADATA_FILE'):
metadata_file_path = os.environ['ECS_CONTAINER_METADATA_FILE']
with open(metadata_file_path) as f:
metadata = json.load(f)
private_ip = metadata["HostPrivateIPv4Address"]
ALLOWED_HOSTS.append(private_ip)
您还可以在容器的 ENTRYPOINT 中将第一种方法与元数据文件结合起来。
#!/usr/bin/env bash
# docker-entrypoint.sh
export ALLOWED_HOST_EC2_PRIVATE_IP=$(jq -r .HostPrivateIPv4Address $ECS_CONTAINER_METADATA_FILE)
exec "$@"
FROM myapplication
COPY docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["gunicorn", "whatever"]
对我有用的解决方案是简单地安装django-ebhealthcheck库。 安装后,只需将 ebhealthcheck.apps.EBHealthCheckConfig 添加到您的 INSTALLED_APPS。
来自 django-ebhealthcheck GitHub:
默认情况下,Elastic Beanstalk 的健康检查系统在发出请求时使用每个负载均衡实例的公共 IP 作为请求的主机头。 除非添加到 ALLOWED_HOSTS,否则这会导致 Django 返回 400 Bad Request 和失败的健康检查。
此应用程序将您实例的公共 IP 地址动态添加到 Django 的 ALLOWED_HOSTS 设置中,以允许运行状况检查成功。 这发生在应用程序启动时。
2.0.0 及更高版本支持 IMDSv2。 如果您使用的是 v1 且无法升级,请改用此库的第 1 版(pip install django-ebhealthcheck<2.0.0)。
pip install django-ebhealthcheck
将 ebhealthcheck.apps.EBHealthCheckConfig 添加到您的 INSTALLED_APPS:
INSTALLED_APPS = [
...
'ebhealthcheck.apps.EBHealthCheckConfig',
...
]
我喜欢上面Watt Imsuri的回答,我想我会在我所有的项目中使用这种方法,所以我把它变成了 Django 的 pip 包。
pip install django-easy-health-check
在django.middleware.common.CommonMiddleware
之前将健康检查中间件添加到 Django 设置中:
MIDDLEWARE = [
...,
'easy_health_check.middleware.HealthCheckMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
默认情况下,健康检查 URL 将在“example.com/healthcheck/”中提供。
您还可以通过在项目的 settings.py 中包含以下内容来自定义和覆盖默认设置:
DJANGO_EASY_HEALTH_CHECK = {
"PATH": "/healthcheck/",
"RETURN_STATUS_CODE": 200,
"RETURN_BYTE_DATA": "",
"RETURN_HEADERS": None
}
在生产中,您可能还想设置以下 Django 设置:
ALLOWED_HOSTS = ["example.com"]
SECURE_SSL_REDIRECT = True
SECURE_REDIRECT_EXEMPT = [r'^healthcheck/$']
有关更多信息,请查看git 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.