繁体   English   中英

Django ALLOWED_HOSTS 与 ELB HealthCheck

[英]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前面CommonMiddlewaresettings.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 提供的答案

虽然这个答案在大多数情况下都很有效,但它有一些潜在的小问题。

AWS ELB 网络负载均衡器

首先,如果您使用的是 ELB网络负载均衡器,则此方法不适用于其 HTTP 运行状况检查,因为负载均衡在 HTTP 主机标头中发送负载均衡器的 IP 地址。 AWS 文档

健康检查请求中的 HTTP 主机头包含负载均衡器节点的 IP 地址和侦听器端口,而不是目标的 IP 地址和健康检查端口。 如果您按主机标头映射传入请求,则必须确保运行状况检查与任何 HTTP 主机标头匹配。 另一种选择是在不同的端口上添加单独的 HTTP 服务,并将目标组配置为使用该端口进行健康检查。 或者,考虑使用 TCP 运行状况检查。

因此,将您的实例(目标组)IP 添加到您的ALLOWED_HOSTS将不起作用。 如上所述,您可以使用 TCP 运行状况检查,或者您可以使用另一个答案中描述的中间件方法。

元数据端点受到限制

其次,由于元数据端点限制并发连接数并限制请求,在某些情况下您可能会遇到问题。

您的 Django settings.py文件为每个进程执行,并且任何时候进程需要重新启动。 如果您的网络服务器配置为使用多个进程,例如使用gunicorn workers 时,这很重要,因为通常配置为正确利用系统 CPU 资源。

这意味着,如果有足够多的进程,您的settings.py文件将被执行多次,向元数据端点发送许多并发请求,并且您的进程可能无法启动。 此外,在后续进程重新启动时,节流将加剧节流问题。 在某些情况下,这可能会导致您的应用程序停止运行或运行的进程比预期的少。

为了解决这个问题,你可以做一些事情:

  1. 启动服务器之前获取 IP 地址并将 IP 地址设置为环境变量,然后读取环境变量以将其添加到允许的主机中。
$ 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'])

如果许多应用程序或其他服务同时使用实例的元数据,您可能仍会遇到元数据端点的限制问题。

  1. 对于在 ECS 上的容器中运行的服务,您可以使用容器元数据文件

您可以在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)。

安装

  1. pip install django-ebhealthcheck

  2. 将 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM