[英]Django ALLOWED_HOSTS with ELB HealthCheck
I have a django application deployed on Elastic Beanstalk.我在 Elastic Beanstalk 上部署了一个 django 应用程序。 The HealthCheck for my app keeps failing because the IP of the ELB HealthCheck is not included in my
ALLOWED_HOSTS
settings variable.我的应用程序的 HealthCheck 一直失败,因为 ELB HealthCheck 的 IP 未包含在我的
ALLOWED_HOSTS
设置变量中。
How can I modify ALLOWED_HOSTS
to make the HealthCheck pass?如何修改
ALLOWED_HOSTS
以使 HealthCheck 通过? I would just pass in the explicit IP address, but I believe that this changes, so whenever the IP changes the check would fail again until I add the new IP.我只会传入显式 IP 地址,但我相信这会发生变化,因此每当 IP 更改时,检查都会再次失败,直到我添加新 IP。
Your EC2 instance can query metadata about itself, including its IP address which is available at: http://169.254.169.254/latest/meta-data/local-ipv4 .您的 EC2 实例可以查询有关其自身的元数据,包括其 IP 地址,该地址位于: http : //169.254.169.254/latest/meta-data/local-ipv4 。
You can test this by ssh-ing into your EC2 instance and running:您可以通过 ssh-ing 到您的 EC2 实例并运行来测试:
curl http://169.254.169.254/latest/meta-data/local-ipv4
So, in your configuration, you can do something like:因此,在您的配置中,您可以执行以下操作:
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
Obviously you can replace requests
with urllib
if you don't want the dependency.显然,如果您不想要依赖
requests
,您可以用urllib
替换requests
。
Here is another solution using Django Middleware.这是使用 Django 中间件的另一个解决方案。
Django's django.middleware.common.CommonMiddleware
calls request.get_host()
, which validates the request with ALLOWED_HOSTS
. Django 的
django.middleware.common.CommonMiddleware
调用request.get_host()
,它使用ALLOWED_HOSTS
验证请求。 If you simply want to check that the application is running, you can create a middleware like this.如果您只是想检查应用程序是否正在运行,您可以创建这样的中间件。
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)
And put your HealthCheckMiddleware
in front of CommonMiddleware
in settings.py
并把你的
HealthCheckMiddleware
前面CommonMiddleware
在settings.py
MIDDLEWARE = [
'yourdjangoapp.middleware.HealthCheckMiddleware',
......
'django.middleware.common.CommonMiddleware',
......
]
And your application will always respond to path /health
with ok
as long as your app is running regardless of any configurations.只要您的应用程序在运行,无论任何配置如何,您的应用程序都将始终以
ok
响应路径/health
。
The other solution doesn't answer the question because it doesn't take into consideration all the various tools AWS has (ELB, etc).另一个解决方案没有回答这个问题,因为它没有考虑 AWS 拥有的所有各种工具(ELB 等)。 What we ended up doing (since we use
nginx
+ uwsgi
) is we set the header to something valid when the user sends a request.我们最终做的(因为我们使用
nginx
+ uwsgi
)是我们在用户发送请求时将标头设置为有效的内容。
As documented on this page:如本页所述:
https://opbeat.com/blog/posts/the-new-django-allowed_hosts-with-elb-uwsgi-and-nginx/ https://opbeat.com/blog/posts/the-new-django-allowed_hosts-with-elb-uwsgi-and-nginx/
We put our nginx
configuration as below:我们把我们的
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;
}
The key here is to put a valid value for $my_host
as per your ALLOWED_HOSTS
.这里的关键是根据您的
ALLOWED_HOSTS
为$my_host
设置一个有效值。
Unfortunately there is no "perfect" solution without increasing overhead.不幸的是,没有不增加开销的“完美”解决方案。 Some configurations will require you to add IP addresses all the time to the
ALLOWED_HOSTS
but this solution would generally work with the least over head.某些配置将要求您始终将 IP 地址添加到
ALLOWED_HOSTS
但此解决方案通常会以最少的ALLOWED_HOSTS
工作。
To expand on the answer provided by dfrdmn :要扩展dfrdmn 提供的答案:
While this answer works well in most cases, it has a couple small potential problems.虽然这个答案在大多数情况下都很有效,但它有一些潜在的小问题。
First, if you are using an ELB network load balancer, this method won't work with its HTTP health checks because the load balancer sends the IP address of the load balancer in the HTTP host header.首先,如果您使用的是 ELB网络负载均衡器,则此方法不适用于其 HTTP 运行状况检查,因为负载均衡器在 HTTP 主机标头中发送负载均衡器的 IP 地址。 From the AWS docs :
从AWS 文档:
The HTTP host header in the health check request contains the IP address of the load balancer node and the listener port, not the IP address of the target and the health check port.
健康检查请求中的 HTTP 主机头包含负载均衡器节点的 IP 地址和侦听器端口,而不是目标的 IP 地址和健康检查端口。 If you are mapping incoming requests by host header, you must ensure that health checks match any HTTP host header.
如果您按主机标头映射传入请求,则必须确保运行状况检查与任何 HTTP 主机标头匹配。 Another option is to add a separate HTTP service on a different port and configure the target group to use that port for health checks instead.
另一种选择是在不同的端口上添加单独的 HTTP 服务,并将目标组配置为使用该端口进行健康检查。 Alternatively, consider using TCP health checks.
或者,考虑使用 TCP 运行状况检查。
So, adding your instance (target group) IP to your ALLOWED_HOSTS
will not work.因此,将您的实例(目标组)IP 添加到您的
ALLOWED_HOSTS
将不起作用。 As stated, you could use TCP health checks, or you could use the middleware approach described in another answer.如上所述,您可以使用 TCP 运行状况检查,或者您可以使用另一个答案中描述的中间件方法。
Second, because the metadata endpoint limits number of concurrent connections and throttles requests, you may encounter issues in some cases.其次,由于元数据端点限制并发连接数并限制请求,在某些情况下您可能会遇到问题。
Your Django settings.py
file is executed for every process and any time processes need to restart.您的 Django
settings.py
文件为每个进程执行,并且任何时候进程需要重新启动。 This is important if your webserver is configured to use multiple processes, such as when using gunicorn workers , as is commonly configured to properly take full advantage of system CPU resources.如果您的网络服务器配置为使用多个进程,例如使用gunicorn workers 时,这很重要,因为通常配置为正确利用系统 CPU 资源。
This means that, with enough processes, your settings.py
file will be executed many times, sending many concurrent requests to the metadata endpoint and your processes could fail to start.这意味着,如果有足够多的进程,您的
settings.py
文件将被执行多次,向元数据端点发送许多并发请求,并且您的进程可能无法启动。 Further, on subsequent process restarts, the throttling will exacerbate the throttling problem.此外,在后续进程重新启动时,节流将加剧节流问题。 In some circumstances, this can cause your application to grind to a halt or have fewer processes running than intended.
在某些情况下,这可能会导致您的应用程序停止运行或运行的进程比预期的少。
To get around this, you could do a few things:为了解决这个问题,你可以做一些事情:
$ 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'])
You may yet still encounter throttling issues with the metadata endpoint if many applications or other services utilize the instance's metadata at the same time.如果许多应用程序或其他服务同时使用实例的元数据,您可能仍会遇到元数据端点的限制问题。
You can do this safely within the settings.py
because there is no throttling or rate limit for accessing this file.您可以在
settings.py
安全地执行此操作,因为访问此文件没有节流或速率限制。 This also avoids your application potentially interfering with other services that need the instance's metadata endpoint.这也避免了您的应用程序可能干扰需要实例元数据端点的其他服务。
# 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)
You could also combine the first approach with the metadata file, in your container's ENTRYPOINT.您还可以在容器的 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"]
The solution that worked for me was to simply install the django-ebhealthcheck library.对我有用的解决方案是简单地安装django-ebhealthcheck库。 After installing it, just add ebhealthcheck.apps.EBHealthCheckConfig to your INSTALLED_APPS.
安装后,只需将 ebhealthcheck.apps.EBHealthCheckConfig 添加到您的 INSTALLED_APPS。
From django-ebhealthcheck GitHub:来自 django-ebhealthcheck GitHub:
By default, Elastic Beanstalk's health check system uses the public IP of each load balanced instance as the request's host header when making a request.
默认情况下,Elastic Beanstalk 的健康检查系统在发出请求时使用每个负载均衡实例的公共 IP 作为请求的主机头。 Unless added to ALLOWED_HOSTS, this causes Django to return a 400 Bad Request and a failed health check.
除非添加到 ALLOWED_HOSTS,否则这会导致 Django 返回 400 Bad Request 和失败的健康检查。
This app dynamically adds your instance's public IP address to Django's ALLOWED_HOSTS setting to permit health checks to succeed.
此应用程序将您实例的公共 IP 地址动态添加到 Django 的 ALLOWED_HOSTS 设置中,以允许运行状况检查成功。 This happens upon application start.
这发生在应用程序启动时。
Version 2.0.0 and higher supports IMDSv2.
2.0.0 及更高版本支持 IMDSv2。 If you are using v1 and cannot upgrade, use version 1 of this library instead (pip install django-ebhealthcheck<2.0.0).
如果您使用的是 v1 且无法升级,请改用此库的第 1 版(pip install django-ebhealthcheck<2.0.0)。
pip install django-ebhealthcheck
Add ebhealthcheck.apps.EBHealthCheckConfig to your INSTALLED_APPS:将 ebhealthcheck.apps.EBHealthCheckConfig 添加到您的 INSTALLED_APPS:
INSTALLED_APPS = [
...
'ebhealthcheck.apps.EBHealthCheckConfig',
...
]
I like Watt Imsuri 's answer above, and I think I'm going to use this approach in all my projects so I turned it into a pip package for Django.我喜欢上面Watt Imsuri的回答,我想我会在我所有的项目中使用这种方法,所以我把它变成了 Django 的 pip 包。
pip install django-easy-health-check
Add the health check middleware to Django settings before django.middleware.common.CommonMiddleware
:在
django.middleware.common.CommonMiddleware
之前将健康检查中间件添加到 Django 设置中:
MIDDLEWARE = [
...,
'easy_health_check.middleware.HealthCheckMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
By default, the health check url will be available at "example.com/healthcheck/".默认情况下,健康检查 URL 将在“example.com/healthcheck/”中提供。
You can also customize and overwrite the default settings by including the following in your project's settings.py:您还可以通过在项目的 settings.py 中包含以下内容来自定义和覆盖默认设置:
DJANGO_EASY_HEALTH_CHECK = {
"PATH": "/healthcheck/",
"RETURN_STATUS_CODE": 200,
"RETURN_BYTE_DATA": "",
"RETURN_HEADERS": None
}
In production, you may also want to set the following Django settings:在生产中,您可能还想设置以下 Django 设置:
ALLOWED_HOSTS = ["example.com"]
SECURE_SSL_REDIRECT = True
SECURE_REDIRECT_EXEMPT = [r'^healthcheck/$']
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.