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

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

我想知道这是否可行,如果可以,如何在一台专用服务器上同时运行 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) 服务。serverserver_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 需要访问它们。

相关内容