我们在上一篇中介绍了Docker中三种网络,none、host和bridge,除了这三种网络,Docker还允许我们创建自定义网络,当我们要创建自定义网络时,Docker提供了三种网络驱动供我们选择:bridge、macvlan和overlay,其中macvlan和ovelay都是用于创建跨主机网络,我们后面在研究,本篇我们主要使用bridge驱动。

一、创建自定义网络

我们通过docker network create命令创建test_net1网络:

  1. $ sudo docker network create --driver bridge test_net1
  2. c7c20444a940135c92958f4434ca2b7428ba17f70a2a8a954a6cf160a011b513
  3. $ sudo docker network ls
  4. NETWORK ID NAME DRIVER SCOPE
  5. 6f0087fd32cd bridge bridge local
  6. 464b3d11003c host host local
  7. faa8eb8310b4 none null local
  8. c7c20444a940 test_net1 bridge local
  9. $ brctl show
  10. bridge name bridge id STP enabled interfaces
  11. br-c7c20444a940 8000.024200e01cd2 no
  12. docker0 8000.0242cef1fc32 no

我们可以看到,此时系统中新增了一个网桥br-c7c20444a940,其中c7c20444a940就是我们刚刚创建的桥接网络的ID。我们可以看下test_net1的详细配置信息:

  1. $ sudo docker network inspect test_net1
  2. [
  3. {
  4. "Name": "test_net1",
  5. "Id": "c7c20444a940135c92958f4434ca2b7428ba17f70a2a8a954a6cf160a011b513",
  6. "Created": "2021-10-23T07:07:47.870947684Z",
  7. "Scope": "local",
  8. "Driver": "bridge",
  9. "EnableIPv6": false,
  10. "IPAM": {
  11. "Driver": "default",
  12. "Options": {},
  13. "Config": [
  14. {
  15. "Subnet": "172.19.0.0/16",
  16. "Gateway": "172.19.0.1"
  17. }
  18. ]
  19. },
  20. "Internal": false,
  21. "Attachable": false,
  22. "Ingress": false,
  23. "ConfigFrom": {
  24. "Network": ""
  25. },
  26. "ConfigOnly": false,
  27. "Containers": {},
  28. "Options": {},
  29. "Labels": {}
  30. }
  31. ]

如果我们在创建网络时,不指定--subnet和--gateway参数时,Docker会自动为其分配网段和网关,我们接着再创建一个网络并指定其网段和网关:

  1. $ sudo docker network create --driver bridge --subnet 173.20.0.0/16 --gateway 173.20.0.1 test_net2
  2. 47542e86dc4474779a3b105f1d22f005fc39a529b1f19aa7af2c68297c1c0a41
  3. $ sudo docker network inspect test_net2
  4. [
  5. {
  6. "Name": "test_net2",
  7. "Id": "47542e86dc4474779a3b105f1d22f005fc39a529b1f19aa7af2c68297c1c0a41",
  8. "Created": "2021-10-23T07:21:47.684305897Z",
  9. "Scope": "local",
  10. "Driver": "bridge",
  11. "EnableIPv6": false,
  12. "IPAM": {
  13. "Driver": "default",
  14. "Options": {},
  15. "Config": [
  16. {
  17. "Subnet": "173.20.0.0/16",
  18. "Gateway": "173.20.0.1"
  19. }
  20. ]
  21. },
  22. "Internal": false,
  23. "Attachable": false,
  24. "Ingress": false,
  25. "ConfigFrom": {
  26. "Network": ""
  27. },
  28. "ConfigOnly": false,
  29. "Containers": {},
  30. "Options": {},
  31. "Labels": {}
  32. }
  33. ]
  34. $ brctl show
  35. bridge name bridge id STP enabled interfaces
  36. br-47542e86dc44 8000.024273a34080 no
  37. br-c7c20444a940 8000.024200e01cd2 no
  38. docker0 8000.0242cef1fc32 no
  39. $ ifconfig br-47542e86dc44
  40. br-47542e86dc44: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
  41. inet 173.20.0.1 netmask 255.255.0.0 broadcast 173.20.255.255
  42. ether 02:42:73:a3:40:80 txqueuelen 0 (Ethernet)
  43. RX packets 0 bytes 0 (0.0 B)
  44. RX errors 0 dropped 0 overruns 0 frame 0
  45. TX packets 0 bytes 0 (0.0 B)
  46. TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

