Docker网络

容器虚拟化网络基础

docker安装完以后,自动提供了3种网络:

创新互联是一家集网站建设,禹会企业网站建设,禹会品牌网站建设,网站定制,禹会网站建设报价,网络营销,网络优化,禹会网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。

  • bridge: 桥接网络,Net桥
  • host: 共享宿主机的网络接口
  • none: 容器内只有lo接口,没有任何网卡
$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
9ac63d7fc6e8        bridge              bridge              local
b46032ae4b5f        host                host                local
60f69f2c7987        none                null                local
$ 

查看容器的网络信息

使用 inspect 命令可以查看docker对象的底层信息。比如可以查看容器的信息,其中网络部分的信息如下:

            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": n ll,
                    "NetworkID": "9ac63d7fc6e871eb47e39f9ec4e3fda6a23cb95a906a9ddc6431ed716e000fa1",
                    "EndpointID": "c8b64271d3d7ea5a0a8357c51fa5c80d398dbd07ad7e920792ebbaab628cb00d",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }

这里可以查看容器内部网卡的IP地址。

共享网络名称空间

每一个容器都有各自一组独立的6个Namespaces:Mount、PID、User、UTS、IPC、Network。
还有一种方案,让每一个容器值只拥有独立的Mount、PID、User。而其他的3个UTS、IPC、Network是共享的。就是有自己隔离的名称空间,但也可以共享其中一部分名称空间。一般只共享网络通信相关的,主机名(UTS)、进程间通信(IPC)、网络(Network)。
这样做带来了一个便利。现在不同的容器共享了网络接口,使用的是同一个网络。每个容器内部的lo接口是同一个lo接口。这样一个容器只要往本地的lo接口发请求,或者是往127.0.0.1发请求,共享网络接口的其他容器也能够接收到。

直接和宿主机共享名称空间
还是上面的共享名称空间,还可以直接和宿主机共享名称空间。那么这个和宿主机共享名称空间的容器,容器内部的接口就是宿主机的网络接口。容器对网络的修改也就是对宿主机的网络进行了修改。这个容器就有了管理网络的特权。
这种就是网络类型中的host类型,就是让容器使用宿主机的网络名称空间。

四种容器网络类型

Docker一共有4种网络模型

  • 封闭式
  • 桥接式
  • 联盟式
  • 开放式

Docker 网络

Bridged containers
桥接式容器一般拥有两个接口:一个环回接口和一个连接至主机上某桥设备的以太网接口。
docker启动时默认会创建一个名为docker0的网络桥,并且创建的容器为桥接式容器,其以太网接口桥接至docker0。
docker0桥为NET桥,因此桥接式容器可通过此桥接口访问外部网络。

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

设定容器网络

创建容器时(run或create),使用参数可以对容器的网络进行设定。

network 参数

使用--nework参数可以指定容器的网络模式,默认值是default,这个就是bridge模式。

brideg模式
正常启动容器,不使用任何网络参数:

$ docker container run --name b1 --rm -it busybox
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1032 (1.0 KiB)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # 

以默认的网络模式启动容器。如果启动容器是加上参数--network bridge效果也是一样的。

none模式
使用--network none启动一个没有任何网络的容器:

$ docker container run --name b1 --rm -it --network none busybox
/ # ifconfig -a
lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # 

这里显示,只有一个lo接口,没有其他网络接口。

其他网络模式
剩下的还有host模式和联盟式网络,这部分需要单独再展开说明。

主机名和域名

在网络上提供服务的时候,一般不是直接提供IP地址。而是提供主机名或域名,不但方便记忆也会有一些其他的便利。所以Docker容器的主机名也是一个重要的网络属性。

主机名
容器的主机名就是它的ID:

/ # hostname
6fb514e1fa3b
/ # 

可以在启动容器时,使用-h参数来设定容器的主机名:

$ docker container run --name b1 --rm -it -h b1.busybox busybox
/ # hostname
b1.busybox
/ # 

DNS服务器
先看一下默认使用的DNS服务器,就是网关的地址:

