简体   繁体   English

docker-compose nginx proxy_pass 到上游容器的行为不符合预期

[英]docker-compose nginx proxy_pass to upstream containers not behaving as expected

I'm trying to get a basic reverse-proxy working to handle multiple websites based on [this tutorial][1], but adapting it to use a single docker-compose file and proxy_pass to upstream containers .我正在尝试使用一个基本的反向代理来处理基于 [本教程][1] 的多个网站,但将其调整为使用单个 docker-compose 文件和 proxy_pass 到上游容器 This appears to be the most concise way to go about it as it's for my learning / testing server and I will be starting and stopping containers often.这似乎是最简洁的方法,因为它适用于我的学习/测试服务器,我将经常启动和停止容器。 I want to get this locked down before I start adding more complex app containers.我想在开始添加更复杂的应用程序容器之前锁定它。 I'm not confident in which part of the configuration I should be forwarding ports because most of the questions and tutorials online are not using upstream containers.我不确定我应该转发端口的配置的哪一部分,因为大多数在线问题和教程都没有使用上游容器。

EDIT - default server was not listening on 443, fixing this removed one confusion.编辑 - 默认服务器未在 443 上侦听,修复此问题消除了一个混乱。 Now I am only getting the expected index.html from xxxx/ and the reverse-proxy custom 404 page from xxxx/site1 or xxxx/site2 (or anything else)现在我只能从xxxx/和来自xxxx/site1xxxx/site2 (或其他任何东西)的反向代理自定义 404 页面获取预期的 index.html

From what I've read, ports are handled internally by docker as long as the containers are linked (on the same docker network) and even the expose statement is not required in docker-compose.yml so long as the container is started with docker-compose up从我读过,端口内部由码头工人,只要处理的容器链接(同泊坞窗网络上),并且不需要甚至暴露声明docker-compose.yml ,只要容器开始docker-compose up

And I've tried forwarding custom ports to the containers with this in docker-compose.yml我已经尝试将自定义端口转发到 docker-compose.yml 中的容器

ports:
  - 8081:443

and this in nginx default.conf这在 nginx default.conf

upstream docker-site1 {
    server website1-container:8081;
}

But this gives me 502 Bad Gateway但这给了我502 Bad Gateway

I am using named containers and external networks to keep names static, in an effort to keep inter-container networking separate from the host, and to take advantage of Docker features in that regard.我使用命名容器和外部网络来保持名称静态,以保持容器间网络与主机分离,并在这方面利用 Docker 功能。

I've spent two days on this now and I really need some direction to keep from going around in circles!我现在已经花了两天时间,我真的需要一些指导来避免绕圈子!

EDIT- still going around in circles.编辑 - 仍在兜兜转转。 Updated default.conf thanks to lmsec, and also added /site1 to the volume path in docker-compose.yml感谢 lmsec 更新了 default.conf,并将 /site1 添加到 docker-compose.yml 中的卷路径

My docker-compose.yml (in the top level directory) EDITED - my best working config我的 docker-compose.yml(在顶级目录中)已编辑 - 我最好的工作配置

version: '3.6'
services:
  proxy:
    build: ./proxy/
    container_name: reverse-proxy
    hostname: reverse-proxy

    networks:
      - public
      - website1
      - website2

    ports:
      - 80:80
      - 443:443


  site1_app:
    build:
      ./site1/
    volumes:
      - ./site1/html:/usr/share/nginx/html/site1
    container_name: website1-container
    hostname: website1-container
    networks:
      - website1
 
  site2_app:
    build:
      ./site2/
    volumes:
      - ./site2/html:/usr/share/nginx/html/site2
    container_name: website2-container
    hostname: website2-container
    networks:
      - website2

networks:
  public:
    external: true
  website1:
    external: true
  website2:
    external: true

Dockerfile in ./proxy/ ./proxy/ 中的 Dockerfile

FROM nginx:1.20-alpine

COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY ./backend-not-found.html /var/www/html/backend-not-found.html
COPY ./index.html /var/www/html/index.html

#  Proxy and SSL configurations
COPY ./includes/ /etc/nginx/includes/
# Proxy SSL certificates
COPY ./ssl/ /etc/ssl/certs/nginx/

the website Dockerfiles only contain FROM nginx:1.20-alpine网站 Dockerfiles 只包含FROM nginx:1.20-alpine

default.conf in ./proxy/ EDITED - My most working config, doesn't link JS,CSS,Images ./proxy/编辑中的default.conf - 我最常用的配置,不链接 JS、CSS、图像

# Default
server {
    # listen on port 80 (http)
    listen 80 default_server;
    server_name _;
    
    location / {
        # redirect any requests to the same URL but on https
        return 301 https://$host$request_uri;
    }
}
    
server {
  listen 443 ssl http2 default_server;

  server_name _;
  root /var/www/html;

  charset UTF-8;

  # Path for SSL config/key/certificate
  ssl_certificate /etc/ssl/certs/nginx/proxy.crt;
  ssl_certificate_key /etc/ssl/certs/nginx/proxy.key;
  include /etc/nginx/includes/ssl.conf;


  error_page 404 /backend-not-found.html;
  location = /backend-not-found.html {
    allow   all;
  }

  location / {
    index index.html;
  }
  location /site1 {
    include /etc/nginx/includes/proxy.conf;
    proxy_pass http://website1-container;
  }
  location /site2 {
    include /etc/nginx/includes/proxy.conf;
    proxy_pass http://website2-container;
  }


  access_log off;
  log_not_found off;
  error_log  /var/log/nginx/error.log error;
}

proxy.conf in ./proxy/includes/ ./proxy/includes/ 中的 proxy.conf

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-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_intercept_errors on;

Each website container has its own network which it shares with the proxy container.每个网站容器都有自己的网络,它与代理容器共享。

 {
    "Name": "website1",
    "Id": "9477470a8689d08776b38c4315882caff75573b7244f77091aa5e5438804ce36",
    "Created": "2021-06-21T02:52:25.402118801Z",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
        "Driver": "default",
        "Options": {},
        "Config": [
            {
                "Subnet": "192.168.160.0/20",
                "Gateway": "192.168.160.1"
            }
        ]
    },
    "Internal": false,
    "Attachable": false,
    "Ingress": false,
    "ConfigFrom": {
        "Network": ""
    },
    "ConfigOnly": false,
    "Containers": {
        "7c1a8b62864642afd5366ef88d762e4c5450eee02acb8c3f1890444b59379340": {
            "Name": "website1-container",
            "EndpointID": "f04d96343737574ca869270954461774f731851b781120119c21e02c0aa9968e",
            "MacAddress": "02:42:c0:a8:a0:02",
            "IPv4Address": "192.168.160.2/20",
            "IPv6Address": ""
        },
        "a88326952fb5f25f9084eb038f22f56b7331032a5ba71848ea6ada677a2ed998": {
            "Name": "reverse-proxy",
            "EndpointID": "b0c97c7f8dfe0febddbd6668481a009cce0c4f20dae3c3d3280dad0069c90394",
            "MacAddress": "02:42:c0:a8:a0:03",
            "IPv4Address": "192.168.160.3/20",
            "IPv6Address": ""
        }
    },
    "Options": {},
    "Labels": {}
}

I can access the website containers through this network and even get index.html with curl: sudo docker exec reverse-proxy curl 192.168.160.2/site1/index.html我可以通过这个网络访问网站容器,甚至可以使用 curl 获取 index.html: sudo docker exec reverse-proxy curl 192.168.160.2/site1/index.html

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0<!DOCTYPE html>
<html>
  <head>
    <title>Site 1</title>
  </head>
  <body>
    <h1>This is a sample "site1" response</h1>
  </body>
</html>
100   142  100   142    0     0  20285      0 --:--:-- --:--:-- --:--:-- 23666

I'm marking this question closed.我将这个问题标记为关闭。 I have come to the conclusion that recent versions of docker do not require any special port forwarding when using proxy_pass to a docker container, although if required it can be done in docker-compose and nginx default.conf - as lmsec answers explain.我得出的结论是,当使用 proxy_pass 到 docker 容器时,最新版本的 docker 不需要任何特殊的端口转发,尽管如果需要,可以在 docker-compose 和 nginx default.conf 中完成 - 正如 lmsec 答案所解释的那样。

[..] in which part of the configuration I should be forwarding ports [..] using upstream containers. [..] 在配置的哪一部分中,我应该使用上游容器转发端口[..]。

You may do it in the upstream definition (abstract from nginx docs below) :您可以在上游定义中执行此操作(摘自下面的nginx 文档):

upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    # [..]
}

[..] when I request my server's root xxxx, I get website1 and when I request xxxx/site1 I get a 404 error. [..] 当我请求服务器的根 xxxx 时,我得到 website1,当我请求 xxxx/site1 时,我得到 404 错误。

You did not define a default_server for https (443), so the first server is used as default for 443. (Not sure about why you get a 404.)您没有为 https (443) 定义default_server ,因此第一个服务器用作443 的默认值。(不确定为什么会得到 404。)

never got a response from website2从未得到网站的回应2

You'll need to request site2 to get a response from it (because of server_name site2; ).您需要请求site2以从中获得响应(因为server_name site2; )。 For testing purpose, you can put it in you hosts file.出于测试目的,您可以将它放在您的主机文件中。

site1     127.0.0.1
site2     127.0.0.1

Here are some other keys to begin faster with nginx-as-a-proxy :以下是使用 nginx-as-a-proxy 更快开始的其他一些关键:

  • server_name acts like a requests filter ; server_name就像一个请求过滤器;
  • use proxy_pass http://docker-site1/;使用proxy_pass http://docker-site1/; (with the trailing / ) so that /example goes to http://docker-site1/example , not http://docker-site1 ; (带有尾随/ )以便/example转到http://docker-site1/example ,而不是http://docker-site1
  • you may proxy to different hosts or upstreams based on the URI (example below : /site2 and /site3 ).您可以根据 URI (以下示例: /site2/site3 )代理到不同的主机或上游。
