docker——网络配置

  

一、网络启动与配置参数

Docker启动时会在主机上自动创建一个docker0虚拟网桥实际上是一个Linux网桥
可以理解为一个软件交换机它会在挂载其上的接口之间进行数据转发
同时,Docker随机分配一个本地未占用的私有网段(在RFC1918中定义)中的一个地址给docker0接口

当创建一个Docker容器时,同时会创建一对veth pair接口
当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包
这对接口一端在容器内,即eth0,另一端在本地并被挂载到docker0网桥
名称以veth开头,通过这种方式,主机可以跟容器通讯,容器之间也可以相互通信。
如此一来,Docker就创建了在主机和所有容器之间的一个虚拟共享网络。

下面是Docker网络相关的参数命令,其中有些命令选项只有在Docker服务启动的时候才能配置,而且不能马上生效。
  -b BRIDGE or --bridge=BRIDGE  指定容器挂载的网桥
  --bip=CIDR  定制docker0的掩码
  -H SOCKET ... or --host=SOCKET...  docker服务端接收命令的通道
  --icc=true|false  是否支持容器之间进行通信
  --ip-forward=true|false  启用net.ipv4.ip_forward
  --iptables=true|false  禁止Docker添加iptables规则
  --mtu=BYTRS  容器网络中的MTU

下面两个命令既可以在启动服务时指定,也可以Docker容器启动时(docker run)指定。
在docker服务启动的时候指定则会成为默认值,后续执行docker run时可以覆盖设置的默认值。
  --dns=IP_ADDRESS  使用指定的DNS服务器
  --dns=search=DOMAIN  指定DNS搜索域

最后这些选项只能在docker run执行时使用,因为他是针对容器的特性内容:
  -h HOSTNAME or --hostname=HOSTNAME  配置容器主机名
  --link=CONTAINER_NAME:ALIAS  添加到另一个容器的连接
  --net=bridge|none|container:NAME_or_ID|host|user_defined_network  配置容器的桥接模式
  -p SPEC or --publish=SPEC  映射容器端口到宿主主机
  -P or --publish-all=true|false  映射容器所有端口到宿主主机

其中 --net选项支持五种模式,如下所示:
  --net=bridge 默认选项,为容器创建一个独立的网络命名空间,分配网卡、ip地址等网络配置。
    并通过veth接口对将容器挂载到一个虚拟网桥(默认为docker0)上。
  --net=none 为容器创建一个独立的网络命名空间,但不进行网络配置,即容器内没有创建网卡、IP等
  --net=container:NAME_or_ID 意味着新创建的容器共享指定的已存在容器的网络命名空间,
    两个容器内的网络配置共享,但其它资源(进程空间、文件系统等)还是隔离的。
  --net=host 意味着不为容器创建独立的网络命名空间,容器内看到的网络配置均与主机保持一致。
  --net=user_defined_network 用户自行用network相关命令创建一个网络,
    同一个网络内的容器彼此可见,可以采用更多类型的网络插件。

 

 

二、配置容器DNC和主机名

Docker支持自定义容器的主机名和DNS配置。

1.相关配置文件

实际上,容器中主机名和DNS配置信息都是通过三个系统配置文件来维护的:/etc/resolv.conf/etc/hostname/etc/hosts


启动一个容器,在容器中使用mount命令可以看到这三个文件的挂载信息。

其中,/etc/resolv.conf文件在创建容器的时候,默认会与宿主机/etc/resolv.conf文件内容保持一致。

这样也可以说明容器和宿主的文件系统是共享的。

/etc/hosts文件默认中只记录容器自身的一些地址和名称。

 

 

2.容器内修改配置文件

Docker1.2.0开始支持在运行中的容器里直接编辑/etc/hosts、/etc/hostname和/etc/resolv.conf文件。
但是这些修改只是临时的,只是在运行的容器中保留,容器终止或重启后并不会被保存下来。也不会被docker commit提交。

 

3.通过参数指定

如果用户想要自定义容器的配置,可以在创建或启动容器的时候利用下面的参数指定。

  指定主机名

    -h HOSTNAME或--hostname=HOSTNAME。设定容器的主机名,它会被写到容器内的/etc/hosts和/etc/hostname文件中。
    但是这个主机名只有在容器内才能看到,在容器外看不到,既不会在docker ps中显示,也不会在其它容器的/etc/hosts中看到。

  记录其它容器主机名

    --link=CONTAINER_NAME:ALLAS。在创建容器的时候,添加一个所连接容器的主机名到容器内/etc/hosts文件中。
    这样新创建容器可以直接使用主机名来与所连接容器通信。

  指定DNS服务器

    --dns=IP_ADDRESS。添加DNS服务器到容器的/etc/resolv.con中,
    容器会用指定的服务器来解析所有不在/etc/hosts中的主机名。

  指定DNS搜索域

    --dns-search=DOMAIN。设定容器的搜索域,当设定搜索域为.example.com时,在搜索一个名为host的主机时,
    DNS不仅搜索host,还会搜索host.example.com。

 

 