/ # cat /etc/resolv.conf 
# Generated by NetworkManager
nameserver 192.168.1.1
/ # nslookup -type=a baidu.com
Server:         192.168.1.1
Address:        192.168.1.1:53

Non-authoritative answer:
Name:   baidu.com
Address: 220.181.38.148
Name:   baidu.com
Address: 123.125.114.144

/ # 

使用参数指定DNS服务器启动容器,然后查看容器的DNS的设置:

$ docker container run --name b1 --rm -it -h b1.busybox --dns 223.5.5.5 busybox
/ # cat /etc/resolv.conf 
nameserver 223.5.5.5
/ # 

搜索域
另外还有个参数是--dns-search,是用来指定搜索域的。这个搜索域就是当给的名称不是FQDN主机名格式的时候,自动补的后缀。

$ docker container run --name b1 --rm -it -h b1.busybox --dns 223.5.5.5 --dns-search baidu.com busybox
/ # cat /etc/resolv.conf 
search baidu.com
nameserver 223.5.5.5
/ # nslookup -type=a www
Server:         223.5.5.5
Address:        223.5.5.5:53

Non-authoritative answer:
www.baidu.com   canonical name = www.a.shifen.com
Name:   www.a.shifen.com
Address: 39.156.66.14
Name:   www.a.shifen.com
Address: 39.156.66.18

/ # 

指定主机名的时候,没有给完整的域名后缀,不过因为设置了搜索域,所以就自动补全了。

hosts文件
除了域名服务器,还可以通过本地hosts文件来管理主机名:

$ docker container run --name b1 --rm -it -h busybox1.idx.net --dns 223.5.5.5 --dns-search idx.net busybox
/ # 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.17.0.2      busybox1.idx.net busybox1
/ # 

可以看到默认就把主机名写入到本地的hosts文件中了。这里加了2个名称,前一个是完整的主机名,后一个是主机名除去后缀的部分。
还可以使用参数向hosts文件中注入信息:

$ docker container run --name b1 --rm -it -h busybox1.idx.net --dns 223.5.5.5 --dns-search idx.net --add-host host1.idx.net:192.168.100.1 --add-host host2.idx.net:192.168.100.2 busybox
/ # 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
192.168.100.1   host1.idx.net
192.168.100.2   host2.idx.net
172.17.0.2      busybox1.idx.net busybox1
/ # 

添加记录使用(host:ip)的格式。这个参数是list属性,如果有多个记录,就调用多次参数。

端口映射-p

Docker0为NET桥,因此容器获得的是私有网络地址(内网地址)。
从拓扑结构上看,容器就是一台在宿主机NET服务之后的一台主机。如果容器需要对外提供服务,就要在宿主机上为其定义DNAT规则。

-p选项的使用格式

-p 

将指定的容器端口映射至主机所有地址的一个动态端口

-p :

将容器端口containerPort映射至主机端口hostPort

-p ::

将指定的容器端口containerPort映射至主机指定ip的动态端口
命令中间是两个冒号,相当于下面的3个变量的命令省略了中间的变量。

-p ::

将指定的容器端口containerPort映射至主机指定ip的端口hostPort

动态端口指随机端口,具体映射结果可以使用docker port命令查看

-P(大写),随机映射端口到内部容器开放的所有网络端口。
这里开放的网络端口是镜像制作时设定的,启动容器时也可以使用参数--expose指定计划要开放的端口。只有使用-P参数,才需要指定具体要开放哪些端口,可以是镜像中设定也可以是参数--expose设定。使用-p参数时,是指定要映射的端口,所处没有设置要开放的端口也没问题。

可以指定使用的协议,默认是tcp。udp需要指定

-p 127.0.0.1:5000:5000/udp

-p参数可以多次使用来绑定多个端口

用httpd镜像测试端口映射
不使用端口映射:

$ docker container run -dit --name app1 --rm httpd:alpine 
78e4e42fdd0c33a0410077731d26e418423a27827ab62e83dfe×××bca40f671
$ curl http://172.17.0.2

It works!

$ curl http://127.0.0.1 curl: (7) Failed connect to 127.0.0.1:80; 拒绝连接 $