server {
  # Filter requests having 'Host: site1' (ignore the others)
  server_name site1;

  location / {
    # Send everything beginning with '/' to docker-site1
    proxy_pass http://docker-site1/;
  }

  location /site2/ {
    # Send everything beginning with '/site2/' to docker-site2
    #   removing the leading `/site2`
    proxy_pass http://docker-site2/;
  }

  location /site3/ {
    # Send everything beginning with '/site3/' to docker-site3
    #   keeping the leading `/site2`
    proxy_pass http://docker-site3/site3/;
  }
}

server {
  # do something else if the requested Host is site2
  server_name site2;  
}

(Of course?) this also works without upstream , with your servers' adresses in the proxy_pass instead of the upstream identifier. (当然?)这也可以在没有upstream情况下工作,在proxy_pass使用服务器的地址而不是upstream标识符。


EDIT - Bonus: Docker(-compose) ports and networking编辑 -奖励:Docker(-compose) 端口和网络

site1_app:
  ports:
    - 8081:443
  • from "outside" Docker, you'll access site1_app 's 443 port from localhost:8081 (or xxxx:8081 )从“外部”Docker,您将从localhost:8081 (或xxxx:8081 )访问site1_app的 443 端口
  • from another container on the same network , you'll access site1_app 's 443 port from site1_app:443 * (or https://site1_app )同一网络上的另一个容器,您将从site1_app:443 * (或https://site1_app )访问site1_app的 443 端口

(Let's imagine site1_app also listens on port 80) : (让我们假设site1_app也监听端口 80):

  • from "outsite" Docker, you can't access site1_app 's 80 port : it is not forwarded (here, only 443 is)从“外部”Docker,您无法访问site1_app的 80 端口:它没有被转发(这里,只有 443 是)
  • from another container on the same network , you'll access site1_app 's 80 port from site1_app:80 * (or http://site1_app )同一网络上的另一个容器,您将从site1_app:80 * (或http://site1_app )访问site1_app的 80 端口

*Not sure this works with docker-compose 's version: '2' , but it does with version: '3.9' . *不确定这适用于docker-composeversion: '2' ,但它适用于version: '3.9'

The following lines you wrote allow you to call website1_container instead of site1_app :您编写的以下几行允许您调用website1_container而不是site1_app

container_name: website1-container 
hostname: website1-container

So if you do :所以如果你这样做:

# 3
upstream docker-site1 {
    server website1-container:8081;
}
server {
  # 1
  listen 80;
  listen 443 ssl http2;
  server_name site1;

  # [..] SSL config/key/certificate

  location / {
    # 2
    proxy_pass http://docker-site1/;
  }

Supposing you're setting the request header to Host: site1 (thanks to your hosts file or forging the request headers yourself) :假设您将请求标头设置为Host: site1 (感谢您的hosts文件或自己伪造请求标头):

  1. the request, HTTP or HTTPS arrives to the site1 block请求,HTTP 或 HTTPS 到达site1
  2. it gets proxied to http://docker-site1/ ( http )它被代理到http://docker-site1/ ( http )
  3. docker-site1 is resolved as the server group containing only one server : website1-container:8081 docker-site1被解析为只包含一台服务器的服务器组: website1-container:8081
  4. The container site1_app receives the request on its 8081 port (not 443 ).容器site1_app在其8081端口(不是443 )上接收请求。
  5. Even if it did, site1_app probably wants HTTPS on the 443 port.即使做了, site1_app可能想HTTPS上的443端口。

So you should :所以你应该:

  1. Use the internal port instead of external,使用内部端口而不是外部端口,
  2. Check that you send HTTP (resp. HTTPS) to a port awaiting for HTTP (resp. HTTPS)检查您是否将 HTTP(相应的 HTTPS)发送到等待 HTTP(相应的 HTTPS)的端口

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 docker-compose + nginx proxy_pass - docker-compose + nginx proxy_pass 容器之间的Docker和Nginx proxy_pass - Docker and Nginx proxy_pass between containers Docker:proxy_pass 到另一个容器 - nginx:在上游找不到主机 - Docker: proxy_pass to another container - nginx: host not found in upstream Docker Nginx proxy_pass-上游连接被拒绝 - Docker nginx proxy_pass - Upstream connection refused 使用Docker和Nginx proxy_pass时重新启动容器 - Restarting Containers When Using Docker and Nginx proxy_pass Nginx + Docker - 与上游“连接到上游时没有实时上游”,但与 proxy_pass 一起工作正常 - Nginx + Docker - "no live upstreams while connecting to upstream" with upstream but works fine with proxy_pass 如何将 Docker 之外的 NGINX 反向代理代理到 proxy_pass 到 Docker 容器 - How to NGINX Reverse Proxy outside of Docker to proxy_pass to docker containers NGINX 和 Docker-Compose:在上游找不到主机 - NGINX and Docker-Compose: host not found in upstream Docker- nginx - 反向代理:使用 docker-compose 构建时在上游找不到主机 - Docker- nginx -Reverse proxy : host not found in upstream when building with docker-compose nginx反向代理上游在docker-compose中失败并显示连接被拒绝的消息 - nginx reverse proxy upstream fails in docker-compose with connection refused message
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM