[英]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/site1
或xxxx/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
就像一个请求过滤器;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
;/site2
and /site3
)./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
site1_app
's 443 port from localhost:8081
(or xxxx:8081
)localhost:8081
(或xxxx:8081
)访问site1_app
的 443 端口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):
site1_app
's 80 port : it is not forwarded (here, only 443 is)site1_app
的 80 端口:它没有被转发(这里,只有 443 是)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-compose
的version: '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
文件或自己伪造请求标头):
site1
blocksite1
块http://docker-site1/
( http )http://docker-site1/
( http )docker-site1
is resolved as the server group containing only one server : website1-container:8081
docker-site1
被解析为只包含一台服务器的服务器组: website1-container:8081
site1_app
receives the request on its 8081
port (not 443
).site1_app
在其8081
端口(不是443
)上接收请求。site1_app
probably wants HTTPS on the 443
port.site1_app
可能想HTTPS上的443
端口。 So you should :所以你应该:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.