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

Posted

技术标签:

【中文标题】在单个 docker 容器环境中配置 nginx (AWS ElasticBeanstalk)【英文标题】:Configuring nginx in a single docker container environment (AWS ElasticBeanstalk) 【发布时间】:2017-05-30 05:04:57 【问题描述】:

我目前正在 AWS ElasticBeanstalk 中使用 single container docker environment 部署 Django + uWSGI 应用程序。这个环境已经随 nginx 一起提供了,我目前正在尝试配置它。

我正在努力实现以下目标:

    在环境的负载平衡器处终止 HTTPS 使用 nginx(环境自带)将 HTTP 请求重定向到 HTTPS 将请求从 nginx 传递到 uwsgi

环境信息:

配置和解决方案堆栈名称:单容器 Docker 1.11 2.3.0 版 AMI:运行 Docker 的 64 位 Amazon Linux 2016.09 v2.3.0 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 机器后,您是否在日志中看到任何错误? /var/log/nginx/error.log 没有显示任何内容。 uwsgi.log 仅显示对 /status/ 的 GET 请求。显然,uwsgi 没有收到来自 nginx 的其他路径(即 /hello/ 和 /)的请求。 超时通常暗示安全组不允许某些流量。我对ELB over Elasticbeanstalk不太熟悉,但是“正常”的ELB需要你专门开通443流量。 @deviavir 就是这样!在安全组中,我必须启用从负载均衡器到端口 443 的入站流量。 @blaze 啊 :) 这比我想象的要容易!我添加了一个“答案”,可以让你有一个更简单的 nginx 配置,也可以解决问题(它可以让你在实例上重用相同的端口 80)。 【参考方案1】:

编辑:

OP 的问题在这里解决了 Configuring nginx in a single docker container environment (AWS ElasticBeanstalk),为了完整起见,我留下以下信息。


从 nginx 配置中删除端口 443 的“服务器”块,并在 .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 似乎表明一个有点奇怪的问题 - 但是,这应该让您获得一个更简单的配置调试并希望您立即开始。

【讨论】:

谢谢。我有单独的服务器指令,因为the only safe things to be done inside an if directive are "return" and "rewrite"。最后,我创建了一个新文件00-resources.config,在其中创建了一个自定义 SecurityGroup、一个自定义 LoadBalancer 和三个自定义 SecurityGroupIngress 资源。【参考方案2】:

按照@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'

【讨论】:

以上是关于在单个 docker 容器环境中配置 nginx (AWS ElasticBeanstalk)的主要内容,如果未能解决你的问题,请参考以下文章

在 Elastic Beanstalk 上部署 Docker 环境

从 docker-compose 替换 NGINX 配置中的环境变量

使用docker容器安装nginx的使用

Mac 使用docker搭建PHP环境

Docker多容器部署LNMP环境

Docker快速搭建PHP+Nginx+Mysql环境(https://notemi.cn/docker-quickly-set-up-php-nginx-mysql-environment.html