2、Docker网络基础

容器技术 / 2020-07-23

一、序言:

​ 前面提到容器技术基于Linux Kernel的NameSpace且老死不相往来,但这会产生一些问题,运行容器是为了运行进程来提供某种服务,好家伙一下子全进去蹲了!那容器之间如何通信?外部机器如何访问宿主机上的容器?这究竟是机器的扭曲,还是容器的沦丧?我不禁陷入了沉思!今天,让我们一起揭开大河女神神秘的面纱!走进科学!走进Docker Network!

二、网络模型

2.1、名称空间共享

​ NameSpace中其实有部分名称空间是允许共享的,比如Network、UTS、IPC这三样;User、PID、Mount肯定是不能共享,共享就乱套了!可以共享Network,这样容器与容器或与外部通信就通畅无阻了!

2.2、docker网络模型

​ 来说说Network,我们使用KVM或者VMware虚拟机时,都会接触到Bridge、Host only、NAT这三种网络类型,同样Docker也支持,并为此设计了四种网络模型。下面来聊聊!

image-20200722145036366

图片来源:马哥教育

2.2.1、查看网络类型
sanxi@container:~$ sudo docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
31ed10ebe495        bridge              bridge              local
4bd82d29df74        host                host                local
abf414b6ad34        none                null                local
#其实还有个container类型,下面会介绍
2.2.2、Bridge模型

默认的网络类型,装完docker自动创建docker0桥,相当于VMware的NAT,经过NAT后可以和外部通信。顺带提一下,docker官方不建议使用默认的桥!

sanxi@container:~$ sudo docker container run --name redis2 -it --rm --network bridge redis:6.0.5-alpine
[sudo] password for sanxi: 

sanxi@container:~$ sudo docker container inspect redis2 | grep NetworkMode
            "NetworkMode": "bridge",

sanxi@container:~$ sudo docker container run --name redis2 -it --rm --network bridge redis:6.0.5-alpine /bin/sh
/data # ifconfig #可以看到这是NAT地址,一个新的B类私有网地址。
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:736 (736.0 B)  TX bytes:0 (0.0 B)
#ifconfig后面的lo回环信息输出我过滤了,太长了,占地方!
2.2.3、Container模型

也叫共享桥,即容器间网络名称空间共享

为了突出效果,我们先起一个容器

sanxi@container:~$ sudo docker container run --name redis2 -it --rm --network bridge redis:6.0.5-alpine /bin/sh
/data # ip addr show eth0
30: eth0@if31: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    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

接着再起一个容器,网络类型是container至redis2

sanxi@container:~$ sudo docker container run --name redis3 -it --rm --network container:redis2 redis:6.0.5-alpine /bin/sh
[sudo] password for sanxi: 
/data # ip addr show eth0
30: eth0@if31: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP 
    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

如上所示,后面的redis3地址是redis2的地址,这就是container类型,这就是容器共享桥。

2.2.4、Host

直接共享宿主机的网络名称空间,类似于VMware的物理桥接模式,但不同的是docker里面的网络配置直接使用宿主机的配置而不是跟VMware那样跟宿主机处于同一个vlan!

熊猫头 就这
#看吧,这直接就是宿主机的网络配置信息,而不是跟VMware的桥接到物理机同一个vlan
sanxi@container:~$ sudo docker container run --name redis2 -it --rm --network host redis:6.0.5-alpine /bin/sh
/data # ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:24:35:C4:CB  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          inet6 addr: fe80::42:24ff:fe35:c4cb/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:24 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:2616 (2.5 KiB)

ens160    Link encap:Ethernet  HWaddr 00:0C:29:3D:33:64  
          inet addr:192.168.0.166  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe3d:3364/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1346560 errors:0 dropped:0 overruns:0 frame:0
          TX packets:172852 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:824042122 (785.8 MiB)  TX bytes:34182647 (32.5 MiB)
