首页 » docker » docker网络连接工具之pipework

docker网络连接工具之pipework

 

把这个pipework名字的脚本复制到宿主主机上的/usr/local/bin/ 目录下

主机A地址为10.10.101.105/24,网关为10.10.101.254,需要给Docker容器的地址配置为10.10.101.150/24。在主机A上做如下操作:

安装pipework
git clone https://github.com/jpetazzo/pipework
cp ~/pipework/pipework /usr/local/bin/ #安装pipework这个软件,放在bin目录下的。
systemctl start docker.service #启动Docker容器。
docker run -itd --name test1 ubuntu /bin/bash
#配置容器网络,并连到网桥br0上。网关在IP地址后面加@指定。
#若主机环境中存在dhcp服务器,也可以通过dhcp的方式获取IP
#pipework br0 test1 dhcp
pipework br0 test1 10.10.101.150/24@10.10.101.254
#将主机eth0桥接到br0上,并把eth0的IP配置在br0上。这里由于是远程操作,中间网络会断掉,所以放在一条命令中执行。
ip addr add 10.10.101.105/24 dev br0; \
    ip addr del 10.10.101.105/24 dev eth0; \#eth0是宿主主机的网卡名字,如果是其他如:eno16777736,则需要更改。
    brctl addif br0 eth0; \
    ip route del default; \
    ip route add default gw 10.10.101.254 dev br0

完成上述步骤后,我们发现Docker容器已经可以使用新的IP和主机网络里的机器相互通信了。

 

这个脚本的内容如下:

#!/bin/sh
# This code should (try to) follow Google's Shell Style Guide
# (https://google-styleguide.googlecode.com/svn/trunk/shell.xml)
set -e

case "$1" in
 --wait)
 WAIT=1
 ;;
esac

IFNAME=$1

# default value set further down if not set here
CONTAINER_IFNAME=
if [ "$2" = "-i" ]; then
 CONTAINER_IFNAME=$3
 shift 2
fi

if [ "$2" = "-l" ]; then
 LOCAL_IFNAME=$3
 shift 2
fi

GUESTNAME=$2
IPADDR=$3
MACADDR=$4

case "$MACADDR" in
 *@*)
 VLAN="${MACADDR#*@}"
 VLAN="${VLAN%%@*}"
 MACADDR="${MACADDR%%@*}"
 ;;
 *)
 VLAN=
 ;;
esac

# did they ask to generate a custom MACADDR?
# generate the unique string
case "$MACADDR" in
 U:*)
 macunique="${MACADDR#*:}"
 # now generate a 48-bit hash string from $macunique
 MACADDR=$(echo $macunique|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
 ;;
esac


[ "$IPADDR" ] || [ "$WAIT" ] || {
 echo "Syntax:"
 echo "pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]"
 echo "pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> dhcp [macaddr][@vlan]"
 echo "pipework --wait [-i containerinterface]"
 exit 1
}

# Succeed if the given utility is installed. Fail otherwise.
# For explanations about `which` vs `type` vs `command`, see:
# http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212
# (Thanks to @chenhanxiao for pointing this out!)
installed () {
 command -v "$1" >/dev/null 2>&1
}

# Google Styleguide says error messages should go to standard error.
warn () {
 echo "$@" >&2
}
die () {
 status="$1"
 shift
 warn "$@"
 exit "$status"
}

# First step: determine type of first argument (bridge, physical interface...),
# Unless "--wait" is set (then skip the whole section)
if [ -z "$WAIT" ]; then 
 if [ -d "/sys/class/net/$IFNAME" ]
 then
 if [ -d "/sys/class/net/$IFNAME/bridge" ]; then
 IFTYPE=bridge
 BRTYPE=linux
 elif installed ovs-vsctl && ovs-vsctl list-br|grep -q "^${IFNAME}$"; then
 IFTYPE=bridge
 BRTYPE=openvswitch
 elif [ "$(cat "/sys/class/net/$IFNAME/type")" -eq 32 ]; then # InfiniBand IPoIB interface type 32
 IFTYPE=ipoib
 # The IPoIB kernel module is fussy, set device name to ib0 if not overridden
 CONTAINER_IFNAME=${CONTAINER_IFNAME:-ib0}
 PKEY=$VLAN
 else IFTYPE=phys
 fi
 else
 case "$IFNAME" in
 br*)
 IFTYPE=bridge
 BRTYPE=linux
 ;;
 ovs*)
 if ! installed ovs-vsctl; then
 die 1 "Need OVS installed on the system to create an ovs bridge"
 fi
 IFTYPE=bridge
 BRTYPE=openvswitch
 ;;
 dummy*)
 IFTYPE=dummy
 ;;
 *) die 1 "I do not know how to setup interface $IFNAME." ;;
 esac
 fi
