前言
Windows上的docker与Linux上的docker底层技术有些许区别,但实现的效果大体上还是差不多的,本文将以windows的视角,Linux的原理来讲解Docker网络。docker的网络共有四种模式,主要讲解bridge桥接模式,这也是使用最多的模式。
-
桥接模式(Bridge):桥接模式是 Docker 默认的网络模式。在桥接模式下,Docker 会为每个容器创建一个虚拟网络接口,并为容器分配一个独立的 IP 地址。容器可以通过桥接网络与主机以及其他容器进行通信。桥接模式适用于在单个主机上运行多个容器,并希望它们能够相互通信。
-
主机模式(Host):主机模式将容器与主机共享网络栈,即容器将直接使用主机的网络接口和 IP 地址。这意味着容器可以通过主机的 IP 地址直接访问外部网络,而不需要进行端口映射。主机模式适用于需要容器与主机之间网络性能最大化的场景,但也可能导致容器之间的网络冲突。
-
容器模式(Container):容器模式允许将一个容器的网络栈直接共享给另一个容器使用,而不是使用独立的网络栈。这些容器可以在同一个网络命名空间中运行,共享相同的 IP 地址和端口空间。容器模式适用于需要在容器之间共享网络栈的特定应用场景,例如微服务架构中的一组容器。
-
无网络模式(None):无网络模式下,容器没有默认的网络连接。这意味着容器将无法通过网络访问外部资源,也无法通过网络与其他容器或主机进行通信。无网络模式适用于那些不需要网络连接的容器,例如批处理作业或仅限于本地访问的容器。
一、Windows上的 " Docker0 "
讲docker网络必然是和容器之间的通信相关的,那我们随便创建的两个容器是否能够相互通信呢,也就是说相互ping通呢?
在以上示例中,启动了两个centos的容器,并尝试了相互ping对方的ip地址,情况是能够ping通的,为什么?
在拥有物理网卡的计算机设备中,网卡可以保存其他主机网卡的Mac地址和IP地址,通过有线或无线的连接即可与其通信。
而容器是一个虚拟的概念,在计算机中可并没有特意为容器准备一个真实存在的网卡,然后这块网卡和其他的网卡进行有线或无线的连接。这也并不现实,容器会随时增加或删减,不可能每增加一个容器就在计算机中专门连接一根物理意义上的线。不过不能连物理的线,却是可以连虚拟的线,那么有以下两种连接方式,哪种更适合呢?
第一种:在一个网络里每增加一个容器就在每个容器上增加一个虚拟网卡,然后相互连接。
第二种:在网络中设置一个路由,每增加一个容器,在该路由和增加的容器上添加一块虚拟网卡,然后容器之间的通信由该路由进行转发。
很显然,第二种方式的实现会更加简单和有效,docker的桥接网络便是采用的第二种这个方式。在安装了docker的Linux系统上使用 ip addr 命令可以发现存在一个名为 docker0 的网卡,这个docker0便是上图中路由的角色。docker0这个网卡在windows上使用ipconfig的命令是无法查看到的,并且也不会显示每个容器上的虚拟网卡,这是因为Windows使用了NAT技术实现桥接网络,隐藏了许多细节。
不过在windows系统上没法直接找到docker0,但也是能找到一些他 "存在" 的证据的。在已经启动的两个centos容器中查看其ip地址和网关,可以发现,他们的网关都为相同的172.17.0.1,且他们的ip前缀都为172.17。这两个centos容器并没有刻意设置让他们处于同一个网段,但是他们确实就在同一个网段下。
实际上,这个网关 172.17.0.1 便是Windows上的 "docker0"。docker进程启动时,就会在主机上创建一个默认网关为 172.17.0.1的"docker0"虚拟网桥,只不过在windows上使用NAT技术将其细节隐藏了而已,一切使用默认桥接网络的容器在启动时会默认接在这个"docker0"的虚拟网桥上。
二、"Docker0"如何实现容器间的通信?
在不使用自定义网络下,每启动一个容器,docker进程就会给该容器分配一个ip地址,并且这个ip地址和默认桥接模式的网卡"docker0"处于同一网段下,使用的技术为veth-pair(Virtual Ethernet Pair)技术。
在centos容器内部使用ip addr可以查看到有一行 37: eth0@if38:
veth-pair 由一对虚拟网络接口组成:veth37 和 veth38。这两个接口是成对出现的,通过一个虚拟的以太网链路连接起来,用于连接各种虚拟设备。在这对接口中,veth37位于容器上,veth38位于"docker0"上。每启动一个容器,就会成对出现这两个接口,hsh_centos_2的接口序号为39、40,以下再开启一个hsh_centos_3 容器,其接口序号为41、42。
正常来说,veth的接口都是在主机上创建且可见的,只是Windows使用NAT技术隐藏了这些细节,而容器内部是基于linux实现的,所以能看见veth接口。
所以,在不使用自定义网络的情况下,默认桥接模式 "docker0" 实现容器间通信的流程就是:
- 打开docker进程,主机上便会自动创建一个"docker0"虚拟网桥。
- 创建容器时,主机会先创建好一对 veth-pair 虚拟接口,分别命名为veth37、veth38,将veth37接口放在容器上,veth38放在"docker0"上。
- 从"docker0"子网中分配一个IP给容器使用,并设置"docker0"的IP地址为容器的默认网关。"docker0"默认IP地址为172.17.0.1。
- 容器之间通信则通过默认的"docker0"进行转发。
- 容器删除,对应的veth-pair也会删除。
三、"Docker0"的缺点
- 使用默认网桥"docker0",只能通过ip地址相互访问
docker创建容器分配的ip地址是会变化的,当你删除容器再重新启动时ip地址会不一样,这造成的问题就是,当"docker0"网络中其中一个容器存在问题删除重新启动后,其他所有的容器都会由于其ip地址变化而不认识他。可以使用--link解决该问题,但也需要双向连接,这种操作在集群中是非常麻烦的。 - 未指定的容器全部添加到"docker0"网络中,不方便管理也存在风险
一般会使用"docker0"网络只是会在一些临时调试的容器中为方便而使用。 - 要从"docker0"网络中移除容器,只能将该容器删除
四、自定义网络
当使用自定义网络时,"docker0"就不适用了。docker的自定义网络模式共有四种,最常使用的为bridge模式,当自定义网络使用bridge模式时,原理和"docker0"是相同的,只是在其之上会提供更加完善的网络服务。
自定义网络和默认桥接网络"docker0"的区别:
- 用户定义的网桥在容器之间提供自动的 DNS 解析功能。
在默认的桥接网络上,容器之间只能通过 IP 地址相互访问,除非使用--link
参数来创建手动链接,但这被认为是过时的。而在用户定义的网桥网络上,容器可以通过名称或别名相互进行 DNS 解析。
一个具有前端 Web 服务和后端数据库的应用程序,如果在用户定义的网桥网络上运行这个应用程序的容器,无论应用程序堆栈在哪个 Docker 主机上运行,Web 容器都可以通过容器名称连接到数据库容器。
相比之下,如果在默认的桥接网络上运行相同的应用程序堆栈,需要手动创建容器之间的链接,并且这些链接需要在双向上进行设置。这在有多个需要进行通信的容器时会变得非常复杂。或者,可以操作容器中的文件,但这可能导致难以调试的问题。 - 用户定义的网桥提供了更好的隔离性。
在默认情况下,所有未指定网络的容器都会连接到默认的桥接网络,这可能存在风险,因为不相关的堆栈、服务或容器之间可以进行通信。而使用用户定义的网络可以提供一个作用域限定的网络,只有连接到该网络的容器才能进行通信。 - 使用用户定义的网络,容器可以动态地附加和分离。
可以在容器的生命周期内动态地连接或断开容器与用户定义的网络的关联。而要从默认的桥接网络中删除容器,需要停止容器并使用不同的网络选项。 - 每个用户定义的网络都会创建一个可配置的网桥。
如果不同的应用程序组具有不同的网络需求,可以单独配置每个用户定义的网桥,在创建时进行设置。 - 默认的桥接网络上的链接容器共享环境变量。
初始时,唯一的共享环境变量方法是使用--link
标志。然而,对于用户定义的网络,有更好的方法来共享环境变量。一些想法包括容器挂载共享的文件或目录,使用 Docker 卷进行共享,或者使用 Docker Compose 文件定义共享变量。
配置自定义网络的两种方式:
- 命令行
docker network create
常用可选项:
--subnet 子网 192.168.0.0/16
--driver 网络模式 bridge、host、none、container
--gateway 网关ip 192.68.0.1docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 hsh_net_1
接下来将之前的centos容器关闭,重新加上--net命令项使其使用刚刚配置的自定义网络。docker run -it --name="hsh_centos_1" --net hsh_net_1 centos /bin/bash docker run -it --name="hsh_centos_2" --net hsh_net_1 centos /bin/bash docker run -it --name="hsh_centos_3" --net hsh_net_1 centos /bin/bash
以上三个centos容器的网段都在192.168.0.1下。
通过docker network inspect 命令可以查看自定义网络的情况。docker network inspect hsh_net_1
可发现三个centos容器都已经包括在了hsh_net_1的自定义网络中。
network其他相关命令Commands: connect Connect a container to a network create Create a network disconnect Disconnect a container from a network inspect Display detailed information on one or more networks ls List networks prune Remove all unused networks rm Remove one or more networks
-
docker-compose
具体配置方式参考Docker compose介绍及入门、docker-compose.yaml文件如何编写及规范-CSDN博客
docker-compose网络配置样例version: '3' networks: kafka-storm-net: services: zookeeper1: image: zookeeper:3.7.1 container_name: zookeeper1 hostname: zookeeper1 ports: - 8101:2181 - 8102:2888 - 8103:3888 - 8108:8080 environment: ZOO_MY_ID: 1 ZOO_SERVERS: server.1=zookeeper1:2888:3888;2181 server.2=zookeeper2:2888:3888;2181 server.3=zookeeper3:2888:3888;2181 networks: - kafka-storm-net zookeeper2: image: zookeeper:3.7.1 container_name: zookeeper2 hostname: zookeeper2 ports: - 8201:2181 - 8202:2888 - 8203:3888 - 8208:8080 environment: ZOO_MY_ID: 2 ZOO_SERVERS: server.1=zookeeper1:2888:3888;2181 server.2=zookeeper2:2888:3888;2181 server.3=zookeeper3:2888:3888;2181 networks: - kafka-storm-net zookeeper3: image: zookeeper:3.7.1 container_name: zookeeper3 hostname: zookeeper3 ports: - 8301:2181 - 8302:2888 - 8303:3888 - 8308:8080 environment: ZOO_MY_ID: 3 ZOO_SERVERS: server.1=zookeeper1:2888:3888;2181 server.2=zookeeper2:2888:3888;2181 server.3=zookeeper3:2888:3888;2181 networks: - kafka-storm-net kafka1: image: bitnami/kafka:latest hostname: kafka1 container_name: kafka1 ports: - 19092:9092 environment: KAFKA_ENABLE_KRAFT: yes KAFKA_CFG_NODE_ID: 1 KAFKA_CFG_PROCESS_ROLES: controller,broker KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: OUTSIDE:PLAINTEXT,CONTROLLER:PLAINTEXT,INSIDE:PLAINTEXT KAFKA_CFG_ADVERTISED_LISTENERS: OUTSIDE://:9092,INSIDE://:9091 KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 1@kafka1:9093,2@kafka2:9093,3@kafka3:9093 KAFKA_CFG_LISTENERS: CONTROLLER://:9093,OUTSIDE://0.0.0.0:9092,INSIDE://:9091 KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE ALLOW_PLAINTEXT_LISTENER: 'yes' KAFKA_KRAFT_CLUSTER_ID: iZWRiSqjZAlYwlKEqHFQWI volumes: - ./kafka-data/kafka1:/bitnami/kafka:rw networks: - kafka-storm-net kafka2: image: bitnami/kafka:latest hostname: kafka2 container_name: kafka2 ports: - 29092:9092 environment: KAFKA_ENABLE_KRAFT: yes KAFKA_CFG_NODE_ID: 2 KAFKA_CFG_PROCESS_ROLES: controller,broker KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: OUTSIDE:PLAINTEXT,CONTROLLER:PLAINTEXT,INSIDE:PLAINTEXT KAFKA_CFG_ADVERTISED_LISTENERS: OUTSIDE://:9092,INSIDE://:9091 KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 1@kafka1:9093,2@kafka2:9093,3@kafka3:9093 KAFKA_CFG_LISTENERS: CONTROLLER://:9093,OUTSIDE://0.0.0.0:9092,INSIDE://:9091 KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE ALLOW_PLAINTEXT_LISTENER: 'yes' KAFKA_KRAFT_CLUSTER_ID: iZWRiSqjZAlYwlKEqHFQWI volumes: - ./kafka-data/kafka2:/bitnami/kafka:rw networks: - kafka-storm-net kafka3: image: bitnami/kafka:latest hostname: kafka3 container_name: kafka3 ports: - 39092:9092 environment: KAFKA_ENABLE_KRAFT: yes KAFKA_CFG_NODE_ID: 3 KAFKA_CFG_PROCESS_ROLES: controller,broker KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: OUTSIDE:PLAINTEXT,CONTROLLER:PLAINTEXT,INSIDE:PLAINTEXT KAFKA_CFG_ADVERTISED_LISTENERS: OUTSIDE://:9092,INSIDE://:9091 KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 1@kafka1:9093,2@kafka2:9093,3@kafka3:9093 KAFKA_CFG_LISTENERS: CONTROLLER://:9093,OUTSIDE://0.0.0.0:9092,INSIDE://:9091 KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE ALLOW_PLAINTEXT_LISTENER: 'yes' KAFKA_KRAFT_CLUSTER_ID: iZWRiSqjZAlYwlKEqHFQWI volumes: - ./kafka-data/kafka3:/bitnami/kafka:rw networks: - kafka-storm-net kafka-ui: image: provectuslabs/kafka-ui:master container_name: kafka-ui ports: - "9080:8080" restart: always environment: - KAFKA_CLUSTERS_0_NAME=hsh_kafka_cluster - KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS= kafka1:9092 ,kafka2:9092,kafka3:9092 - DYNAMIC_CONFIG_ENABLED= 'true' - AUTH_TYPE=LOGIN_FORM - SPRING_SECURITY_USER_NAME=hsh - SPRING_SECURITY_USER_PASSWORD=241817 depends_on: - kafka1 - kafka2 - kafka3 volumes: - ./kafka-ui/config.yml:/etc/kafkaui/dynamic_config.yaml networks: - kafka-storm-net storm-nimbus: image: storm:2.5.0 container_name: storm-nimbus hostname: storm-nimbus command: storm nimbus ports: - 6627:6627 depends_on: - zookeeper1 - zookeeper2 - zookeeper3 environment: - STORM_ZOOKEEPER_SERVERS=zookeeper1,zookeeper2,zookeeper3 volumes: - ./storm-conf/storm.yaml:/conf/storm.yaml networks: - kafka-storm-net storm-supervisor1: image: storm:2.5.0 container_name: storm-supervisor1 hostname: storm-supervisor1 command: storm supervisor depends_on: - storm-nimbus volumes: - ./storm-conf/storm.yaml:/conf/storm.yaml networks: - kafka-storm-net storm-supervisor2: image: storm:2.5.0 container_name: storm-supervisor2 hostname: storm-supervisor2 command: storm supervisor depends_on: - storm-nimbus volumes: - ./storm-conf/storm.yaml:/conf/storm.yaml networks: - kafka-storm-net storm-supervisor3: image: storm:2.5.0 container_name: storm-supervisor3 hostname: storm-supervisor3 command: storm supervisor depends_on: - storm-nimbus volumes: - ./storm-conf/storm.yaml:/conf/storm.yaml networks: - kafka-storm-net storm-ui: image: storm:2.5.0 container_name: storm-ui hostname: storm-ui command: storm ui ports: - "8080:8080" depends_on: - storm-nimbus volumes: - ./storm-conf/storm.yaml:/conf/storm.yaml networks: - kafka-storm-net