Question:
I am testing out deploying my Django application into AWS’s Fargate Service.
Everything seems to run, but I am getting Health Check errors as the Application Load Balancer is sending requests to my Django application using the Local Ip of the host. This give me an Allowed Host error in the logs.
Invalid HTTP_HOST header: ‘172.31.86.159:8000’. You may need to add ‘172.31.86.159’ to ALLOWED_HOSTS
I have tried getting the Local ip at task start up time and appending it to my ALLOWED_HOSTS, but this fails under Fargate:
1 2 3 4 5 6 7 8 9 10 11 |
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) |
Is there a way to get the ENI IP Address so I can append it to ALLOWED_HOSTS?
Answer:
Now this works, and it lines up with the documentation, but I don’t know if it’s the BEST way or if there is a BETTER WAY.
My containers are running under the awsvpc
network mode.
https://aws.amazon.com/blogs/compute/under-the-hood-task-networking-for-amazon-ecs/
…the ECS agent creates an additional “pause” container for each task before starting the containers in the task definition. It then sets up the network namespace of the pause container by executing the previously mentioned CNI plugins. It also starts the rest of the containers in the task so that they share their network stack of the pause container. (emphasis mine)
I assume the
so that they share their network stack of the pause container
Means we really just need the IPv4 Address of the pause container. In my non-exhaustive testing it appears this is always Container[0]
in the ECS meta: http://169.254.170.2/v2/metadata
With those assumption in play this does work, though I don’t know how wise it is to do:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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) |
Of course, if we pass in the container name that we must set in the ECS task definition, we could do this too:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
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) |
Either of these snippets of code would then in in the production settings for Django.
Is there a better way to do this that I am missing? Again, this is to allow the Application Load Balancer health checks. When using ECS (Fargate) the ALB sends the host header as the Local IP of the container.