fi

# Set the default container interface name to eth1 if not already set
CONTAINER_IFNAME=${CONTAINER_IFNAME:-eth1}

[ "$WAIT" ] && {
 while true; do
 # This first method works even without `ip` or `ifconfig` installed,
 # but doesn't work on older kernels (e.g. CentOS 6.X). See #128.
 grep -q '^1$' "/sys/class/net/$CONTAINER_IFNAME/carrier" && break
 # This method hopefully works on those older kernels.
 ip link ls dev "$CONTAINER_IFNAME" && break
 sleep 1
 done > /dev/null 2>&1
 exit 0
}

[ "$IFTYPE" = bridge ] && [ "$BRTYPE" = linux ] && [ "$VLAN" ] && {
 die 1 "VLAN configuration currently unsupported for Linux bridge."
}

[ "$IFTYPE" = ipoib ] && [ "$MACADDR" ] && {
 die 1 "MACADDR configuration unsupported for IPoIB interfaces."
}

# Second step: find the guest (for now, we only support LXC containers)
while read _ mnt fstype options _; do
 [ "$fstype" != "cgroup" ] && continue
 echo "$options" | grep -qw devices || continue
 CGROUPMNT=$mnt
done < /proc/mounts

[ "$CGROUPMNT" ] || {
 die 1 "Could not locate cgroup mount point."
}

# Try to find a cgroup matching exactly the provided name.
N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l)
case "$N" in
 0)
 # If we didn't find anything, try to lookup the container with Docker.
 if installed docker; then
 RETRIES=3
 while [ "$RETRIES" -gt 0 ]; do
 DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' "$GUESTNAME")
 [ "$DOCKERPID" != 0 ] && break
 sleep 1
 RETRIES=$((RETRIES - 1))
 done

 [ "$DOCKERPID" = 0 ] && {
 die 1 "Docker inspect returned invalid PID 0"
 }

 [ "$DOCKERPID" = "<no value>" ] && {
 die 1 "Container $GUESTNAME not found, and unknown to Docker."
 }
 else
 die 1 "Container $GUESTNAME not found, and Docker not installed."
 fi
 ;;
 1) true ;;
 *) die 1 "Found more than one container matching $GUESTNAME." ;;
esac

case "$IPADDR" in
 # Let's check first if the user asked for DHCP allocation.
 dhcp|dhcp:*)
 # Use Docker-specific strategy to run the DHCP client
 # from the busybox image, in the network namespace of
 # the container.
 if ! [ "$DOCKERPID" ]; then
 warn "You asked for a Docker-specific DHCP method."
 warn "However, $GUESTNAME doesn't seem to be a Docker container."
 warn "Try to replace 'dhcp' with another option?"
 die 1 "Aborting."
 fi
 DHCP_CLIENT=${IPADDR%%:*}
 ;;
 udhcpc|udhcpc:*|dhcpcd|dhcpcd:*|dhclient|dhclient:*)
 DHCP_CLIENT=${IPADDR%%:*}
 if ! installed "$DHCP_CLIENT"; then
 die 1 "You asked for DHCP client $DHCP_CLIENT, but I can't find it."
 fi
 ;;
 # Alright, no DHCP? Then let's see if we have a subnet *and* gateway.
 */*@*)
 GATEWAY="${IPADDR#*@}" GATEWAY="${GATEWAY%%@*}"
 IPADDR="${IPADDR%%@*}"
 ;;
 # No gateway? We need at least a subnet, anyway!
 */*) : ;;
 # ... No? Then stop right here.
 *)
 warn "The IP address should include a netmask."
 die 1 "Maybe you meant $IPADDR/24 ?"
 ;;
