跳转至

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 网卡设备通信。

但这两个容器在其他的资源上,如文件系统、进程列表等还是隔离的。

image-20220406165034947

我们开始创建一个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。

image-20201009150754136

默认选择 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”则表示成功了

这时使用外部的浏览器访问就会发现没有问题啦