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

image-20230415200020631

在容器中可以看到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就是新创建容器的虚拟网卡。

下面看一下容器的网络配置

image-20230415205601502

容器有一个网卡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网络的配置信息

image-20230415205802288

原来bridge网络配置的subnet就是172.17.0.0/16,并且网关是172.17.0.1。这个网关在哪儿呢?大概你已经猜出来了,就是docker0

image-20230415205842356

image-20230415210709360

容器创建时,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的配置信息

image-20230415212202757

这里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 ~]#

image-20230415212429613

这里我们创建了新的bridge网络my_net2,网段为172.22.16.0/24,网关为172.22.16.1。与前面一样,网关在my_net2对应的网桥br-aa56fbecfa0c上

image-20230415212605915

容器要使用新的网络,需要在启动时通过 --network指定sh

docker run -it --network=my_net2 busybox

image-20230415212702827

容器分配到的IP为172.22.16.2。

到目前为止,容器的IP都是docker自动从subnet中分配,我们也可以指定一个静态ip。

通过--ip指定

docker run -it --network=my_net2 --ip 172.22.16.8 busybox

image-20230415213018054

注:只有使用 --subnet创建的网络才能指定静态IP。

创建了一个 docker host的网络拓扑结构

image-20230415223242653

两个busybox容器都挂在my_net2上,应该能够互通,我们验证一下

image-20230415215613917

可见同一网络中的容器、网关之间都是可以通信的。

my_net2与默认my_net网络能通信吗?从拓扑图可知,两个网络属于不同的网桥,不能通信。

image-20230415221136410

如果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容器中查看一下网络配置

image-20230415222548644

容器中增加了一个网卡eth1,分配了my_net2的IP 172.22.16.3。现在busybox应该能够访问httpd了,验证一下。

image-20230415222729043

当前网络结构如图

image-20230415223356399

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了。

image-20230415224003805

使用docker DNS有个限制:只能在user-defined网络中使用。也就是说,默认的bridge网络是无法使用DNS的。

创建bbox3和bbox4,均连接到bridge网络。

docker run -it --name=bbox3 busybox
docker run -it --name=bbox4 busybox

bbox4无法ping到bbox3

image-20230415224206586

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

image-20230415230038194

请注意busybox容器中的网络配置信息,下面我们查看一下web1的网络

docker exec -it web1 sh

image-20230415230210651

busybox和web1的网卡mac地址与IP完全一样,它们共享了相同的网络栈。

joined容器非常适合以下场景:

(1)不同容器中的程序希望通过loopback高效快速地通信,比如Web Server与App Server。

(2)希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。

6. 将容器与外部世界连接

前面我们已经解决了容器间通信的问题,接下来讨论容器如何与外部世界通信。这里涉及两个方向:

(1)容器访问外部世界。

(2)外部世界访问容器。

6.1 容器访问外部世界

在我们当前的实验环境下,docker host是可以访问外网的。

image-20230415230530798

容器也能访问外网

image-20230415230445045

可见,容器默认就能访问外网。

请注意:这里外网指的是容器网络以外的网络环境,并非特指Internet。

现象很简单,但更重要的:我们应该理解现象下的本质。

在上面的例子中,busybox位于docker0这个私有bridge网络中(172.17.0.0/16),当busybox从容器向外ping时,数据包是怎样到达bing.com的呢?

这里的关键就是NAT。我们查看一下docker host上的iptables规则。

iptables -t nat -S

image-20230415230751403

在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

image-20230415230955549

默认路由通过eth0发出去,所以我们要同时监控eth0和docker0上的icmp(ping)数据包。

当busybox ping bing.com时,在host上的tcpdump输出如下:

tcpdump -i docker0 -n icmp

image-20230415232741185docker0收到busybox的ping包,源地址为容器IP 172.17.0.2,这没问题,交给MASQUERADE处理。

这时,在eth0上我们看到了变化

image-20230415232839317

ping包的源地址变成了 eth0上的IP 172.28.74.213

这就是iptable NAT规则处理的结果,从而保证数据包能够到达外网。下面用一张图来说明这个过程

image-20230415233104954

(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

image-20230415234404828

容器启动后,可通过docker ps或者docker port查看到host映射的端口。在上面的例子中,httpd容器的80端口被映射到host 32773上,这样就可以通过:<32768>访问容器的Web服务了

[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

image-20230415234836875

以0.0.0.0:32773->80/tcp为例分析整个过程

image-20230415235403802

(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
posted @   0x1e61  阅读(79)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示