1  概述

docker0net桥,自动创建,默认容器网络只做snat,没做dnat,容器能访问外网,但不能被外网访问。默认跨物理主机不能通信。需要在net桥做dnat或者通过隧道方式,实现网络的访问。

docker启动容器后会自动在宿主机创建NAT桥,网段默认为172.17.0.0/16,容器启动后,宿主机的防火墙会创建很多和docker相关的规则,使用命令iptables -t nat -vnL进行查看,以下的规则,说明只要源地址是172.16.0.16/16,且转发出去的网络不经过docker0桥,即不是内部转发,就做地址伪装,即SNAT,target为MASQUERADE

Chain POSTROUTING (policy ACCEPT 1485 packets, 128K bytes)

 pkts bytes target     prot opt in     out     source               destination        

    4   160 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0

NAT桥的特征是当容器被关联到docker0,网关指向172.17.0.1(docker0),且该容器需要和外部通讯时,不经过docker0桥转发,而是通过和外部网络通讯的网卡进行转发,如本机还有地址192.168.0.66,可以与外部访问,则容器主动发出去的报文要被192.168.0.66转发出去,且容器的对外访问地址会被转换为192.168.0.66,因此可以和外部访问,外部回包后,192.168.0.66要通过dnat把地址转转换,才能把外部回来的报文返回给容器

但是,外部访问的包,除非外部主机的网关指向了容器所在的物理网卡192.168.0.66,否则默认不能正常访问到其他主机docker容器。

SNAT默认是容器可以访问外部,外部可以回包,但是如果是外部主动访问,则报文不可到达容器

两个物理主机间的容器要相互访问,即夸主机间容器访问,此时需要做dnat(端口映射)才能实现,但是端口映射一般只映射有限的端口,如把一台容器的80把映射出去(即暴露容器,或者叫发布),和宿主机的80口关联(也可以是宿主机其他端口)。

要是的跨主机间的容器无障碍互相访问,即在二层间通信,可以通过隧道网络实现,但是有个特例,如果跨主机容器都是同一私有ip,访问会异常,为了防止这种情况出现,需要改变不同主机容器的默认地址,即默认为172.17.0.0/16,此时可以使用在不同物理主机,设置容器的地址为172.16.1.0/24和172.17.2.0/24等不同网段,即人为改变docker0桥的地址,做好不同宿主机的容器的网段规划。这种情况也称为叠加网络

docker的网络一般有两个场景:

1.单主机虚拟网络:提供容器隔离

2.多主机虚拟网络:使用叠加网络,确保多个物理主机间的容器能在二层间通信,但是这个通常要借助于其他网络构建工具,如k8s的服务注册或者发现工具。

docker本地的网络拓扑如下图

一个容器,默认有本地接口(lookback口)和私有接口(如容器的eth0的172.17.0.2对应的口),注意虚拟网卡是成对出现的,如容器内部有eth0,而宿主机上出来docker0作为网关,还有会随机生成veth1e10b1f@if5这种网卡的名称与容器的eth0配对。相当于是有两根网线,一个插到容器,一个插到宿主机上。可以通过安装工具包bridge-utils来查看,命令brctl show

2  四种网络模型

4种网络模式的通信方式如下截图

1.桥接式容器,bridged container,为本地网络模式,默认是这种方式,无论是隔离桥,nat桥,仅主机桥,物理桥,只不过是桥的方式不一样,都叫桥接式网络,本质上,都是关联到一个桥上。

2.封闭式容器,closed container.只有本地回环口的容器。通常用于批处理操作或者运行备份的操作,没有网络需求的主机

3.加入式容器,joined container,也叫联盟式容器,如两个容器,A和B,使用同一组回环口和相同的私有网卡对外访问,但是容器除了网卡之外,其他的网络名称空间是不一样的。这种模型用途是,如LAMP架构中,php要和mysql通信,如果网络是桥接模式,则容器只能通过tcp通信,如果是加入式网络,php使用私有网络,mysql使用回环口,相当于两个容器是一个主间上,则php和mysql可通过io通信(使用回环口)。php对外服务,但是mysql仅提供给php访问,不给外部访问。这种形式,使得容器相当于同一主机。

4.开放式容器,open container.容器共享宿主机的名称空间,直接使用宿主机的名称空间,这种方式不安全,因为对容器的网络

docker使用网络的方式

Docker安装完成后,会自动创建三个网络,可使用“docker network ls”命令查看,都是本地

bridge :桥接式网络,,默认指docker0,即bridged container