#同上,lo过滤掉
/data # hostname
container
#连主机名都是一样的
2.2.6、None

不需要网络,因此没有网卡和IP地址!没有IP地址???搞毛呢!

这三张「黑人问号」表情图,分别有哪些来历和特殊含义、用法? - 知乎

其实这种类型是可以链接至Data Volume(下一章节会讲到),主要用于像mysqldump那样备份本地数据库,一个本地备份你要网络干啥呢!对不!

###看!除了127.0.0.1,因此也就没有额外的网卡自然就没有IP/V4地址!
sanxi@container:~$ sudo docker container run --name redis2 -it --rm --network none redis:6.0.5-alpine /bin/sh
/data # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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

好了 关于docker网络基础知识就讲到这了,下面是一些其它的选项使用说明


三、Docker网络操作命令

3.1、指定容器主机名
使用语法 -h, --hostname string        Container host name
docker container run --hostname sanxi
3.2、添加hosts解析
使用语法 --add-host DOMAIN_NAME:IP     Add a custom host-to-IP mapping (host:ip) #可以重复使用
sanxi@container:~$ sudo docker container run --name redis2 -it --rm --add-host sanxi.info:172.17.0.2 redis:6.0.5-alpine /bin/sh
/data # 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	sanxi.info
172.17.0.2	b905954199ed
3.3、指定DNS服务器
使用语法:--dns list                       Set custom DNS servers
docker container run --dns 223.5.5.5

3.4、端口暴露

容器是与外部隔离的,里边的服务若想被外部主机访问,需要将服务端口暴露出去,我们称之为expose或者publish,原理其实就是NAT转换。比如说手动编写iptables的DNAT规则,但这很麻烦,因为每一次启动一个容器你就得写一条规则;或者使用docker自带选项自动生成规则,有四种用法

使用语法:-p, --publish list                   Publish a container's port(s) to the host
3.4.1、ContainerPort

容器指定端口映射至宿主机所有地址的一个动态端口;如下所示

sanxi@container:~$ sudo docker container run --name redis -it --rm -p 6379 redis:6.0.5-alpine /bin/sh

sanxi@container:~$ sudo docker container port redis
[sudo] password for sanxi: 
6379/tcp -> 0.0.0.0:32768
3.4.2、HostPort:ContainerPort

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

sanxi@container:~$ sudo docker container run --name redis -it --rm -p 6379:6379 redis:6.0.5-alpine /bin/sh

sanxi@container:~$ sudo docker container port redis
6379/tcp -> 0.0.0.0:6379

端口映射成功后,我们再来看一下docker自动生成的iptables规则

sanxi@container:~$ sudo iptables -t nat -vnL
#我过滤前面的规则信息,仅显示docker生成的规则。
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2           tcp dpt:6379

Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0           
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:6379 to:172.17.0.2:6379
3.4.3、ip::containerPort

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

sanxi@container:~$ sudo docker container run --name redis -it --rm -p 192.168.0.166::6379 redis:6.0.5-alpine /bin/sh

sanxi@container:~$ sudo docker container port redis
6379/tcp -> 192.168.0.166:32768
3.4.4、IP:HostPort:ContainerPort

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

sanxi@container:~$ sudo docker container run --name redis -it --rm -p 192.168.0.166:888:6379 redis:6.0.5-alpine /bin/sh
#把redis的6379映射到宿主机的192.168.0.166上的888端口
sanxi@container:~$ sudo docker container port redis
6379/tcp -> 192.168.0.166:888

查看映射的端口

sanxi@container:~$ sudo docker container port --help

Usage:	docker container port CONTAINER [PRIVATE_PORT[/PROTO]]
#具体实践,如上演示所示!

3.5、Docker网络桥

前面提到docker官方文档不建议使用docker0桥,我们自建网络桥或者修改默认桥,需要在/etc/docker/daemon.json文件中定义,语法有严格要求!我这里贴的是Docker官方文档的说明

Use the default bridge network

