一、Docker网络的介绍
Docker作为目前最火的轻量级容器技术,有很多令人称道的功能,如Docker的镜像管理
然而,Docker同样有着很多不完善的地方,网络方面就是Docker比较薄弱的部分
因此,我们有必要深入了解Docker的网络知识,以满足更高的网络需求
先介绍Docker自身的4种网络工作方式,然后介绍一些自定义网络模式
安装Docker,它会自动创建三个原生网络,bridge(创建容器默认连接到此网络)、 none 、host

网络模式

介绍

Bridge

此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信

Host

容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口

None

该模式关闭了容器的网络功能

Container

创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围

二、默认网络
安装Docker时,它会自动创建三个网络。
列出:

[root@server1 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
786dfb545dfd        bridge              bridge              local
18863c9f4ba7        host                host                local
0e2c1989739c        none                null                local
注意:
Docker内置这三个网络,运行容器时,你可以使用该–network标志来指定容器应连接到哪些网络
该bridge网络代表docker0所有Docker安装中存在的网络
除非你使用该docker run --network=选项指定,否则Docker守护程序默认将容器连接到此网络

docker 容器中使用外部命令_docker 容器中使用外部命令


在使用docker run创建Docker容器时,可以用 --net 选项指定容器的网络模式,Docker可以有以下4种网络模式

host模式:     使用 --net=host 指定。
none模式:     使用 --net=none 指定。
bridge模式:   使用 --net=bridge 指定,默认设置。
container模式:使用 --net=container:NAME_or_ID 指定。

Docker的各个网络模式介绍

2.1 Host模式

相当于Vmware中的桥接模式,与宿主机在同一个网络中,但没有独立IP地址

众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程;Mount Namespace隔离文件系统,Network Namespace隔离网络等;一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。

一个Docker容器一般会分配一个独立的Network Namespace;但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace;而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。

例如,我们在172.25.254.1/24的机器上用host模式启动一个ubuntu容器

host模式与物理机共享网络,让外部直接与容器通信,但是当容器数量庞大,都在访问一个端口会导致冲突。

docker 容器中使用外部命令_docker 容器中使用外部命令_02


可以看到显示的网络信息与主机的信息一致,但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。

2.2 Container模式

理解了host模式后,这个模式也就好理解了

这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享

新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等

同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信

2.3 None模式

该模式将容器放置在它自己的网络栈中,但是并不进行任何配置

实际上,该模式关闭了容器的网络功能,

在以下两种情况下是有用的

容器并不需要网络(例如只需要写磁盘卷的批处理任务)

overlay:在docker1.7代码进行了重构,单独把网络部分独立出来编写,所以在docker1.8新加入的一个overlay网络模式。

Docker对于网络访问的控制也是在逐渐完善的。

docker 容器中使用外部命令_网络_03


2.4 Bridge模式

相当于Vmware中的Nat模式,容器使用独立network Namespace,并连接到docker0虚拟网卡(默认模式);通过docker0网桥以及Iptables nat表配置与宿主机通信;bridge模式是Docker默认的网络设置。

此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上

当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。

虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。

接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,

连接到docker0的容器就从这个子网中选择一个未占用的IP使用。

如一般Docker会使用172.17.0.0/16这个网段,并将172.17.0.1/16分配给docker0网桥

(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)。

单机环境下的网络拓扑如下,主机地址为10.10.0.186/24。

四、自定义网络

使用自定义的网桥来控制哪些容器可以相互通信,还可以自动DNS解析容器名称到IP地址。

Docker提供了创建这些网络的默认网络驱动程序,你可以创建一个新的Bridge网络,Overlay或Macvlan网络。

你还可以创建一个网络插件或远程网络进行完整的自定义和控制。

你可以根据需要创建任意数量的网络,并且可以在任何给定时间将容器连接到这些网络中的零个或多个网络。

此外,您可以连接并断开网络中的运行容器,而无需重新启动容器。

当容器连接到多个网络时,其外部连接通过第一个非内部网络以词法顺序提供。

4.1bridge

一个bridge网络是Docker中最常用的网络类型。

桥接网络类似于默认bridge网络,但添加一些新功能并删除一些旧的能力

docker network create -d bridge my_net1

docker 容器中使用外部命令_docker_04


可以看到分配了一个172.18网段的ip,容器的ip是单调递增,网段也是

指定子网掩码和网关

docker network create -d bridge --subnet 172.22.0.0/24 --gateway 172.22.0.1 my_net2
[root@server1 ~]# docker network ls  
NETWORK ID          NAME                DRIVER              SCOPE
786dfb545dfd        bridge              bridge              local
18863c9f4ba7        host                host                local
3b48afbf37d8        my_net1             bridge              local
9a0ffa241c79        my_net2             bridge              local
0e2c1989739c        none                null                local
[root@server1 ~]# docker run -it --name vm2 --network my_net2 ubuntu
root@fb8631889376:/# ip a
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
129: eth0@if130: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:16:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.22.0.2/24 brd 172.22.0.255 scope global eth0
       valid_lft forever preferred_lft forever

指定ip

docker run -it --name vm3 --network my_net2 --ip 172.22.0.100 ubuntu 使用–ip参数时,必须有之前的bridge网络模式的配置:–subnet

my_net1和my_net2之间不能通信

docker 容器中使用外部命令_docker 容器中使用外部命令_05


桥接在不同网桥上的容器,彼此之间不能通信

看到my_net2上桥接了2块网卡;my_net1 只有一个

docker 容器中使用外部命令_运维_06

[root@server1~]#iptables -S ##查看防火墙规则,来自docker网卡之间的数据 包双向都被丢弃

docker 容器中使用外部命令_网络_07


如何让不同网桥上的容器通信?

[root@server1 ~]# docker network  connect my_net2 vm1
[root@server1 ~]# docker container attach vm1
root@aa3762e40cba:/# ip a  可以看到多了一块网卡ip:172.18.0.2
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
133: eth0@if134: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
135: eth1@if136: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:16:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.22.0.2/24 brd 172.22.0.255 scope global eth1
       valid_lft forever preferred_lft forever

docker 容器中使用外部命令_docker 容器中使用外部命令_08

'vm1能ping通vm3的原因是给vm1上又添加了一个网络接口并桥接在my_net2上'

docker 容器中使用外部命令_网络_09


五、容器间的通信
5.1除了使用–network指定ip访问以外,docker在1.10以后,都会内嵌一个DNSserver

docker 容器中使用外部命令_运维_10


5.2)joined方式(类似于默认的host模式,容器之前共享网络)

