实现宿主机网卡添加到Docker容器

在AWS上部署docker应用有两种途径,一种是直接使用AWS的EC2 Container Service(ECS)产品,优点是可基于资源需求跨集群放置容器,并且与AWS的其他产品,如负载均衡、安全组、卷、角色等集成。另一种是全自助从头搭建,这种方式有助于学到更多东西。

以下方法是为了解决全自助方式下,配置容器网络遇到的这个问题:想要给EC2中运行的容器实例添加虚拟网卡并分配IP地址,使其像普通EC2实例一样加入到指定的VPC的某个子网里,同时受控于安全组策略和路由设置。

步骤

首先在AWS控制台创建新的网络接口,并将其附加到docker容器所在的宿主机上。允许附加的网络接口数量受限于宿主机的机型,附加成功后在宿主机上用ifconfig命令可以看到。

docker-net-01

第二步,在宿主机上创建并启动无网络设置的docker容器实例。

docker create -itd --name=paycb --cap-add SYS_ADMIN --net=none docker.xxx.cn/paycallback
docker start paycb

第三步,准备虚拟化网络环境。这里不需要新建的netns(ip netns add netfoo1),而是直接在/var/run/netns下创建软连接,指向运行中的docker容器的netns。

mkdir -p /var/run/netns
NS_PID=$(docker inspect -f '{{.State.Pid}}' ${CONTAINER_NAME})
ln -s /proc/${NS_PID}/ns/net /var/run/netns/${NS_PID}

第四步,添加虚拟网卡。这里不需要新建veth pair(ip link add vethfoo1 type veth peer name vethfoo2),而是直接将宿主机上附加的第二块虚拟网卡eth1改名为d-eth0,添加到前面的虚拟网络环境中(/var/run/netns/下的${NS_PID})。

ip link set dev eth1 name d-eth0 netns ${NS_PID}

最后,启用虚拟网络环境中新添加的虚拟网卡,并重新设置好添加到容器前的IP和网关。

ip netns exec ${NS_PID} ip link set dev d-eth0 up
ip netns exec ${NS_PID} ip addr add ${CONTAINER_IP}/24 dev eth0
ip netns exec ${NS_PID} route add -net 0.0.0.0/0 gw ${CONTAINER_GATEWAY}

到这里就完成了,可以测试下连通性。

一个完整的Shell脚本如下:

#!/bin/bash

#local CONTAINER_IP=$(ifconfig ${HOST_NET_NAME} |grep "inet " |awk '{print $2}')

CONTAINER_NAME=$1
CONTAINER_IP=$2

if [ "${CONTAINER_NAME}" == "" ] || [ "${CONTAINER_IP}" == "" ]; then
    echo "need to set container name and container ip."
    exit 1
fi

HOST_NET_NAME=$(ifconfig|awk '/'${CONTAINER_IP}'/{print a}{a=substr($1,1,length($1)-1)}')

if [ "${HOST_NET_NAME}" == "" ]; then
    echo "net interface bind to ip ${CONTAINER_IP} was not found."
    exit 1
fi

CONTAINER_GATEWAY=$(route -n |grep ${HOST_NET_NAME} |grep UG |head -n 1 |awk '{print $2}')
if [ "${CONTAINER_GATEWAY}" == "" ]; then
    CONTAINER_GATEWAY=$(route -n |grep UG |head -n 1 |awk '{print $2}')
fi

if [ "${CONTAINER_GATEWAY}" == "" ]; then
    echo "gateway was not found. that is impossible."
    exit 1
fi

docker start ${CONTAINER_NAME}
NS_PID=$(docker inspect -f '{{.State.Pid}}' ${CONTAINER_NAME})

if [ "${NS_PID}" == "" ]; then
    echo "docker start failed. ip: ${CONTAINER_IP}. gw: ${CONTAINER_GATEWAY}."
    exit 1
fi

mkdir -p /var/run/netns
ln -s /proc/${NS_PID}/ns/net /var/run/netns/${NS_PID}
ip link set dev ${HOST_NET_NAME} name eth0 netns ${NS_PID}
ip netns exec ${NS_PID} ip link set dev eth0 up
ip netns exec ${NS_PID} ip addr add ${CONTAINER_IP}/24 dev eth0
ip netns exec ${NS_PID} route add -net 0.0.0.0/0 gw ${CONTAINER_GATEWAY}

echo "docker start success. pid: ${NS_PID}. ip: ${CONTAINER_IP}. gw: ${CONTAINER_GATEWAY}."
Creative Commons License

本文基于署名-非商业性使用-相同方式共享 4.0许可协议发布,欢迎转载、使用、重新发布,但请保留文章署名wanghengbin(包含链接:https://wanghengbin.com),不得用于商业目的,基于本文修改后的作品请以相同的许可发布。

发表评论