The default bridge network is considered a legacy detail of Docker and is not recommended for production use. Configuring it is a manual operation, and it has technical shortcomings.

Configure the default bridge network
To configure the default bridge network, you specify options in daemon.json. Here is an example daemon.json with several options specified. Only specify the settings you need to customize.
#就是前面讲到的编辑/etc/docker/daemon.json文件,如果此前有在里面配置了镜像加速服务,需要特别注意语法,下面演示会再次提到
{
  "bip": "192.168.1.5/24",
  "fixed-cidr": "192.168.1.5/25",
  "fixed-cidr-v6": "2001:db8::/64",
  "mtu": 1500,
  "default-gateway": "10.20.1.1",
  "default-gateway-v6": "2001:db8:abcd::89",
  "dns": ["10.20.1.2","10.20.1.3"]
}
Restart Docker for the changes to take effect.
3.5.1、修改默认桥

看了上面官方文档,下面来演示一下修改默认的bridge

sanxi@container:~$ sudo vim /etc/docker/daemon

{
"registry-mirrors": ["https://XXXXXX.mirror.aliyuncs.com"], #有多条语句一定要用逗号隔开,最后一句不能有逗号
"bip": "10.0.1.0/24"
}
#wq保存退出
sanxi@container:~$ sudo systemctl daemon-reload #重载配置文件
sanxi@container:~$ sudo snap restart docker #我这里是kubuntu20.0.4,因此是snap而不是用systemctl,大家要注意这一点。
Restarted.

**出问题了!**重启完后我ip a看了下docker0地址还是原来的没有变,反复看了json就俩句没错,后面Google了一下,找到了解决方法,至于原因我打算向docker社区反应后面有结果再更新此文章。

sanxi@container:~$ sudo snap stop docker #先停掉docker服务 #再强调一次,我这是kubuntu20.0.4采用snap命令
Stopped.
sanxi@container:~$ sudo ip link set dev docker0 down #停掉网络桥
sanxi@container:~$ sudo brctl delbr docker0 #删除默认桥
sanxi@container:~$ sudo snap start docker #重启docker服务
Started.
#如果你执行命令提示brctl not found,需要安装bridge-utils这个包,里面带了brctl
3.5.2、自建网络桥

感觉修改默认桥还是麻烦,所以让我们自建网络桥吧!先来看看命令用法

sanxi@container:~$ sudo docker network create --help

Usage:	docker network create [OPTIONS] NETWORK

Create a network

Options:
      --attachable           Enable manual container attachment
      --aux-address map      Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
      --config-from string   The network from which copying the configuration
      --config-only          Create a configuration only network
  -d, --driver string        Driver to manage the Network (default "bridge")
      --gateway strings      IPv4 or IPv6 Gateway for the master subnet
      --ingress              Create swarm routing-mesh network
      --internal             Restrict external access to the network
      --ip-range strings     Allocate container ip from a sub-range
      --ipam-driver string   IP Address Management Driver (default "default")
      --ipam-opt map         Set IPAM driver specific options (default map[])
      --ipv6                 Enable IPv6 networking
      --label list           Set metadata on a network
  -o, --opt map              Set driver specific options (default map[])
      --scope string         Control the network's scope
      --subnet strings       Subnet in CIDR format that represents a network segment

好的,有个--subnet选项可以让我们来自建网络桥!来试试!

sanxi@container:~$ sudo docker network create --subnet 10.1.1.0/24 testbridge
2f4eeb1c85b9b8a04a4164626579776777d712ec92ada073fcf8678ef5a3ecf7
sanxi@container:~$ sudo docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
31ed10ebe495        bridge              bridge              local
4bd82d29df74        host                host                local
abf414b6ad34        none                null                local
2f4eeb1c85b9        testbridge          bridge              local
#桥建好了,只需要在启动容器时指定此网络即可

比修改默认桥简单多了!是不!

世间微尘里 独爱茶酒中