【问题标题】:Change default route in docker container更改 docker 容器中的默认路由
【发布时间】:2016-08-21 08:17:07
【问题描述】:

我有一个连接到两个网络的 docker 容器,默认网桥和自定义网桥。默认情况下,它只链接到默认网络中的另一个容器,通过自定义桥接,它获取本地网络中的 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 以使其持续重启?

我可以在 container1 运行时更改它

 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

但重启后就没有了。我该如何改变呢?

更新:显然,the lexicographical order of the networks 也可能是问题的一部分。有机会我会测试的。

【问题讨论】:

  • 感谢您提及“字典顺序...”!我刚刚通过重命名要用作默认路由的网络解决了类似的问题,以便它按字典顺序排在第一位。

标签: networking docker routing


【解决方案1】:

如果我理解这个问题,问题是:重新启动连接到多个网桥的容器时,如何选择一个网桥作为默认路由?

我搜索了可用选项并进行了一些测试,当容器连接到多个网桥时,我没有找到任何 docker 命令行选项来指定默认路由或首选网桥作为默认值。当我重新启动连接到默认网桥(bridge)和自定义网桥(您的homenet)的容器时,默认路由会自动设置为使用默认网桥(网关172.17.0.1)。这与您描述的行为相对应。

解决方案 1:在运行命令中指定一个启动脚本,该脚本负责更改默认路由并启动容器必须运行的服务

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

此脚本必须在容器内可用,它可以位于共享文件夹中(-v 选项)或在构建镜像时使用 Dockerfile 加载。

注意:在将容器连接到您的自定义网桥 (docker network connect homenet container1) 之前,your_start_script.sh 将崩溃,因为默认路由不对应任何可用网络。

我测试了在container1 中记录ip route 的输出,使用--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

这个方案避免了给容器特殊的权限,将路由改变的责任转移给了宿主。

【讨论】:

  • 谢谢,这是一个很好的答案。但是,我不喜欢我需要赋予容器特殊功能。命令运行后,你能以某种方式删除它们吗?对于奖励积分,有没有办法自动让正确的脚本/程序自动运行(这样,如果 docker 容器中的脚本名称发生变化,我就不必调整你的脚本)?
  • 我喜欢脚本可以驻留在卷中的提示,我忘记了这一点,但这让我有些头疼。
  • 我更新了我的答案,添加了第二个解决方案,可以避免您描述的问题。
  • 谢谢,确实活动部分是我一直在寻找但不知道要搜索的内容。
  • 这不适用于 Docker Swarm。在独立的 Docker 主机上,这不是必需的。换个bip就够了
【解决方案2】:

你可以用nsenter命令进入容器的命名空间,然后在里面执行一个命令。差不多这样:

nsenter -n -t $(docker inspect --format {{.State.Pid}} $dockername) ip route add something.
nsenter -n -t $(docker inspect --format {{.State.Pid}} $dockername) ip route del something.

【讨论】:

  • 我们有一个docker exec 命令可以做到这一点。那为什么要使用nsenter,除非你正在学习?
  • 如果容器中没有 ip 命令,这将非常有用;谢谢!
【解决方案3】:

@Silicium14

非常感谢您的第二个解决方案。我花了很长时间才找到一种在容器启动时设置路线的方法。我根据需要稍微更改了您的行,因为我需要从 docker events 向脚本提供容器名称

首先我为我的事件启动监听器。

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

我使用更多过滤器,因为我需要两个 startstop 类型容器的事件 使用 --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 '{}' \;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 2019-07-21
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    相关资源
    最近更新 更多