Мне было интересно, возможно ли это, и если да, то как я смогу запустить почтовый сервер 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
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:
Визуализация
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 должен иметь к ним доступ.