Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configuring nginx in a single docker container environment (AWS ElasticBeanstalk)

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:

  1. Terminate HTTPS at the environment's load balancer
  2. Redirect HTTP requests to HTTPS using nginx (which is shipped with the environment)
  3. 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

option_settings:
  aws:elb:listener:443:
    ListenerEnabled: true
    ListenerProtocol: HTTPS
    SSLCertificateId: <resource-id-here>
    InstancePort: 443
    InstanceProtocol: HTTP
  aws:elb:listener:80:
    ListenerEnabled: true
    ListenerProtocol: HTTP
    InstancePort: 80
    InstanceProtocol: HTTP

.ebextensions/01-nginx-proxy.config

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

option_settings:
  - namespace:  aws:elasticbeanstalk:application
    option_name:  Application Healthcheck URL
    value:  /status/

application.ini (uwsgi config)

[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

$ 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.

$ 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.
like image 370
blaze Avatar asked Jan 15 '17 05:01

blaze


People also ask

Can I run nginx in Docker?

Running NGINX Plus in a Docker ContainerDocker can also be used with NGINX Plus. The difference between using Docker with NGINX Open Source is that you first need to create an NGINX Plus image, because as a commercial offering NGINX Plus is not available at Docker Hub.

Is multiple containers per Amazon EC2 instance configuration possible with Elastic Beanstalk?

Each of the instances in the environment run the same set of containers, which are defined in a Dockerrun. aws. json v2 file. In order to get the most out of Docker, Elastic Beanstalk lets you create an environment where your Amazon EC2 instances run multiple Docker containers side by side.

Should nginx run inside Docker?

If nginx is running in a container then your site is going to be 100% dead to the world while Docker isn't running. Users will get a connection error. When nginx is installed directly on your host you can serve a 503 maintenance page that doesn't depend on Docker or any containers running.


2 Answers

Following @deviavir's suggestion, I needed to allow traffic from the load balancer into the EC2 instance.

This is my final configuration:

   .ebextensions
   |-- 00-resources.config
   |-- 01-nginx-proxy.config

.ebextensions/00-resources.config:

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:

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'
like image 179
blaze Avatar answered Oct 05 '22 21:10

blaze


edit:

OP's problem was resolved here Configuring nginx in a single docker container environment (AWS ElasticBeanstalk), I'm leaving the below info for completeness.


Remove the "server" block for port 443 from your nginx config, and in .ebxtensions/00-loadbalancer-terminatehttps.config, set aws:elb:listener:443:'s InstancePort to 80, and check for HTTPS using nginx '$http_x_forwarded_proto'.

if ($http_x_forwarded_proto != "https") {
  rewrite ^(.*)$ https://$server_name$1 permanent;
}

Why? Currently it seems your nginx+elb are trying to terminate HTTPS together, but your nginx does not reply with any. If you're not planning on having internal HTTPS, it makes more sense to just stick to one nginx server block and do all of your logic there.

I'd propose the following config files:

.ebxtensions/00-loadbalancer-terminatehttps.config

option_settings:
  aws:elb:listener:443:
    ListenerEnabled: true
    ListenerProtocol: HTTPS
    SSLCertificateId: <resource-id-here>
    InstancePort: 80
    InstanceProtocol: HTTP
  aws:elb:listener:80:
    ListenerEnabled: true
    ListenerProtocol: HTTP
    InstancePort: 80
    InstanceProtocol: HTTP

.ebextensions/01-nginx-proxy.config

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;

        if ($http_x_forwarded_proto != "https") {
          set $test  http;
        }

        if ($request_uri = /status/) {
          set $test  "${test}+status";
        }

        if ($test = "http") {
          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;
          client_max_body_size 100m;
        }

        location /static {
            alias /var/www/static;
        }

        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;
        }

      }

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'

Note that I am by no means claiming this is the best solution to your problem (or even a solution at all), the 408 seems to indicate a somewhat strange problem- however, this should get you to a somewhat simpler config to debug and hopefully get you going immediately.

like image 37
deviavir Avatar answered Oct 05 '22 21:10

deviavir