安装Docker时会自动创建三个网络。可以使用 docker network ls 命令列出网络:

[root@centos-01 ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
cdda3ae8795a   bridge    bridge    local
ed7ffc7437dd   host      host      local
fa66bc1a17f4   none      null      local

四种网络模式#

在使用 docker run 创建容器时,可以用 --network  指定容器的网络模式,Docker有以下四种网络模式:

  • --net=host :容器和宿主机共享 Network namespace
  • --net=none :容器有独立的 Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接、配置IP等
  • --net=bridge:默认设置
  • --net=container:name_Or_id  :容器和另外一个容器共享 Network namespace

Bridge#

简介#

该模式是在启动docker服务后默认的网络模式,Docker 启动时,Docker使用Linux桥接(参考《Linux虚拟网络技术》),在宿主机虚拟一个Docker容器网桥 docker0,Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为 Container-IP ,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的 Container-IP 互相通信。

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

在安装和启动docker服务之后即可查看到这个 docker0 的虚拟网桥设备:

[root@centos-01 ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
       
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:50:75:ae brd ff:ff:ff:ff:ff:ff
    inet 192.168.126.143/24 brd 192.168.126.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 2409:8900:2b86:13cc:a958:4c6e:c162:309d/64 scope global noprefixroute dynamic 
       valid_lft 3569sec preferred_lft 3569sec
    inet6 fe80::1f3a:afc1:82f6:3f66/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
       
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:cb:f1:35:ea brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

docker0 网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法通过ip地址直接寻址的,需要通过其他方式来使外部网络可以访问容器,一般会通过 访问宿主机ip结合容器的端口(端口映射)进行容器的访问

实现步骤

  1. Docker Daemon 利用 veth pair  技术,在宿主机上创建两个虚拟网络接口设备,假设为 veth0  和 veth1veth pair 技术的特性可以保证无论哪一个 veth  接收到网络报文,都会将报文传输给另一方
  2. Docker Daemon  将 veth0  附加到 Docker Daemon 创建的 docker0 网桥上。保证宿主机的网络报文可以发往 veth0 
  3. Docker Daemon  将 veth1  添加到 Docker Container 所属的 namespace  下,并被改名为 eth0 。保证宿主机的网络报文若发往 veth0 则立即会被 eth0 接收,实现宿主机到Docker Container网络的联通,同时也保证容器单独使用 eth0,实现容器网络环境的隔离

缺陷

  1. 该模式下容器不具有公有IP ,就是说和宿主机的 eth0 不处于同一个网段。导致的结果是:在宿主机以外不能直接和容器进行通信
  2. 虽然经过中间处理(NAT模式) 可以解决公有IP的问题,但是NAT模式仍然存在问题与不便,比如:容器都需要在宿主机上竞争端口,容器内部服务的访问者需要使用服务发现获知服务的外部端口等
  3. 另外由于NAT模式是在三层网络上的实现手段,会影响网络传输效率

测试#

1):查看本机网络

[root@centos-01 ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
       
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:50:75:ae brd ff:ff:ff:ff:ff:ff
    inet 192.168.126.143/24 brd 192.168.126.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 2409:8900:2b86:13cc:a958:4c6e:c162:309d/64 scope global noprefixroute dynamic 
       valid_lft 3569sec preferred_lft 3569sec
    inet6 fe80::1f3a:afc1:82f6:3f66/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
       
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:cb:f1:35:ea brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever

2):创建容器并查看IP

[root@centos-01 ~]# docker run -d  --name nginx  nginx
8afe1080ba992614f96b6868dbbcab463e37c98f8d3fa729245d48c2e6a1e3fe

[root@centos-01 ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
8afe1080ba99   nginx     "/docker-entrypoint.…"   3 seconds ago   Up 2 seconds   80/tcp    nginx

[root@centos-01 ~]# docker inspect nginx

发现其ip为 172.17.0.2 ,当容器桥接 docker0 后,会自动分配 ip 地址,之后的IP地址递增。

3):查看网桥和端口连接

[root@centos-01 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242cbf135ea	no		veth3a98010

一个新的网络接口 veth3a98010  被挂到了 docker0 上, veth3a98010 就是新创建容器的虚拟网卡。

4):查看veth pair 配置

[root@centos-01 ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
       
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:50:75:ae brd ff:ff:ff:ff:ff:ff
    inet 192.168.126.143/24 brd 192.168.126.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 2409:8900:2b86:13cc:a958:4c6e:c162:309d/64 scope global noprefixroute dynamic 
       valid_lft 3520sec preferred_lft 3520sec
    inet6 fe80::1f3a:afc1:82f6:3f66/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
       
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:cb:f1:35:ea brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:cbff:fef1:35ea/64 scope link 
       valid_lft forever preferred_lft forever
       
21: veth3a98010@if20: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 16:ea:7d:ca:a3:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::14ea:7dff:feca:a307/64 scope link 
       valid_lft forever preferred_lft forever
       
[root@centos-01 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242cbf135ea	no		veth3a98010

[root@centos-01 ~]# docker exec  -it nginx /bin/bash
root@8afe1080ba99:/# cat /sys/class/net/eth0/iflink 
21

注意veth  设备是成双成对出现的,一端在容器内部名为 eth0,一端加入到网桥名为 vethxxx (通常命名为veth),它们组成一个数据传输通道,一端进一端出,veth设备连接了两个网络设备并实现了数据通信;在bridge模式下,连在同一网桥上的容器可以相互通信。若出于安全考虑,也可以禁止它们之间通信,方法是在 DOCKER_OPTS 变量中设置 –icc=false,这样只有使用 –link 才能使两个容器通信。

Host#

简介#

如果启动容器时使用的是host模式,那么容器将不会获得一个独立的 Network Namespace(网络命名空间) ,而是和宿主机系统共用一个。这意味着:容器不会虚拟出自己的网卡以及配置自己的ip等,而是使用宿主机的ip以及端口。不过在其他方面例如文件系统、进程列表等还是与之隔离的。使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行 NAT ,Host最大的优势就是网络性能比较好,但是已经被使用的端口就不能再用了。

介绍

  • host 网络模式需要在容器创建时指定 --network=host 
  • host 模式可以直接使用宿主机的IP地址与外界进行通信,若宿主机的 eth0  是一个公有 IP,那么容器也拥有这个公有IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT  转换
  • host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性

缺陷

  • 容器网络环境隔离性的弱化。即容器不再拥有隔离、独立的网络栈
  • 使用 host 模式的容器虽然可以让容器内部的服务和传统情况无差别、无改造的使用,但是由于网络隔离性的弱化,该容器会与宿主机竞争网络栈的使用
  • 容器内部将不再拥有所有的端口资源,原因是部分端口资源已经被宿主机本身的服务占用,还有部分端口已经用以 bridge  网络模式容器的端口映射

测试#

[root@centos-01 ~]# docker run -d --network=host --name nginx  nginx
ba0eb1c89ab6b505619db9b6789074db397a9f729ce18aeb82543986971b83d1

[root@centos-01 ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS     NAMES
ba0eb1c89ab6   nginx     "/docker-entrypoint.…"   3 seconds ago   Up 2 seconds             nginx

注意

  1. 不需要添加 -p 参数,因为它使用的就是主机的IP和端口,添加 -p 参数后,反而会出现警告: WARNING: Published ports are discarded when using host network mode 
  2. 宿主机的ip路由转发功能一定要打开,否则所创建的容器无法联网。echo 1 > /proc/sys/net/ipv4/ip_forward 

host端口占用模式是容器占用主机上当前所监听的端口(官网描述为publish)。比如这里nginx占用80端口,那么用host模式启动的时候,主机上的80端口会被nginx占用,这时其他的容器就不能再指定80端口,但可以指定其他端口,所以说一台主机上可以运行多个host模式的容器,只要彼此监听的端口不一样即可

[root@centos-01 ~]# curl 192.168.126.143:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Container#

Container模式Host模式 类似,指定新创建的容器和已经存在的一个容器共享一个 Network Namespace 。这意味着:新建的容器不会创建自己的网卡等相关操作,而是和与指定的容器共享这些资源。除了网络方面,其文件系统、进程列表等都是隔离的。

注:它并没有改善容器与宿主机以外世界通信的情况(和桥接模式一样,不能连接宿主机以外的其他设备)

[root@centos-01 ~]# docker run -d --name nginx --network=container:ba0eb1c89ab6b50 nginx

None#

Docker容器拥有自己的 Network Namespace ,但不会对容器进行任何的网络配置。这意味着:这个容器没有网卡、IP、路由等信息。需要自己为Docker容器添加网卡、配置IP等。这种方式网络的隔离性最为彻底,即表明关闭了容器的网络功能,也无法访问这个容器。

[root@centos-01 ~]# docker run -d --name nginx --network=none nginx

参考#