二、自定义网络的使用

容易要使用自定义的网络,同样需在启动时通过--network参数指定即可:

  1. $ sudo docker run -it --network=test_net2 busybox
  2. / # ip a
  3. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
  4. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  5. inet 127.0.0.1/8 scope host lo
  6. valid_lft forever preferred_lft forever
  7. 50: eth0@if51: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
  8. link/ether 02:42:ad:14:00:02 brd ff:ff:ff:ff:ff:ff
  9. inet 173.20.0.2/16 brd 173.20.255.255 scope global eth0
  10. valid_lft forever preferred_lft forever

我们可以看到Docker会根据容易所使用的网络的网段自动为容器分配IP,当然,容器IP也是可以指定的,即通过--ip参数,但是要注意两点:

  • 只有使用--subnet参数指定网段创建的网络才能指定IP

  • 所指定的IP一定要在所使用的网络的网段中

下图所示的错误就是容器使用的网络并没有通过--subnet指定网段:

  1. $ sudo docker run -it --network=test_net1 --ip 172.19.0.5 busybox
  2. docker: Error response from daemon: user specified IP address is supported only when connecting to networks with user configured subnets.

下图所示的错误是容器所指定IP不在其网络的指定网段内:

  1. $ sudo docker run -it --network=test_net2 --ip 172.20.0.2 busybox
  2. ERRO[0000] error waiting for container: context canceled
  3. docker: Error response from daemon: Invalid address 172.20.0.2: It does not belong to any of this network's subnets.

以下是正确的指定容器IP的例子:

  1. $ sudo docker run -it --network=test_net2 --ip 173.20.0.4 busybox
  2. / # ip a
  3. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
  4. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  5. inet 127.0.0.1/8 scope host lo
  6. valid_lft forever preferred_lft forever
  7. 54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
  8. link/ether 02:42:ad:14:00:04 brd ff:ff:ff:ff:ff:ff
  9. inet 173.20.0.4/16 brd 173.20.255.255 scope global eth0
  10. valid_lft forever preferred_lft forever

三、Docker网络隔离及原理

到目前位置我们创建使用test_net2网络创建了两个容器,那么这两个容器网络应该是互通的,我们来验证一下:

  1. $ sudo docker run -it --network=test_net2 --ip 173.20.0.4 busybox
  2. / # ip a
  3. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
  4. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  5. inet 127.0.0.1/8 scope host lo
  6. valid_lft forever preferred_lft forever
  7. 54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
  8. link/ether 02:42:ad:14:00:04 brd ff:ff:ff:ff:ff:ff
  9. inet 173.20.0.4/16 brd 173.20.255.255 scope global eth0
  10. valid_lft forever preferred_lft forever
  11. / # ping 173.20.0.2
  12. PING 173.20.0.2 (173.20.0.2): 56 data bytes
  13. 64 bytes from 173.20.0.2: seq=0 ttl=64 time=1.319 ms
  14. 64 bytes from 173.20.0.2: seq=1 ttl=64 time=0.106 ms
  15. 64 bytes from 173.20.0.2: seq=2 ttl=64 time=0.107 ms
  16. ^C
  17. --- 173.20.0.2 ping statistics ---
  18. 3 packets transmitted, 3 packets received, 0% packet loss
  19. round-trip min/avg/max = 0.106/0.510/1.319 ms

所以,我们得出结论,使用同一网络创建的容器是能够互通的。相反,如果是不同网络创建的容器应该是无法通信,我们使用test_net1创建一个容器验证一下:

  1. $ sudo docker run -it --network=test_net1 busybox
  2. / # ip a
  3. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
  4. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  5. inet 127.0.0.1/8 scope host lo
  6. valid_lft forever preferred_lft forever
  7. 58: eth0@if59: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
  8. link/ether 02:42:ac:13:00:02 brd ff:ff:ff:ff:ff:ff
  9. inet 172.19.0.2/16 brd 172.19.255.255 scope global eth0
  10. valid_lft forever preferred_lft forever
  11. / # ping 173.20.0.2
  12. PING 173.20.0.2 (173.20.0.2): 56 data bytes
  13. ^C
  14. --- 173.20.0.2 ping statistics ---
  15. 146 packets transmitted, 0 packets received, 100% packet loss

