Маршрутизация из Docker-контейнеров с использованием другого физического сетевого интерфейса и шлюза по умолчанию


13

Исходная информация

У меня есть сервер с двумя сетевыми интерфейсами, на котором работает Docker. Docker, как и некоторые инструменты виртуализации, создает интерфейс моста Linux под названием docker0. Этот интерфейс по умолчанию настроен с IP-адресом, 172.17.42.1и все контейнеры Docker обмениваются данными с этим интерфейсом в качестве своего шлюза, и им назначаются IP-адреса в том же /16диапазоне. Насколько я понимаю, весь сетевой трафик в / из контейнеров проходит через NAT, поэтому исходящие данные, похоже, поступают 172.17.42.1, а входящие отправляются 172.17.42.1.

Моя настройка выглядит так:

                                          +------------+        /
                                          |            |       |
                            +-------------+ Gateway 1  +-------
                            |             | 10.1.1.1   |     /
                     +------+-------+     +------------+    |
                     |     eth0     |                      /
                     |   10.1.1.2   |                      |
                     |              |                      |
                     | DOCKER HOST  |                      |
                     |              |                      | Internet
                     |   docker0    |                      |
                     |   (bridge)   |                      |
                     |  172.17.42.1 |                      |
                     |              |                      |
                     |     eth1     |                      |
                     |  192.168.1.2 |                      \
                     +------+-------+     +------------+    |
                            |             |            |     \
                            +-------------+ Gateway 2  +-------
                                          | 192.168.1.1|       |
                                          +------------+            

Проблема

Я хочу направить весь трафик от / к любым контейнерам Docker из второго eth1 192.168.1.2интерфейса на шлюз по умолчанию 192.168.1.1, при этом весь трафик с / на хост-машину выходитeth0 10.1.1.2 интерфейса на шлюз по умолчанию 10.1.1.1. Я пробовал разные вещи, но безрезультатно, но единственное, что я считаю наиболее близким к исправлению, - это использовать iproute2 следующим образом:

# Create a new routing table just for docker
echo "1 docker" >> /etc/iproute2/rt_tables

# Add a rule stating any traffic from the docker0 bridge interface should use 
# the newly added docker routing table
ip rule add from 172.17.42.1 table docker

# Add a route to the newly added docker routing table that dictates all traffic
# go out the 192.168.1.2 interface on eth1
ip route add default via 192.168.1.2 dev eth1 table docker

# Flush the route cache
ip route flush cache

# Restart the Docker daemon so it uses the correct network settings
# Note, I do this as I found Docker containers often won't be able
# to connect out if any changes to the network are made while it's     
# running
/etc/init.d/docker restart

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


Ваш комментарий о NAT не прав. Он настроен на маскарад по адресу источника 172.17.0.0/16, где интерфейс вывода не является docker0. т.е. -A POSTROUTING -s 172.17.0.0/16! -o docker0 -j MASQUERADE. Это означает, что если пакет, исходящий из док-контейнера, выходит через eth0 или eth1, он получит IP-адрес этого интерфейса, а не 172.17.xx IP.
Мэтт

Спасибо за ваш комментарий. Так что это изменит мой подход к маршрутизации из определенного интерфейса, правильно? Вы знаете, как я мог бы сделать это? В настоящее время Docker, похоже, просто использует шлюз по умолчанию хост-системы, а это не желаемое поведение. Я хочу, чтобы все вещи из Docker направлялись из определенного интерфейса.

Я не уверен, почему то, что я предложил, не работает. Но другим вариантом может быть использование трубопроводов. github.com/jpetazzo/pipework Также может быть полезно прочитать расширенный сетевой раздел справки Docker. docs.docker.com/articles/networking
Мэтт

@Matt Я благодарю вас за ваши предложения по этому вопросу. Из того, что я прочитал, конвейер требует дополнительных команд после запуска контейнера, чтобы получить доступ к правильному шлюзу, который выглядит как уязвимость безопасности. Первоначально он был создан для соединения контейнеров между несколькими хостами, что на самом деле не является моей целью. Что касается продвинутых сетевых документов Docker, я еще раз посмотрю, но это не помогло в этой ситуации до сих пор (я читал это перед публикацией здесь). Я собираюсь опубликовать ссылку на этот пост на странице проблем Github Docker и посмотреть, как это будет происходить.

Вот ссылка на выпуск Github: github.com/docker/docker/issues/13762

Ответы:


1

Возможно, вам придется больше посмотреть на настройку iptables. Docker маскирует весь трафик, исходящий из подсети контейнера, скажем, 172.17.0.0/16, в 0.0.0.0. Если вы запустите iptables -L -n -t nat, вы увидите цепочку POSTROUTING под таблицей nat, которая делает это:

