docker nginx 代理与 poste.io 邮件服务器

kil*_*eet 1 email-server reverse-proxy docker

我想知道这是否可能,如果可以,我如何在一台专用服务器上同时运行 nginx-proxy 和 poste.io 邮件服务器?

我可以单独运行这两个容器,但是当我尝试同时运行这两个容器时,它说我无法运行后一个容器,因为端口 443 已被另一个容器使用。

现在,当我只使用 nginx 反向代理时,我会在服务器上运行多个网站,所有这些网站都会公开端口 80 和 443 以及代理本身,这让我很困惑为什么我不能运行另一个容器做同样的事情(是的,我知道通常两个进程不应该能够使用同一个端口而不需要一些摆弄)。

我使用以下代理: 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
Run Code Online (Sandbox Code Playgroud)

在我的 nginx 的 Dockerfile 中,我公开了端口 80 和 443

FROM debian:jessie

MAINTAINER Purinda Gunasekara <purinda@gmail.com>

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
Run Code Online (Sandbox Code Playgroud)

所以这就是让我困惑的地方。为什么 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.
Run Code Online (Sandbox Code Playgroud)

理想情况下,我能够在一台服务器上运行该邮件服务器和我的网站,这是因为只有一小部分网站要托管,并且预计这些网站都不会在短时间内增长太多。

更新

这些网站使用来自 nginx-proxy 的卷,因此它们可以在其旁边运行,同时暴露端口 80 和 443 本身,但是当我尝试将 nginx-proxy 的相同卷与邮件服务器链接时,我不断收到相同的错误正在使用的端口。

NoM*_*Mad 5

如果一个 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
Run Code Online (Sandbox Code Playgroud)

由于 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  |
+--------------+    +--------------+
Run Code Online (Sandbox Code Playgroud)

您不需要将多个容器绑定到同一个端口,而是需要一些绑定到端口的软件,将连接重定向到适当的容器。

通过运行专用反向代理最容易完成此操作,这是绑定到端口 (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;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,严格来说这些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(registry容器的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  |
+--------------+    +--------------+
Run Code Online (Sandbox Code Playgroud)

通过使用不同指令定义其他upstream对象,您可以使用相同的接口 IP + 端口来提供其他 HTTP(S) 服务。serverserver_name

请注意,该listen 10.10.10.40:443指令在 nginx 配置中多次使用。这是可能的,因为 nginx 仅绑定到该 IP 一次,然后解析Host客户端请求中的标头以确定哪个server(虚拟主机)将服务该请求。

我的配置在upstream定义中使用静态 IP,但您也可以使用容器的主机名,只需确保它们事先已知(在 docker-compose 中定义,请参阅https://docs.docker.com/compose/compose-file/#域名-主机名-ipc-mac_address-privileged-read_only-shm_size-stdin_open-tty-user-working_dir)并可由 nginx 解析。

最后,不要将容器/服务的端口映射到主机端口!它们不需要对外界可用,只有 nginx 需要访问它们。