docker run -it --name vm2 --network container:vm1 ubuntu  指定共享的容器

这样两个容器之间可以使用localhost(回环接口)进行快速通信 ;适用于web服务器和应用服务器

docker 容器中使用外部命令_运维_11


5.3)使用–link来链接两个容器

[root@server1 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
e981e80d9b39        ubuntu              "/bin/bash"         2 hours ago         Up 2 hours                              vm2
d6faec8168a7        ubuntu              "/bin/bash"         2 hours ago         Up 2 hours                              vm1
[root@server1 ~]# docker rm -f vm1  
vm1
[root@server1 ~]# docker rm -f vm2
vm2
[root@server1 ~]# docker run -d nginx  打开一个容器
7193bf3bb1f4957a447b5a6ca780f2d1a87b7d1e3ee6f4bbf815f6ca164ac1c1
[root@server1 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
7193bf3bb1f4        nginx               "/docker-entrypoint.…"   9 seconds ago       Up 7 seconds        80/tcp              confident_mclaren打开的容器名称不指定系统会自动分配

docker 容器中使用外部命令_docker 容器中使用外部命令_12


环境变量里设置了相应的地址和ip

docker 容器中使用外部命令_docker 容器中使用外部命令_13

[root@server1 ~]# docker stop confident_mclaren  停止容器会释放掉ip地址
confident_mclaren
[root@server1 ~]# docker run -d nginx  新建容器会重新分配到之前的ip
7658fc6052a87584c2c86b68b88d6864ee6b056be246c9c8738876a2bbc6f1fc
[root@server1 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
7658fc6052a8        nginx               "/docker-entrypoint.…"   9 seconds ago       Up 8 seconds                80/tcp              kind_goldstine
00a2b68fbc93        ubuntu              "/bin/bash"              31 minutes ago      Exited (0) 43 seconds ago                       vm1
7193bf3bb1f4        nginx               "/docker-entrypoint.…"   About an hour ago   Exited (0) 18 seconds ago                       confident_mclaren
[root@server1 ~]# docker inspect kind_goldstine  ip已经获得

新建容器的ip成为172.17.0.2

docker 容器中使用外部命令_docker 容器中使用外部命令_14


打开刚才停止的VM1容器查看ip

[root@server1 ~]# docker start confident_mclaren
[root@server1 ~]# docker start  vm1
[root@server1 ~]# docker attach vm1  连接容器
root@00a2b68fbc93:/# 
root@00a2b68fbc93:/# ip a  ip已经改变172.17.0.4  新的ip
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
177: eth0@if178: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
root@00a2b68fbc93:/# read escape sequence

容器访问外网是通过iptables的SNAT实现的,容器和docker0是桥接的方式,docker0是 容器的网关,到达docker0后,通过linux内核的路由功能(ip_forward),然后防火墙会做 伪装,也就是SNAT,然后通过物理网卡接口到外网

docker 容器中使用外部命令_运维_15


docker 容器中使用外部命令_docker_16


六、外网如何访问容器

删除刚才创建的容器

[root@server1 ~]# docker run -d --name vm1 -p 80:80 nginx 做端口映射(冒号 后的是容器内部的端口
[root@server1 ~]# docker port vm1 查看容器端口映射情况 
[root@server1 ~]# iptables -t nat -S  查看防火墙策略

docker 容器中使用外部命令_docker 容器中使用外部命令_17


外部主机访问容器的流程

外部主机访问时–> 宿主机的eth0(172.25.0.1:80)–>DNAT–>172.17.0.3(容器地 址)

docker 容器中使用外部命令_docker 容器中使用外部命令_18


查看开启80端口

docker 容器中使用外部命令_网络_19


每当运行一个容器,就会开启一个docker-proxy进程

docker 容器中使用外部命令_docker 容器中使用外部命令_20


1.宿主机访问本机容器使用iptables的DNAT

2.外部主机访问容器或者容器之间访问是 docker-proxy实现的’

3.‘外部主机 --> 宿主机eth0–>docker-proxy(外部访问容器时通过docker-proxy处理数据 包,不是防火墙)–>docker0(172.17.0.1)–> 容器’