host :开放式网络,主机桥,表示使用主机的网络,即open container,主机网络

none:封闭式网络,指没有网络,不使用网络,即closed container ,本地回环口

创建容器时,可为docker run命令使用--net选项指定要加入的网络

封闭式容器:

不参与网络通信,运行于此类容器中的进程仅能访问本地环回接口 v仅适用于进程无须网络通信的场景中,例如备份、进程诊断及各种离线任务等

 docker run --rm --net none busybox:latest ifconfig -a

桥接式容器一般拥有两个接口:一个环回接口和一个连接至主机上某桥设备的以太网接口

docker daemon启动时默认会创建一个名为docker0的网络桥,并且创建的容器为桥接式容器,其以太网接口桥接至docker0 . -默认--net bridge即为将容器接口添加至 docker0

docker0桥为NAT桥,因此,桥接式容器可通过此桥接口访问外部网络,但防火墙规则阻止了一切从外部网络访问桥接式容器的请求 ,如果要实现外部访问容器,需要将容器的端口暴露,即使用dnat规则来暴露

使用docker network create 创建docker桥,可以新建docker桥

docker run命令使用 ,跟网络相关的选项如下:

 “--hostname HOSTNAME”选项为容器指定主机名,例如

docker run --rm --net bridge --hostname bbox.magedu.com busybox:latest nslookup bbox.magedu.com .

“--dns DNS_SERVER_IP”选项能够为容器指定所使用的 dns服务器地址,如下为172.16.0.1,例如

docker run --rm --dns 172.16.0.1 busybox:latest

“--add-host HOSTNAME:IP”选项能够为容器指定本地主机名解析,表示在本机的/etc/hosts文件中写入主机名docker.com解析为ip 172.16.0.100,

docker run --rm --dns 172.16.0.1 --add-host "docker.com:172.16.0.100" busybox cat /etc/hosts

很多时候,网络相关参数,可以在容器创建时,加入以上的参数,直接设定,不需要容器启动后再进行添加

暴露端口的方法

Docker0NAT桥,因此容器一般获得的是私有网络地址 v可以把容器想像为宿主机NAT服务背后的主机 v如果开放容器或其上的服务为外部网络访问,需要在宿主机上为其定义DNAT规则,例如 对宿主机某 IP地址的访问全部映射给某容器地址

.主机 IP容器 IP

l -A PREROUTING -d主机 IP -j DNAT --to-destination容器 IP

.对宿主机某 IP地址的某端口的访问映射给某容器地址的某端口

-p选项的使用格式 ,有四种写法

-p .指明容器端口,将指定的容器端口映射至主机所有地址的一个动态端口 ,宿主机上的主机为随机生成

例子

启动容器b1,暴露80口,-f表示httpd运行于前端

[root@docker ~]# docker run -it --name b1 --net bridge --rm --hostname www.sunny.com --dns 8.8.8.8 --add-host www.ghbsunny.cn:1.1.1.1 -v /docker/data/volumes/sunny:/data/htdocs -p 80 busybox /bin/httpd -f  -h /data/htdocs

通过port命令查看被随机分配的口为32769与容器的80口对应

[root@docker ~]# docker port b1

80/tcp -> 0.0.0.0:32769

0.0.0.0表示宿主机的任意ip

[root@docker ~]#

在其他主机上,访问docker宿主机10.10.10.72的32769端口即可访问到容器的80口

[root@monitor 73 ~]# curl 10.10.10.72:32769

hello sunny~

[root@monitor 73 ~]#

即此时容器b1的80口可以被外部访问了

 

-p : .将容器端口 映射至指定的主机端口 .

例子

将容器的80口暴露为宿主机的80口

[root@docker ~]# docker run -it --name b1 --net bridge --rm --hostname www.sunny.com --dns 8.8.8.8 --add-host www.ghbsunny.cn:1.1.1.1 -v /docker/data/volumes/sunny:/data/htdocs -p 80:80 busybox /bin/httpd -f  -h /data/htdocs

查看端口对应关系

[root@docker ~]# docker port b1

80/tcp -> 0.0.0.0:80

0.0.0.0表示宿主机的任意ip

[root@docker ~]#

在其他主机上访问

[root@monitor 73 ~]# curl 10.10.10.72

hello sunny~

[root@monitor 73 ~]#

 

-p :: .将指定的容器端口 映射至宿主主机指定 的动态端口 .

例子

把容器的80口暴露为宿主机的ip 10.10.10.72的随机端口

