Question:
I am currently deploying a Django + uWSGI app using a single container docker environment in AWS ElasticBeanstalk. This environment already ships with nginx, which I am currently trying to configure.
I am trying to achieve the following:
- Terminate HTTPS at the environment’s load balancer
- Redirect HTTP requests to HTTPS using nginx (which is shipped with the environment)
- Pass requests from nginx to uwsgi
Environment information:
- Configuration and Solution Stack Name: Single Container Docker 1.11
version 2.3.0 - AMI: 64bit Amazon Linux 2016.09 v2.3.0 running Docker
1.11.2
2016.09.0 - Docker Version: 1.11.2
- Proxy Server: nginx 1.10.1
This is my current configuration:
.ebxtensions/00-loadbalancer-terminatehttps.config
1 2 3 4 5 6 7 8 9 10 11 12 13 |
option_settings: aws:elb:listener:443: ListenerEnabled: true ListenerProtocol: HTTPS SSLCertificateId: InstancePort: 443 InstanceProtocol: HTTP aws:elb:listener:80: ListenerEnabled: true ListenerProtocol: HTTP InstancePort: 80 InstanceProtocol: HTTP |
.ebextensions/01-nginx-proxy.config
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
files: "/etc/nginx/sites-available/test.domain.com.conf": mode: "000644" owner: root group: root content: | server { listen 80; server_name test.domain.com; access_log /var/log/nginx/$server_name.access.log; location / { return 301 https://$server_name$request_uri; } location = /status/ { access_log /var/log/nginx/$server_name.healthd.log healthd; include uwsgi_params; uwsgi_pass docker; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; } } server { listen 443; server_name test.domain.com; access_log /var/log/nginx/$server_name.access.log; location / { include uwsgi_params; uwsgi_pass docker; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; client_max_body_size 100m; } location /static { alias /var/www/static; } } commands: 00_enable_site: command: 'rm -f /etc/nginx/sites-enabled/* && ln -s /etc/nginx/sites-available/test.domain.com.conf /etc/nginx/sites-enabled/test.domain.com.conf' |
.ebextensions/02-healthcheckurl.config
1 2 3 4 5 |
option_settings: - namespace: aws:elasticbeanstalk:application option_name: Application Healthcheck URL value: /status/ |
application.ini (uwsgi config)
1 2 3 4 5 6 7 8 9 10 11 12 |
[uwsgi] master = true socket = :3031 processes = 4 enable-threads = true threads = 2 chdir = /opt/app/ wsgi-file = test/wsgi.py logto2 = /var/log/uwsgi.log callable = application py-autoreload = 3 |
Now, when testing the configuration:
Checking http://test.domain.com/status/ works fine
1 2 3 4 5 6 |
$ wget http://test.domain.com/status/ --2017-01-14 23:00:18-- http://test.domain.com/status/ Resolving test.domain.com... 52.xx.xx.xx, 52.xx.xx.xy Connecting to test.domain.com|52.xx.xx.xx|:80... connected. HTTP request sent, awaiting response... 200 OK |
Checking http://test.domain.com/hello/ doesn’t work as expected. It redirects fine, but it then hangs until the request times out.
1 2 3 4 5 6 7 8 9 10 11 |
$ wget http://test.domain.com/hello/ --2017-01-14 22:59:13-- http://test.domain.com/hello/ Resolving test.domain.com... 52.xx.xx.xx, 52.xx.xx.xy Connecting to test.domain.com|52.xx.xx.xx|:80... connected. HTTP request sent, awaiting response... 301 Moved Permanently Location: https://test.domain.com/hello/ [following] --2017-01-14 22:59:15-- https://test.domain.com/hello/ Connecting to test.domain.com|52.xx.xx.xx|:443... connected. HTTP request sent, awaiting response... 408 REQUEST_TIMEOUT 2017-01-14 23:00:17 ERROR 408: REQUEST_TIMEOUT. |
Answer:
Following @deviavir’s suggestion, I needed to allow traffic from the load balancer into the EC2 instance.
This is my final configuration:
1 2 3 4 |
.ebextensions |-- 00-resources.config |-- 01-nginx-proxy.config |
.ebextensions/00-resources.config:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
Resources: AWSEBSecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: GroupDescription: "Allow traffic to ports 80 and 443 from the load balancer. Restrict SSH access." AWSEBLoadBalancer: Type: "AWS::ElasticLoadBalancing::LoadBalancer" Properties: Listeners: - {LoadBalancerPort: 80, Protocol: 'HTTP', InstancePort: 80, InstanceProtocol: 'HTTP'} - {LoadBalancerPort: 443, Protocol: 'HTTPS', InstancePort: 443, InstanceProtocol: 'HTTP', SSLCertificateId: 'arn:aws:acm:us-east-1:xxxx:certificate/yyyy'} HealthCheck: Target: HTTP:80/status/ HealthyThreshold: '3' UnhealthyThreshold: '5' Interval: '30' Timeout: '5' Port80SecurityGroupIngress: Type: "AWS::EC2::SecurityGroupIngress" Properties: GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]} IpProtocol: tcp ToPort: 80 FromPort: 80 SourceSecurityGroupName: {"Fn::GetAtt" : ["AWSEBLoadBalancer" , "SourceSecurityGroup.GroupName"]} Port443SecurityGroupIngress: Type: "AWS::EC2::SecurityGroupIngress" Properties: GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]} IpProtocol: tcp ToPort: 443 FromPort: 443 SourceSecurityGroupName: {"Fn::GetAtt" : ["AWSEBLoadBalancer" , "SourceSecurityGroup.GroupName"]} SSHSecurityGroupIngress: Type: "AWS::EC2::SecurityGroupIngress" Properties: GroupId: {"Fn::GetAtt" : ["AWSEBSecurityGroup", "GroupId"]} IpProtocol: tcp ToPort: 22 FromPort: 22 CidrIp: xx.xx.xx.xx/yy |
.ebextensions/01-nginx-proxy.config:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 |
files: "/etc/nginx/sites-available/test.domain.com.conf": mode: "000644" owner: root group: root content: | server { listen 80; server_name test.domain.com; access_log /var/log/nginx/$server_name.access.log; location / { return 301 https://$server_name$request_uri; } location = /status/ { access_log /var/log/nginx/$server_name.status.log; uwsgi_pass docker; include uwsgi_params; } } server { listen 443; server_name test.domain.com; access_log /var/log/nginx/$server_name.access.log; location / { uwsgi_pass docker; include uwsgi_params; client_max_body_size 100m; } location /static/ { root /var/www; } } commands: 00_enable_site: command: 'rm -f /etc/nginx/sites-enabled/* && ln -s /etc/nginx/sites-available/test.domain.com.conf /etc/nginx/sites-enabled/test.domain.com.conf' |