Docker技术系列文章,第三篇——容器网络基础
Docker技术系列文章,第三篇——容器网络基础
在容器化应用的世界里,容器之间以及容器与外部世界的通信至关重要。Docker 提供了一套强大的容器网络功能,让用户能够轻松配置和管理容器的网络连接。本文将详细介绍Docker的容器网络基础,包括网络模式、端口映射、容器间通信以及自定义网络等核心概念。
一、容器网络概述
1. 为什么需要容器网络
当我们在 Docker 中运行多个容器时,这些容器可能需要相互通信,例如一个 Web 应用容器需要与数据库容器进行数据交互。同时,容器也可能需要与外部网络(如互联网)进行通信,以便提供服务或获取资源。容器网络就是为了解决这些通信需求而存在的。
2. Docker 网络模型
Docker 采用了一种称为 CNM(Container Network Model)的网络模型。CNM 定义了三个核心组件:
- Sandbox:每个容器都有自己的网络命名空间,即一个隔离的网络环境,包含网络接口、路由表等。这就好比每个容器都有自己独立的 “网络房间”。
- Endpoint:连接到网络的网络接口。一个容器可以有多个 Endpoint,用于连接到不同的网络。可以将 Endpoint 看作是容器网络房间的 “门”,通过这个门与外界通信。
- Network:一组相互连接的 Endpoint,这些 Endpoint 可以在不同的容器中,实现容器间的通信。网络就像是连接各个容器网络房间的 “走廊”。
二、Docker 容器网络模式
1. 桥接模式(Bridge)
桥接模式是 Docker 默认的网络模式。在这种模式下,Docker 会在宿主机上创建一个虚拟网桥(通常命名为docker0),容器通过虚拟网卡连接到这个网桥上。容器之间可以通过网桥进行通信,同时容器也可以通过宿主机的网络接口访问外部网络。
示例代码与说明
首先,启动一个基于ubuntu镜像的容器,使用默认的桥接网络模式:
docker run -d --name ubuntu1 ubuntu sleep infinity
这里-d选项表示在后台运行容器,--name选项为容器指定名称为ubuntu1,ubuntu是镜像名称,sleep infinity让容器持续运行。
查看宿主机上的网络接口,可以看到docker0网桥以及与容器对应的虚拟网卡(例如vethxxxxx):
ip link show
容器ubuntu1的 IP 地址可以通过以下命令查看:
docker inspect -f'{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'ubuntu1
假设容器ubuntu1的 IP 地址为172.17.0.2。现在,再启动另一个容器ubuntu2:
docker run -d --name ubuntu2 ubuntu sleep infinity
获取ubuntu2的 IP 地址,假设为172.17.0.3。在ubuntu1容器内,可以通过ping命令测试与ubuntu2的连通性:
docker exec -it ubuntu1 ping172.17.0.3
由于两个容器都连接到同一个docker0网桥,所以它们可以相互通信。
容器ubuntu1和ubuntu2通过虚拟网卡连接到docker0网桥,从而实现相互通信。同时,docker0网桥通过宿主机的物理网卡连接到外部网络,使得容器也能够访问外部网络。
2. 主机模式(Host)
在主机模式下,容器直接使用宿主机的网络命名空间,容器的网络配置与宿主机完全相同。这意味着容器可以直接使用宿主机的 IP 地址和端口,容器之间不存在网络隔离。
示例代码与说明
启动一个使用主机模式的容器:
docker run -d --network=host --name ubuntu_host ubuntu sleep infinity
这里--network=host指定了容器使用主机网络模式。此时,在容器内查看网络配置,会发现与宿主机的网络配置一致:
docker exec -it ubuntu_host ip addr show
由于容器直接使用宿主机的网络,所以如果宿主机的 80 端口被占用,容器内就无法再使用 80 端口提供服务。这种模式适用于对网络性能要求较高,且不需要网络隔离的场景。
3. 无网络模式(None)
在无网络模式下,容器有自己独立的网络命名空间,但没有任何网络配置,容器无法与外部网络通信,也无法与其他容器通信。
示例代码与说明
启动一个使用无网络模式的容器:
docker run -d --network=none --name ubuntu_none ubuntu sleep infinity
查看该容器的网络配置,可以发现只有本地回环接口(lo):
docker exec -it ubuntu_none ip addr show
这种模式通常用于对网络安全性要求极高,只需要在容器内部进行一些不需要网络连接的操作的场景。
三、容器端口映射
1. 为什么需要端口映射
当我们在容器中运行一个应用程序,例如一个 Web 服务器,它在容器内部监听某个端口(如 80 端口)。但是,从外部网络访问容器时,无法直接访问容器内部的端口。端口映射就是将容器内部的端口映射到宿主机的某个端口,使得外部网络可以通过访问宿主机的端口来访问容器内的应用。
2. 端口映射示例
假设我们有一个基于nginx镜像的容器,要将容器内部的 80 端口映射到宿主机的 8080 端口,可以使用以下命令启动容器:
docker run -d -p8080:80--name mynginx nginx
这里-p选项用于指定端口映射,格式为宿主机端口:容器端口。现在,在浏览器中访问http://宿主机IP:8080,就可以看到nginx的默认欢迎页面,就像访问一个在宿主机上直接运行的nginx服务器一样。
如果我们想要将容器的多个端口映射到宿主机,可以使用多个-p选项。例如,将容器的 80 端口映射到宿主机的 8080 端口,将容器的 443 端口映射到宿主机的 8443 端口:
docker run -d -p8080:80-p8443:443--name mynginx nginx
四、容器间通信
1. 基于 IP 地址通信
如前文所述,在桥接模式下,容器之间可以通过彼此的 IP 地址进行通信。例如,有两个容器app和db,app容器需要连接到db容器的数据库服务。首先获取db容器的 IP 地址:
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' db
然后在app容器内的应用配置中,使用获取到的db容器 IP 地址来连接数据库。这种方式虽然可行,但存在一个问题,即容器的 IP 地址可能会在容器重启后发生变化,导致应用连接失败。
2. 使用容器名称通信
为了解决 IP 地址变化的问题,Docker 提供了一种通过容器名称进行通信的方式。在同一网络中的容器,可以通过容器名称解析到对应的 IP 地址。
首先,创建一个自定义网络:
docker network create mynetwork
然后,启动两个容器并将它们连接到这个自定义网络:
docker run -d --name app --network=mynetwork ubuntu sleep infinity
docker run -d --name db --network=mynetwork ubuntu sleep infinity
在app容器内,可以通过ping db命令来测试与db容器的连通性,此时db会被解析为对应的 IP 地址。这样,即使容器的 IP 地址发生变化,只要容器名称不变,应用就可以正常通信。
五、自定义网络
1. 自定义网络的优势
虽然 Docker 默认的桥接网络模式能够满足大部分场景的需求,但在一些复杂的应用架构中,自定义网络可以提供更好的网络管理和隔离。例如,我们可以创建多个自定义网络,将不同类型的容器连接到不同的网络中,实现网络层面的隔离。同时,自定义网络还支持 DNS 解析,方便容器间通过名称进行通信。
2. 创建和管理自定义网络
创建自定义网络
我们可以使用docker network create命令创建自定义网络。例如,创建一个名为mynetwork的桥接网络:
docker network create mynetwork
默认情况下,创建的是桥接类型的网络。我们还可以指定网络的驱动类型、子网等参数。例如,创建一个子网为192.168.1.0/24的自定义网络:
docker network create --subnet=192.168.1.0/24mynetwork
连接容器到自定义网络
创建好自定义网络后,我们可以在启动容器时将其连接到该网络。例如,启动一个容器并连接到mynetwork网络:
docker run -d --name mycontainer --network=mynetwork ubuntu sleep infinity
也可以将已经运行的容器连接到自定义网络:
docker network connect mynetwork mycontainer
同样,我们可以使用docker network disconnect命令将容器从网络中断开。
通过本文的介绍,相信你对Docker容器网络的工作原理、网络模式、端口映射、容器间通信以及自定义网络有了全面的了解。