參考文獻:
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