esac

# If a DHCP method was specified, extract the DHCP options.
if [ "$DHCP_CLIENT" ]; then
 case "$IPADDR" in
 *:*) DHCP_OPTIONS="${IPADDR#*:}" ;;
 esac
fi

if [ "$DOCKERPID" ]; then
 NSPID=$DOCKERPID
else
 NSPID=$(head -n 1 "$(find "$CGROUPMNT" -name "$GUESTNAME" | head -n 1)/tasks")
 [ "$NSPID" ] || {
 # it is an alternative way to get the pid
 NSPID=$(lxc-info -n "$GUESTNAME" | grep PID | grep -Eo '[0-9]+')
 [ "$NSPID" ] || {
 die 1 "Could not find a process inside container $GUESTNAME."
 }
 }
fi

# Check if an incompatible VLAN device already exists
[ "$IFTYPE" = phys ] && [ "$VLAN" ] && [ -d "/sys/class/net/$IFNAME.VLAN" ] && {
 ip -d link show "$IFNAME.$VLAN" | grep -q "vlan.*id $VLAN" || {
 die 1 "$IFNAME.VLAN already exists but is not a VLAN device for tag $VLAN"
 }
}

[ ! -d /var/run/netns ] && mkdir -p /var/run/netns
rm -f "/var/run/netns/$NSPID"
ln -s "/proc/$NSPID/ns/net" "/var/run/netns/$NSPID"

# Check if we need to create a bridge.
[ "$IFTYPE" = bridge ] && [ ! -d "/sys/class/net/$IFNAME" ] && {
 [ "$BRTYPE" = linux ] && {
 (ip link add dev "$IFNAME" type bridge > /dev/null 2>&1) || (brctl addbr "$IFNAME")
 ip link set "$IFNAME" up
 }
 [ "$BRTYPE" = openvswitch ] && {
 ovs-vsctl add-br "$IFNAME"
 }
}

[ "$IFTYPE" != "dummy" ] && MTU=$(ip link show "$IFNAME" | awk '{print $5}')
# If it's a bridge, we need to create a veth pair
[ "$IFTYPE" = bridge ] && {
 if [ -z "$LOCAL_IFNAME" ]; then
 LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}"
 fi
 GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}"
 # Does the link already exist?
 if ip link show "$LOCAL_IFNAME" >/dev/null 2>&1; then
 # link exists, is it in use?
 if ip link show "$LOCAL_IFNAME" up | grep -q "UP"; then
 echo "Link $LOCAL_IFNAME exists and is up"
 exit 1
 fi
 # delete the link so we can re-add it afterwards
 ip link del "$LOCAL_IFNAME"
 fi
 ip link add name "$LOCAL_IFNAME" mtu "$MTU" type veth peer name "$GUEST_IFNAME" mtu "$MTU"
 case "$BRTYPE" in
 linux)
 (ip link set "$LOCAL_IFNAME" master "$IFNAME" > /dev/null 2>&1) || (brctl addif "$IFNAME" "$LOCAL_IFNAME")
 ;;
 openvswitch)
 if ! ovs-vsctl list-ports "$IFNAME" | grep -q "^${LOCAL_IFNAME}$"; then
 ovs-vsctl add-port "$IFNAME" "$LOCAL_IFNAME" ${VLAN:+tag="$VLAN"}
 fi
 ;;
 esac
 ip link set "$LOCAL_IFNAME" up
}

# If it's a physical interface, create a macvlan subinterface
[ "$IFTYPE" = phys ] && {
 [ "$VLAN" ] && {
 [ ! -d "/sys/class/net/${IFNAME}.${VLAN}" ] && {
 ip link add link "$IFNAME" name "$IFNAME.$VLAN" mtu "$MTU" type vlan id "$VLAN"
 }
 ip link set "$IFNAME" up
 IFNAME=$IFNAME.$VLAN
 }
 GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME
 ip link add link "$IFNAME" dev "$GUEST_IFNAME" mtu "$MTU" type macvlan mode bridge
 ip link set "$IFNAME" up
}

