用ovs-docker让容器网络支持Vlan隔离

参考文献:
https://blog.csdn.net/canxinghen/article/details/50321573

docker原生使用linux bridge来创建网桥,这样无法使用vlan容器间的网络进行网络隔离。
openvswitch社区提供了一个工具ovs-docker来给docker快速搭建ovs网络。

1、使用ovs创建网桥

ovs-vsctl add-br br0

2、创建3个容器,容器网络模型设置为none

2.1、拉取测试镜像busybox

docker pull busybox:latest

2.2、创建容器

docker run -it -d --name routerA --net=none --privileged busybox
docker run -it -d --name routerB --net=none --privileged busybox
docker run -it -d --name routerC --net=none --privileged busybox

3、使用ovs-docker给容器添加网口

ovs-docker add-port br0 eth0 routerA --ipaddress=192.168.100.1/24
ovs-docker add-port br0 eth0 routerB --ipaddress=192.168.100.2/24
ovs-docker add-port br0 eth1 routerB --ipaddress=192.168.200.2/24
ovs-docker add-port br0 eth0 routerC --ipaddress=192.168.100.3/24

上面给容器routeB,添加了两个网卡;


可见,通过ovs-docker 给容器添加网卡时,会在逻辑交换机上添加端口,在宿主机上添加接口信息

4、测试容器之间是否连通

5、设置vlan隔离

设置Vlan的参数为:网桥名、容器内网口名、容器名、vlan号

ovs-docker set-vlan br0 eth0 routerA 100
ovs-docker set-vlan br0 eth0 routerB 100
ovs-docker set-vlan br0 eth1 routerB 200
ovs-docker set-vlan br0 eth0 routerC 203


6、测试是否连通呢?

7、ovs-docker原理介绍

ovs-docker简单又好用,它的原理就是用netns和veth来作容器和ovs网桥间的桥接!

#!/bin/bash
 
#检查对应的可执行程序有没有在$PATH目录下
#检查的可执行程序有 ovs-vsctl、docker、uuidgen
search_path () {
    save_IFS=$IFS
    IFS=:
    for dir in $PATH; do
        IFS=$save_IFS
        if test -x "$dir/$1"; then
            return 0
        fi
    done
    IFS=$save_IFS
    echo >&2 "$0: $1 not found in \$PATH, please install and try again"
    exit 1
}
 
#给所有的ovs-vsctl命令增加超时时间
ovs_vsctl () {
    ovs-vsctl --timeout=60 "$@"
}
 
#每个容器是一个netns
#但是只有在 /va/run/netns 下面建立链接,才能将veth挂在到该netns里面
#也只有了链接,才能用ip netns list查询到
create_netns_link () {
    mkdir -p /var/run/netns
    if [ ! -e /var/run/netns/"$PID" ]; then
        ln -s /proc/"$PID"/ns/net /var/run/netns/"$PID"
        trap 'delete_netns_link' 0
        for signal in 1 2 3 13 14 15; do
            #在收到特定信号后,删掉链接
            #这些信号有:程序退出、ctrl+c、进程被杀掉等
            trap 'delete_netns_link; trap - $signal; kill -$signal $$' $signal
        done
    fi
}
 
#删除链接
delete_netns_link () {
    rm -f /var/run/netns/"$PID"
}
 
#从ovs的数据库中查询port
#查询条件是在创建port的时候设进去的 external_ids 
get_port_for_container_interface () {
    CONTAINER="$1"
    INTERFACE="$2"
 
    PORT=`ovs_vsctl --data=bare --no-heading --columns=name find interface \
             external_ids:container_id="$CONTAINER"  \
             external_ids:container_iface="$INTERFACE"`
    if [ -z "$PORT" ]; then
        echo >&2 "$UTIL: Failed to find any attached port" \
                 "for CONTAINER=$CONTAINER and INTERFACE=$INTERFACE"
    fi
    echo "$PORT"
}
 
