[英]docker-compose nginx proxy_pass to upstream containers not behaving as expected
我正在尝试使用一个基本的反向代理来处理基于 [本教程][1] 的多个网站,但将其调整为使用单个 docker-compose 文件和 proxy_pass 到上游容器。 这似乎是最简洁的方法,因为它适用于我的学习/测试服务器,我将经常启动和停止容器。 我想在开始添加更复杂的应用程序容器之前锁定它。 我不确定我应该转发端口的配置的哪一部分,因为大多数在线问题和教程都没有使用上游容器。
编辑 - 默认服务器未在 443 上侦听,修复此问题消除了一个混乱。 现在我只能从xxxx/
和来自xxxx/site1
或xxxx/site2
(或其他任何东西)的反向代理自定义 404 页面获取预期的 index.html
从我读过,端口内部由码头工人,只要处理的容器链接(同泊坞窗网络上),并且不需要甚至暴露声明docker-compose.yml
,只要容器开始docker-compose up
我已经尝试将自定义端口转发到 docker-compose.yml 中的容器
ports:
- 8081:443
这在 nginx default.conf
upstream docker-site1 {
server website1-container:8081;
}
但这给了我502 Bad Gateway
我使用命名容器和外部网络来保持名称静态,以保持容器间网络与主机分离,并在这方面利用 Docker 功能。
我现在已经花了两天时间,我真的需要一些指导来避免绕圈子!
编辑 - 仍在兜兜转转。 感谢 lmsec 更新了 default.conf,并将 /site1 添加到 docker-compose.yml 中的卷路径
我的 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
./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/
网站 Dockerfiles 只包含FROM nginx:1.20-alpine
./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/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;
每个网站容器都有自己的网络,它与代理容器共享。
{
"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": {}
}
我可以通过这个网络访问网站容器,甚至可以使用 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
我将这个问题标记为关闭。 我得出的结论是,当使用 proxy_pass 到 docker 容器时,最新版本的 docker 不需要任何特殊的端口转发,尽管如果需要,可以在 docker-compose 和 nginx default.conf 中完成 - 正如 lmsec 答案所解释的那样。
[..] 在配置的哪一部分中,我应该使用上游容器转发端口[..]。
您可以在上游定义中执行此操作(摘自下面的nginx 文档):
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
# [..]
}
[..] 当我请求服务器的根 xxxx 时,我得到 website1,当我请求 xxxx/site1 时,我得到 404 错误。
您没有为 https (443) 定义default_server
,因此第一个服务器用作443 的默认值。(不确定为什么会得到 404。)
从未得到网站的回应2
您需要请求site2
以从中获得响应(因为server_name site2;
)。 出于测试目的,您可以将它放在您的主机文件中。
site1 127.0.0.1
site2 127.0.0.1
以下是使用 nginx-as-a-proxy 更快开始的其他一些关键:
server_name
就像一个请求过滤器;proxy_pass http://docker-site1/;
(带有尾随/
)以便/example
转到http://docker-site1/example
,而不是http://docker-site1
;/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;
}
(当然?)这也可以在没有upstream
情况下工作,在proxy_pass
使用服务器的地址而不是upstream
标识符。
编辑 -奖励:Docker(-compose) 端口和网络
site1_app:
ports:
- 8081:443
localhost:8081
(或xxxx:8081
)访问site1_app
的 443 端口site1_app:443
* (或https://site1_app
)访问site1_app
的 443 端口 (让我们假设site1_app
也监听端口 80):
site1_app
的 80 端口:它没有被转发(这里,只有 443 是)site1_app:80
* (或http://site1_app
)访问site1_app
的 80 端口 *不确定这适用于docker-compose
的version: '2'
,但它适用于version: '3.9'
。
您编写的以下几行允许您调用website1_container
而不是site1_app
:
container_name: website1-container
hostname: website1-container
所以如果你这样做:
# 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/;
}
假设您将请求标头设置为Host: site1
(感谢您的hosts
文件或自己伪造请求标头):
site1
块http://docker-site1/
( http )docker-site1
被解析为只包含一台服务器的服务器组: website1-container:8081
site1_app
在其8081
端口(不是443
)上接收请求。site1_app
可能想HTTPS上的443
端口。所以你应该:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.