Email-Server

帶有 poste.io 郵件伺服器的 docker nginx 代理

  • August 17, 2018

我想知道是否有可能,如果可以,我將如何在一台專用伺服器上同時執行 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 <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

所以這就是讓我感到困惑的原因。為什麼 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 與郵件伺服器連結時,我一直收到相同的錯誤正在使用的埠。

如果一個 docker 容器已經綁定到您的一個介面 IP 上的埠 443(或 0.0.0.0 表示所有介面),則其他 docker 容器不能綁定到同一個 IP。使用 netstat 檢查,當一個 1 容器啟動時:

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  |
+--------------+    +--------------+

通過使用不同的指令定義其他upstreamserver對象server_name,您可以使用相同的介面 IP + 埠使其他 HTTP(S) 服務可用。

請注意,該listen 10.10.10.40:443指令在 nginx 配置中多次使用。這是可能的,因為 nginx 只綁定到該 IP 一次,然後解析Host客戶端請求中的標頭以確定哪個server(vhost)將為該請求提供服務。

我的配置在定義中使用靜態 IP upstream,但您也可以使用容器的主機名,只需確保事先知道它們(在 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 需要訪問它們。

引用自:https://serverfault.com/questions/926368