#给容器添加port,原理是创建一个veth,一端挂在ovs网桥上,一端设到netns里面
add_port () {
    BRIDGE="$1"
    INTERFACE="$2"
    CONTAINER="$3"
 
    #参数验证
    if [ -z "$BRIDGE" ] || [ -z "$INTERFACE" ] || [ -z "$CONTAINER" ]; then
        echo >&2 "$UTIL add-port: not enough arguments (use --help for help)"
        exit 1
    fi
 
    #获取附加参数:ip地址、mac地址、网关信息、mtu信息
    shift 3
    while [ $# -ne 0 ]; do
        case $1 in
            --ipaddress=*)
                ADDRESS=`expr X"$1" : 'X[^=]*=\(.*\)'`
                shift
                ;;
            --macaddress=*)
                MACADDRESS=`expr X"$1" : 'X[^=]*=\(.*\)'`
                shift
                ;;
            --gateway=*)
                GATEWAY=`expr X"$1" : 'X[^=]*=\(.*\)'`
                shift
                ;;
            --mtu=*)
                MTU=`expr X"$1" : 'X[^=]*=\(.*\)'`
                shift
                ;;
            *)
                echo >&2 "$UTIL add-port: unknown option \"$1\""
                exit 1
                ;;
        esac
    done
 
    #查看对应容器的特定端口是否已经存在
    # Check if a port is already attached for the given container and interface
    PORT=`get_port_for_container_interface "$CONTAINER" "$INTERFACE" \
            2>/dev/null`
    if [ -n "$PORT" ]; then
        echo >&2 "$UTIL: Port already attached" \
                 "for CONTAINER=$CONTAINER and INTERFACE=$INTERFACE"
        exit 1
    fi
 
    #网桥不存在就创建网桥
    if ovs_vsctl br-exists "$BRIDGE" || \
        ovs_vsctl add-br "$BRIDGE"; then :; else
        echo >&2 "$UTIL: Failed to create bridge $BRIDGE"
        exit 1
    fi
 
    #通过docker inspect命令获取容器的PID(netns的id)
    if PID=`docker inspect -f '{{.State.Pid}}' "$CONTAINER"`; then :; else
        echo >&2 "$UTIL: Failed to get the PID of the container"
        exit 1
    fi
 
    #为后面将veth打入netns,需要建立链接
    create_netns_link
 
    #创建一个veth对,veth的名字用uuid,加了“_l”后缀的是挂在网桥上的,加了“_c”的是挂在容器内
    ID=`uuidgen | sed 's/-//g'`
    PORTNAME="${ID:0:13}"
    ip link add "${PORTNAME}_l" type veth peer name "${PORTNAME}_c"
 
    # 将“_l”挂到网桥上,挂时增加external_ids信息用于后面查找和删除
    if ovs_vsctl --may-exist add-port "$BRIDGE" "${PORTNAME}_l" \
       -- set interface "${PORTNAME}_l" \
       external_ids:container_id="$CONTAINER" \
       external_ids:container_iface="$INTERFACE"; then :; else
        echo >&2 "$UTIL: Failed to add "${PORTNAME}_l" port to bridge $BRIDGE"
        ip link delete "${PORTNAME}_l"
        exit 1
    fi
 
    #拉起“_l”的veth端口
    ip link set "${PORTNAME}_l" up
 
    #将“_c”移到容器的netns里面
    ip link set "${PORTNAME}_c" netns "$PID"
 
    #将“_c”改名为用户设置的接口名,例如ethx
    ip netns exec "$PID" ip link set dev "${PORTNAME}_c" name "$INTERFACE"
    ip netns exec "$PID" ip link set "$INTERFACE" up
 
    #设置mtu
    if [ -n "$MTU" ]; then
        ip netns exec "$PID" ip link set dev "$INTERFACE" mtu "$MTU"
    fi
 
    #设置ip
    if [ -n "$ADDRESS" ]; then
        ip netns exec "$PID" ip addr add "$ADDRESS" dev "$INTERFACE"
    fi
 
    #设置mac
    if [ -n "$MACADDRESS" ]; then
        ip netns exec "$PID" ip link set dev "$INTERFACE" address "$MACADDRESS"
    fi
 
    #设置网关
    if [ -n "$GATEWAY" ]; then
        ip netns exec "$PID" ip route add default via "$GATEWAY"
    fi
}
 
