【问题标题】:When deploying Django into AWS Fargate how do you add the local ip into ALLOWED_HOSTS将 Django 部署到 AWS Fargate 时,如何将本地 ip 添加到 ALLOWED_HOSTS
【发布时间】:2018-09-24 11:28:49
【问题描述】:

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

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

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?

【问题讨论】:

    标签: django amazon-web-services aws-fargate


    【解决方案1】:

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

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

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

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

    我认为

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

    意味着我们真的只需要暂停容器的 IPv4 地址。在我的非详尽测试中,ECS 元数据中似乎始终是Container[0]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)
    

    这些 sn-ps 代码中的任何一个都将在 Django 的生产设置中。

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

    【讨论】:

    • 我们做了类似的事情,它正在工作,在 entrypoint.sh 添加:export machineIP=$(echo `curl -s http://169.254.170.2/v2/metadata` | python -c "import sys, json; print json.load(sys.stdin)['Containers'][0]['Networks'][0]['IPv4Addresses'][0]") 这会将 ip 放入环境变量中。
    • 我的数据看起来与此不同。我怀疑 ECS 元数据 1.13 可能有所不同。
    • 为什么只使用container_meta['Networks'][0]['IPv4Addresses'][0]中的第一个IP地址而不是像container_meta['Networks'][0]['IPv4Addresses']一样?
    【解决方案2】:

    在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")
    

    【讨论】:

    • 我已更新答案以包含您列出的环境变量。谢谢!
    • 这对我有用,因为响应没有像接受的答案那样的“容器”部分。我不确定这是因为我只运行一个容器(我当然希望格式保持一致!)还是因为我正在运行更新版本的平台(1.3.0)。跨度>
    • 对于平台 1.4.0,变量现在是 ECS_CONTAINER_METADATA_URI_V4。参考:docs.aws.amazon.com/AmazonECS/latest/userguide/…
    猜你喜欢
    • 2018-04-26
    • 2020-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-04
    • 2020-12-28
    • 1970-01-01
    • 2018-06-23
    相关资源
    最近更新 更多