it-swarm.com.de

Standardroute im Docker-Container ändern

Ich habe einen Docker-Container, der mit zwei Netzwerken verbunden ist, der Standardbrücke und einer benutzerdefinierten Brücke. Über die Standardeinstellung ist sie nur im Standardnetzwerk mit einem anderen Container verknüpft. Über die benutzerdefinierte Brücke erhält sie eine IP-Adresse im lokalen Netzwerk.

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": {}}]

und Brücke:

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": {}
}]

Dies funktioniert ziemlich gut vom internen Netzwerk aus, jedoch habe ich ein Routing-Problem:

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

Wie kann ich die Standardroute in 192.169.130.3 ändern, sodass ein Neustart bestehen bleibt?

Ich kann es ändern, während container1 mit läuft

 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

das ist aber nach einem neustart weg. Wie kann ich das ändern?

Update: Offensichtlich könnte die lexikographische Reihenfolge der Netzwerke auch Teil des Problems sein. Ich werde es testen, wenn ich eine Chance habe.

17
martin

Wenn ich die Frage verstehe, ist das Problem: beim Neustart eines Containers, der mit mehreren Bridges verbunden ist, wie soll eine Brücke für die Standardroute bevorzugt werden?

Ich habe nach verfügbaren Optionen gesucht und einige Tests durchgeführt. Ich habe keine Docker-Befehlszeilenoption gefunden, um eine Standardroute anzugeben oder eine Brücke als Standard zu bevorzugen, wenn der Container mit mehreren Brücken verbunden ist. Wenn ich einen Container neu starte, der mit der Standardbrücke (bridge) und einer benutzerdefinierten Brücke (Ihrem homenet) verbunden ist, wird die Standardroute automatisch auf die Verwendung der Standardbrücke (Gateway 172.17.0.1). Dies entspricht dem von Ihnen beschriebenen Verhalten.

Lösung 1: Geben Sie im Befehl run ein Startskript an, mit dem die Standardroute geändert und die Dienste gestartet werden sollen, die der Container ausführen soll.:

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

Der 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

Dieses Skript muss im Container verfügbar sein. Es kann sich in einem freigegebenen Ordner befinden (Option -v) Oder beim Erstellen eines Images mit einer Docker-Datei geladen werden.

Hinweis: Bevor Sie den Container mit Ihrer benutzerdefinierten Bridge verbinden (docker network connect homenet container1), Stürzt your_start_script.sh Ab, da die Standardroute keinem verfügbaren Netzwerk entspricht.

Ich habe getestet, um die Ausgabe von ip route In container1 Zu protokollieren und mit --restart always Auszuführen. Nachdem ich es mit der benutzerdefinierten Bridge verbunden habe, hat es die gewünschte Standardroute.

Lösung 2: Festlegen der Container-Standardroute vom Host aus bei Container-Startereignissen

docker events --filter "container=container1" |\
  awk '/container start/ { system("/path/to/route_setting.sh") }'

Wobei route_setting.sh Ihre Anweisungen zum Ändern der Standardroute des Containers enthält:

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

Diese Lösung vermeidet, dem Container spezielle Berechtigungen zu erteilen, und überträgt die Verantwortung für die Routenänderung auf den Host.

19
Silicium14

nsenter -n -t $ (Docker inspect --format {{.State.Pid}} $ Dockername) IP-Route etwas hinzufügen.

nsenter -n -t $ (Docker inspect --format {{.State.Pid}} $ Dockername) ip route del etwas.

0
Yeats Raw

@ Silicium14

Vielen Dank für Ihre 2. Lösung. Ich habe lange gebraucht, um einen Weg zu finden, Routen beim Start des Containers festzulegen. Ich habe Ihre Zeilen ein wenig nach meinen Bedürfnissen geändert, da ich dem Skript einen Containernamen von docker events angeben muss

Zuerst starte ich den Hörer für meine Veranstaltungen.

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) }'

Ich verwende mehr Filter, da ich die Ereignisse für zwei Container vom Typ start oder stop .__ benötige. Mit --format kann man die Ausgabe sehr gut steuern. Daher wird nur der Containername an awk weitergeleitet. Dadurch wird mein Routing-Skript mit dem richtigen Containernamen ausgelöst.

#!/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 '{}' \;
0
jahlives