Docker 容器网络,主要采用架构由三部分组成:CNM、Libnetwork、驱动。
CNM :
Docker网络采用的设计规范时CNM(Container Network Model) : CNM规定了Docker网络的基础组成要素:Sandbox、Endpoint、Network。
容器包含 Endpoint 和 Sandbox 而 Sandbox 包含 Endpoint 然后 Endpoint 和 Network 对接
。Sandbox:提供了容器的虚拟网络栈,也即端口套接字、IP路由表、防火墙、DNS配置等内容。 主要用于隔离容器网络与宿主机网络,形成了完成独立的容器网络环境。
。Network,Docker内部的虚拟子网,网络内的参与者相互可见并能够进行通讯。 Docker的虚拟网路和宿主机网络存在隔离关系,主要目的是形成容器间的安全通讯环境。
。Endpoint,主要负责创建连接,
![](https://i-blog.csdnimg.cn/blog_migrate/a988e35a9a41187aa5fc53cf984613ad.jpeg)
Libetwork
是CNM 的标准实行,Libnetwork(Go语言开发)实现了CNM中定义的全部三个组件,此外它还实现了本地服务发现、基于Ingress容器负载均衡,以及网络控制层和管理层功能。
驱动
如果说Libnetwork实现控制层和管理层功能,那么
驱动 就负责实现数据层。 驱动通过实现特定网络类型的方式扩展了Docker网络栈,例入桥接网络和覆盖网络。
Docker内置了诺干驱动,通常被称为原生驱动或者本地驱动。比如Bridge Driver、Host Driver、Overlay Driver、MacLan Driver、None Driver等等。 第三方也可以编写Docker 网络驱动,这些驱动被叫做远程驱动,例入Calico、Contiv、Kuryr 以及Weave。每个驱动负责创建其上所有网络资源的创建和管理。
其中 Bridge 和 Overlay 在开发过程中使用频率较高
。Bridge,Docker容器的默认网络驱动,通过网桥来实现网络通讯。
。Overlay,借助 Docker 集群模块 Docker Swarm 搭建的跨 Docker Daemon 网络。
![](https://i-blog.csdnimg.cn/blog_migrate/b606637eebfbf0bfbca1f718585eb83a.jpeg)
Docker安装时,会自动安装一块Docker 网卡称为 docker0
$ ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
inet6 fe80::42:23ff:feab:dd7e prefixlen 64 scopeid 0x20<link>
ether 02:42:23:ab:dd:7e txqueuelen 0 (Ethernet)
RX packets 6 bytes 452 (452.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 450 (450.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
2.桥接网络
Docker 的bridge 网络采用内置的bridge驱动,而bridge的底层采用的是Linux内核中 Linux bridge技术。
容器间的通信 bridge:
一、创建两个容器
$ docker container run -d --rm --name box1 busybox /bin/sh -c "while true; do sleep 3600; done”
Unable to find image 'busybox:latest' locally
Trying to pull repository docker.io/library/busybox ...
latest: Pulling from docker.io/library/busybox
7e5209d2300f: Pull complete
Digest: sha256:34c3559bbdedefd67195e766e38cfbb0fcabff4241dbee3f390fd6e3310f5ebc
Status: Downloaded newer image for docker.io/busybox:latest
019a0daed76f2c71ccb7879a259cc04eac9b60a826529c383f104ffa1ad12f3d
$ docker container run -d --rm --name box2 busybox /bin/sh -c "while true; do sleep 3600; done"
3325eedba7c2b16956b20d1eea968c74471e0fb42747fb73079f74bae460e16c
查看容器
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3325eedba7c2 busybox "/bin/sh -c 'while..." 7 seconds ago Up 6 seconds box2
019a0daed76f busybox "/bin/sh -c 'while..." 14 seconds ago Up 13 seconds box1
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
48bcb226d17e bridge bridge local
3ea7c9283722 host host local
eee2e5b9cf19 none null local
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "48bcb226d17e09092378deb2c4e9a070edb64f5bc6e0b0378edd45f5f248a6f2",
"Created": "2022-03-04T18:00:02.632533487+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,
"Containers": {
"019a0daed76f2c71ccb7879a259cc04eac9b60a826529c383f104ffa1ad12f3d": {
"Name": "box1",
"EndpointID": "8d09e83384eeeb6cb9fc31fadb1d9f39339ae4ccd6971dc790f60410797c6a07",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"3325eedba7c2b16956b20d1eea968c74471e0fb42747fb73079f74bae460e16c": {
"Name": "box2",
"EndpointID": "52c096c53b562a205a8880417cbba8fe0ff0fb1c0cbf3fd7fa67f828fbd72ac4",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"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": {}
}
]
从此可以看出,两个容器都在一个叫docker0的Linux bridge上面,而docker0是docker默认安装添加上去的,可以证明:
容器相互之间可以拼通的。
![](https://i-blog.csdnimg.cn/blog_migrate/29f6c6ae7a5f943b5202ab813e35127f.jpeg)
而默认的 bridge网络是被映射到内核中为 docker0的 网桥上。
$ ip link show docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:23:ab:dd:7e brd ff:ff:ff:ff:ff:ff
Note: 你就可以把 bridge 和 docker0 当成 Linux 网桥的两个名字,两个都是代表同一个东西。docker 为了管理网络,又给 docker0 这个网桥取名为 bridge
![](https://i-blog.csdnimg.cn/blog_migrate/433a8befd5aee6d11d47fcb5ed7d6359.jpeg)
Node:也可以通过 brctl 命令进行 bridge 连接接口查看(
brctl
使用前需要安装, 对于CentOS, 可以通过
sudo
yum
install
-y
bridge-utils
安装. 对于Ubuntu, 可以通过
sudo
apt-get
install
-y
bridge-utils
)
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.024223abdd7e no veth33f963f
vethceb182f
从上面也可以确认两个接口进行通信。
2、容器对外通信
一、 查看路由
$ ip route
default via 172.19.79.253 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.19.64.0/20 dev eth0 proto kernel scope link src 172.19.77.225
(个人理解:从此可以看出,如果是其他应用访问走的默认,如果docker就会走docker0接口,而经过桥接docker0其实意义上可以说是eth0 所以容器是可以访问外网。这里里面不得不提iptables转发)
二、端口转发
命令通过
-p
$ docker container run -d --rm --name -p8080:80 web nginx
![](https://i-blog.csdnimg.cn/blog_migrate/30b08f2900f85ba7067dfd674ffb6acb.jpeg)
效果
http://我的IP地址:8080/
![](https://i-blog.csdnimg.cn/blog_migrate/098a22ec6ed9bd26519e3ad5cda33258.jpeg)
端口映射
如上效果图所示,容器内部开放端口为 80 ,该端口映射到了Docker主机的8080端口上,最终访问上面的IP地址所有流量都会转发到容器的80端口上面。
通过iptables命令可以更直观的感受
$ iptables -t nat -nvxL
Chain PREROUTING (policy ACCEPT 3 packets, 112 bytes)
pkts bytes target prot opt in out source destination
8398 514593 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT 3 packets, 112 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 33 packets, 2300 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 35 packets, 2428 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * !br-43435188760c 172.18.0.0/16 0.0.0.0/0
0 0 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
0 0 MASQUERADE tcp -- * * 172.17.0.4 172.17.0.4 tcp dpt:80
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- br-43435188760c * 0.0.0.0/0 0.0.0.0/0
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
2 128 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.4:80
tcp dpt:8080 to :172.17.0.4:80 刚刚好对应上面的效果。
3、自定义桥接bridge
$ docker network create -d bridge mybridge
43435188760ce70d58099ab5fe27b85fc07e2f5e14d6748e6532daa5749d4963
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
48bcb226d17e bridge bridge local
3ea7c9283722 host host local
43435188760c mybridge bridge local
eee2e5b9cf19 none null local
$ docker network inspect mybridge
[
{
"Name": "mybridge",
"Id": "43435188760ce70d58099ab5fe27b85fc07e2f5e14d6748e6532daa5749d4963",
"Created": "2022-03-07T15:41:06.440007809+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
$ docker container run -d --rm --name box3 --network mybridge busybox /bin/sh -c "while true; do sleep 3600; done”
2d7ce63d7e3f956564e684e343b7da2ae747e5679292c109096f619e73bce719
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d7ce63d7e3f busybox "/bin/sh -c 'while..." 9 seconds ago Up 8 seconds box3
3325eedba7c2 busybox "/bin/sh -c 'while..." 2 hours ago Up 2 hours box2
019a0daed76f busybox "/bin/sh -c 'while..." 2 hours ago Up 2 hours box1
$ docker container inspect box3
[
{
"Id": "2d7ce63d7e3f956564e684e343b7da2ae747e5679292c109096f619e73bce719",
"Created": "2022-03-07T07:44:18.269363219Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true; do sleep 3600; done"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 6911,
"ExitCode": 0,
"Error": "",
"StartedAt": "2022-03-07T07:44:18.567776199Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:829374d342ae65a12f3a95911bc04a001894349f70783fda841b1a784008727d",
"ResolvConfPath": "/var/lib/docker/containers/2d7ce63d7e3f956564e684e343b7da2ae747e5679292c109096f619e73bce719/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/2d7ce63d7e3f956564e684e343b7da2ae747e5679292c109096f619e73bce719/hostname",
"HostsPath": "/var/lib/docker/containers/2d7ce63d7e3f956564e684e343b7da2ae747e5679292c109096f619e73bce719/hosts",
"LogPath": "",
"Name": "/box3",
"RestartCount": 0,
"Driver": "overlay2",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "journald",
"Config": {}
},
"NetworkMode": "mybridge",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": true,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "docker-runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DiskQuota": 0,
"KernelMemory": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": -1,
"OomKillDisable": false,
"PidsLimit": 0,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0
},
"GraphDriver": {
"Name": "overlay2",
"Data": {
"LowerDir": "/var/lib/docker/overlay2/fba5480131b3e84ac8abfa7905de44c4049dbe627139833b40ce38c39dcf8eb6-init/diff:/var/lib/docker/overlay2/98edee56f7a26a7781c93c13d5bbef9bbb9e8f32ad3c37201bc45c00bfb5a0a6/diff",
"MergedDir": "/var/lib/docker/overlay2/fba5480131b3e84ac8abfa7905de44c4049dbe627139833b40ce38c39dcf8eb6/merged",
"UpperDir": "/var/lib/docker/overlay2/fba5480131b3e84ac8abfa7905de44c4049dbe627139833b40ce38c39dcf8eb6/diff",
"WorkDir": "/var/lib/docker/overlay2/fba5480131b3e84ac8abfa7905de44c4049dbe627139833b40ce38c39dcf8eb6/work"
}
},
"Mounts": [],
"Config": {
"Hostname": "2d7ce63d7e3f",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"while true; do sleep 3600; done"
],
"Image": "busybox",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "f967524cdbe0c5a0d70da4a45cde98bc250c28467678d7fe6de8fb38485d9788",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/f967524cdbe0",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"mybridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"2d7ce63d7e3f"
],
"NetworkID": "43435188760ce70d58099ab5fe27b85fc07e2f5e14d6748e6532daa5749d4963",
"EndpointID": "0e2fa562b7f2050eb84114356ac9882dbac340a675a32036b14040c0db35b035",
"Gateway": "172.18.0.1",
"IPAddress": "172.18.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:02"
}
}
}
}
]
$ docker network inspect mybridge
[
{
"Name": "mybridge",
"Id": "43435188760ce70d58099ab5fe27b85fc07e2f5e14d6748e6532daa5749d4963",
"Created": "2022-03-07T15:41:06.440007809+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Containers": {
"2d7ce63d7e3f956564e684e343b7da2ae747e5679292c109096f619e73bce719": {
"Name": "box3",
"EndpointID": "0e2fa562b7f2050eb84114356ac9882dbac340a675a32036b14040c0db35b035",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
通过上面操作就是已经建立好了自己的容器且指向自己创建的桥接。
记录上面没有使用过的命令
//容器连接第二个bridge
$ docker network connect bridge box3
//进入到容器 sh 里面
$ docker container exec -it box3 sh
//断开容器 bridge 连接
$ docker network disconnect bridge box3
//创建网络参数
$ 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[])
-d, --driver string Driver to manage the Network (default "bridge")
--gateway stringSlice IPv4 or IPv6 Gateway for the master subnet
--help Print usage
--internal Restrict external access to the network
--ip-range stringSlice 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 (default [])
-o, --opt map Set driver specific options (default map[])
--subnet stringSlice Subnet in CIDR format that represents a network segment
//网络配置
$ docker container inspect --format '{{.NetworkSettings.IPAddress}}’ box2
172.17.0.3
引用文档