Публикация сервисов Docker Swarm на определенных IP-адресах
На Centos 7.4 я настраиваю рой, где я хочу запустить несколько маршрутизаторов, доступных через порт 80/443.
Цель состоит в том, чтобы разместить несколько сред (тестирование / постановка...) на одном рое, все симметрично.
Я использую Docker 17.12.0-ce и Traefik v1.4.6 в качестве маршрутизатора.
Основная идея заключается в том, чтобы иметь виртуальный IP-адрес для каждой среды и публиковать порты Traefik только по этому адресу. С роем Docker это невозможно, поэтому я вынужден прибегнуть к тому, чтобы экземпляры Traefik прослушивали порты 81/82 и т. Д. И каким-то образом переносили трафик с VIP:80 на:81/:82.
Виртуальные IP-адреса для всех сред через менеджеры роя обрабатываются Keepalived.
Соответствующий конфиг службы Docker для Traefik:
"Ports": [
{
"Protocol": "tcp",
"TargetPort": 80,
"PublishedPort": 81,
"PublishMode": "ingress"
},
# netstat -anp|grep 81
tcp6 7 0 :::81 :::* LISTEN 4578/dockerd
firewalld настроен на пропуск трафика на порты 80, 81, 82 и т. д.
Доступ к внутренним службам, предоставляемым Traefik, напрямую через порт 81 на VIP-работах.
Доступ к порту 80 на VIP, когда на нем ничего не настроено, приводит к отказу в соединении
Экземпляр докера Traefik работает на том же хосте, который я использую для следующих тестов.
Я впервые попробовал с базовым DNAT:
firewall-cmd --add-forward-port=port=80:proto=tcp:toport=81:toaddr=127.0.0.1
Это привело к тайм-аутам, на сервере не было установлено соединение, и tcpdump сказал мне, что SYN игнорируются
Затем я попытался с более конкретным DNAT:
firewall-cmd --add-rich-rule='rule family=ipv4 forward-port port=80 protocol=tcp to-port=81 to-addr=127.0.0.1'
с такими же результатами.
Я обнаружил GORB, который, кажется, с учетом моего варианта использования, и снабдил его
Обслуживание:
{
"host": "<VIP>",
"port": 80,
"protocol": "tcp",
"method": "rr",
"persistent": true,
"flags": "sh-port"
}
Бэкенд для указанного сервиса:
{
"host": "<VIP>",
"port": 81,
"method": "nat",
"weight": 100,
"pulse": {
"type": "tcp",
"interval": "30s",
"args": null
}
}
Я проверил настройку с помощью ipvsadm, и она кажется правильной:
# ipvsadm -l -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP <VIP>:80 rr (flag-2)
-> <VIP>:81 Masq 100 0 0
в этом случае, хотя на сервере не появилось никакого соединения, tcpdump показал обмен SYN, SYNACK и ACK, а затем HTTP-запрос и его ACK.
Никакой другой трафик не прошел, и время ожидания запроса на стороне клиента истекло.
ipvsadm зарегистрировал соединение как активное.
Если я настрою HAProxy для прослушивания VIP:80 и для прокси-запросов через HTTP до 127.0.0.1:81, все будет работать, но я бы хотел этого избежать, так как все данные должны проходить через HAProxy, тратя ресурсы впустую. и требует локальной конфигурации.
У меня нет идей, и я не знаю, как дальше устранять неполадки.
РЕДАКТИРОВАТЬ для уточнения. Мой вопрос:
Можно ли перенаправить трафик с VIP:80 на: 81 /: 82 и т. Д. Без использования HAProxy или другого процесса, который бы просто перекачивал данные на реальный маршрутизатор (Traefik)?
2 ответа
Во-первых, вы можете использовать несколько IP-адресов на хосте, если у вас есть возможность добавлять IP-адреса в реальной сети. Это работает в Swarm на Linux. Посмотрите документы Macvlan и Google вокруг "Macvlan Swarm".
Во-вторых, вы используете оверлей и входную сеть Swarm, верно?
В-третьих, большинство людей просто помещают Traefik (или мой любимый http://proxy.dockerflow.com/) для прослушивания 80/443, и он направляет к нужному сервису / стеку в Swarm на основе заголовка хоста. Как Флорин спросил, почему ты не пытаешься это сделать?
У нас возникла необходимость опубликовать отдельные docker swarm-сервисы на одних и тех же портах, но на отдельных конкретных IP-адресах. Вот как мы это сделали.
Docker добавляет правила в цепочку DOCKER-INGRESS таблицы nat для каждого опубликованного порта. Добавляемые им правила не зависят от IP, поэтому обычно любой опубликованный порт будет доступен на всех IP-адресах хостов. Вот пример правила, которое Docker добавит для службы, опубликованной на порту 80:
iptables -t nat -A DOCKER-INGRESS -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:80
(Вы можете просмотреть их, запустивiptables-save -t nat | grep DOCKER-INGRESS
).
Наше решение состоит в том, чтобы опубликовать наши сервисы на разных портах и использовать сценарий, который перехватывает команды iptables dockerd и переписывает их, чтобы они соответствовали правильному IP-адресу и паре общедоступных портов.
Например:
- служба №1 публикуется на порту 1080, но должна прослушивать порт 1.2.3.4:80.
- служба №2 публикуется на порту 1180, но должна прослушиваться на 1.2.3.5:80.
Затем мы соответствующим образом настраиваем наш скрипт:
# cat /usr/local/sbin/iptables
#!/bin/bash
REGEX_INGRESS="^(.*DOCKER-INGRESS -p tcp) (--dport [0-9]+) (-j DNAT --to-destination .*)"
IPTABLES=/usr/sbin/iptables
SRV_1_IP=1.2.3.4
SRV_2_IP=1.2.3.5
ipt() {
echo "EXECUTING: $@" >>/tmp/iptables.log
$IPTABLES "$@"
}
if [[ "$*" =~ $REGEX_INGRESS ]]; then
START=${BASH_REMATCH[1]}
PORT=${BASH_REMATCH[2]}
END=${BASH_REMATCH[3]}
echo "REQUESTED: $@" >>/tmp/iptables.log
case "$PORT" in
'--dport 1080') ipt $START --dport 80 -d $SRV_1_IP $END; exit $?; ;;
'--dport 2080') ipt $START --dport 80 -d $SRV_2_IP $END; exit $?; ;;
*) ipt "$@"; exit $?; ;;
esac
fi
echo "PASSING-THROUGH: $@" >>/tmp/iptables.log
$IPTABLES "$@"
NB. Сценарий должен быть установлен в PATH dockerd перед командой iptables вашего дистрибутива. В Debian Buster iptables установлен для/usr/sbin/iptables
и PATH dockerd имеет/usr/local/sbin
впереди/usr/sbin
, поэтому имеет смысл установить скрипт по адресу/usr/local/sbin/iptables
. (Вы можете проверить PATH dockerd, запустивcat /proc/$(pgrep dockerd)/environ | tr '\0' '\012' | grep ^PATH
).
Теперь при запуске этих докер-сервисов правила iptables будут переписаны следующим образом:
iptables -t nat -A DOCKER-INGRESS -d 1.2.3.4/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:1080
iptables -t nat -A DOCKER-INGRESS -d 1.2.3.5/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.18.0.2:2080
В результате запросы http://1.2.3.4/ передаются сервису №1, а запросы http://1.2.3.5/ — сервису №2.
Сценарий можно настроить и расширить в соответствии с вашими потребностями, он должен быть установлен на всех узлах, на которые вы будете направлять запросы, и настроен на общедоступные IP-адреса этих узлов.