从结果来看确实无法通信,但是如果我们开启系统的ip forwarding,把当前主机当作路由,那么不同网络应该是能通信的,我们首先来看下系统的配置:

  1. $ ip r
  2. default via 172.16.194.2 dev ens33 proto dhcp src 172.16.194.135 metric 100
  3. 172.16.194.0/24 dev ens33 proto kernel scope link src 172.16.194.135
  4. 172.16.194.2 dev ens33 proto dhcp scope link src 172.16.194.135 metric 100
  5. 172.19.0.0/16 dev br-c7c20444a940 proto kernel scope link src 172.19.0.1
  6. 173.20.0.0/16 dev br-47542e86dc44 proto kernel scope link src 173.20.0.1
  7. $ sysctl net.ipv4.ip_forward
  8. net.ipv4.ip_forward = 1

看来我们的主机已经开启了ip forwarding并且172.19.0.0/16和173.20.0.0/16的路由也定义好了,那为什么网络还是不通呢?我们看看iptables:

  1. $ sudo iptables-save
  2. # Generated by iptables-save v1.6.1 on Sat Oct 23 08:19:03 2021
  3. *filter
  4. ...
  5. # 如果数据包是从br-47542e86dc44流入但不从br-47542e86dc44流出则跳转到DOCKER-ISOLATION-STAGE-2处理
  6. -A DOCKER-ISOLATION-STAGE-1 -i br-47542e86dc44 ! -o br-47542e86dc44 -j DOCKER-ISOLATION-STAGE-2
  7. # 如果数据包是从br-c7c20444a940流入但不从br-c7c20444a940流出则跳转到DOCKER-ISOLATION-STAGE-2处理
  8. -A DOCKER-ISOLATION-STAGE-1 -i br-c7c20444a940 ! -o br-c7c20444a940 -j DOCKER-ISOLATION-STAGE-2
  9. # 如果数据包是从docker0流入但不从docker0流出则跳转到DOCKER-ISOLATION-STAGE-2处理
  10. -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
  11. # 如果非以上三种情况则返回上一层处理
  12. -A DOCKER-ISOLATION-STAGE-1 -j RETURN
  13.  
  14. # 如果数据包流出到br-47542e86dc44则drop
  15. -A DOCKER-ISOLATION-STAGE-2 -o br-47542e86dc44 -j DROP
  16. # 如果数据包流出到br-c7c20444a940则drop
  17. -A DOCKER-ISOLATION-STAGE-2 -o br-c7c20444a940 -j DROP
  18. # 如果数据包流出到docker0则drop
  19. -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
  20. # 如果非以上三种情况则返回上一层处理
  21. -A DOCKER-ISOLATION-STAGE-2 -j RETURN
  22. -A DOCKER-USER -j RETURN
  23. ...

看来原因就在这里了,我们发现Docker创建了一系列网络隔离策略,当数据包流入的网络与流出的网络相同则不作处理,不同则弃数据包,我们通过一个流程图来说明DOCKER-ISOLATION-STAGE-1和DOCKER-ISOLATION-STAGE-2规则的逻辑:

如果我一定要两个使用不同网络的容器通信,该怎么处理呢?也很简单,我们只需要给容器添加对应的网络的的网卡即可,我们通过docker network connect命令可以实现:

  1. # 0da5e260a4b6容器使用的是test_net1,另外两个使用的是test_net2,
  2. # 如果大家在这一步不确定的话可以使用docker inspect 容器id进行确定
  3. $ sudo docker ps
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  5. 0da5e260a4b6 busybox "sh" 2 hours ago Up 2 hours objective_blackburn
  6. c6871db3efbb busybox "sh" 2 hours ago Up 2 hours goofy_swartz
  7. c683eacc4eae busybox "sh" 2 hours ago Up 2 hours modest_swartz
  8. yangye@ayato:~$ sudo docker network connect test_net2 0da5e260a4b6