三、容器访问控制

容器的访问控制主要通过Linux上的iptables防火墙软件来进行管理和实现。
iptables是系统流行的防火墙软件,在大部分发行版本中都会自带。

1.容器访问外部网络

容器默认指定了网关为docker0网桥上的docker0内部接口。
docker0内部接口也是宿主机的一个本地接口,因此默认情况下是可以访问到宿主机本地的。

容器想要通过宿主机访问到外部网络,需要宿主机进行转发。
在Linux系统中,检查转发是否打开(默认是打开的):

如果为0,说明没有开启转发,则需要手动打开:

 

更简单的,在启动Docker服务的时候设定--ip-forward=true,docker服务会自动打开宿主机系统的转发服务。

2.容器之间的访问

容器之间相互访问,需要两方面的支持:
  网络拓朴是否已经连通。默认情况下,所有容器都会连接到docker0网桥上,这意味着默认情况下拓扑是互通的;
  本地系统的防火墙软件iptables是否允许访问通过。这取决于防火墙的默认规则是允许还是禁止。
下面分两种情况介绍容器之间的访问。

(1)访问所有端口

当启动Docker服务的时候,默认会添加一条“允许”转发策略到iptables的FORWARD链上。
通过配置--icc=true|false(默认为true)参数可以控制默认的策略。
为了安全考虑,可以在Docker配置文件中配置DOCKER_OPTS=--icc=false来默认禁止容器之间的相互访问。
同时,如果启动Docker服务时手动指定--iptables=false参数则不会修改宿主机系统上的iptables规则。

(2)访问指定端口

在通过-icc=false禁止容器间相互访问后,仍可以通过--link=CONTAINER_NAME:ALIAS选项来允许访问指定容器的开放端口。
--link=CONTAINER_NAME:ALIAS中的CONTAINER_NAME必须是docker自动分配的主机名或使用--name指定的主机名,不能使用-h参数配置的主机名。

 

 

四、映射容器端口到宿主机的实现

默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。

1.容器访问外部实现

假设容器内部的网络地址为172.12.0.2,本地网络地址为10.0.2.2。
容器要能访问外部网络,源地址不能为172.12.0.2,需要进行源地址映射(Source NAT,SNAT),
修改为本地系统的IP地址10.0.2.2。
映射是通过iptables的源地址伪装操作实现的。

查看主机nat表上POSTROUTING链的规则。该链负责网包离开主机之前,还写其源地址。

其中,上述规则将所有源地址为172.12.0.0/16网段,而不是从docker0接口发出的流量,动态伪装为系统网卡发出。
MASQUERADE行动跟传统SNAT行动相比,好处是它能从网卡动态获取地址。

2.外部访问容器实现

容器允许外部访问,可以在docker run时候通过-p或-P参数来启用。
不管用那种方法,其实也是在本地的iptable的nat表中添加相应的规则,
将访问外部IP地址的网包进行目标地址DANT,将目标地址修改为容器的IP地址
以一个开放的8080端口为例,使用-P会自动映射本地的一个随即端口到容器的8080端口。

 

可以看到,nat中涉及两条链,PREROUTING链负责包到达网络接口时,改写其目标地址。
其中规则将所有流量都仍到DOCKER链。而DOCKER链中所有不是从docker0进来的网包(意味着不是本地主机产生),
将目标端口为49153的,修改目标地址为172.17.0.11,目标端口修改为80。

需要注意的是,这里的而规则映射了0.0.0.0,意味着将接收主机来自所有网络接口上的流量。
可以通过-p IP:host_port:container_port或-p IP::port来指定绑定的外部网络接口,以制定更严格的访问规则。
如果希望映射永久绑定到某个固定的IP地址,可以在docker配置文件/etc/default/docker中指定DOCKER_OPTS="--ip=IP_ADDRESS",之后重启Docker服务即可生效。

 

五、配置docker0网桥

 Docker服务默认会创建一个名称为docker0的Linux网桥,它在内核层连通了其它的物理或虚拟网卡。
这就将所有容器和本地主机都放到了同一个物理网络。用户使用Docker创建多个自定义网络时可能会出现多个容器网桥。

Docker默认制定了docker0接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
它还给出了MTU(接口允许接收的最大传输单元),通常是1500字节,或宿主网络路由上支持的默认值。
这些值都可以在服务启动的时候进行配置:
  --bip=CIDR:IP地址加掩码格式,例如192.168.1.5/24
  --mtu=BYTES:覆盖默认的Docker mtu配置。

