Docker——docker网络详解

一、Docker原生网络

Docker作为目前最火的轻量级容器技术,有很多令人称道的功能,如Docker的镜像管理。然而,Docker同样有着很多不完善的地方,网络方面就是Docker比较薄弱的部分。因此,我们有必要深入了解Docker的网络知识,以满足更高的网络需求,便于我们以后学习如何优化docker网络。

安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 和host。

[root@server1 registry]# docker network ls 
NETWORK ID          NAME                DRIVER              SCOPE
f3aff7e4dad5        bridge              bridge              local
c5f20e91b8ca        host                host                local
0e8b7355d807        none                null                local
docker网络模式功能
host容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
Container(Joined)创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围。
None该模式关闭了容器的网络功能。
Bridge此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。

运行容器的时候指定网络模式:

模式参数
host模式使用 --net=host(–network host) 指定。
none模式使用 --net=none 指定。
bridge模式使用 --net=bridge 指定,默认设置。
container模式使用 --net=container:NAME_or_ID 指定。

我们这里需要提前安装网桥工具:yum install -y bridge-utils.x86_64

1 bridge网桥模式

bridge模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的。
容器通过宿主机的NAT规则后可以访问外网。
在这里插入图片描述

[root@server2 shadowsocks-go-master]# docker start busy 
busy
[root@server2 shadowsocks-go-master]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
6f274191a8d3        busybox             "sh"                4 hours ago         Up 3 seconds                            busy
[root@server2 shadowsocks-go-master]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.024259f20048	no		vethdd31213

可以发现,当挂起一个容器时,就会出现相对应的网桥接口。

这时,我们的容器就通过docker0网桥与宿主机建立通信了。

[root@server2 shadowsocks-go-master]# docker attach busy
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ping 172.25.10.1
PING 172.25.10.1 (172.25.10.1): 56 data bytes
64 bytes from 172.25.10.1: seq=0 ttl=63 time=0.705 ms

不仅如此,我们上面说了,只要宿主机做了NAT,我们同时在容器中做好dns解析,就能与外网通信。

在宿主虚拟机确保可以上网,且开启了内核路由转发功能。
[root@server2 ~]# sysctl -a | grep ip_forward
net.ipv4.ip_forward = 1

进入容器,做好dns解析(一般的镜像会默认做好dns解析的),发现可以上网。
[root@server2 ~]# docker attach busy 
/ # cat /etc/resolv.conf 
nameserver 114.114.114.114
/ # ping baidu.com
PING baidu.com (39.156.69.79): 56 data bytes
64 bytes from 39.156.69.79: seq=0 ttl=49 time=68.977 ms
64 bytes from 39.156.69.79: seq=1 ttl=49 time=49.094 ms

2 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和端口。

host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。

在这里插入图片描述

[root@server2 ~]# docker run -it --rm --name vm1 --net=host busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 52:54:00:34:ac:55 brd ff:ff:ff:ff:ff:ff
    inet 172.25.10.2/24 brd 172.25.10.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe34:ac55/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop qlen 1000
    link/ether 52:54:00:f8:9a:b0 brd ff:ff:ff:ff:ff:ff
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue 

3 none模式

none模式是指禁用网络功能,只有lo接口,在容器创建时使用--network none

none模式关闭了容器的网络功能,但是在以下两种情况下是有用的:

  • 容器并不需要网络(例如只需要写磁盘卷的批处理任务)。
  • overlay:在docker1.7代码进行了重构,单独把网络部分独立出来编写,所以在docker1.8新加入的一个overlay网络模式。
[root@server2 ~]# docker run -it --rm --name vm1 --network none busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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

4 container模式

这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。

[root@server2 ~]# docker run -it  --name vm1  busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0

新建一个容器vm1,此时ctrl+p+q打入后台,同时使用joined模式指定新容器vm3的网络模式

[root@server2 ~]# docker run -it --rm --name vm3 --network container:vm1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
12: eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

可以发现,二者网络完全一致,使用相同的网络name space

二、Docker自定义网络

以上都是不用动手的,真正需要配置的是自定义网络。

Docker 提供三种 user-defined 网络驱动:bridge, overlay 和 macvlan。

  • bridge驱动类似默认的bridge网络模式,但增加了一些新的功能
  • overlay 和 macvlan 用于创建跨主机的网络,我们后面有章节单独讨论
