Публикация сервисов 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-адреса этих узлов.

Другие вопросы по тегам