由于没有做端口映射,容器可以通过NAT访问外部网络,但是无法被外部网络访问到,就是容器没有暴露任何接口到公网。宿主机可以通过容器的私网地址访问页面,但是无法通过宿主机的接口地址访问页面,这样外网也无法访问到页面。

使用-P参数:

$ docker container run -dit --name app1 --rm -P httpd:alpine 
fde094cc000be912e68b8f6321f38d97c76cbabb54454da11c65e0aabd90dc1e
$ docker container port app1
80/tcp -> 0.0.0.0:32769
$ curl http://127.0.0.1:32769

It works!

$

这次可以用个宿主机的环回口访问了,直接用浏览器使用宿主机的地址也能够访问。但是端口是随机的,这就是需要使用port命令查看随机分配的端口号。

联盟式和开放式

联盟式是容器之间共享网络名称空间,而开放式是容器共享使用宿主机的网络名称空间。在原理上这两者是一样的。

联盟式网络

联盟式容器(joined containrs),是指使用某个已经存在容器的网络接口的容器。接口被联盟内的各容器共享使用。
联盟式容器彼此间共享的是同一个网络名称空间,UTS、IPC、Network。其他名称空间还是隔离的,Mount、PID、User。
联盟式容器彼此间存在端口冲突的可能性。因此,通常只会在多个容器上的程序需要程序loopback接口互相通信、或对某已存的容器的网络属性进行监控时才使用此种模式的网络类型。
介绍共享网络名称空间的时候,也讲过共享后的便利,就是多个容器可以使用本地环回口实现互相间的通信。

启动第一个容器
使用交互式接口,开启一个容器:

