Docker 网络工作原理详解
Docker 网络是 Docker 中比较核心、也比较复杂的一个部分,本篇文章就来详细讲解 Docker 网络的工作原理。我们将先介绍 Docker 网络中的一些基本概念、网络模式,然后讲解 Docker 内置网络的实现原理、Docker 容器间的通信方式,最后通过两个示例演示 Docker 容器间的通信方式。
Docker 网络基本概念
在开始学习 Docker 网络的工作原理之前,我们需要了解一些基本概念。
Docker 网络模式
Docker 命令中提供了多种网络模式,分别是:
- bridge 桥接模式:Docker 默认使用的网络模式,使用 docker0 这个虚拟网桥将容器连接到主机上。
- host 主机模式:将容器直接放到主机的网络上,容器共享主机的网络栈,可以直接使用主机上面的 IP 地址。
- none 无网络模式:关闭容器的网络,容器内无法访问网络,网络也无法访问容器。
- container 容器模式:将容器连接到已经存在的容器的网络栈上,这样两个容器可以直接通信,而无需通过网络设备。
使用不同的网络模式可以实现不同的网络功能,选择合适的网络模式有助于提升网络性能和安全性。
Docker 网络驱动
Docker 网络驱动是指一组插件程序和库,容器使用它来实现网络流量的转发和管理。Docker 内置了多种网络驱动,包括 bridge、host、macvlan、overlay、ipvlan 等,也支持用户自定义网络驱动。
Docker 网络命名
Docker 在网络管理中使用的名称是非常重要的,一个网络名称可以代表一个虚拟网络,让多个容器连接在同一个虚拟网络中。Docker 支持使用自定义的名称来创建网络,也支持使用自动生成的名称。
Docker 网络流量
在 Docker 网络中,所有的网络流量都是要经过 Docker Host 的虚拟网桥 docker0 的。Docker 经过网桥来管理创建的每个容器和它们的网络配置。
Docker 内置网络实现原理
Docker 内置网络是 Docker 在 1.9 版本中推出的一个新的网络功能,它以插件方式实现了容器的互联。内置网络实现原理大致如下:
- Docker Host 的容器引擎会在安装时就创建一个虚拟网桥 docker0。容器启动后,Docker 会为每个容器创建一个网络命名空间,这个命名空间中包含一套网络设备(如 veth pair 设备)、路由表、iptables 规则等。
- Docker 在容器中创建一个名为 eth0 的网络接口,并将这个接口放在容器的命名空间中, eth0 对应 Docker Host 的虚拟网桥上的一个 veth pair 设备的其中之一。容器内的网络流量都需要通过 eth0 才能访问 docker0 上的其他容器或者宿主机上的其他设备。
- 对于一个加入了默认 bridge 网络的容器,在容器启动时,Docker 会默认为其配置一个 IP 地址和路由表规则,并将其添加到 bridge 网络的 Linux bridge 中。同时,Docker 还会给容器配一个 DNS 服务器(默认为以 Google 的 DNS 服务器)。
Docker 容器间通信方式
容器间通信方式视网络模式的选择不同而不同,这里我们讲解两种常见的 Docker 容器间通信方式。
bridge 网络模式下的容器间通信
在默认模式下,Docker 创建的容器会使用 bridge 网络模式,并且被分配到 Docker Host 的默认 bridge 网络上。在这种情况下,Docker 容器可以使用 Docker 自带的 DNS 服务器(通常为容器 IP 地址的最后一段加上 1)进行访问其他容器。
$ docker run -itd --name web nginx
$ docker run -itd --name db mysql
通过上面两条命令创建了一个 nginx 和一个 mysql 的容器。使用以下命令打开 nginx 容器并访问 mysql 容器:
$ docker exec -it web /bin/bash
root@nginx:/# ping db
PING db (172.17.0.2) 56(84) bytes of data.
64 bytes from db.docker_default (172.17.0.2): icmp_seq=1 ttl=64 time=0.127 ms
64 bytes from db.docker_default (172.17.0.2): icmp_seq=2 ttl=64 time=0.119 ms
64 bytes from db.docker_default (172.17.0.2): icmp_seq=3 ttl=64 time=0.185 ms
user-defined 网络模式下的容器间通信
使用 user-defined 网络模式下,每个容器会被分配到一个独立的虚拟网络中,并且可以设置子网和网关,同时可以自定义容器的访问规则。在这种情况下,Docker 容器可以使用容器名或 IP 地址进行通信。
$ docker network create my-net
$ docker run -itd --name web --net=my-net nginx
$ docker run -itd --name db --net=my-net mysql
创建了一个自定义网络,然后在这个自定义网络中创建了一个 nginx 和一个 mysql 的容器。使用以下命令打开 nginx 容器并访问 mysql 容器:
$ docker exec -it web /bin/bash
root@9b0b8a9ad976:/# ping db
PING db (172.19.0.2) 56(84) bytes of data.
64 bytes from db.my-net (172.19.0.2): icmp_seq=1 ttl=64 time=0.046 ms
64 bytes from db.my-net (172.19.0.2): icmp_seq=2 ttl=64 time=0.041 ms
64 bytes from db.my-net (172.19.0.2): icmp_seq=3 ttl=64 time=0.062 ms
两个示例
在这里我们演示两个容器间通信的例子,分别是在默认 bridge 网络下实现容器间服务发现,以及使用 user-defined 网络实现一个简单的多容器的 web 应用。
示例 1:在默认 bridge 网络下实现容器间服务发现
在这个例子中,我们将创建三个容器:web、redis、db,其中 web 容器提供 web 服务,redis 容器提供缓存服务,db 容器提供数据库服务。
- 创建三个容器:
$ docker run -d -p 8080:80 --name web nginx
$ docker run -d --name redis redis
$ docker run -d --name db mysql -e MYSQL_ROOT_PASSWORD=root
- 查看容器 ip:
$ docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)
输出应该是这样的:
/web - 172.17.0.2
/redis - 172.17.0.3
/db - 172.17.0.4
- 将这些 IP 地址添加到 web 容器的 /etc/hosts 文件中:
$ for container in web redis db; do
ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $container)
echo "$ip $container" >> /etc/hosts
done
现在,web 容器可以使用 redis 和 db 的名称进行访问。例如,可以在 nginx 配置文件中使用这些名称:
upstream php {
server redis:6379;
server db:3306;
}
server {
listen 80 default_server;
server_name localhost;
root /usr/share/nginx/html;
location / {
proxy_pass http://php;
}
}
示例 2:使用 user-defined 网络实现多容器 web 应用
在这个例子中,我们将使用 user-defined 网络创建多个容器同时运行一个简单的 web 应用程序。该容器提供 Apache 服务器、PHP 环境和 MySQL 数据库。
- 创建自定义网络:
$ docker network create my-net
- 创建 MySQL 数据库容器:
$ docker run -d --name db --net my-net -e MYSQL_ROOT_PASSWORD=PASSWORD mysql
- 创建 Apache 服务器容器:
$ docker run -d --name web --net my-net -p 8080:80 -v "$PWD":/var/www/html php:7.2-apache
在这里,我们使用 PHP 官方镜像中的 Apache 服务器镜像来运行我们的 web 应用程序。我们将本地文件挂载到容器中作为 Apache 服务器的文档根目录。
- 在 Apache 服务器容器中安装 PHP 和 MySQL 扩展:
$ docker exec -it web docker-php-ext-install mysqli pdo pdo_mysql
- 创建一个简单的 PHP 网页:
在本地创建一个 index.php
文件,放在你想要的目录下(例如 /path/to/your/dir
)。在文件中写入以下内容:
<?php
$servername = "db";
$username = "root";
$password = "PASSWORD";
$dbname = "test";
$conn = mysqli_connect($servername, $username, $password, $dbname);
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
echo "Connected successfully to MySQL database!";
mysqli_close($conn);
?>
这个 PHP 网页将检查 MySQL 数据库的连接并输出一条消息。
- 访问你的 web 应用程序:
在浏览器中键入 http://localhost:8080/index.php
,如果一切正常,你应该会看到一条消息 “Connected successfully to MySQL database!”。
总结
Docker 网络是 Docker 中比较核心、也比较复杂的一个部分。本文详细介绍了 Docker 网络的基本概念和原理,并演示了两个容器间通信的例子。通过深入了解 Docker 网络,我们可以更好地管理我们的 Docker 容器,提高容器应用的安全性和网络性能。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Docker 网络工作原理详解 - Python技术站