前言
最近遇到一个需求,我想要把自己写python程序使用dockerfile的方式构建为一个镜像,之后启动容器服务,当前的python程序需要的环境需要一个minio环境,而minio也是docker的方式进行启动的,这就涉及到了一个问题,那就是如何让2个容器进行通信,基于此去学习docker network的使用,就有了这篇学习记录,这篇文章采用mysql和nginx的为案例
Docker默认网络模式
Docker 默认有三个网络模式,可以使用 docker network ls查看
安装docker后,默认会创建三大网络模式
桥接模式(bridge)
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
桥接模式下,Docker 会为每个容器分配一个 IP 地址,成称为container-id,同时docker网桥是每一个容器的默认网关。因为同一宿主机内的容器都接入同一个网桥,这样容器之间就可以相互通信,网桥docker0会创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。发出去的数据包先到达docker0,然后交给主机的协议栈,由于目的 IP 是外网 IP,且主机会开启 IP forward 功能,于是数据包通过主机的 eth0 发出去。由于docker0是内网 IP ,所以一般发出去之前会做 NAT 转换
bridge模式是docker的默认网络模式,不写--net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。
桥接模式原理图
查看网络数据源
# 截取一些比较重要的信息
docker network inspect bridge
[
{
"Name": "bridge",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16", # 子网范围
"Gateway": "172.17.0.1" # 网关
}
]
},
"Containers": {
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
创建两个容器
docker run -it --rm -d -p 3311:3306 -e MYSQL_ROOT_PASSWORD=yunzhi --name db mysql
docker run -it --rm -d -p 8087:80 --name web nginx
查看网络数据源
# 截取一些比较重要的信息
docker network inspect bridge
[
{
"Name": "bridge",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16", # 子网范围
"Gateway": "172.17.0.1" # 网关
}
]
"Containers": {
"32fe0526ee36d08c5c67ba862894d56e28f24fde8cfb0e8d7fa2746f92f39cef": {
"Name": "web",
"EndpointID": "43576f55a383392b543adcc4308a14e7dcc92ec30dcb14447869d4d4354a0e4e",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16", # id 地址
"IPv6Address": ""
},
"701dedd62762abecf36591a3f426dcc5a79a580fc512f987e7df03d83b6fd0cc": {
"Name": "db",
"EndpointID": "ba6b19859855a7558e11d32a284e4c0c4c9fc95d07065d1de28d23cfad69c64b",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16", # id 地址
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
在容器内部执行网络连通性测试
进入容器内部:
docker exec -it web /bin/bash
Ping 另一个容器:
在一个容器的 shell 提示符下,尝试使用 ping 命令 ping 另一个容器的 IP 地址。当前在 web 容器内部,要测试能否与 db 容器通信,可以执行:
ping 172.17.0.2
这里发现可以ping通,也就证明了在同一个网桥内的容器进行通信
使用容器名进行尝试
ping db
从这里我们就可以发现不能根据容器名进行ping通
所以这里我们发现,默认的网桥bridge上的容器只能通过IP互连,无法通过DNS解析名称或别名。如果我们当前container1有一个Web服务,在container2有一个mysql,container1中的Web服务往往需要连接container2的mysql,这是只能靠IP进行连接,但是docker也无法保证容器重启后的IP地址不变,所以更好的方式是通过别名进行互联。
不同容器通过容器名或者别名进行联通
使用--link参数
--link参数允许在启动容器时创建容器间的链接,使一个容器可以通过环境变量访问另一个容器。这种方式已经被废弃。
docker run -it --rm -d -p 8087:80 --link db:db --name web nginx
docker exec -it web sh
ping -c3 db
手动修改/etc/hosts文件
编辑容器的/etc/hosts文件,添加其他容器的IP和主机名映射。
docker exec -it web sh
echo "172.17.0.2 db" >> /etc/hosts
用户自定义bridge网桥:自动DNS解析,这是目前解决此类问题的主要方法
docker network create my_network
docker run -it --rm -d -p 3311:3306 --network my_network -e MYSQL_ROOT_PASSWORD=yunzhi --name db mysql
docker run -it --rm -d -p 8087:80 --network my_network --name web nginx
再次尝试连接
docker exec -it web sh
ping -c3 db
主机模式(host)
主机模式下,Docker容器与宿主机共享网络栈。容器将直接使用宿主机的网络接口,而不会获得自己的网络命名空间。这意味着容器可以使用宿主机上的所有网络资源,在 --network host 模式下,容器之间可以使localhost 进行通信。如果在同一台宿主机上运行多个容器并使用 --network host 模式,请确保每个容器使用不同的端口,否则会发生端口冲突。
docker run -it --rm -d -p 8080:80 --network host --name web nginx
警告:使用主机网络模式时,已发布的端口将被丢弃
WARNING: Published ports are discarded when using host network mode
服务的测试页面
docker exec -it web curl http://localhost
无网络模式(none)
在无网络模式下,容器没有网络连接。这意味着容器无法通过网络与其他容器或外部网络进行通信。通常情况下,无网络模式用于特殊应用场景,例如需要完全与网络隔离的容器。
指定网络模式
docker run创建Docker容器时,可以用–net选项指定容器的网络模式
- bridge模式:使–net =bridge指定,默认设置;
- host模式:使–net =host指定;
- none模式:使–net =none指定;
- container模式:使用–net =container:NAME orID指定。
参考文章
https://www.runoob.com/docker/docker-container-connection.html
https://docs.docker.com/network/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。