在单个Docker容器环境(AWS ElasticBeanstalk)中配置nginx

6
我目前正在使用AWS ElasticBeanstalk中的单容器Docker环境部署Django+uWSGI应用程序。该环境已经包含了nginx,我正在尝试配置它。
我想实现以下功能:
  1. 在环境的负载均衡器上终止HTTPS
  2. 使用nginx(随环境一起提供)将HTTP请求重定向到HTTPS
  3. 将请求从nginx传递给uwsgi
环境信息:
  • 配置和解决方案堆栈名称:单容器Docker 1.11版本2.3.0
  • AMI:64位Amazon Linux 2016.09 v2.3.0运行Docker 1.11.2 2016.09.0
  • Docker版本:1.11.2
  • 代理服务器:nginx 1.10.1
这是我的当前配置: .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配置文件)

[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

现在,在测试配置时:
检查http://test.domain.com/status/正常工作
$ 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

检查http://test.domain.com/hello/的效果不如预期。 它可以正常重定向,但在请求超时之前会停顿。

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

在登录由Beanstalk服务创建的EC2 / ECS机器后,您是否看到日志中出现任何错误? - Sairam
/var/log/nginx/error.log 没有显示任何内容。uwsgi.log 只显示了对 /status/ 的 GET 请求。显然,uwsgi 没有从 nginx 接收到其他路径(即 /hello/ 和 /)的请求。 - blaze
1
超时通常提示安全组不允许某些流量通过。我对 Elasticbeanstalk 上的 ELB 不是太熟悉,但 "正常的" ELB 需要您特别打开 443 流量。 - deviavir
@deviavir 就是这样!在安全组中,我需要启用从负载均衡器到443端口的入站流量。 - blaze
@blaze 哦 :) 比我想象中的要容易!我添加了一个“答案”,可以让您拥有一个更简单的nginx配置,并解决了问题(它允许您在实例上重用相同的80端口)。 - deviavir
2个回答

5

根据@deviavir的建议,我需要允许来自负载均衡器到EC2实例的流量。

这是我的最终配置:

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

1

编辑:

OP的问题在这里得到解决在单个Docker容器环境中配置nginx(AWS ElasticBeanstalk), 我将下面的信息留下来以保证完整性。


从您的nginx配置中删除端口443的“server”块,在.ebxtensions/00-loadbalancer-terminatehttps.config中将aws:elb:listener:443:的InstancePort设置为80,并使用nginx'$http_x_forwarded_proto'检查HTTPS。
if ($http_x_forwarded_proto != "https") {
  rewrite ^(.*)$ https://$server_name$1 permanent;
}

为什么?目前您的Nginx + ELB正在尝试一起终止HTTPS,但您的Nginx没有响应任何内容。如果您不打算使用内部HTTPS,则最好只使用一个Nginx服务器块并在其中执行所有逻辑。
我建议使用以下配置文件:
.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'

请注意,我绝不声称这是您问题的最佳解决方案(甚至可能根本不是解决方案),408似乎表明存在某种奇怪的问题- 但是,这应该可以让您进入一个更简单的配置以进行调试,并希望立即使您开始工作。

1
谢谢。我有单独的服务器指令,因为if指令中唯一安全的操作是“return”和“rewrite”。最终,我创建了一个新文件00-resources.config,在其中创建了一个自定义SecurityGroup、一个自定义LoadBalancer和三个自定义SecurityGroupIngress资源。 - blaze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接