Назад | Перейти на главную страницу

прокси-сервер 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

Внутри Dockerfile моего nginx я открываю порт 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

Вот что меня смущает. Почему докер позволяет моему сайту работать (прокси-сервер Eventhough nginx уже работает на портах 80 и 443) без проблем. Но когда я пытаюсь запустить свой почтовый сервер, он жалуется, что порт 443 уже используется?

Вот фактическая ошибка, отправленная докером

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 с почтовым сервером, я продолжал получать ту же ошибку используемые порты.

Если один контейнер докеров уже привязан к порту 443 на IP-адресе одного из ваших интерфейсов (или 0.0.0.0, что означает все интерфейсы), другие контейнеры докеров не могут привязаться к тому же IP. Проверьте с помощью netstat, пока один контейнер 1 открыт:

sudo netstat -nalp64 | grep 443
tcp     0    0    0.0.0.0:443     0.0.0.0:*    LISTEN     26547/docker-proxy

Поскольку порт 443 в 0.0.0.0 уже используется контейнером докеров, новые контейнеры не могут привязаться к этому IP + порту.

Визуализация

  0.0.0.0:443   (Error: Port 443 already in use)
        |               \
+--------------+    +--------------+
|  CONTAINER   |    |  CONTAINER   |
|   172.0.0.2  |    |   172.0.0.3  |
+--------------+    +--------------+

Вместо привязки нескольких контейнеров к одному и тому же порту вам понадобится привязка программного обеспечения к порту, перенаправляющая подключения к соответствующему контейнеру.

Это проще всего сделать, запустив выделенный обратный прокси-сервер, который является единственной программой, привязанной к порту (443). Обратный прокси-сервер предназначен для пересылки входящих соединений на основе запрошенного HTTP-хоста.

Обратный прокси-сервер может работать на физическом хосте, на котором выполняется докер, или внутри контейнера докера.

Обратный прокси-сервер также может разрывать SSL-соединения, то есть этот экземпляр nginx обрабатывает все шифрование / дешифрование от / до клиентов, в то время как соединения с серверной частью (контейнерами) не зашифрованы.

Я не думаю, что это строго необходимо, современные браузеры поддерживают SNI, поэтому nginx может перенаправлять запросы на соответствующий бэкэнд, не расшифровывая весь трафик. Но при использовании централизованного завершения SSL вам просто нужны сертификаты в одном месте, а SSL нужно настроить глобально только один раз для большинства случаев использования.

Чтобы настроить такой обратный прокси с завершением SSL

  • Установите nginx (обратный прокси) на хост-докер
  • Определите статические IP-адреса или имена хостов для контейнеров
  • Сделайте доступными файлы сертификата SSL + закрытого ключа контейнеров для обратного прокси-сервера nginx
  • Определите восходящие потоки nginx для ваших контейнеров Docker в конфигурации обратного прокси
  • Определите серверы 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] (IP-адрес контейнера gitlab)
  • Запросы прокси для 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 и server объекты, использующие разные server_name директив, вы можете сделать доступными другие службы HTTP (S), используя тот же интерфейс IP + порт.

Обратите внимание, что 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 должен иметь к ним доступ.