[root@docker ~]# docker run -it --name b1 --net bridge --rm --hostname www.sunny.com --dns 8.8.8.8 --add-host www.ghbsunny.cn:1.1.1.1 -v /docker/data/volumes/sunny:/data/htdocs -p 10.10.10.72::80 busybox /bin/httpd -f  -h /data/htdocs

查看

[root@docker ~]# docker port b1

80/tcp -> 10.10.10.72:32768

[root@docker ~]#

此时容器的80口被映射为宿主机ip10.10.10.7232768端口

测试

[root@monitor 73 ~]# curl 10.10.10.72:32768

hello sunny~

[root@monitor 73 ~]#

 

 -p :: 将指定的容器端口映射至宿主机指定的的端口

例子

把容器的80口映射为宿主机的ip 10.10.10.72的8090口

[root@docker ~]# docker run -it --name b1 --net bridge --rm --hostname www.sunny.com --dns 8.8.8.8 --add-host www.ghbsunny.cn:1.1.1.1 -v /docker/data/volumes/sunny:/data/htdocs -p 10.10.10.72:8090:80 busybox /bin/httpd -f  -h /data/htdocs

查看

[root@docker ~]# docker port b1

80/tcp -> 10.10.10.72:8090

[root@docker ~]#

测试

[root@monitor 73 ~]# curl 10.10.10.72:8090

hello sunny~

[root@monitor 73 ~]#

 

端口暴露,实际上就是动态生成dnat规则,使得外部可以访问宿主机内部的容器服务,动态端口指随机端口,具体的映射结果可以使用docker port命令查看

 “-P”选项(大写P)或“--publish-all”将容器的所有计划要暴露端口全部映射至主机端口

计划要暴露的端口使用使用--expose选项指定 .例如 .

docker run -d -P --expose 2222 --expose 3333 --name web busybox:latest /bin/httpd -p 2222 -f

表示把容器的2222和3333端口随机暴露

.查看映射结果

[root@docker ~]# docker port web

3333/tcp -> 0.0.0.0:32770

2222/tcp -> 0.0.0.0:32771

[root@docker ~]#

 

 

联盟式网络

联盟式容器是指使用某个已存在容器的网络接口的容器,接口被联盟内的各容器共享使用;因此,联盟式容器彼此间完全无隔离

例如 .创建一个监听于 2222端口的 http服务容器

docker run -it --rm -p 2222 --name b1 --dns 8.8.8.8 --add-host www.ghbsunny.cn:1.1.1.1  -v /docker/data/volumes/sunny:/container/data busybox /bin/httpd -p 2222 -f -h /container/data

 

创建一个联盟式容器,并查看其监听的端口 .注意--net后面的container是关键词,而container后面的web为容器名称,即新的容器要加入b1这个容器的网络,查看到此时容器的网络相关属性都是和web这个容器一样

[root@docker ~]# docker run -it --rm --net container:b1 --name joined busybox:latest

/ # cat /etc/hosts

127.0.0.1        localhost

::1        localhost ip6-localhost ip6-loopback

fe00::0        ip6-localnet

ff00::0        ip6-mcastprefix

ff02::1        ip6-allnodes

ff02::2        ip6-allrouters

1.1.1.1        www.ghbsunny.cn

172.17.0.2        882d643c642d

/ # cat /etc/resolv.conf

search localdomain

nameserver 8.8.8.8

/ #

如果要使得加载的卷也是一样的,添加选项--volumes-from

[root@docker ~]# docker run -it --rm --net container:b1 --volumes-from b1 --name joined busybox:latest

/ # ls

bin        dev        home       root       tmp        var

container  etc        proc       sys        usr

/ # cd container/

/container #

注意,联盟式的网络的不同容器,仅仅是网络属性相同,其他的资源,如果要共享,要加入相关选项。即其他名称空间,如User,Mount等还是隔离的。

联盟式容器彼此间存在端口冲突的可能性,因此,通常只会在多个容器上的程序需要程序loopback接口互相通信,或者对某已存在的容器的容量属性进行监控时才使用此种网络模式的网络模型

如下例子,使用exec命令查看对比两个容器运行进程

[root@docker ~]# docker exec b1 ps aux

PID   USER     TIME  COMMAND

    1 root      0:00 /bin/httpd -p 2222 -f -h /container/data

    6 root      0:00 ps aux

[root@docker ~]# docker exec joined ps aux

PID   USER     TIME  COMMAND

    1 root      0:00 sh

    7 root      0:00 ps aux

[root@docker ~]#

 

 

