由于Docker在工作中大多数场景用不到,对于一个后端开发者而言,仅仅从使用上和原理上了解一下即可,所以网络配置是Docker学习笔记基础篇的最后一篇,后续一段时间的精力可能会投入到《设计模式》上去,至于Docker更高级的用法,涉及Docker Compose
容器编排、Docker Swarm
集群部署、CICD/Jenkins
以及Kubernates
等内容后续有精力有时间或者成为工作痛点的时候再花时间了解。
Docker再安装
因为网络部分内容比较复杂,为了效果更明显些,不受历史内容影响,我决定换个宿主机操作,毕竟咱有三个虚拟机,召唤TML-2即可。还是按照之前学习的安装方式:
详细步骤就不重复之前的了,感兴趣可以参考我专栏的这篇Blog:【Docker学习笔记 二】Docker安装、运行流程与常用命令, 经过一系列命令操作可以看到我已经在第二台虚拟机上安装了Docker:
[root@192 ~]# docker version Client: Docker Engine - Community Version: 20.10.12 API version: 1.41 Go version: go1.16.12 Git commit: e91ed57 Built: Mon Dec 13 11:45:41 2021 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.12 API version: 1.41 (minimum version 1.12) Go version: go1.16.12 Git commit: 459d0df Built: Mon Dec 13 11:44:05 2021 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.4.13 GitCommit: 9cc61520f4cd876b86e77edfeb88fbcd536d1f9d runc: Version: 1.0.3 GitCommit: v1.0.3-0-gf46b6ba docker-init: Version: 0.19.0 GitCommit: de40ad0
并且配置好了阿里云的镜像加速。
Docker默认的网络模式
首先我们使用查看ip地址的命令ip addr
查看下当前的网络地址有哪些:
[root@192 ~]# 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 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:39:b4:c3 brd ff:ff:ff:ff:ff:ff inet 192.168.5.102/24 brd 192.168.5.255 scope global noprefixroute ens33 valid_lft forever preferred_lft forever inet6 fe80::f808:3fa:3eec:4af6/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000 link/ether 52:54:00:0b:48:4f brd ff:ff:ff:ff:ff:ff inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 valid_lft forever preferred_lft forever 4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000 link/ether 52:54:00:0b:48:4f brd ff:ff:ff:ff:ff:ff 5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:c5:e1:d8:5b brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever [root@192 ~]#
以上各个网络地址解释如下:
lo
为本机环回地址,也就是我自己的windows主机ens33
是虚拟机TML-2的地址,也就是Docker的宿主机的网络地址192.168.5.102
。virbr0
是 KVM 默认创建的一个 Bridge,其作用是为连接其上的虚机网卡提供 NAT 访问外网的功能。 virbr0 默认分配了一个IP192.168.122.1
,并为连接其上的其他虚拟网卡提供 DHCP 服务virbr0-nic
代表虚拟网桥NIC。 它基本上是物理网卡和虚拟机的虚拟网卡之间的桥梁docker0
即是docker使用的网络地址
属实是三层套娃了。使用以下命令查看所有的Docker网络模式:
[root@192 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 173795075c5b bridge bridge local fcddcee8a2d1 host host local 9efbda880fae none null local [root@192 ~]#
Docker默认提供了四个网络模式,说明:
- bridge:容器默认的网络是桥接模式(自己搭建的网络默认也是使用桥接模式,启动容器默认也是使用桥接模式)。此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及
Iptables nat
表配置与宿主机通信。 - none:不配置网络,容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。
- host:容器和宿主机共享Network namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
- container:创建的容器不会创建自己的网卡,配置自己的IP容器网络连通。容器和另外一个容器共享Network namespace(共享IP、端口范围)。
容器默认使用bridge网络模式,我们使用该docker run --network=选项指定容器使用的网络:
host模式:使用 --net=host 指定。 none模式:使用 --net=none 指定。 bridge模式:使用 --net=bridge 指定,默认设置。 container模式:使用 --net=container:NAME_or_ID 指定
Namespace:Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。
1 host模式
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。
2 container模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。
同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
3 none模式
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种网络模式下容器只有lo环回网络,没有其他网卡。none模式可以在容器创建时通过–network=none
来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
4 bridge模式
当Docker进程启动时,会在宿主机上创建一个名为docker0
的虚拟网桥,宿主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡)
,另一端放在主机中,以vethxxx
这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show
命令查看。
bridge模式是docker的默认网络模式,不写–net
参数,就是bridge模式。使用docker run -p
时,docker实际是在iptables
做了DNAT
规则,实现端口转发功能。可以使用iptables -t nat -vnL
查看。
当Docker server启动时,会在宿主机上创建一个名为docker0的虚拟网桥,此宿主机启动的Docker容器会连接到这个虚拟网桥上。Docker0使用到的技术是evth-pair
技术。在默认bridge网络模式下,我们每启动一个Docker容器,Docker就会给Docker容器配置一个ip。Docker容器完成bridge网络配置的过程如下:
- 在宿主机上创建一对虚拟网卡
veth pair
设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。 - Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以
veth65f9
这样类似的名字命名,并将这个网络设备加入到docker0网桥中。 - 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。
网络请求到来时可以通过端口转发功能到达。我们来测试下这种默认模式,在上篇Blog中我们成功将一个自制的tomcat镜像推送到了DockerHub,那么本篇Blog我们就来使用自制的tomcat来操作下,梦幻联动一波:
下载镜像查看结果:
[root@192 ~]# docker run -d -P --name tomcat-tml tianmaolin/tml-mydockerfile-tomcat:1.0 Unable to find image 'tianmaolin/tml-mydockerfile-tomcat:1.0' locally 1.0: Pulling from tianmaolin/tml-mydockerfile-tomcat 2d473b07cdd5: Pull complete 4adf06756b56: Pull complete 25761eba323d: Pull complete 30ccbd4f8175: Pull complete 83bf41c456b8: Pull complete Digest: sha256:d829761d327aea380c934da8c58873843cfe0e1872d5cdb958afc2557b268db0 Status: Downloaded newer image for tianmaolin/tml-mydockerfile-tomcat:1.0 50b626fc91d2660232e9b299b09d65c232e33cb2ccfcbbc65307fbadf6eb91ef [root@192 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE tianmaolin/tml-mydockerfile-tomcat 1.0 94dbe2d0cb91 6 days ago 799MB [root@192 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 50b626fc91d2 tianmaolin/tml-mydockerfile-tomcat:1.0 "/bin/sh -c '/usr/lo…" 2 minutes ago Up About a minute 0.0.0.0:49154->8080/tcp, :::49154->8080/tcp tomcat-tml [root@192 ~]#
访问测试如下:
然后进入容器查看容器网络设置:
[root@192 ~]# docker exec -it 50b626fc91d2 ip addr OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "ip": executable file not found in $PATH: unknown [root@192 ~]# docker exec -it 50b626fc91d2 /bin/bash
好吧,缺少网络配置,因为之前的tomcat是基于centos基础版本构建的,所有缺少网络工具,我还是使用最新版的集成版tomcat8:
[root@192 ~]# docker run -d -P --name tomcat01 tomcat:8.0 Unable to find image 'tomcat:8.0' locally 8.0: Pulling from library/tomcat f189db1b88b3: Pull complete 3d06cf2f1b5e: Pull complete edd0da9e3091: Pull complete eb7768aae14e: Pull complete e2780f585e0f: Pull complete e5ed720afeba: Pull complete d9e134700cfc: Pull complete e4804b33d02a: Pull complete b9df0c24315e: Pull complete 49fdae8eaa20: Pull complete 1aea3d9a32e6: Pull complete Digest: sha256:8ecb10948deb32c34aeadf7bf95d12a93fbd3527911fa629c1a3e7823b89ce6f Status: Downloaded newer image for tomcat:8.0 WARNING: IPv4 forwarding is disabled. Networking will not work. 6cd417097796215ffdb1563bb71ba2c500b8e91adb4049880fc0d7f1b366def9 [root@192 ~]# docker exec -it tomcat01 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 10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 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 [root@192 ~]#
可以看到容器启动后Docker分配给容器一个格式如eth0@if11
的ip地址。宿主机运行ip addr命令查看变化:
[root@192 ~]# 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 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 00:0c:29:39:b4:c3 brd ff:ff:ff:ff:ff:ff inet 192.168.5.102/24 brd 192.168.5.255 scope global noprefixroute ens33 valid_lft forever preferred_lft forever inet6 fe80::f808:3fa:3eec:4af6/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000 link/ether 52:54:00:0b:48:4f brd ff:ff:ff:ff:ff:ff inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 valid_lft forever preferred_lft forever 4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000 link/ether 52:54:00:0b:48:4f brd ff:ff:ff:ff:ff:ff 5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:c5:e1:d8:5b brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:c5ff:fee1:d85b/64 scope link valid_lft forever preferred_lft forever 9: vethd4ac7ba@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether 9e:dd:50:f2:d3:ae brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::9cdd:50ff:fef2:d3ae/64 scope link valid_lft forever preferred_lft forever 11: veth1f00e42@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether 9e:71:e9:ab:e6:8e brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::9c71:e9ff:feab:e68e/64 scope link valid_lft forever preferred_lft forever [root@192 ~]#
可以看到宿主机多了两个网卡,其中veth1f00e42@if10
和tomcat01的网卡相对,vethd4ac7ba@if8
就是和我自制镜像生成的容器tomcat-tml
相对,而这两个网卡都是成对的。使用的技术就是evth-pair。evth-pair 就是一对的虚拟设备接口,他们是成对出现的,一段连着协议,一段彼此相连。evth-pair充当一个桥梁,连接各种虚拟网络设备。执行iptables -t nat -vnL
命令查看一下
[root@192 ~]# iptables -t nat -vnL Chain PREROUTING (policy ACCEPT 1 packets, 229 bytes) pkts bytes target prot opt in out source destination 10 520 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL Chain INPUT (policy ACCEPT 1 packets, 229 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 32 packets, 2368 bytes) pkts bytes target prot opt in out source destination 0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL Chain POSTROUTING (policy ACCEPT 32 packets, 2368 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 3 303 RETURN all -- * * 192.168.122.0/24 224.0.0.0/24 0 0 RETURN all -- * * 192.168.122.0/24 255.255.255.255 0 0 MASQUERADE tcp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535 0 0 MASQUERADE udp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535 0 0 MASQUERADE all -- * * 192.168.122.0/24 !192.168.122.0/24 0 0 MASQUERADE tcp -- * * 172.17.0.3 172.17.0.3 tcp dpt:8080 0 0 MASQUERADE tcp -- * * 172.17.0.2 172.17.0.2 tcp dpt:8080 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 6 312 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:49154 to:172.17.0.3:8080 0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:49155 to:172.17.0.2:8080 [root@192 ~]#
可以看到DNAT的端口映射技术。尝试从宿主机 ping tomcat01
容器:
[root@192 ~]# ping 172.17.0.2 PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data. 64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.095 ms 64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.055 ms 64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.068 ms 64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.039 ms 64 bytes from 172.17.0.2: icmp_seq=5 ttl=64 time=0.043 ms 64 bytes from 172.17.0.2: icmp_seq=6 ttl=64 time=0.099 ms 64 bytes from 172.17.0.2: icmp_seq=7 ttl=64 time=0.043 ms 64 bytes from 172.17.0.2: icmp_seq=8 ttl=64 time=0.044 ms 64 bytes from 172.17.0.2: icmp_seq=9 ttl=64 time=0.039 ms