[root@server2 ~]# docker network create --driver bridge my_net1
285073f4b7a7c520d96384a12cd553d5467e383b8432ca13b0344cf2f491d712
[root@server2 ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
201a7d8ebdd6        bridge              bridge              local
6c2e07c100fb        host                host                local
285073f4b7a7        my_net1             bridge              local
990e4e557b1e        none                null                local
[root@server2 ~]# docker network inspect my_net1 
[
    {
        "Name": "my_net1",
        "Id": "285073f4b7a7c520d96384a12cd553d5467e383b8432ca13b0344cf2f491d712",
        "Created": "2019-08-06T15:22:25.297122817+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"

这里 172.18.0.0/16 是 Docker 自动分配的 IP 网段。

查看一下当前 host 的网络结构变化:

[root@server2 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
br-285073f4b7a7		8000.02420ce57251	no		
docker0		8000.024259f20048	no		vethc88734b
							vethdd31213

这里的br-285073f4b7a7,这里285073f4b7a7正好新建 bridge 网络 my_net1的短 id。

除此之外,我们也可以自己指定 IP 网段
需要在创建网段时指定 --subnet 和 --gateway 参数:

[root@server2 ~]# docker network ls 
NETWORK ID          NAME                DRIVER              SCOPE
201a7d8ebdd6        bridge              bridge              local
6c2e07c100fb        host                host                local
285073f4b7a7        my_net1             bridge              local
b0aed0b82886        my_net2             bridge              local
990e4e557b1e        none                null                local
[root@server2 ~]# docker network inspect my_net2
[
    {
        "Name": "my_net2",
        "Id": "b0aed0b828862e1a88efa2f9126a2fc9a25a111af0abb966eda3811dae2aa770",
        "Created": "2019-08-06T16:14:44.421580492+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
  • 运行容器使用自定义网络
[root@server2 ~]# docker run -it --name vm11 --network my_net2 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
16: eth0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
       valid_lft forever preferred_lft forever

容器分配到的 IP 为 172.19.0.2。

容器的 IP 都是 docker 自动从 subnet 中分配,我们能否指定一个静态 IP 呢?

答案是:可以,通过--ip指定,当然,你必须指定该网络模式的网段ip,否则会报错。

[root@server2 ~]# docker  run -it --rm --name vm1 --network my_net2 --ip 172.19.0.25 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
    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
18: eth0@if19: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:13:00:19 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.25/16 brd 172.19.255.255 scope global eth0
       valid_lft forever preferred_lft forever

注意:只有使用 --subnet 创建的网络才能指定静态 IP。

三、Docker容器间通信

1 为容器添加网卡

桥接到不同网桥上的容器,彼此是不通信的。
docker在设计上就是要隔离不同network的。

我们开启两个容器基于不同的docker网络:

docker run -it --name vm1 --network my_net1 busybox
docker run -it --name vm2 --network my_net2 busybox

使用iptables-save查看策略:

-A DOCKER-ISOLATION-STAGE-2 -o br-82797b4a772d -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-b05957ab3cce -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -j RETURN
-A DOCKER-USER -j RETURN

可以看到:此时拒绝通信。那么如何让两个不同网桥的容器通信?

解决方法就是:使用docker network connect 为vm1添加一块my_net2网卡即可。

[root@server1 yum.repos.d]# docker network connect my_net1 vm2
[root@server1 yum.repos.d]# docker attach vm2
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.19.0.2/24 brd 172.19.0.255 scope global eth0
       valid_lft forever preferred_lft forever
10: eth1@if11: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.3/16 brd 172.18.255.255 scope global eth1
       valid_lft forever preferred_lft forever

/ # ping vm1
PING vm1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.087 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.059 ms

/ # 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
172.19.0.2	7b8442e98675
172.18.0.3	7b8442e98675

这里也是因为docker内嵌了dns server。

2 使用--link连接容器

  • 格式:--link <name or id>:alias
  • name和id是源容器的,alias是源容器在link下的别名
[root@server1 yum.repos.d]# docker run -it --rm --name vm2 --network my_net1 --link vm1:myweb busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ping myweb
PING myweb (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.074 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.065 ms

使用link的时候,可能会出现错误:Cannot link to /vm1, as it does not belong to the default network.

注意:使用link的方式互联容器时由于默认不在一个网络中,会报上述错误,解决办法即添加--net XX_default,使新的container与link目标在同一个网络中。

容器是如何访问外网的?

容器与外网通信是通过iptables的SNAT实现的,我们可以查看iptables-save

-A POSTROUTING -s 172.19.0.0/24 ! -o br-82797b4a772d -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-b05957ab3cce -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

外网是如何访问容器的呢/

是通过端口映射访问的-p host端口:容器内端口

外网访问容器使用到了docker-proxy和iptables的DNAT:

  • 宿主机host访问本机容器使用的是iptables DNAT
  • 外部主机访问容器内部或者容器之间的访问是docker-proxy实现的
[root@server1 yum.repos.d]# docker run -d --name nginx -p 80:80 nginx
e6e364cf8b2f8fa5ca89d9d8c0ab878c6d32dba5eda413a98de5d409a4497b51
[root@server1 yum.repos.d]# netstat -atnlp  | grep 80
tcp6       0      0 :::80                   :::*                    LISTEN      4414/docker-proxy   
[root@server1 yum.repos.d]# ps aux | grep docker-proxy
root      4414  0.0  0.2 108964  2620 ?        Sl   22:17   0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80
root      4475  0.0  0.0 112704   980 pts/0    R+   22:17   0:00 grep --color=auto docker-proxy

四、Docker跨主机的容器网络

1 docker跨主机的网络方式:

  • docker原生的overlay和macvlan
  • 第三方的flannel、weave、calico

2 如何与docker集成?

  • 使用libnetwork:docker网络库
  • CNM(container network model)模型:对容器网络进行抽象

3 CNM(container network model)模型

三类组件作用
Sandbox容器网络栈(namespace)
Endpoint将sandbox接入network(veth)
Network包含一组endpoint,同一network的endpoint可以通信

4 使用macvlan实现Docker容器跨主机网络

我们这里只将macvlan,以后我会把overlay以及一些第三方的网络方案都讲一下。

macvlan特点:

  • 使用linux内核提供的一种网卡虚拟化技术
  • 性能好:因为无需使用桥接,直接使用宿主机物理网口。

做法:

  • 1 为各自docker宿主机各添加一块网卡,打开网卡混杂模式:
首先,激活新添加的网卡
 ip link set eth1 up
打开混杂模式
ip link set eth1 promisc on
  • 2 在两台docker宿主机各自创建macvlan网络:
在宿主机server1上:
建立macvlan的docker网络
docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth1 macvlan1
运行一个基于该网络模式的容器
docker run -it --name vm1 --network macvlan1 --ip 172.20.0.13 busybox


在宿主机server2上:
建立macvlan的docker网络
docker network create -d macvlan --subnet 172.20.0.0/24 --gateway 172.20.0.1 -o parent=eth1 macvlan1
运行一个基于该网络模式的容器
docker run -it --name vm2 --network macvlan1 --ip 172.20.0.23 busybox
  • 测试:
/ # ping 172.20.0.13
PING 172.20.0.13 (172.20.0.13): 56 data bytes
64 bytes from 172.20.0.13: seq=0 ttl=64 time=0.474 ms
64 bytes from 172.20.0.13: seq=1 ttl=64 time=0.326 ms

5 分析macvlan

  • 1 网络结构:没有新建桥接,容器的接口直接与宿主机网卡连接,不需要NAT或者端口映射
# [root@server2 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
docker0		8000.0242a5f9d41e	no	
  • 2 我们也注意到了,macvlan会独占网卡的,但是可以使用vlan的子接口实现多macvlan网络,最多可以将物理二层网络划分成4094各逻辑网络,且彼此隔离。
docker network create -d macvlan --subnet 172.21.0.0/24 --gateway 172.21.0.1 -o parent=eth1.1 macvlan2

docker network create -d macvlan --subnet 172.22.0.0/24 --gateway 172.22.0.1 -o parent=eth1.2 macvlan3

使用ip a查看:
4: eth1: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:2a:ff:ec brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:fe2a:ffec/64 scope link 
       valid_lft forever preferred_lft forever
6: eth1.1@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 52:54:00:2a:ff:ec brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:fe2a:ffec/64 scope link 
       valid_lft forever preferred_lft forever
7: eth1.2@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 52:54:00:2a:ff:ec brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:fe2a:ffec/64 scope link 
       valid_lft forever preferred_lft forever
  • 3 macvlan网络在物理网络二层是隔离的,无法直接通信,但是可以在三层上(即容器内部)通过网关将macvlan网络连通起来。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值