#根据添加port时的external_ids查询port后删除
del_port () {
    BRIDGE="$1"
    INTERFACE="$2"
    CONTAINER="$3"
 
    if [ "$#" -lt 3 ]; then
        usage
        exit 1
    fi
 
    PORT=`get_port_for_container_interface "$CONTAINER" "$INTERFACE"`
    if [ -z "$PORT" ]; then
        exit 1
    fi
 
    ovs_vsctl --if-exists del-port "$PORT"
 
    ip link delete "$PORT"
}
 
del_ports () {
    BRIDGE="$1"
    CONTAINER="$2"
    if [ "$#" -lt 2 ]; then
        usage
        exit 1
    fi
 
    PORTS=`ovs_vsctl --data=bare --no-heading --columns=name find interface \
             external_ids:container_id="$CONTAINER"`
    if [ -z "$PORTS" ]; then
        exit 0
    fi
 
    for PORT in $PORTS; do
        ovs_vsctl --if-exists del-port "$PORT"
        ip link delete "$PORT"
    done
}
 
#使用ovs的set port tag的方式设置vlan
set_vlan () {
    BRIDGE="$1"
    INTERFACE="$2"
    CONTAINER_ID="$3"
    VLAN="$4"
 
    if [ "$#" -lt 4 ]; then
        usage
        exit 1
    fi
 
    PORT=`get_port_for_container_interface "$CONTAINER_ID" "$INTERFACE"`
    if [ -z "$PORT" ]; then
        exit 1
    fi
    ovs_vsctl set port "$PORT" tag="$VLAN"
}
 
usage() {
    cat << EOF
${UTIL}: Performs integration of Open vSwitch with Docker.
usage: ${UTIL} COMMAND
 
Commands:
  add-port BRIDGE INTERFACE CONTAINER [--ipaddress="ADDRESS"]
                    [--gateway=GATEWAY] [--macaddress="MACADDRESS"]
                    [--mtu=MTU]
                    Adds INTERFACE inside CONTAINER and connects it as a port
                    in Open vSwitch BRIDGE. Optionally, sets ADDRESS on
                    INTERFACE. ADDRESS can include a '/' to represent network
                    prefix length. Optionally, sets a GATEWAY, MACADDRESS
                    and MTU.  e.g.:
                    ${UTIL} add-port br-int eth1 c474a0e2830e
                    --ipaddress=192.168.1.2/24 --gateway=192.168.1.1
                    --macaddress="a2:c3:0d:49:7f:f8" --mtu=1450
  del-port BRIDGE INTERFACE CONTAINER
                    Deletes INTERFACE inside CONTAINER and removes its
                    connection to Open vSwitch BRIDGE. e.g.:
                    ${UTIL} del-port br-int eth1 c474a0e2830e
  del-ports BRIDGE CONTAINER
                    Removes all Open vSwitch interfaces from CONTAINER. e.g.:
                    ${UTIL} del-ports br-int c474a0e2830e
  set-vlan BRIDGE INTERFACE CONTAINER VLAN
                    Configures the INTERFACE of CONTAINER attached to BRIDGE
                    to become an access port of VLAN. e.g.:
                    ${UTIL} set-vlan br-int eth1 c474a0e2830e 5
Options:
  -h, --help        display this help message.
EOF
}
 
#查看$PATH是否存在对应的命令
UTIL=$(basename $0)
search_path ovs-vsctl
search_path docker
search_path uuidgen
 
if (ip netns) > /dev/null 2>&1; then :; else
    echo >&2 "$UTIL: ip utility not found (or it does not support netns),"\
             "cannot proceed"
    exit 1
fi
 
if [ $# -eq 0 ]; then
    usage
    exit 0
fi
 
#根据输入的参数决定动作
case $1 in
    "add-port")
        shift
        add_port "$@"
        exit 0
        ;;
    "del-port")
        shift
        del_port "$@"
        exit 0
        ;;
    "del-ports")
        shift
        del_ports "$@"
        exit 0
        ;;
    "set-vlan")
        shift
        set_vlan "$@"
        exit 0
        ;;
    -h | --help)
        usage
        exit 0
        ;;
    *)
        echo >&2 "$UTIL: unknown command \"$1\" (use --help for help)"
        exit 1
        ;;
esac

把上面的脚本,放到这个目录下,就行
文件名是ovs-docker

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码二哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值