也可以在配置文件中配置DOCKER_OPTS,然后重启服务。
由于目前Docker网桥是Linux网桥,用户可以使用brctl show来查看网桥和端口连接信息。

每次创建一个新的容器时,Docker从可用的地址段中选择一个空闲的IP地址来分配给容器的eth0端口。
并且使用本地主机上docker0接口的IP作为容器的默认网关:

 

 

六、自定义网桥

除了默认使用docker0网桥,用户也可以指定网桥来连接各个容器。
在启动docker服务的时候,使用-b BRIDGE或--bridge=BRIDGE来指定使用的网桥。
停止服务:
  service docker stop
关闭docker网桥:
  ip link set dev docker0 down
删除旧的网桥:
  brctl delbr docker0
新建网桥:
  brctl addbr bridge0
给新的网桥配置参数:
  ip addr add 192.168.5.1/24 dev bridge0
  ip link set dev bridge0 up
  ip addr show bridge0

这是传统的方法,现在直接使用docker create network。

 

七、使用OpenvSwitch网桥

Docker默认使用的是Linux自带的网桥实现,实际上,OpenvSwitch项目作为一个成熟的虚拟交换机,具备更丰富的功能。

1.安装OpenvSwitch

安装依赖包:
  yum -y install openssl-devel wget kernel-devel
安装开发工具:
  yum groupinstall "Development Tools"
下载源码:
  wget http://openvswitch.org/releases/openvswitch-2.3.1.tar.gz
解压:
  tar xfz openvswitch-2.3.1.tar.gz
创建编译目录:
  mkdir -p ~/rpmbuild/SOURCES
从spec文件中删除openvswitch-kmod的依赖包,并创建一个新的spec文件:
  sed 's/openvswitch-kmod, //g' openvswitch-2.3.1/rhel/openvswitch.spec > openvswitch-2.3.1/rhel/openvswitch_no_kmod.spec
开始编译:
  cp openvswitch-2.3.1.tar.gz rpmbuild/SOURCES
  rpmbuild -bb --without=check ~/openvswitch-2.3.1/rhel/openvswitch_no_kmod.spec
安装编译生成的rpm文件:
  yum localinstall /home/ovswitch/rpmbuild/RPMS/x86_64/openvswitch-2.3.1-1.x86_64.rpm

启动服务:systemctl start openvswitch.service
查看服务状态:systemctl -l status openvswitch.service

  2.配置容器连接到OpenvSwitch

目前OpenvSwitch网桥还不能直接支持挂载容器,需要手动在OpenSwitch网桥上创建虚拟网口被挂载到容器中。
步骤如下:

(1)创建无网络容器   

  docker run --net=noen --privileged=true -it base /bin/bash
查看网络信息:

   只有一个本地网卡lo

(2)手动为容器添加网络

下载辅助脚本ovs-docker:
  wget https://github.com/openvswitch/ovs/raw/master/utilities/ovs-docker
  chmod a+x ovs-docker
添加网桥br1,并挂载:
  ovs-vsctl add-br br1
  ovs-vsctl show(查看)  

    ./ovs-docker add-port br1 eth0 c26f1bbdb3f5 --ipaddress=172.17.0.2/24
可以看到容器内多了一个网卡:

在容器外,为br1配置接口地址。
  ifconfig br1 172.17.0.10/24

 

八、创建一个点到点的连接

默认情况下,Docker会将所有容器连接由docker0提供的虚拟子网中,
有时候需要两个容器之间可以直接通讯,而不同通过主机网桥进行桥接。

创建一对peer接口,分别放到两个容器中,配置成点到点链路类型即可。

启动两个容器:
  docker run -it --rm --net=none centos /bin/bash

找到进程号,然后创建网络命名空间的跟踪文件:

docker inspect -f "{{.State.Pid}}" d3e573778246
docker inspect -f "{{.State.Pid}}" 3882bde4d974
mkdir -p /var/run/netns
ln -s /proc/25974/ns/net /var/run/netns/25974
ln -s /proc/26023/ns/net /var/run/netns/26023
ip link add AA type veth peer name BB

添加IP地址和路由信息:

ip link set AA netns 25974
ip netns exec 25974 ip addr add 10.1.1.1/32 dev AA
ip netns exec 25974 ip link set AA up
ip netns exec 25974 ip route add 10.1.1.2/32 dev AA

ip link set BB netns 26023
ip netns exec 26023 ip addr add 10.1.1.2/32 dev BB
ip netns exec 26023 ip link set BB up
ip netns exec 26023 ip route add 10.1.1.1/32 dev BB

ping:

此外也可以不指定--net=none来创建点链路。

 

posted @ 2018-12-21 00:57  明王不动心  阅读(4693)  评论(0编辑  收藏  举报