2019-05-26 | Docker | UNLOCK

Docker容器的网络连接

Docker容器的网络连接

一、Docker容器的网络基础

首先使用ifconfig命令来查看宿主机的网络配置,结果如下:

在系统中有一个名为docker0的网络设备,docker的守护进程就是通过docker0为docker的容器提供网络连接的各种服务。docker0实际上就是Linux的虚拟网桥网桥数据链路层的一种设备,它用来通过Mac地址也就是网络设备的物理地址来对网络进行划分,以及在不同的网络之间传递数据(协议转换)。Linux的虚拟网桥相比于普通的网桥有如下的特点:

  • 可以设置IP地址——docker0就有一个ip地址

Linux的虚拟网桥是通用网络设备的一种,当虚拟网桥拥有ip之后,linux便可通过路由表或者ip表规则在网络层定位网桥,这就相当于拥有一个隐藏的虚拟网卡,这个网卡的名字就是docker0

默认docker0的网络地址划分为:

  • IP:172.17.0.1 子网掩码:255.255.0.0
  • MAC:02:42:22:0b:00:00 到 02:42:22:0b:ff:ff
  • 总共提供了65534个地址

docker根据ip范围为每一个容器提供相应的mac地址,从而避免mac地址冲突。docker守护进程在一个容器启动时还要创建创建网络连接的两端,一端是在容器中的网络设备,而另一端是在运行docker守护进程的主机上。打开一个名为veth*的接口,用来实现docker0这个网桥与容器的网络通信

1.查看网桥设备

安装网桥管理程序:

1
2
sudo apt-get insatll bridge-utils  #在ubuntu中
sudo yum install bridge-utils #centos 7

查看网桥设备:

1
sudo brctl show

结果如下,有一个docker0 的网桥设备。

2.运行一个容器查看网络设备情况

随便运行一个docker容器,然后查看这个容器的网络情况:

1
2
3
docker start -i web #重新启动一个容器
apt-get install net-tools #在容器中安装网络工具,不然无法使用ifconfig
ifconfig #查看容器的网络设备

可以看到容器的ip地址为172.17.0.2,mac地址为:02:42:ac:11:00:02

以守护进程的方式运行成容器,然后在host中查看网桥的状态:

可以看到interfaces中多了一个接口,这就是docker在容器创建时为容器连接docker0所创建的一个网络接口,同样使用ifconfig也能查看到这个网络接口:

3.自定义docker0,修改docker0默认ip地址

使用linux自带的ifconfig命令来修改docker0:

1
sudo ifconfig docker0 192.168.200.1 netmask 255.255.255.0

再次查看网络设备信息,发现docker0的ip地址已经发生了变化:

注意不能直接在容器中修改ip地址,会报没有权限错误:

1
2
3
4
root@4acef7f0c1a9:/# ifconfig docker0 192.168.200.2 netmask 255.255.255.0
SIOCSIFADDR: Operation not permitted
docker0: ERROR while getting interface flags: No such device
SIOCSIFNETMASK: Operation not permitted

然后重新启动docker以及重新运行一个容器,查看它们的ip地址。

4.自定义虚拟网桥

也可以自定义虚拟网桥,配置号ip地址和子网掩码,然后让docker使用这个网桥:

1
2
sudo brctl addbr br0  #新建网桥br0
sudo ifconfig br0 192.168.100.1 netmask 255.255.255.0#配置网桥的ip地址和子网掩码

然后更改docker守护进程的启动配置:

在/etc/default/docker 中添加DOCKER_OPS值 -b=br0

1
2
vim /etc/default/docker #在ubuntu中,在/etc/default目录下新建docker文件,添加DOCKER_OPS=“ -b=br0”
vim /etc/docker/daemon.json #在centos中

在centos中编辑/etc/docker/daemon.json为:

1
2
3
{
"bridge":"br0"
}

然后重新加载守护进程以及重启docker服务:

1
2
systemctl daemon-reload #重启守护进程
systemctl restart docker #重启docker

然后新建或重新运行一个docker容器:

1
2
3
docker start web
docker attach web
ifconfig

此时容器的ip已经变成了br0网桥设置的ip段了。

更多关于docker守护进程的配置和操作查看:Docker守护进程的配置和操作

二、Docker容器的互联

1.docker在默认情况下运行所有容器互联

新建Dockerfile:

1
2
3
4
5
6
7
8
9
10
#Dockerfile for network test
FROM ubuntu:latest
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get install net-tools #安装ifconfig
RUN apt-get install -y inetutils-ping #安装ping命令
RUN apt-get install -y nginx
RUN apt-get install -y curl
EXPOSE 80
CMD /bin/bash

build:

1
2
3
docker build -t df/web .
docker iamges
docker run --name=web1 -it df/cnt /bin/bash

之后安装net-utils查看ip

apt insatll net-tools

这个容器的ip为192.168.100.3

现在docker中运行了两个容器:

container ip images intsall
web 192.168.100.2 ubuntu:latest nginx,ping,ifconfig
web1 192.168.100.3 df/cnt:latest nginx,ping,ifconfig,curl

docker默认允许容器互相连接:

1
2
3
4
5
6
7
▶ docker attach web1
root@3488bca33165:/# ping 192.168.100.2
PING 192.168.100.2 (192.168.100.2): 56 data bytes
64 bytes from 192.168.100.2: icmp_seq=0 ttl=64 time=0.110 ms
64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=0.077 ms
64 bytes from 192.168.100.2: icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from 192.168.100.2: icmp_seq=3 ttl=64 time=0.074 ms