Цепь РАЗМЕЩЕНИЯ (ПРИНЯТЬ ПОЛИТИКУ)
целевой целевой источник назначения
MASQUERADE all - 172.17.0.0/16 0.0.0.0/0

Теперь вы можете удалить это правило и заменить его правилом, которое маскирует весь трафик, исходящий из подсети контейнеров, в IP-адрес вашего второго интерфейса - 192.168.1.2, поскольку это то, что вам нужно. Правило удаления будет, при условии, что это первое правило в цепочке POSTROUTING -

iptables -t nat -D POSTROUTING 1

Затем вы добавляете это пользовательское правило -

iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT - к источнику 192.168.1.2

На самом деле он маскирует его на выходной интерфейс. Эта часть назначения просто говорит, что любой трафик, исходящий из исходной подсети в любое место назначения, будет маскироваться.
Мэтт

К сожалению, это не сработало. Мой полный процесс должен был работать: ip rule add from 172.17.0.0/16 table docker ip route add default via 192.168.1.2 dev eth1 table docker ip route flush cache iptables -t nat -D POSTROUTING 1 iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT --to-source 192.168.1.2 /etc/init.d/docker restart. Затем я попытался запустить ping out и traceroute из контейнера и не смог ничего достичь.

У меня было похожее требование, хост имеет вторичную настройку IP для доступа к общедоступному IP. И я хотел, чтобы докер-контейнер следовал тому же самому. Это сработало в этом случае: «iptables -t nat -I POSTROUTING 1 -s 172.17.0.0/16 -d PUBIP / 32 -j SNAT - к источнику SECONDARYIP»
Ram

1

Мы с другом столкнулись с этой проблемой, когда нам хотелось, чтобы докер поддерживал несколько запросов на обслуживание сетевых интерфейсов. Мы специально работали со службой AWS EC2, где мы также подключали / настраивали / вызывали дополнительные интерфейсы. В этом проекте есть не только то, что вам нужно, поэтому я постараюсь включить сюда только то, что вам нужно.

Сначала мы создали отдельную таблицу маршрутов для eth1:

ip route add default via 192.168.1.2 dev eth1 table 1001

Затем мы настроили таблицу mangle для установки некоторых меток соединения, поступающих из eth1:

iptables -t mangle -A PREROUTING -i eth1 -j MARK --set-xmark 0x1001/0xffffffff
iptables -t mangle -A PREROUTING -i eth1 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff

Наконец, мы добавляем это правило для всех fwmarks, чтобы использовать новую таблицу, которую мы создали.

ip rule add from all fwmark 0x1001 lookup 1001

Приведенная ниже iptablesкоманда восстановит метку соединения, а затем разрешит правилу маршрутизации использовать правильную таблицу маршрутизации.

iptables -w -t mangle -A PREROUTING -i docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff

Я считаю, что это все, что нужно из нашего более сложного примера, где (как я уже сказал) наш проект подключал / настраивал / вызывал eth1интерфейс во время загрузки.

Теперь этот пример не помешает соединениям eth0обслуживать запросы, docker0но я думаю, что вы можете добавить правило маршрутизации, чтобы предотвратить это.


@stuntmachine просто любопытно, если бы у тебя была возможность попробовать это? Если вы уже что-то выяснили самостоятельно, сможете ли вы поделиться своим решением?
williamsbdev

Привет @williamsbdev - старый пост и длинный план, но вы думаете, что это решение также подойдет для моей проблемы с SO? stackoverflow.com/questions/51312310/…
Веселый Роджер

2
Веселый Роджер - я верю, что это решит вашу проблему. Недавно у меня была другая команда, спросившая меня об этом посте в блоге (по сути, то же решение, что и здесь), и они сказали, что он отлично работает. williamsbdev.com/posts/docker-connection-marking
williamsbdev

0

Маскарад не из 172.17.42.1, а скорее

 -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

Это означает, что это правило не будет работать правильно.

ip rule add from 172.17.42.1 table docker

Попробуй вместо

ip rule add from 172.17.0.0/16 table docker

К сожалению, это не сработало. Мои контейнеры все еще направлялись к тому же шлюзу по умолчанию, что и хост. Вот что я побежал: ip rule add from 172.17.0.0/16 table docker ip route add default via 192.168.1.2 dev eth1 table docker ip route flush cache /etc/init.d/docker restart Из контейнера я запустил traceroute, и первый прыжок был 10.1.1.1, когда он должен был быть 192.168.1.1
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.