$ docker container run --name b1 --rm -it busybox
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:516 (516.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # 

启动第二个容器
上一个终端被占用着,所以再开一个终端依然使用交互式接口开启第二个容器。这里多了--network参数,就是建立联盟式容器的:

$ docker container run --name b2 --rm -it --network container:b1 busybox
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:656 (656.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # 

查看两个容器的ip地址、MAC地址,可见两个容器的网络接口是同一个。效果就相当于传统模式时,同一个主机里的两个进程。和传统的模式相比,容器之间有更多个隔离。

联盟式容器的应用场景
联盟式容器可以直接向本地的lo接口发送请求,联盟的其他容器也可以收到这个请求,就好比联盟中的容器是运行在同一个主机上的两个进程一样。
比如,首先有一个brideg模式的容器,提供一个静态Web页面的服务。然后对于动态页面的请求则发送给另外一个容器处理。
这时启动第二个动态页面的容器。如果这个容器也是brideg模式,由于ip地址出动态获得的,那么静态页面容器就无法确定向哪个ip地址发送请求。此时如果这两个容器是联盟式的网络,直接向本地的lo接口发送请求就可以了。

开放式网络

要使用开放式网络,指定--network的参数为host即可:

[root@Docker ~]# docker container run --name b3 --rm -it --network host busybox
/ # ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:3C:BE:06:75  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::42:3cff:febe:675/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:71 errors:0 dropped:0 overruns:0 frame:0
          TX packets:82 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:4773 (4.6 KiB)  TX bytes:7313 (7.1 KiB)

eth0      Link encap:Ethernet  HWaddr 00:15:5D:03:67:56  
          inet addr:192.168.24.170  Bcast:192.168.24.175  Mask:255.255.255.240
          inet6 addr: fe80::4c95:4028:8e1:a795/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:36462 errors:0 dropped:0 overruns:0 frame:0
          TX packets:20392 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:42167574 (40.2 MiB)  TX bytes:1953899 (1.8 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:48 errors:0 dropped:0 overruns:0 frame:0
          TX packets:48 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:3777 (3.6 KiB)  TX bytes:3777 (3.6 KiB)

/ # 

不要退出终端,继续在容器里开放一个httpd:

/ # echo "

Hello b3, network host

" > /var/www/index.html / # httpd -h /var/www/ / # netstat -tnl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN tcp 0 0 192.168.24.170:10010 0.0.0.0:* LISTEN tcp 0 0 :::80 :::* LISTEN tcp 0 0 :::22 :::* LISTEN tcp 0 0 ::1:25 :::* LISTEN / #

启动httpd服务后,也检查了本地监听端口,没有问题。

防火墙问题
在宿主机上是可以直接访问这个Web页面的:

$ curl 172.17.0.1

Hello b3, network host

$

但是外部网络依然无法访问,这个主要是宿主机的防火墙问题。
之前也开放过服务,并且访问都没有问题。这个应该是宿主机将访问容器的流量都默认放行了。而这次是要直接访问宿主机,虽然服务是在容器内的,但是使用的网络是宿主机的,所以需要防火墙放开策略。
临时开放宿主机上的http服务

$ firewall-cmd --add-service=http
success
$ firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources: 
  services: ssh dhcpv6-client http
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 

$

这个只是临时测试,防火墙firewalld服务重启后就会回复原样。
宿主机防火墙策略开放后,就可以使用浏览器访问宿主机的80端口打开页面了。

开放式容器的应用场景
部署方式简单,方便迁移。充分利用容器的优势,并能保证程序工作在宿主机上的要求,至少是通过宿主机的网络接口对外提供服务。

以开放式容器的方式运行和进程差不多。都是在一个机器上运行多个进程。进程之间原本就是互相隔离的,但是使用容器后,还可以隔离文件系统和用户。另外一个好处就是部署和迁移方便。以往工作为宿主机首部进程的那些系统级管理的进程,以后就可以使用容器的方式来运行。

修改默认docker网络

上一节的内容是使用docker默认创建好的3个网络,容器选择其中一个网络,并在运行容器时对可选的参数进行设置。
本节的内容是不使用默认提供的网络,而是先对网络进行自定义。自定义有两种实现方式,一种是对默认的网络进行修改,还有一种是完全创建一个新的网络。

默认的bridge网络

默认设置,docker使用的是172.17.0.1/16的网络。这个网络的网络类型是bridge,对应的网络名称也是bridge,对应在宿主机上的网卡是docker0。
使用ifconfig命令查看docker0桥的信息:

$ ifconfig
docker0: flags=4099  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:3cff:febe:675  prefixlen 64  scopeid 0x20
        ether 02:42:3c:be:06:75  txqueuelen 0  (Ethernet)
        RX packets 71  bytes 4773 (4.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 82  bytes 7313 (7.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

这个默认的dockr0桥接口是可以修改的。另外还可以额外定义新的桥接口网络。

修改默认docker0桥

docker0桥是在docker daemon启动时创建的,会根据默认属性自动创建,也可以通过修改配置文件来进行自定义。
配置文件就是/etc/docker/daemon.json,在添加镜像加速器的时候已经用过了。主要的属性有如下这些:

  • bip: docker0桥的ip地址和掩码。docker会自动为这个网络配置一个dhcp
  • fixed-cidr: 限定为容器分配的IP地址范围
  • fixed-cidr-v6: 同上,ipv6地址的设置
  • mtu: 所能通过的最大数据包大小。一般为1500,最好不要设置保持默认。
  • default-gateway: 默认网关
  • default-gateway-v6: 默认的ipv6网关
  • dns: dns服务器地址,可以指定多个,这是一个数组类型

json配置内容示例:

{
    "bip": "192.168.10.1/24",
    "fixed-cidr": "192.168.10.128/25",
    "fixed-cidr-v6": "2001:db8::/64",
    "mtu": 1500,
    "default-gateway": "192.168.1.1",
    "default-gateway-v6": "2001:db8:abcd::89",
    "dns": ["114.114.114.114", "223.5.5.5"]
}

核心选项是bip(bridge ip),用于指定docker0桥自身的IP地址。根据需要进行自定义,可以只设置一个bip,其他保持默认。其他选项会根据bip自动计算得出,还有一些是默认使用宿主机的网络属性。

实际修改本机的配置
修改后的配置文件如下:

{
    "registry-mirrors": ["http://hub-mirror.c.163.com", "https://docker.mirrors.ustc.edu.cn"],
    "bip": "192.168.101.1/24",
    "fixed-cidr": "192.168.101.128/25",
    "dns": ["114.114.114.114", "223.5.5.5"]
}

重启服务后,先查看宿主机的docker0桥:

$ systemctl restart docker
$ ifconfig
docker0: flags=4099  mtu 1500
        inet 192.168.101.1  netmask 255.255.255.0  broadcast 192.168.101.255
        inet6 fe80::42:3cff:febe:675  prefixlen 64  scopeid 0x20
        ether 02:42:3c:be:06:75  txqueuelen 0  (Ethernet)
        RX packets 81  bytes 5445 (5.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 92  bytes 8125 (7.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

此时docker0的网络属性已经变了。

新建容器查看网络
创建容器,查看容器内的网络属性:

$ docker container run --name b4 --rm -it busybox
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:C0:A8:65:80  
          inet addr:192.168.101.128  Bcast:192.168.101.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:516 (516.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # cat /etc/resolv.conf 
nameserver 114.114.114.114
nameserver 223.5.5.5
/ # exit
$ 

这里可以确认到自动获取的ip地址也符合设置要求了,还有dns服务器的地址也是自定义的。

创建自定义桥

查看已有的网络:

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
8ec74d5bd709        bridge              bridge              local
d086953087bb        host                host                local
fa0c7f1fb6ca        none                null                local
$ 

查看网络插件
这里先展开一下,看看docker支持哪些类型的网络。
命令docker info里的插件Plugins的内容:

$ docker info
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
 Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog

在Network插件中,除了bridge、host、nul之外,还有overlay(叠加网络)、macvlan(基于mac的vlan虚拟网络),这两种网络类型没有展开。这些网络插件在下面创建网路时,通过-d参数可以指定,默认是bridge。

创建网络
使用命令docker network create命令来创建网络:

$ docker network create -d bridge --subnet "192.168.111.0/24" mybr1

7128a28bbbf39a6ca483ecad03d5d85c8179507aff66ced73ca8de5233f16fee
[root@Docker ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
8ec74d5bd709        bridge              bridge              local
d086953087bb        host                host                local
7128a28bbbf3        mybr1               bridge              local
fa0c7f1fb6ca        none                null                local
[root@Docker ~]# ifconfig
br-7128a28bbbf3: flags=4099  mtu 1500
        inet 192.168.111.1  netmask 255.255.255.0  broadcast 192.168.111.255
        ether 02:42:1f:ff:fd:5d  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker0: flags=4099  mtu 1500
        inet 192.168.101.1  netmask 255.255.255.0  broadcast 192.168.101.255
        inet6 fe80::42:3cff:febe:675  prefixlen 64  scopeid 0x20
        ether 02:42:3c:be:06:75  txqueuelen 0  (Ethernet)
        RX packets 81  bytes 5445 (5.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 92  bytes 8125 (7.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

-d参数可以省略,默认就是bridge。更多的参数可以使用--help选项查看:

  • --gateway: 设置网关
  • --ip-range: 等同fixed-cdir设置,从一个IP范围分配IP地址
  • --internal: 限制外网网络连接到这个网络
  • --ipv6: 启用ipv6网络
  • --subnet: 等同bip设置,子网

指定网卡的名称
在ifconfig查看的时候,网卡显示的名称是根据这个docker网络额ID号自动生成的。在创建网络时使用-o参数可以进行指定:

$ docker network create --subnet "192.168.112.0/24" -o "com.docker.network.bridge.name=docker1" mybr2
b8a2639ce1baef83e54b5a0bca5ba6c7bbd2e6b607e62016c930350235bea965
$ ifconfig
br-7128a28bbbf3: flags=4099  mtu 1500
        inet 192.168.111.1  netmask 255.255.255.0  broadcast 192.168.111.255
        ether 02:42:1f:ff:fd:5d  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker0: flags=4099  mtu 1500
        inet 192.168.101.1  netmask 255.255.255.0  broadcast 192.168.101.255
        inet6 fe80::42:3cff:febe:675  prefixlen 64  scopeid 0x20
        ether 02:42:3c:be:06:75  txqueuelen 0  (Ethernet)
        RX packets 81  bytes 5445 (5.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 92  bytes 8125 (7.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

docker1: flags=4099  mtu 1500
        inet 192.168.112.1  netmask 255.255.255.0  broadcast 192.168.112.255
        ether 02:42:74:95:cd:0b  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

这次创建的网卡的名字就舒服多了。
关于-o参数,主要有下面这些:

选项等同描述
com.docker.network.bridge.name - 创建Linux bridge使用的bridge名称
com.docker.network.bridge.enable_ip_masquerade ip-masq 启用IP伪装
com.docker.network.bridge.enable_icc icc 启用或禁用容器间连接
com.docker.network.bridge.host_binding_ipv4 ip 绑定容器端口时默认绑定的IP
com.docker.network.driver.mtu mtu 设置容器网络MTU

这里没有网络基础可能不太好理解,不过我们还可以参考默认的bridge网络的设置:

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "80631c00ea3ece0280c786b90f5157be68fe76c26d52f4d9d870a7f5b59edde1",
        "Created": "2019-07-21T10:16:03.635792707+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]
$ 

这个是默认的没有修改过的bridge桥的信息。除了Options参数,其他参数也可以参考一下。

使用自定义网络
这个之前已经用到过了。之前可选的网络只有默认提供的3个,brideg、host、none,现在创建的自定义网络也可以使用了。命令docker network ls可以查看,引用的时候使用--network参数指定网络的名称(NAME):

$ docker container run --rm -it --network mybr2 busybox
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:C0:A8:70:02  
          inet addr:192.168.112.2  Bcast:192.168.112.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:12 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1032 (1.0 KiB)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # 

通过容器内的eth0的IP地址可以判断使用的是刚才创建的自定义网络。

远程管理docker

docker守护进程是C/S构架,默认只监听本机的UNIX sock文件。该文件位于/var/run/目录下:

$ ls /var/run/*.sock
/var/run/docker.sock
$ 

可以设置为监听TCP端口,这样就可以让网络上其他主机上的客户端连接到本地的服务端。

服务端配置

服务端需要修改配置文件,让服务监听网络端口。配置文件/etc/docker/daemon.json添加一个hosts属性:

{
    "hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]
}

修改配置文件后需要重启服务。默认本地的UNIX sock文件还是保留着。

客户端连接

客户端要连接到服务端,使用-H或--host参数来添加服务器。直接不带任何参数执行docker命令,可以查看到帮助信息:

-H, --host list          Daemon socket(s) to connect to

之前使用客户端的时候,都是不带这个参数的,也就是默认连接本机的UNIX sock文件。加上参数后,就可以指定连接的服务端了。
使用-H参数,不过指定的服务器依然是本地的UNIX sock文件:

$ docker -H unix:///var/run/docker.sock network ls

如果开启了网络的监听,可以这样:

$ docker -H 127.0.0.1 version

协议和端口号都可以省略,默认是tcp的2375端口。

不能指定多个服务器
看帮助,这个参数是个list,就是可以多次调用-H来添加多个服务端。参数是这么设计的,但是程序的逻辑不允许:

$ docker -H unix:///var/run/docker.sock -H 127.0.0.1 images
Please specify only one -H
$ 

这里找到了源码中对应的处理函数:

func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) {
    var host string
    switch len(hosts) {
    case 0:
        host = os.Getenv("DOCKER_HOST")
    case 1:
        host = hosts[0]
    default:
        return "", errors.New("Please specify only one -H")
    }

    return dopts.ParseHost(tlsOptions != nil, host)
}

参数只能是0个或1个,否则就返回错误。

设置环境变量
如果不使用-H参数指定,还可以通过环境变量DOCKER_HOST指定。好处是不用每次连接都加上-H参数了。
下面是设置和验证的命令:

$ export DOCKER_HOST="unix:///var/run/docker.sock"
$ echo $DOCKER_HOST
unix:///var/run/docker.sock
$ 

这里设置的环境变量是临时生效的,重新登录就没有了。如果想让环境变量永久生效请写入 ~/.bashrc 。


文章题目:Docker网络
URL链接:http://pwwzsj.com/article/ihipdi.html