开放式容器

开放式共享主机网络名称空间的容器,它们对主机的网络名称空间拥有全部的访问权限,包括访问那些关键性服务,这对宿主机安全性有很大潜在威胁

docker run命令使用“--net host”选项即可创建开放式容器,例如:

docker run -it --rm --net  host --name b3 busybox:latest /bin/sh

 

创建一个桥

新建一个桥,四种桥,不使用默认的docker0,使用选项docker network create

docker桥创建后,需要有一个dhcp分配服务器ip,用ip-range指定dhcp范围,在多主机网络模型可以使用-d overlay来创建叠加网络,实现多主机间网络通信,这里仅介绍普通的网络

可用用 docker network create --help 查看相关参数选项

例子如下

创建名称为br0的桥(默认为docker0),--driver指定为对应的网络类型,不指定driver,默认为bridge的模式,注意,新增的网卡,不要和物理机的网络冲突,否则网络会异常,如本机有物理网卡是10.10.10.0/24,,那么新增的网络就不要指定--ip-range为这个网络

以下例子由于地址分配范围是从10.10.20.0开始分配,网卡为10.10.20.1 ,所以,第一个加入到这个网络的容器ip为10.10.20.0,第二个加入到这个网络的容器ip为10.10.20.2。但是新增的网卡,不能访问宿主机之外的网络,如果要跨网络互通,需要在宿主机的防火墙做nat。docker0默认是可以访问外部网络。

[root@docker ~]# docker network create --subnet 10.10.0.0/16 --ip-range 10.10.20.0/24 --gateway 10.10.20.1 br0

此时默认新生成的br0为桥接式网络

删除新增桥命令为

docker network rm br0

注意,新增的网卡,在宿主机上,网卡名称会改变,为任意名称,不是br0,这里可以通过在宿主机上配置,更改网卡名称,不建议更改,如下

先把新增网卡关闭(不关闭改名,会提示设备繁忙),然后改名,最后开启

[root@docker ~]# ip link set name br0 dev br-eda8e223510b

RTNETLINK answers: Device or resource busy

[root@docker ~]# ip link set dev br-eda8e223510b down

[root@docker ~]# ip link set name br0 dev br-eda8e223510b

[root@docker ~]# ip link set dev br0 up

更改前

更改后

但是,改名后,新建容器加入到这个接口名被更改的网络名网卡时,会报错,如下

所以不建议改名,宿主机的新增网卡名称保持原来的随机生成名称即可

接口名和网络名不一定一致,宿主机网卡名称建议使用默认的就可以,不更改,如下

如果要使得一个容器拥有多个网络,那么可以使用connect命令来实现

执行命令如下

docker run --name b3 -it --rm --net br0 busybox

移除容器内部的某一个网卡

这里移除容器b6的br0网卡

docker network disconnect br0 b6

配置docker守护进程的属性信息的方法:/etc/docker/daemon.json

每一个可设置的键是dockerd的可用的选项,操作系统不同,选项也不一样,其值为选项的参数;但有些参数不可用于此文件中,例如add-registry, insecure-registry;有些选项的参数是数组的格式,需要放置于[]

        官方手册(完整的可用参数列表):

  https://docs.docker.com/engine/reference/commandline/dockerd/#run-multiple-daemons

dockerd守护进程的C/S,其默认仅监听Unix SOcket格式的地址,/var/run/docker.sock;如果使用TCP套接字,

            /etc/docker/daemon.json

                "hosts": ["tcp://0.0.0.0:2375"]          

 也可向dockerd直接传递“-H|--host”选项;

例子

修改默认桥分配的地址,如更改docker0,不使用默认的172.17.0.0/16的网络

  自定义docker0桥的网络属性信息:修改/etc/docker/daemon.json文件,注意,每对键值对最后用逗号隔开,每行一对,最后一行不能有逗号

        {

            "bip": "10.20.1.5/16"

        }  

注意这里的bip就是bridge ip,为默认的宿主机docker0对应的网卡ip.其他参数可以不指定,会根据这个参数进行生成。

配置完成后,重启docker服务

 systemctl restart docker.service

docker0的ip信息如下

注意,修改/etc/docker/daemon.json文件后,重启docker服务,那么即使把对应的信息去掉,但不更改,重启服务,配置还是会保留为去掉原来的配置信息

如下

修改ip的配置已经去掉,重启服务后,还是一样的docker0为10.20.1.5的ip,不是默认的172.17.0.1这个ip。

docker0的ip信息依旧为10.20.1.5,如下