我想知道这是否可行,如果可以,如何在一台专用服务器上同时运行 nginx-proxy 和 poste.io 邮件服务器?
我可以分别运行它们,但是当我尝试同时运行它们时,它说我无法运行后一个容器,因为端口 443 已被另一个容器使用。
现在,当我仅使用我的 nginx 反向代理时,我会在我的服务器上运行多个网站,所有这些网站都会与代理本身一起公开端口 80 和 443,这让我有点困惑为什么我不能运行另一个执行相同操作的容器(是的,我知道通常 2 个进程不应该能够使用同一个端口而不进行一些摆弄)。
我使用以下代理:https://github.com/jwilder/nginx-proxy
我用https://poste.io对于我的邮件服务器
这是我在我的服务器上运行的一个网站 docker-compose 的示例。
application:
build: code
volumes:
- /websites/domain:/var/www/laravel
- /docker/webs/domain/logs:/var/www/laravel/storage/logs
tty: true
redis:
image: redis:alpine
db:
image: mariadb:10.2
environment:
MYSQL_ROOT_PASSWORD: toor
MYSQL_DATABASE: laravel
TEST_DB_NAME: laravel_test
MYSQL_USER: laravel
MYSQL_PASSWORD: laravel
php:
build: php7-fpm
volumes_from:
- application
links:
- db
- redis
nginx:
build: nginx
links:
- php
volumes_from:
- application
- nginx-proxy
volumes:
- ./logs/nginx/:/var/log/nginx
environment:
- VIRTUAL_HOST=www.domain.com
在我的 nginx 的 Dockerfile 中,我公开了端口 80 和 443
FROM debian:jessie
MAINTAINER Purinda Gunasekara <[email protected]>
RUN apt-get update && apt-get install -y \
nginx
ADD nginx.conf /etc/nginx/
ADD *.conf /etc/nginx/sites-enabled/
RUN rm /etc/nginx/sites-enabled/default
RUN rm /etc/nginx/sites-enabled/nginx.conf
# remove the https for local development
#RUN rm /etc/nginx/sites-enabled/*.ssl.conf
RUN echo "upstream php-upstream { server php:9000; }" >
/etc/nginx/conf.d/upstream.conf
RUN usermod -u 1000 www-data
CMD ["nginx"]
EXPOSE 80
EXPOSE 443
这就是让我困惑的地方。为什么 docker 允许我的网站顺利运行(即使 nginx 代理已经在端口 80 和 443 上运行)。但是当我尝试运行我的邮件服务器时,它却抱怨端口 443 已被使用?
这是 docker 发布的实际错误
docker: Error response from daemon:
driver failed programming external connectivity on endpoint
nginx_proxy <containerID>: Bind for 0.0.0.0:443 failed: port is already allocated.
理想情况下,我可以在一台服务器上同时运行这个邮件服务器和我的网站,这是因为只有少数网站需要托管,并且预计在短时间内这些网站都不会增长太多。
更新
网站使用来自 nginx-proxy 的卷,因此它们可以在其旁边运行,同时自己公开端口 80 和 443,但是当我尝试将 nginx-proxy 的相同卷与邮件服务器链接时,我一直收到相同的端口正在使用错误。
答案1
如果一个 docker 容器已经绑定到您某个接口 IP 上的端口 443(或 0.0.0.0 表示所有接口),则其他 docker 容器无法绑定到同一 IP。当一个 1 容器启动时,使用 netstat 检查:
sudo netstat -nalp64 | grep 443
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 26547/docker-proxy
由于 0.0.0.0 上的端口 443 已被 docker 容器使用,因此新容器无法绑定到该 IP + 端口。
可视化
0.0.0.0:443 (Error: Port 443 already in use)
| \
+--------------+ +--------------+
| CONTAINER | | CONTAINER |
| 172.0.0.2 | | 172.0.0.3 |
+--------------+ +--------------+
您不需要将多个容器绑定到同一个端口,而是需要一些软件绑定到端口,将连接重定向到适当的容器。
最简单的方法是运行专用的反向代理,这是绑定到端口 (443) 的唯一程序。反向代理的目的是根据请求的 HTTP 主机转发传入的连接。
反向代理可以在运行docker的物理主机上运行,也可以在docker容器内运行。
反向代理还可以终止 SSL 连接,这意味着这个 nginx 实例处理所有往返于客户端的加密/解密,而与后端(容器)的连接未加密。
我不认为这绝对必要,现代浏览器支持 SNI,因此 nginx 仍然可以将请求转发到适当的后端,而无需解密所有流量。但使用中央 SSL 终端,您只需在一个地方配置证书,并且对于大多数用例,只需全局配置一次 SSL。
为了设置具有 SSL 终止功能的反向代理
- 在docker主机上安装nginx(反向代理)
- 为容器定义静态 IP 或主机名
- 使容器的 SSL 证书 + 私钥文件可供 nginx 反向代理使用
- 在反向代理配置中定义 Docker 容器的 nginx 上游
- 定义 nginx 服务器(“vhosts”)来为以下定义的域名提供服务
server_name
location
将请求转发到使用中定义的上游proxy_pass
例子:
我的/etc/nginx/sites-enabled/dockerproxy
样子是这样的:
# gitlab
upstream gitlab
{
server 172.20.0.2;
}
# docker registry
upstream registry
{
server 172.20.0.3:5050;
}
# dev.mycompany.org
server
{
listen 10.10.10.40:80 default;
listen 10.10.10.40:443 ssl default;
server_name dev.mycompany.org;
ssl_certificate /data/run/certbot/data/live/dev.mycompany.org/fullchain.pem;
ssl_certificate_key /data/run/certbot/data/live/dev.mycompany.org/privkey.pem;
location /
{
proxy_pass http://gitlab/;
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;
}
}
# registry.mycompany.org
server
{
listen 10.10.10.40:443 ssl;
server_name registry.mycompany.org;
ssl_certificate /data/run/certbot/data/live/registry.mycompany.org/fullchain.pem;
ssl_certificate_key /data/run/certbot/data/live/registry.mycompany.org/privkey.pem;
ssl_session_cache builtin:1000 shared:SSL:60m;
ssl_session_timeout 60m;
client_max_body_size 0;
chunked_transfer_encoding on;
location /
{
proxy_pass http://registry/;
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_set_header
指令严格来说并不是必需的,它们取决于各个后端应用程序的期望。
如你所见,这个配置告诉 nginx:
- 绑定到 10.10.10.40:443
- 将 dev.mycompany.org 的请求代理到 172.20.0.2[:80](gitlab 容器的 IP)
- 将 registry.mycompany.org 的请求代理到 172.20.0.3:5050(注册容器的 IP)
- 使用给定的证书文件终止 SSL(在我的情况下,直接来自 certbot 容器)
可视化
0.0.0.0:443
|
+-----------------------+
| nginx |
+-----------------------+
| |
+--------------+ +--------------+
| VHOST | | VHOST |
| web.app1.com | | web.app2.com |
+--------------+ +--------------+
| |
+--------------+ +--------------+
| CONTAINER | | CONTAINER |
| 172.0.0.2 | | 172.0.0.3 |
+--------------+ +--------------+
通过使用不同的指令定义其他upstream
对象,您可以使用相同的接口 IP + 端口提供其他 HTTP(S) 服务。server
server_name
请注意,该listen 10.10.10.40:443
指令在 nginx 配置中被多次使用。这是可能的,因为 nginx 仅绑定到该 IP 一次,然后解析Host
客户端请求中的标头以确定哪个server
(vhost) 将处理此请求。
我的配置在upstream
定义中使用静态 IP,但你也可以使用容器的主机名,只需确保它们是预先知道的(在 docker-compose 中定义,请参阅https://docs.docker.com/compose/compose-file/#domainname-hostname-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir) 并可由 nginx 解析。
最后,不要将容器/服务的端口映射到主机端口!它们不需要向外界开放,只有 nginx 需要访问它们。