# If it's an IPoIB interface, create a virtual IPoIB interface (the IPoIB
# equivalent of a macvlan device)
#
# Note: no macvlan subinterface nor Ethernet bridge can be created on top of an
# IPoIB interface. InfiniBand is not Ethernet. IPoIB is an IP layer on top of
# InfiniBand, without an intermediate Ethernet layer.
[ "$IFTYPE" = ipoib ] && {
 GUEST_IFNAME="${IFNAME}.${NSPID}"

 # If a partition key is provided, use it
 [ "$PKEY" ] && {
 GUEST_IFNAME="${IFNAME}.${PKEY}.${NSPID}"
 PKEY="pkey 0x$PKEY"
 }

 ip link add link "$IFNAME" name "$GUEST_IFNAME" type ipoib $PKEY
 ip link set "$IFNAME" up
}

# if its a dummy interface, create a dummy interface
[ "$IFTYPE" = dummy ] && {
 GUEST_IFNAME=du$NSPID$CONTAINER_IFNAME
 ip link add dev "$GUEST_IFNAME" type dummy
}

ip link set "$GUEST_IFNAME" netns "$NSPID"
ip netns exec "$NSPID" ip link set "$GUEST_IFNAME" name "$CONTAINER_IFNAME"
[ "$MACADDR" ] && ip netns exec "$NSPID" ip link set dev "$CONTAINER_IFNAME" address "$MACADDR"

# When using any of the DHCP methods, we start a DHCP client in the
# network namespace of the container. With the 'dhcp' method, the
# client used is taken from the Docker busybox image (therefore
# requiring no specific client installed on the host). Other methods
# use a locally installed client.
case "$DHCP_CLIENT" in
 dhcp)
 docker run -d --net container:$GUESTNAME --cap-add NET_ADMIN \
 busybox udhcpc -i "$CONTAINER_IFNAME" -x "hostname:$GUESTNAME" \
 $DHCP_OPTIONS \
 >/dev/null
 ;;
 udhcpc)
 ip netns exec "$NSPID" "$DHCP_CLIENT" -qi "$CONTAINER_IFNAME" \
 -x "hostname:$GUESTNAME" \
 $DHCP_OPTIONS
 ;;
 dhclient)
 ip netns exec "$NSPID" "$DHCP_CLIENT" "$CONTAINER_IFNAME" \
 -pf "/var/run/dhclient.$NSPID.pid" \
 $DHCP_OPTIONS
 # kill dhclient after get ip address to prevent device be used after container close
 kill "$(cat "/var/run/dhclient.$NSPID.pid")"
 rm "/var/run/dhclient.$NSPID.pid"
 ;;
 dhcpcd)
 ip netns exec "$NSPID" "$DHCP_CLIENT" -q "$CONTAINER_IFNAME" -h "$GUESTNAME"
 ;;
 "")
 if installed ipcalc; then
 eval $(ipcalc -b $IPADDR)
 ip netns exec "$NSPID" ip addr add "$IPADDR" brd "$BROADCAST" dev "$CONTAINER_IFNAME"
 else
 ip netns exec "$NSPID" ip addr add "$IPADDR" dev "$CONTAINER_IFNAME"
 fi

 [ "$GATEWAY" ] && {
 ip netns exec "$NSPID" ip route delete default >/dev/null 2>&1 && true
 }
 ip netns exec "$NSPID" ip link set "$CONTAINER_IFNAME" up
 [ "$GATEWAY" ] && {
 ip netns exec "$NSPID" ip route get "$GATEWAY" >/dev/null 2>&1 || \
 ip netns exec "$NSPID" ip route add "$GATEWAY/32" dev "$CONTAINER_IFNAME"
 ip netns exec "$NSPID" ip route replace default via "$GATEWAY"
 }
 ;;
esac

# Give our ARP neighbors a nudge about the new interface
if installed arping; then
 IPADDR=$(echo "$IPADDR" | cut -d/ -f1)
 ip netns exec "$NSPID" arping -c 1 -A -I "$CONTAINER_IFNAME" "$IPADDR" > /dev/null 2>&1 || true
else
 echo "Warning: arping not found; interface may not be immediately reachable"
fi

# Remove NSPID to avoid `ip netns` catch it.
rm -f "/var/run/netns/$NSPID"

# vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab :

 

原文链接:docker网络连接工具之pipework,转载请注明来源!

0