Изменение маршрута по умолчанию в контейнере докеров
У меня есть контейнер докеров, который подключен к двум сетям, мосту по умолчанию и настраиваемому мосту. По умолчанию он связан с другим контейнером только в сети по умолчанию и через пользовательский мост, он получает IP-адрес в локальной сети.
LAN -- [homenet] -- container1 -- [bridge] -- container2
sudo docker network inspect homenet
[{ "Name": "homenet",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [{ "Subnet": "192.168.130.0/24",
"Gateway": "192.168.130.8",
"AuxiliaryAddresses": { "DefaultGatewayIPv4": "192.168.130.3" }}]
},
"Internal": false,
"Containers": {
"$cid1": { "Name": "container",
"EndpointID": "$eid1_1",
"MacAddress": "$mac1_1",
"IPv4Address": "192.168.130.38/24", }
},
"Options": { "com.docker.network.bridge.name": "br-homenet" },
"Labels": {}}]
и мост:
sudo docker network inspect bridge
[{
"Name": "bridge",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [{ "Subnet": "172.17.0.0/16" }]
},
"Internal": false,
"Containers": {
"$cid2": {
"Name": "container2",
"EndpointID": "$eid2",
"MacAddress": "$mac2",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": "" },
"$cid1": {
"Name": "container1",
"EndpointID": "$eid1_2",
"MacAddress": "$mac1_2",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": "" }
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}]
Это очень хорошо работает во внутренней сети, однако у меня есть проблема маршрутизации:
sudo docker exec -it container1 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
192.168.130.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
Как изменить маршрут по умолчанию на 192.169.130.3, чтобы он не перезагрузился?
Я могу изменить его, пока контейнер1 работает с
pid=$(sudo docker inspect -f '{{.State.Pid}}' container1)
sudo mkdir -p /var/run/netns
sudo ln -s /proc/$pid/ns/net /var/run/netns/$pid
sudo ip netns exec $pid ip route del default
sudo ip netns exec $pid ip route add default via 192.168.130.3
но это исчезло после перезапуска. Как это изменить?
Обновление: Очевидно, лексикографический порядок сетей также может быть частью проблемы. Я проверю его, когда у меня появится шанс.
Ответы
Ответ 1
Если я понимаю вопрос, проблема заключается в следующем: при перезапуске контейнера, подключенного к нескольким мостам, как выбрать мост для использования по умолчанию.
Я искал доступные параметры и делал некоторые тесты, я не нашел опции командной строки докера, чтобы указать маршрут по умолчанию или предпочесть мост по умолчанию, когда контейнер подключен к нескольким мостам. Когда я перезапускаю контейнер, подключенный к мосту по умолчанию (bridge
) и настраиваемому мосту (ваш homenet
), по умолчанию маршрут автоматически устанавливается на использование моста по умолчанию (шлюз 172.17.0.1
). Это соответствует описанному вами поведению.
Решение 1: укажите команду запуска script в команде запуска, которая отвечает за изменение маршрута по умолчанию и запустите службы (ы), которые должен выполнить контейнер,:
docker run \
--cap-add NET_ADMIN \ # to allow changing net settings inside the container
--name container1 \
--restart always \ # restart policy
your_image \
/path/to/your_start_script.sh
your_start_script.sh
:
ip route del default
ip route add default via 192.168.130.3
# here goes instructions/services your container is supposed to run
Этот script должен быть доступен внутри контейнера, он может находиться в общей папке (опция -v
) или загружаться при создании образа с помощью файла Docker.
Примечание: перед подключением контейнера к вашему пользовательскому мосту (docker network connect homenet container1
), your_start_script.sh
выйдет из строя, потому что маршрут по умолчанию не соответствует какой-либо доступной сети.
Я протестировал для регистрации вывода ip route
внутри container1
с --restart always
, после подключения к пользовательскому мосту у него есть желаемый маршрут по умолчанию.
Решение 2. Задайте маршрут контейнера по умолчанию с хоста в событиях запуска контейнера
docker events --filter "container=container1" |\
awk '/container start/ { system("/path/to/route_setting.sh") }'
Где route_setting.sh
содержит ваши инструкции по изменению маршрута по умолчанию для контейнера:
pid=$(sudo docker inspect -f '{{.State.Pid}}' container1)
sudo mkdir -p /var/run/netns
sudo ln -s /proc/$pid/ns/net /var/run/netns/$pid
sudo ip netns exec $pid ip route del default
sudo ip netns exec $pid ip route add default via 192.168.130.3
Это решение избегает предоставления специальных разрешений контейнеру и передает маршрут, изменяя ответственность на хост.
Ответ 2
nsenter -n -t $ (докер проверяет --format {{.State.Pid}} $ dockername) ip route добавить что-то.
nsenter -n -t $ (проверка докера --format {{.State.Pid}} $ dockername) ip route del что-то.
Ответ 3
@Silicium14
Большое спасибо за ваше второе решение. Мне потребовалось довольно много времени, чтобы найти способ задавать маршруты при запуске контейнера. Я немного изменил ваши строки в соответствии со своими потребностями, так как мне нужно предоставить имя контейнера из событий докера в сценарий
Сначала я запускаю слушателя для моих событий.
docker events --filter 'container=box1' --filter 'container=box2' --filter 'event=start' --filter 'event=stop' --format '{{.Actor.Attributes.name}}'|awk '{ system("/work/route_setting.sh " $1) }'
Я использую больше фильтров, так как мне нужны события для двух контейнеров типа start или stop. Используя --format, можно очень хорошо контролировать вывод. Таким образом, только имя контейнера передается в awk. Который затем запускает мой скрипт маршрутизации с правильным именем контейнера.
#!/bin/bash
# exit if no container name provided as $1
[ "x$1" = 'x' ] && exit 1
# holds pid of the docker container
pid=''
# read the pid for container
pid=$(docker inspect -f '{{.State.Pid}}' "${1}" 2>/dev/null)
# if for whatevery reason we get pid 0 avoid setting routes
[ "x$pid" = 'x0' ] && pid=''
if [ "x$pid" != 'x' ] ; then
# let the routing happen
mkdir -p /var/run/netns
ln -s /proc/$pid/ns/net /var/run/netns/$pid
ip netns exec $pid ip route add 10.0.0.0/8 via 10.66.101.1
ip netns exec $pid ip route add 192.168.0.0/16 via 10.66.101.1
fi
# clean up broken symlinks which occur when a container is stopped
# verify that your find supports -xtype l
find /var/run/netns -xtype l -exec rm -f '{}' \;
Ответ 4
с опцией
Пример:
dockerd --bip 10.1.0.1/24
если вы используете ubuntu 14.04, измените
/etc/default/docker
если вы используете ubuntu 16.04, измените
/lib/systemd/system/docker