Docker网络
Docker网络
Docker网络从覆盖范围可分为单个host上的容器网络和跨多个host的网络
Docker安装时会自动在host上创建三个网络,我们可用docker network ls命令查看
[root@Wesuiliye ~]# docker network ls NETWORK ID NAME DRIVER SCOPE fc691ffef80b bridge bridge local 0b59752a9a35 host host local 9985f4a7d23e none null local
1. none网络
顾名思义,none网络就是什么都没有的网络。挂在这个网络下的容器除了lo,没有其他任何网卡。容器创建时,可以通过 --network=none指定使用none网络。
[root@Wesuiliye ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@Wesuiliye ~]# docker run -it --network=none busybox / # ifconfig 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) / #
我们不禁会问,这样一个封闭的网络有什么用呢?
其实还真有应用场景。封闭意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用none网络。
比如某个容器的唯一用途是生成随机密码,就可以放到none网络中避免密码被窃取。
当然大部分容器是需要网络的,我们接着看host网络。
2. host网络
连接到host网络的容器共享Docker host的网络栈,容器的网络配置与host完全一样。可以通过 --network=host指定使用host网络
docker run -it --network=host busybox
在容器中可以看到host的所有网卡,并且连hostname也是host的。host网络的使用场景又是什么呢?
直接使用Docker host的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择host网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host上已经使用的端口就不能再用了。
Docker host的另一个用途是让容器可以直接配置host网路,比如某些跨host的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理iptables。
3. bridge网络
Docker安装时会创建一个命名为docker0的Linux bridge。如果不指定--network,创建的容器默认都会挂到docker0上
[root@Wesuiliye ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.02421c0126fc no virbr0 8000.525400080532 yes virbr0-nic
当前docker0上没有任何其他网络设备,我们创建一个容器看看有什么变化
[root@Wesuiliye ~]# docker run -d centos fdd0e61aab8e2be6c575fb69a267b8c1228cf1c240833a48efeebcfde1a05a47 [root@Wesuiliye ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.02421c0126fc no veth5155b33 virbr0 8000.525400080532 yes virbr0-nic
一个新的网络接口veth5155b33被挂到了docker0上,veth5155b33就是新创建容器的虚拟网卡。
下面看一下容器的网络配置
容器有一个网卡eth0@if15。大家可能会问了,为什么不是veth5155b33呢?
实际上eth0@if15和veth5155b33是一对veth pair。veth pair是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if15)在容器中,另一头(veth5155b33)挂在网桥docker0上,其效果就是将eth0@if15也挂在了docker0上。
我们还看到eth0@if15已经配置了IP 172.17.0.2,为什么是这个网段呢?让我们通过docker network inspect bridge看一下bridge网络的配置信息
原来bridge网络配置的subnet就是172.17.0.0/16,并且网关是172.17.0.1。这个网关在哪儿呢?大概你已经猜出来了,就是docker0
容器创建时,docker会自动从172.17.0.0/16中分配一个IP,这里16位的掩码保证有足够多的IP可以供容器使用。
4. user-defined网络
除了none、host、bridge这三个自动创建的网络,用户也可以根据业务需要创建user-defined网络。
Docker提供三种user-defined网络驱动:bridge、overlay和macvlan。overlay和macvlan用于创建跨主机的网络
我们可通过bridge驱动创建类似前面默认的bridge网络。
[root@Wesuiliye ~]# docker network create --driver bridge my_net 4e98aa1cf90691d7d66447e63acfcddd9874a707d0f6494ee3fb73d0a9a6eb84 [root@Wesuiliye ~]#
查看一下当前host的网络结构变化
[root@Wesuiliye ~]# brctl show bridge name bridge id STP enabled interfaces br-4e98aa1cf906 8000.0242e3231371 no docker0 8000.02421c0126fc no virbr0 8000.525400080532 yes virbr0-nic
新增了一个网桥br-4e98aa1cf906 ,这里4e98aa1cf906正好是新建bridge网络my_net的短id。
执行docker network inspect
查看一下my_net的配置信息
这里172.18.0.0/16是Docker自动分配的IP网段。
可以自己指定IP网段,只需在创建网段时指定 --subnet和 --gateway参数
[root@Wesuiliye ~]# docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2 aa56fbecfa0c69d0ca675dbcfec65a06243619e2a26960a9ad6cdd89222afc4b [root@Wesuiliye ~]#
这里我们创建了新的bridge网络my_net2,网段为172.22.16.0/24,网关为172.22.16.1。与前面一样,网关在my_net2对应的网桥br-aa56fbecfa0c上
容器要使用新的网络,需要在启动时通过 --network指定sh
docker run -it --network=my_net2 busybox
容器分配到的IP为172.22.16.2。
到目前为止,容器的IP都是docker自动从subnet中分配,我们也可以指定一个静态ip。
通过--ip指定
docker run -it --network=my_net2 --ip 172.22.16.8 busybox
注:只有使用 --subnet创建的网络才能指定静态IP。
创建了一个 docker host的网络拓扑结构
两个busybox容器都挂在my_net2上,应该能够互通,我们验证一下
可见同一网络中的容器、网关之间都是可以通信的。
my_net2与默认my_net网络能通信吗?从拓扑图可知,两个网络属于不同的网桥,不能通信。
如果host上对每个网络都有一条路由,同时操作系统上打开了ip forwarding(数据包转发), host就成了一个路由器,挂接在不同网桥上的网络就能够相互通信。
ip r查看host上的路由表:
[root@Wesuiliye ~]# ip r ... 172.18.0.0/16 dev br-4e98aa1cf906 proto kernel scope link src 172.18.0.1 172.22.16.0/24 dev br-aa56fbecfa0c proto kernel scope link src 172.22.16.1 ...
172.18.0.0/16和172.22.16.0/24两个网络的路由都定义好了。
再看看ip forwarding:
[root@Wesuiliye ~]# cat /proc/sys/net/ipv4/ip_forward 1
如果返回值为 1,说明该功能已经启用,否则就是没有启用。
如果没有打开,可以通过下面命令临时生效下,重启后,配置就恢复默认值了。
sysctl -w net.ipv4.ip_forward=1
ip forwarding也已经启用了。条件都满足,还是不能通信
再看看iptables
[root@Wesuiliye ~]# iptables-save -A DOCKER-ISOLATION-STAGE-1 -i br-aa56fbecfa0c ! -o br-aa56fbecfa0c -j DOCKER-ISOLATION-STAGE-2 -A DOCKER-ISOLATION-STAGE-1 -i br-4e98aa1cf906 ! -o br-4e98aa1cf906 -j DOCKER-ISOLATION-STAGE-2
原因就在这里了:iptables DROP掉了网桥br-aa56fbecfa0c与br-4e98aa1cf906之间双向的流量。
从规则的命名DOCKER-ISOLATION可知docker在设计上就是要隔离不同的netwrok。
那么接下来的问题是:怎样才能通信呢?
答案是:为ip 172.18.0.2 的busybox容器添加一块net_my2的网卡。这个可以通过docker network connect命令实现
[root@Wesuiliye ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 74b2746803a0 busybox "sh" 46 minutes ago Up 46 minutes modest_cartwright 0b87d394ec2e busybox "sh" 47 minutes ago Up 47 minutes relaxed_rosalind c9af9b9c79b0 busybox "sh" 47 minutes ago Up 47 minutes epic_mclaren [root@Wesuiliye ~]# docker network connect my_net2 c9af9b9c79b0
我们在ip 172.18.0.2 的busybox容器中查看一下网络配置
容器中增加了一个网卡eth1,分配了my_net2的IP 172.22.16.3。现在busybox应该能够访问httpd了,验证一下。
当前网络结构如图
5. 容器间通信
容器之间可通过IP, Docker DNS Server或joined容器三种方式通信。
5.1 IP通信
从前面的例子可以得出这样一个结论:
两个容器要能通信,必须要有属于同一个网络的网卡。
满足这个条件后,容器就可以通过IP交互了。具体做法是在容器创建时通过 --network指定相应的网络,或者通过docker network connect将现有容器加入到指定网络。
5.2 Docker DNS Server
通过IP访问容器虽然满足了通信的需求,但还是不够灵活。因为在部署应用之前可能无法确定IP,部署之后再指定要访问的IP会比较麻烦。对于这个问题,可以通过docker自带的DNS服务解决。
从Docker 1.10版本开始,docker daemon实现了一个内嵌的DNS server,使容器可以直接通过“容器名”通信。方法很简单,只要在启动时用 --name为容器命名就可以了。
下面启动两个容器bbox1和bbox2:
docker run -it --network=my_net2 --name bbox1 busybox docker run -it --network=my_net2 --name bbox2 busybox
然后,bbox2就可以直接ping到bbox1了。
使用docker DNS有个限制:只能在user-defined网络中使用。也就是说,默认的bridge网络是无法使用DNS的。
创建bbox3和bbox4,均连接到bridge网络。
docker run -it --name=bbox3 busybox docker run -it --name=bbox4 busybox
bbox4无法ping到bbox3
5.3 joined容器
oined容器是另一种实现容器间通信的方式。
joined容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined容器之间可以通过127.0.0.1直接通信。请看下面的例子:
先创建一个busybox容器,名字为web1。
docker run -d -it --name=web1 busybox
然后再创建一个busybox容器并通过 --network=container:web1指定joined容器为web1
docker run -d -it --name=web1 busybox
请注意busybox容器中的网络配置信息,下面我们查看一下web1的网络
docker exec -it web1 sh
busybox和web1的网卡mac地址与IP完全一样,它们共享了相同的网络栈。
joined容器非常适合以下场景:
(1)不同容器中的程序希望通过loopback高效快速地通信,比如Web Server与App Server。
(2)希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。
6. 将容器与外部世界连接
前面我们已经解决了容器间通信的问题,接下来讨论容器如何与外部世界通信。这里涉及两个方向:
(1)容器访问外部世界。
(2)外部世界访问容器。
6.1 容器访问外部世界
在我们当前的实验环境下,docker host是可以访问外网的。
容器也能访问外网
可见,容器默认就能访问外网。
请注意:这里外网指的是容器网络以外的网络环境,并非特指Internet。
现象很简单,但更重要的:我们应该理解现象下的本质。
在上面的例子中,busybox位于docker0这个私有bridge网络中(172.17.0.0/16),当busybox从容器向外ping时,数据包是怎样到达bing.com的呢?
这里的关键就是NAT。我们查看一下docker host上的iptables规则。
iptables -t nat -S
在NAT表中,有这么一条规则:
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
其含义是:如果网桥docker0收到来自172.17.0.0/16网段的外出包,把它交给MASQUERADE处理。而MASQUERADE的处理方式是将包的源地址替换成host的地址发送出去,即做了一次网络地址转换(NAT)。
下面我们通过tcpdump查看地址是如何转换的。先查看docker host的路由表
ip r
默认路由通过eth0发出去,所以我们要同时监控eth0和docker0上的icmp(ping)数据包。
当busybox ping bing.com时,在host上的tcpdump输出如下:
tcpdump -i docker0 -n icmp
docker0收到busybox的ping包,源地址为容器IP 172.17.0.2,这没问题,交给MASQUERADE处理。
这时,在eth0上我们看到了变化
ping包的源地址变成了 eth0上的IP 172.28.74.213
这就是iptable NAT规则处理的结果,从而保证数据包能够到达外网。下面用一张图来说明这个过程
(1)busybox发送ping包:172.17.0.2 > www.bing.com。
(2)docker0收到包,发现是发送到外网的,交给NAT处理。
(3)NAT将源地址换成eth0的IP:172.28.74.213 > www.bing.com。
(4)ping包从eth0发送出去,到达www.bing.com。通过NAT, docker实现了容器对外网的访问。
6.2 外部世界访问容器
docker可将容器对外提供服务的端口映射到host的某个端口,外网通过该端口访问容器。
容器启动时通过-p参数映射端口sh
docker run -d -p 80 httpd
容器启动后,可通过docker ps或者docker port查看到host映射的端口。在上面的例子中,httpd容器的80端口被映射到host 32773上,这样就可以通过
[root@Wesuiliye ~]# curl 172.28.74.213:32768 <html><body><h1>It works!</h1></body></html> [root@Wesuiliye ~]#
除了映射动态端口,也可在 -p中指定映射到host某个特定端口,例如可将80端口映射到host的8080端口
docker run -d -p 8080:80 httpd dcd7c800bc16607dfcd08a0d5ce2f4c0f55622745f845d393bf8fd96099c7c80 [root@Wesuiliye ~]# curl 172.28.74.213:8080 <html><body><h1>It works!</h1></body></html>
每一个映射的端口,host都会启动一个docker-proxy进程来处理访问容器的流量
ps -ef | grep docker-proxy
以0.0.0.0:32773->80/tcp为例分析整个过程
(1)docker-proxy监听host的32768端口。
(2)当curl访问172.28.74.213:32768时,docker-proxy转发给容器172.17.0.3:80。
(3)httpd容器响应请求并返回结果。
docker network 命令用法列表
1、ls: 查看网络列表 示例:
docker network ls
2、create: 创建一个网络
# 不指定网络驱动时默认创建的bridge网络: docker network create test-create # 创建网络时,使用参数`-d`指定驱动类型为overlay docker network create -d overlay my-multihost-network
3、rm: 删除一个网络. 示例
docker rm test-create ps: 如果网络中有容器连接需要加 -f 参数强制删除,建议不要这样执行,网络中若没有任何容器连接直接执行删除即可.
4、inspect: 查看一个网络的详情 示例:
docker network inspect test-create
5、prune: 删除所有未使用的网络. 示例:
docker network prune ps: -f 强制删除,不提供任何确认情况下删除.
6、connect: 将一个容器加入到一个网络中. 示例:
docker network connect 网络名称 容器ID
7、disconnect: 与 connect 刚好相反, 从网络中断开一个容器的链接. 示例:
docker network disconnect 网络名称 容器 ID ps: -f 参数强制删除
8、默认情况下,来自连接到默认网桥网络的容器的流量 不会转发到外界。要启用转发,您需要更改两个设置。这些不是Docker命令,它们会影响Docker主机的内核。
配置Linux内核以允许IP转发。
#如果下面命令的值为0,说明禁止进行IP转发;如果是1,则说明IP转发功能已经打开。 cat /proc/sys/net/ipv4/ip_forward #临时开启,(写入内存,在内存中开启) echo "1" > /proc/sys/net/ipv4/ip_forward 或 sysctl -w net.ipv4.ip_forward=1 #永久开启,(写入内核) echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p ----加载,使得配置文件立即生效,等同于sysctl -p /etc/sysctl.conf
将策略的iptables FORWARD策略从更改DROP为 ACCEPT
iptables -P FORWARD ACCEPT
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律