可以发现在web1容器中ping web容器,正常ping通,同样在web1中ping web1也是正常ping通。然后分别打开两个容器的nginx,在web1容器中使用curl命令访问web容器,结果如下:

这里使用的是ip进行访问,但是容器停止或者重启之后,ip地址可能发生变化,所以如果写死了ip,则很容易出问题,docker提供了另一种方式来获得容器的ip地址:

1
2
docker run --link=[CONTAINER_NAME]:[ALIAS] [IMAGE] [COMMOND]
docker run -it --name web2 --link=web df/cnt #web2容器和web容器绑定

这里新建了一个web2容器,并让web2容器和web容器绑定,接着在web2中ping web容器,可以顺利地ping通。

查看web2的/etc/hosts文件,可以发现web2有到web ip地址的映射,并且这个映射永远是准确的,docker会自动修改ip地址和别名对应。

2.拒接容器间的互联

1
vim /etc/docker/daemon.json

添加:"icc":false

然后重新允许容器web,web1,web2,发现都不能ping通了。

1
2
3
4
root@ef62e24881b3:/# ping 192.168.100.2
PING 192.168.100.2 (192.168.100.2): 56 data bytes
192.168.100.2 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss

3.运行特定容器间的互联

  • –icc设置为false
  • –iptables设置为true
  • 设置–link

在centos中:vim /etc/docker/daemon.json

1
2
3
4
{
"icc":false,
"iptables":true
}

在ubuntu中:

vim /etc/default/docker

1
DOCKER_OPTS="--icc=false  --iptables=true"

重启docker,重启容器

docker run -it --name web2 --link=web:别名 df/cnt #web2容器和web容器绑定

三、Docker容器与外部网络的连接

1.ip_forward

Linux ip_forward 数据包转发

出于安全考虑,Linux系统默认是禁止数据包转发的。所谓转发即当主机拥有多于一块的网卡时,其中一块收到数据包,根据数据包的目的ip地址将数据包发往本机另一块网卡,该网卡根据路由表继续发送数据包。这通常是路由器所要实现的功能。

要让Linux系统具有路由转发功能,需要配置一个Linux的内核参数net.ipv4.ip_forward。这个参数指定了Linux系统当前对路由转发功能的支持情况;其值为0时表示禁止进行IP转发;如果是1,则说明IP转发功能已经打开。

  • 临时生效的配置方式

临时生效的配置在主机重启或者主机网络重启之后失效,适合用于测试。

1
2
3
sysctl -w net.ipv4.ip_forward=1
或者
echo 1 > /proc/sys/net/ipv4/ip_forward

配置完之后,输入sysctl net.ipv4.ip_forward查看,结果如下:

1
net.ipv4.ip_forward = 1
  • 永久生效的配置方式

修改/etc/sysctl.conf 配置文件,在sysctl.conf配置文件中有一项名为net.ipv4.ip_forward的配置项,将其值设为1。修改sysctl.conf文件后需要执行指令sysctl -p 后新的配置才会生效。

或者:

修改/etc/sysconfig/network配置文件,在文件最后添加一行:FORWARD_IPV4=YES,修改配置文件后需要重启网络服务(service netwrok restart)才能使新的配置生效。

配置完之后输入命令sysctl net.ipv4.conf.all.forwarding查看当前的配置,输出结果如下:

1
net.ipv4.conf.all.forwarding = 1

2.iptables

Iptables是与linux内核集成的包过滤防火墙,几乎所有的linux发行版本都会包含Iptables的功能。

  • 表(table)

  • 链(chain)

  • 规则(rule)

    ACCEPT REJECT DROP

当主机收到一个数据包后,数据包先在内核空间中处理,若发现目的地址是自身,则传到用户空间中交给对应的应用程序处理,若发现目的不是自身,则会将包丢弃或进行转发。

iptables实现防火墙功能的原理是:在数据包经过内核的过程中有五处关键地方,分别是PREROUTING、INPUT、OUTPUT、FORWARD、POSTROUTING,称为钩子函数,iptables这款用户空间的软件可以在这5处地方写规则,对经过的数据包进行处理,规则一般的定义为“如果数据包头符合这样的条件,就这样处理数据包”。

iptables中定义有5条链,说白了就是上面说的5个钩子函数,因为每个钩子函数中可以定义多条规则,每当数据包到达一个钩子函数时,iptables就会从钩子函数中第一条规则开始检查,看该数据包是否满足规则所定义的条件。如果满足,系统就会根据该条规则所定义的方法处理该数据包;否则iptables将继续检查下一条规则,如果该数据包不符合钩子函数中任一条规则,iptables就会根据该函数预先定义的默认策略来处理数据包

iptables中定义有表,分别表示提供的功能,有filter表(实现包过滤)、nat表(实现网络地址转换)、mangle表(实现包修改)、raw表(实现数据跟踪),这些表具有一定的优先级:raw–>mangle–>nat–>filter

3.允许端口映射访问

4.限制IP访问容器

在iptables中禁用ip就行了

四、容器跨主机网络访问

宿主主机可以对容器进行访问(端口映射),同一机器上的容器之间可以互相访问(同一网段),但是容器默认是无法访问主机的,因此别的主机上的容器也不能访问别的主机。因为相对于容器来说,主机是外网,可以使用主机的外网ip进行访问,但这相当于走了一次网络,并不是直接访问,没有享受到同一主机上进行网络访问的优点。

使用网桥实现跨主机连接

解决方案就是让docker容器和host主机ip在同一个网段,可以将它们都使用一个相同的网桥br0,默认是使用不同的网桥(docker0/br0,eth0)。

评论加载中