05-Docker网络管理¶
一、Docker网络模式简介¶
基于对Network Namespace的控制,Docker可以为容器创建隔离的网络环境。在隔离的网络环境下,容器具有完全独立的网络栈,与宿主机隔离,也可以使容器共享主机或其他容器的网络命名空间,基本可以满足开发者在各种场景下的需要。按Docker官方的说法,Docker容器的网络有五种模式。
二、默认网络¶
当你安装Docker时,它会自动创建三个网络(bridge、host、none)。你可以使用以下docker network ls
命令列出这些网络:
[root@centos8-nat-168-182-152 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c0184302f6a8 bridge bridge local
420492e04276 host host local
fc5e9b954735 none null local
在使用docker run
创建Docker容器时,可以用--net
选项指定容器的网络模式。Docker有以下4种网络模式:
- bridge模式:使用
--net=bridge
指定,默认设置。 - host模式:使用
--net=host
指定。 - none模式:使用
--net=none
指定。 - container模式:使用
--net=container:NAME_or_ID
指定。
三、Bridge模式(默认方式)¶
当Docker server启动时,会在主机上创建一个名为docker0
的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
当创建一个Docker容器的时候,同时会创建了一对veth pair接口(当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包)。这对接口一端在容器内,即eth0
;另一端在本地并被挂载到docker0
网桥,名称以veth
开头(例如vethAQI2QT
)。通过这种方式,主机可以跟容器通信,容器之间也可以相互通信。Docker就创建了在主机和所有容器之间一个虚拟共享网络。
示例:
# --net=bridge可以省略,默认模式
$ docker run --name=nginx_bridge --net=bridge -p8080:80 -td nginx
# 查看容器详情
$ docker inspect nginx_bridge
# 筛选出容器IP
$ docker inspect --format='{{.NetworkSettings.IPAddress}}' nginx_bridge
对比一下宿主机/etc/hosts
,不一样:
$ docker exec nginx_bridge cat /etc/hosts
$ cat /etc/hosts
四、Host模式¶
相当于Vmware中的NAT模式,与宿主机在同一个网络中,但没有独立IP地址。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。可以通过--net=host
指定使用host网络。
示例:
$ docker run --name=nginx_host --net=host -p8081:80 -td nginx
$ docker inspect nginx_host
对比一下宿主机/etc/hosts
,一模一样:
$ docker exec nginx_host cat /etc/hosts
$ cat /etc/hosts
五、Container模式¶
Docker网络container模式是指定其和已经存在的某个容器共享一个Network Namespace,此时这两个容器共同使用同一网卡、主机名、IP地址,容器间通讯可直接通过本地回环lo
接口通讯。但这两个容器在其他的资源上,如文件系统、进程列表等还是隔离的。
示例:
# 创建容器
$ docker run --name=busybox_container --net=container:nginx_bridge -td busybox
# 查看绑定容器的IP
$ docker inspect --format='{{.NetworkSettings.IPAddress}}' nginx_bridge
# 查看新创建容器的IP,上面这种方式查不出来
$ docker inspect --format='{{.NetworkSettings.IPAddress}}' busybox_container
# 通过ifconfig查询ip
$ docker exec busybox_container ifconfig
六、None模式¶
容器有自己的网络命名空间,但不做任何配置,它与宿主机、与其他容器都不连通的。
示例:
$ docker run -dt --net=none --name busybox_none busybox
# 查看它的网络状态,验证它仅有lo接口,不能与容器外通信
$ docker exec busybox_none ip a
七、Docker NAT iptables实现内外网络通信原理¶
默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器。
注意:如果容器的宿主机上的ip_forward
未打开,那么该宿主机上的容器则不能被其他宿主机访问。
打开转发配置:
# 临时修改
$ echo 1 > /proc/sys/net/ipv4/ip_forward
# 永久修改
$ echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
$ sysctl -p
7.1 容器访问外部实现¶
容器所有到外部网络的连接,源地址都会被NAT成本地系统的IP地址(即docker0
地址)。这是使用iptables的源地址伪装操作实现的。
查看主机的NAT规则:
bash
$ iptables -t nat -vnL
其中,上述规则将所有源地址在172.17.0.0/16
网段的包(也就是从Docker容器产生的包),并且不是从docker0
网卡发出的,进行源地址转换(SNAT),转换成主机网卡的地址。
科普:MASQUERADE:IP伪装,自动获取当前ip地址来做NAT。
示例:当前主机有一块网卡为bond0
,IP地址为192.168.182.152/24
,网关为192.168.182.2
。从主机上一个IP为172.17.0.10/16
的容器中ping百度(180.76.3.151
)。IP包首先从容器发往自己的默认网关docker0
,包到达docker0
后,也就到达了主机上。然后会查询主机的路由表,发现包应该从主机的bond0
发往主机的网关192.168.182.2/24
。接着包会转发给bond0
,并从bond0
发出去(主机的ip_forward
转发应该已经打开)。这时候,上面的Iptable规则就会起作用,通过MASQUERADE IP伪装,自动获取bond0
ip地址来对包做SNAT转换,将源地址换为bond0
的地址。这样,在外界看来,这个包就是从192.168.182.152
上发出来的,Docker容器对外是不可见的。
查看路由表,可以看到是走默认路由:
$ route -n
# 或者
$ ip route
容器访问外部流程图:
7.2 外部访问容器实现¶
容器允许外部访问,可以在docker run
时候通过-p
或-P
参数来启用,不管用哪种办法,其实也是在本地的iptable的nat表中添加相应的规则。
使用-P
时(使用-P
标记时,Docker会随机映射一个49000~49900的端口到内部容器开放的网络端口。):
$ docker run -d -P nginx
$ docker run -d -p 8880:80 nginx
$ iptables -t nat -nvL
上面圈中的就是刚刚创建两个容器的端口转发规则,其中,上述的规则映射了0.0.0.0
,意味着将接受主机来自所有接口的包,并且不是从docker0
网卡导入的,进行目的地址转换(DNAT),转换成容器网卡的地址。
外部访问容器流程图:
容器的网络基础¶
1、docker0: 安装 docker 的时候,会生成一个 docker0 的虚拟网桥
2、Linux 虚拟网桥的特点: 可以设置 ip 地址 相当于拥有一个隐藏的虚拟网卡
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:7f:4f:f4:1b brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:7fff:fe4f:f41b/64 scope link
valid_lft forever preferred_lft forever
每运行一个 docker 容器都会生成一个 veth 设备对,这个 veth 一个接口在容器里,一个接口在物理机 上。
3、安装网桥管理工具:
yum install bridge-utils -y
brctl show 可以查看到有一个 docker0 的网桥设备,下面有很多接口,每个接口都表示一个启动的 docker 容器,因为我在 docker 上启动了很多容器,所以 interfaces 较多,如下所示:
[root@linux-bkce-node21 ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02427f4ff41b no
容器的网络互联¶
启动一个nginx容器,并查看他的IP地址
docker run --name nginx01 -it -d -p 8080:80 nginx
docker exec -it nginx01 /bin/bash
root@7edf3b000b1c:/# hostname -I
172.17.0.3
启动一个centos容器,并查看他的IP地址
docker run --name centos01 -it -d centos:7.9.2009 /bin/bash
docker exec -it centos01 /bin/bash
[root@57200f9459a2 /]# ping -c 4 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.080 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.104 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.092 ms
^C
[root@57200f9459a2 /]# curl 172.17.0.3|grep nginx
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
<a href="http://nginx.org/">nginx.org</a>.<br/>
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
缺点:但是如果通过IP的方式进行容器之间的访问会存在问题,因为容器的IP地址会发生改变,所以在特殊情况下会造成连接失败的问题
清理环境
docker rm -f centos01
docker rm -f nginx01
容器的网络别名¶
我们可以通过给容器起一个代号,这样可以直接以代号访问,避免了容器重启 ip 变化带来的问题
启动一个nginx容器,
docker run --name nginx01 -it -d -p 8080:80 nginx
启动一个centos容器,并开始通过网络别名访问nginx
docker run --name centos01 -it -d --link=nginx01:nginx01link centos:7.9.2009 /bin/bash
docker exec -it centos01 /bin/bash
[root@cb4d59e39900 /]# curl nginx01link|grep nginx
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
<a href="http://nginx.org/">nginx.org</a>.<br/>
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
清理环境
docker rm -f centos01
docker rm -f nginx01
容器的网络模式¶
none模式¶
这个模式和前两个不同。在这种模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
Docker 网络 none 模式是指创建的容器没有网络地址,只有 lo 网卡
我们开始创建一个none网络模式的容器
docker run --name centos01 -it -d --net=none centos:7.9.2009 /bin/bash
[root@linux-bkce-node21 ~]# sh in01.sh centos01
[root@3cdae9a8dd0d ~]# 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
清理环境
docker rm -f centos01
container模式¶
Docker网络container模式是指,创建新容器的时候,通过--net container
参数,指定其和已经存在的某个容器共享一个 Network Namespace。如下图所示,右方黄色新创建的container,其网卡共享左边容器。因此就不会拥有自己独立的 IP,而是共享左边容器的 IP 172.17.0.2,端口范围等网络资源,两个容器的进程通过 lo 网卡设备通信。
但这两个容器在其他的资源上,如文件系统、进程列表等还是隔离的。
我们开始创建一个container网络模式的容器
docker run --name centos01 -it -d centos:7.9.2009 /bin/bash
docker run --name nginx01 -it -d --net=container:centos01 nginx
sh in01.sh centos01
[root@98ac20343bf8 ~]# curl 127.0.0.1|grep nginx
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
<a href="http://nginx.org/">nginx.org</a>.<br/>
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
清理环境
docker rm -f centos01
docker rm -f nginx01
host模式¶
众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
例如,我们在10.10.101.105/24的机器上用host模式启动一个含有web应用的Docker容器,监听tcp80端口。当我们在容器中执行任何类似ifconfig命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用10.10.101.105:80即可,不用任何NAT转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
Docker 网络 host 模式是指共享宿主机的网络
docker run --name nginx01 -it -d --net=host nginx
[root@linux-bkce-node21 ~]# curl 192.168.1.21|grep nginx
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
<a href="http://nginx.org/">nginx.org</a>.<br/>
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
清理环境
docker rm -f nginx01
bridge模式¶
当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。接下来就要为容器分配IP了,Docker会从RFC1918所定义的私有IP网段中,选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用。如一般Docker会使用172.17.0.0/16这个网段,并将172.17.42.1/16分配给docker0网桥(在主机上使用ifconfig命令是可以看到docker0的,可以认为它是网桥的管理接口,在宿主机上作为一块虚拟网卡使用)。单机环境下的网络拓扑如下,主机地址为10.10.101.105/24。
默认选择 bridge 的情况下,容器启动后会通过 DHCP 获取一个地址
我们默认启动的容器的网络模式就是bridge模式
docker run --name centos01 -it -d centos:7.9.2009 /bin/bash
docker exec -it centos01 /bin/bash
清理环境
docker rm -f centos01
出现错误总结¶
当我配置好docker的nginx镜像时,出现无法访问,并且在创建nginx容器的时候出现如下提示
WARNING: IPv4 forwarding is disabled. Networking will not work.
警告:IPv4转发被禁用。网络将无法工作。
这里表示没有设置内核转发
解决方法:
# vi /etc/sysctl.conf
或者
# vi /usr/lib/sysctl.d/00-system.conf
添加如下代码:
net.ipv4.ip_forward=1
重启network服务
# systemctl restart network
查看是否修改成功
# sysctl net.ipv4.ip_forward
如果返回为“net.ipv4.ip_forward = 1”则表示成功了
这时使用外部的浏览器访问就会发现没有问题啦