13

我正在测试将我的 Django 应用程序部署到 AWS 的 Fargate 服务中。

一切似乎都在运行,但是由于 Application Load Balancer 正在使用主机的本地 IP 向我的 Django 应用程序发送请求,因此我收到了运行状况检查错误。这在日志中给了我一个允许的主机错误。

无效的 HTTP_HOST 标头:“172.31.86.159:8000”。您可能需要将“172.31.86.159”添加到 ALLOWED_HOSTS

我尝试在任务启动时获取本地 ip 并将其附加到我的 ALLOWED_HOSTS,但这在 Fargate 下失败:

import requests

EC2_PRIVATE_IP  =   None
try:
    EC2_PRIVATE_IP  =   requests.get('http://169.254.169.254/latest/meta-data/local-ipv4', timeout = 0.01).text
except requests.exceptions.RequestException:
    pass

if EC2_PRIVATE_IP:
    ALLOWED_HOSTS.append(EC2_PRIVATE_IP)

有没有办法获取 ENI IP 地址,以便我可以将其附加到 ALLOWED_HOSTS?

4

2 回答 2

11

现在这可行,并且与文档一致,但我不知道这是否是最好的方法,或者是否有更好的方法。

我的容器在awsvpc网络模式下运行。

https://aws.amazon.com/blogs/compute/under-the-hood-task-networking-for-amazon-ecs/

...ECS 代理在启动任务定义中的容器之前为每个任务创建一个额外的“暂停”容器。然后它通过执行前面提到的 CNI 插件来设置暂停容器的网络命名空间。它还启动任务中的其余容器,以便它们共享暂停容器的网络堆栈。(强调我的)

我假设

以便他们共享暂停容器的网络堆栈

意味着我们真的只需要暂停容器的 IPv4 地址。在我的非详尽测试中,这似乎总是Container[0]在 ECS 元中:http://169.254.170.2/v2/metadata

有了这些假设,这确实有效,尽管我不知道这样做有多明智:

import requests

EC2_PRIVATE_IP = None
METADATA_URI = os.environ.get('ECS_CONTAINER_METADATA_URI', 'http://169.254.170.2/v2/metadata')

try:
    resp = requests.get(METADATA_URI)
    data = resp.json()
    # print(data)

    container_meta = data['Containers'][0]
    EC2_PRIVATE_IP = container_meta['Networks'][0]['IPv4Addresses'][0]
except:
    # silently fail as we may not be in an ECS environment
    pass

if EC2_PRIVATE_IP:
    # Be sure your ALLOWED_HOSTS is a list NOT a tuple
    # or .append() will fail
    ALLOWED_HOSTS.append(EC2_PRIVATE_IP)

当然,如果我们传入我们必须在 ECS 任务定义中设置的容器名称,我们也可以这样做:

import os
import requests

EC2_PRIVATE_IP = None
METADATA_URI = os.environ.get('ECS_CONTAINER_METADATA_URI', 'http://169.254.170.2/v2/metadata')

try:
    resp = requests.get(METADATA_URI)
    data = resp.json()
    # print(data)

    container_name = os.environ.get('DOCKER_CONTAINER_NAME', None)
    search_results = [x for x in data['Containers'] if x['Name'] == container_name]

    if len(search_results) > 0:
        container_meta = search_results[0]
    else:
        # Fall back to the pause container
        container_meta = data['Containers'][0]

    EC2_PRIVATE_IP = container_meta['Networks'][0]['IPv4Addresses'][0]
except:
    # silently fail as we may not be in an ECS environment
    pass

if EC2_PRIVATE_IP:
    # Be sure your ALLOWED_HOSTS is a list NOT a tuple
    # or .append() will fail
    ALLOWED_HOSTS.append(EC2_PRIVATE_IP)

然后,这些代码片段中的任何一个都将出现在 Django 的生产设置中。

有没有更好的方法来做到这一点,我错过了?同样,这是为了允许 Application Load Balancer 运行状况检查。使用 ECS (Fargate) 时,ALB 将主机标头作为容器的本地 IP 发送。

于 2018-04-14T13:33:52.793 回答
8

在fargate中,有一个AWS容器代理注入的环境变量:${ECS_CONTAINER_METADATA_URI}

这包含元数据端点的 URL,所以现在你可以做

curl ${ECS_CONTAINER_METADATA_URI}

输出看起来像

{  
   "DockerId":"redact",
   "Name":"redact",
   "DockerName":"ecs-redact",
   "Image":"redact",
   "ImageID":"redact",
   "Labels":{  },
   "DesiredStatus":"RUNNING",
   "KnownStatus":"RUNNING",
   "Limits":{  },
   "CreatedAt":"2019-04-16T22:39:57.040286277Z",
   "StartedAt":"2019-04-16T22:39:57.29386087Z",
   "Type":"NORMAL",
   "Networks":[  
      {  
         "NetworkMode":"awsvpc",
         "IPv4Addresses":[  
            "172.30.1.115"
         ]
      }
   ]
}

Networks你会发现的钥匙下IPv4Address

把它放到python中,你得到

METADATA_URI = os.environ['ECS_CONTAINER_METADATA_URI']
container_metadata = requests.get(METADATA_URI).json()
ALLOWED_HOSTS.append(container_metadata['Networks'][0]['IPv4Addresses'][0])

ALLOWED_HOSTS对此的另一种解决方案是创建一个中间件,仅针对您的运行状况检查端点绕过检查,例如

from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

class HealthEndpointMiddleware(MiddlewareMixin):
    def process_request(self, request):
        if request.META["PATH_INFO"] == "/health/":
            return HttpResponse("OK")
于 2019-10-28T17:02:07.697 回答