我们在容器0da5e260a4b6中先ping下173.20.0.2发现网络是通的,我们再看看其网络配置,发现容器中添加了my_net2网络的网卡eth1:

  1. / # ping 173.20.0.2
  2. PING 173.20.0.2 (173.20.0.2): 56 data bytes
  3. 64 bytes from 173.20.0.2: seq=0 ttl=64 time=0.291 ms
  4. 64 bytes from 173.20.0.2: seq=1 ttl=64 time=0.100 ms
  5. 64 bytes from 173.20.0.2: seq=2 ttl=64 time=0.097 ms
  6. 64 bytes from 173.20.0.2: seq=3 ttl=64 time=0.100 ms
  7. 64 bytes from 173.20.0.2: seq=4 ttl=64 time=0.147 ms
  8. ^C
  9. --- 173.20.0.2 ping statistics ---
  10. 5 packets transmitted, 5 packets received, 0% packet loss
  11. round-trip min/avg/max = 0.097/0.147/0.291 ms
  12. / # ifconfig
  13. eth0 Link encap:Ethernet HWaddr 02:42:AC:13:00:02
  14. inet addr:172.19.0.2 Bcast:172.19.255.255 Mask:255.255.0.0
  15. UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
  16. RX packets:25 errors:0 dropped:0 overruns:0 frame:0
  17. TX packets:150 errors:0 dropped:0 overruns:0 carrier:0
  18. collisions:0 txqueuelen:0
  19. RX bytes:1830 (1.7 KiB) TX bytes:14476 (14.1 KiB)
  20.  
  21. eth1 Link encap:Ethernet HWaddr 02:42:AD:14:00:03
  22. inet addr:173.20.0.3 Bcast:173.20.255.255 Mask:255.255.0.0
  23. UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
  24. RX packets:18 errors:0 dropped:0 overruns:0 frame:0
  25. TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
  26. collisions:0 txqueuelen:0
  27. RX bytes:1440 (1.4 KiB) TX bytes:574 (574.0 B)
  28.  
  29. lo Link encap:Local Loopback
  30. inet addr:127.0.0.1 Mask:255.0.0.0
  31. UP LOOPBACK RUNNING MTU:65536 Metric:1
  32. RX packets:0 errors:0 dropped:0 overruns:0 frame:0
  33. TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
  34. collisions:0 txqueuelen:1000
  35. RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

好的,以上就是本篇的内容,下一篇我们再看看容器间通信方式。

Docker 与 K8S学习笔记(八)—— 自定义容器网络的更多相关文章

  1. Docker 与 K8S学习笔记(九)—— 容器间通信

    容器之间可通过IP.Docker DNS Server或joined三种方式进行通信,今天我们来详细学习一下. 一.IP通信 IP通信很简单,前一篇中已经有所涉及了,只要容器使用相同网络,那么就可以使 ...

  2. Docker 与 K8S学习笔记(七)—— 容器的网络

    本节我们来看看Docker网络,我们这里主要讨论单机docker上的网络.当docker安装后,会自动在服务器中创建三种网络:none.host和bridge,接下来我们分别了解下这三种网络: $ s ...

  3. Docker 与 K8S学习笔记(六)—— 容器的资源限制

    我们在启动Docker容器时,默认情况下容器所使用的资源是没有限制的,这样就会存在部分特别耗资源的容器会占用大量系统资源,从而导致其他容器甚至整个服务器性能降低,为此,Docker提供了一系列参数方便 ...

  4. Docker 与 K8S学习笔记(十 二)容器间数据共享

    数据共享是volume的关键特性,今天我们来看一下通过volume实现容器与host.容器与容器之间共享数据. 一.容器与host共享数据 在上一篇中介绍到的bind mount和docker man ...

  5. Docker 与 K8S学习笔记(五)—— 容器的操作(下篇)

    上一篇我们学习了容器的启动和常用的进入容器的方式,今天我们来看看如何控制容器起停以及容器删除操作. 一.stop.kill.start和restart stop.kill命令都可以停止运行的容器,二者 ...

  6. Docker 与 K8S学习笔记(二)—— 容器核心知识梳理

    本篇主要对容器相关核心知识进行梳理,通过本篇的学习,我们可以对容器相关的概念有一个全面的了解,这样有利于后面的学习. 一.什么是容器? 容器是一种轻量级.可移植.自包含的软件打包技术,使应用程序可以在 ...

  7. Docker 与 K8S学习笔记(十)—— 容器的端口映射

    我们一般将应用部署在容器里面,而一个服务器上会有许许多多的容器,那么外界该如何访问我们的应用呢?答案是:端口映射. Docker可以将容器对外提供服务的端口映射到host的某个端口上,外网通过此端口访 ...

  8. Docker 与 K8S学习笔记(五)—— 容器的操作(上篇)

    上一篇我们介绍了Dockerfile的基本编写方法,这一节我们来看看Docker容器的常用操作. 一.容器的运行方式 容器有两种运行方式,即daemon形式运行与非daemon形式运行,通俗地讲就是长 ...

  9. Docker 与 K8S学习笔记(十八)—— Pod的使用

    Pod 是一组紧密关联的容器集合,它们共享IPC.Network和UTS namespace,是 Kubernetes 调度的基本单元.Pod 的设计理念是支持多个容器在一个 Pod 中共享网络和文件 ...

随机推荐

  1. SCRDet——对小物体和旋转物体更具鲁棒性的模型

    引言 明确提出了三个航拍图像领域内面对的挑战: 小物体:航拍图像经常包含很多复杂场景下的小物体. 密集:如交通工具和轮船类,在航拍图像中会很密集.这个DOTA数据集的发明者也提到在交通工具和轮船类的检 ...

  2. C#表格GridView显示2位百分比

    <asp:BoundField HeaderText="占比" DataField="number" DataFormatString="{0: ...

  3. [源码解析] PyTorch分布式优化器(1)----基石篇

    [源码解析] PyTorch分布式优化器(1)----基石篇 目录 [源码解析] PyTorch分布式优化器(1)----基石篇 0x00 摘要 0x01 从问题出发 1.1 示例 1.2 问题点 0 ...

  4. 带你全面了解 OAuth2.0

    最开始接触 OAuth2.0 的时候,经常将它和 SSO单点登录搞混.后来因为工作需要,在项目中实现了一套SSO,通过对SSO的逐渐了解,也把它和OAuth2.0区分开了.所以当时自己也整理了一篇文章 ...

  5. MYSQL获取更新行的主键ID 【转】

    在某些情况下我们需要向数据表中更新一条记录的状态,然后再把它取出来,但这时如果你在更新前并没有一个确认惟一记录的主键就没有办法知道哪条记录被更新了. 举例说明下: 有一个发放新手卡的程序,设计数据库时 ...

  6. Copy elision in C++

    Copy elision (or Copy omission) is a compiler optimization technique that avoids unnecessary copying ...

  7. 【编程思想】【设计模式】【行为模式Behavioral】中介者模式Mediator

    Python版 https://github.com/faif/python-patterns/blob/master/behavioral/mediator.py #!/usr/bin/env py ...

  8. 【Linux】【Services】【SaaS】Docker+kubernetes(6. 安装和配置ceph)

    1. 简介 1.1. 这个在生产中没用上,生产上用的是nfs,不过为了显示咱会,也要写出来 1.2. 官方网站:http://ceph.com/ 1.3. 中文网站:http://docs.ceph. ...

  9. ExecutorService 线程池详解

    1.什么是ExecutorService,为什么要使用线程池? 许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务,每当一个请求到达就创建一个新线程,然后在新线程中为请求服务,但是频繁创建新 ...

  10. Gitlab-CICD实践篇

    一.背景 随着公司项目使用gitlab越来越多,业务发布的次数越来越频繁,对于发布效率提出了更高的要求.从2012开始,Gitlab官方开始集成了Continuous Integration (CI) ...