Docker网络详解与实战
Posted Code0cean
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Docker网络详解与实战相关的知识,希望对你有一定的参考价值。
文章目录
1. Docker默认的网络模式
使用以下命令查看所有的Docker网络:
docker network ls
Docker默认提供了四个网络模式,说明:
-
bridge:容器默认的网络是桥接模式(自己搭建的网络默认也是使用桥接模式,启动容器默认也是使用桥接模式)。此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。
-
none:不配置网络,容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。
-
host:容器和宿主机共享Network namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
-
container:创建的容器不会创建自己的网卡,配置自己的IP容器网络连通。容器和另外一个容器共享Network namespace(共享IP、端口范围)。
容器默认使用bridge网络模式,我们使用该docker run --network=
选项指定容器使用的网络:
- host模式:使用 --net=host 指定。
- none模式:使用 --net=none 指定。
- bridge模式:使用 --net=bridge 指定,默认设置。
- container模式:使用 --net=container:NAME_or_ID 指定。
1.1 host模式
Namespace的简要说明:
Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的NetworkNamespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。Host模式的模型图,如下图所示:
备注:eth0为宿主机的10.126.130.4为宿主机的内网地址。
1.2 container模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。Container模式模型示意图如下:
1.3 none模式
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过–network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
None模式示意图:
1.4 bridge模式
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。
bridge模式是docker的默认网络模式,不写–net参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。bridge模式如下图所示:
当Docker server启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。Docker0使用到的技术是evth-pair技术。在默认bridge网络模式下,我们每启动一个Docker容器,Docker就会给Docker容器配置一个ip。
Docker容器完成bridge网络配置的过程如下:
- 在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。
- Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth65f9这样类似的名字命名,并将这个网络设备加入到docker0网桥中。
- 从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。
执行命令 ip addr
,可以看到安装Docker后,默认宿主机有三个地址。
Docker是如何处理容器网络访问的呢?启动一个容器,进入容器使用ip addr
查看网络设置
docker run -d -P --name tomcat01 tomcat:8.0
可以看到容器启动后Docker分配给容器一个格式如eth0@if262 的ip地址。宿主机运行ip addr
命令查看变化:
可以看到容器内部和Linux主机都会创建一个新的网卡,而这两个网卡都是成对的。使用的技术就是evth-pair。evth-pair 就是一对的虚拟设备接口,他们是成对出现的,一段连着协议,一段彼此相连。evth-pair充当一个桥梁,连接各种虚拟网络设备。
==Linux主机可以直接网络连接Docker容器。Docker容器和容器之间同样可以直接网络连接的。==下面具体实验验证一下。
docker exec -it tomcat01 ip addr
下面再启动一个Tomcat容器,尝试容器之间的网络连接是否能够成功。
尝试在tomcat01容器中ping tomcat02容器,可以看到两个容器是可以连接上的。两个Tomcat容器之间的网络交互模型图如下:
说明:
Tomcat01和Tomcat02都使用公用的路由器docker0。所有的容器不指定网络下,都是由docker0路由的,Docker会给我们容器默认分配一个随机的可用IP地址。
容器网络互联的通用模型,如下图所示:
Docker中的所有的网络接口都是虚拟的。只要容器删除,容器对应的网桥也会删除。
2. 容器互联
在微服务部署的场景下,注册中心是使用服务名来唯一识别微服务的,而我们上线部署的时候微服务对应的IP地址可能会改动,所以我们需要使用容器名来配置容器间的网络连接。使用–link可以完成这个功能。
首先不设置连接的情况下,是无法通过容器名来进行连接的。
下面启动一个Tomcat容器Tomcat03使用–link 连接已经启动的Tomcat02容器。这样容器Tomcat03就可以通过容器名Tomcat02连接到容器Tomcat02。
但是反过来容器Tomcat02通过容器名Tomcat03直接ping容器Tomcat03是不行的。
这是因为--link
的原理是在指定运行的容器上的/etc/hosts文件中添加容器名和ip地址的映射,如下:
而tomcat02容器不能够通过容器名连接tomcat03是因为tomcat02容器中并没有添加容器名tomcat03和ip地址的映射.
目前–link设置容器互连的方式已经不推荐使用。因为docker0不支持容器名访问,所以更多地选择自定义网络。
3. 自定义网络
因为docker0,默认情况下不能通过容器名进行访问。需要通过–link进行设置连接。这样的操作比较麻烦,更推荐的方式是自定义网络,容器都使用该自定义网络,就可以实现通过容器名来互相访问了。
下面查看network的相关命令
docker network --help
查看默认的网络bridge的详细信息
查看 network create命令的相关参数
下面自定义一个网络
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
参数说明:
--driver bridge #指定bridge驱动程序来管理网络
--subnet 192.168.0.0/16 #指定网段的CIDR格式的子网
--gateway 192.168.0.1 #指定主子网的IPv4或IPv6网关
网络mynet创建成功后,查看网络信息:
docker network inspect mynet
下面启动两个容器,指定使用该自定义网络mynet,测试处于自定义网络下的容器,是否可以直接通过容器名进行网络访问。
docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat
docker network inspect mynet
下面通过容器名来测试容器tomcat-net-01 和容器tomcat-net-02之间是否能正常网络通信。
可以发现,在我们的自定义网络下,容器之间既可以通过容器名也可以通过ip地址进行网络通信。 我们自定义的网络默认已经帮我们维护了容器间的网络通信问题,这是实现网络互联的推荐方式。
4. Docker网络之间的互联
没有设置的情况下,不同网络间的容器是无法进行网络连接的。如图,两个不同的网络docker0和自定义网络mynet的网络模型图:
在默认网络bridge下启动容器tomcat-01,尝试连接mynet网络下的tomcat-net-01容器。
可以看到是无法网络连接的。不同Docker网络之间的容器需要连接的话需要把作为调用方的容器注册一个ip到被调用方所在的网络上。需要使用docker connect
命令。
下面设置容器tomcat-01连接到mynet网络上。并查看mynet的网络详情,可以看到给容器tomcat-01分配了一个ip地址。
docker network connect mynet tomcat-01
设置完成后我们就可以实现不同网络之间的容器互联了。
5. Docker网络实战练习
5.1 Redis集群部署
下面部署如图所示的三主三从的Redis集群
首先停掉所有的容器
docker rm -f $(docker ps -aq)
创建网络名为redis的自定义网络
docker network create redis --subnet 172.38.0.0/16
通过以下脚本创建六个Redis 的配置信息:
for port in $(seq 1 6); \\
do \\
mkdir -p /mydata/redis/node-$port/conf
touch /mydata/redis/node-$port/conf/redis.conf
cat << EOF >/mydata/redis/node-$port/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1$port
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
下面启动6个Redis容器,设置对应的容器数据卷挂载,
#第1个Redis容器
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \\
-v /mydata/redis/node-1/data:/data \\
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第2个Redis容器
docker run -p 6372:6379 -p 16372:16379 --name redis-2 \\
-v /mydata/redis/node-2/data:/data \\
-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第3个Redis容器
docker run -p 6373:6379 -p 16373:16379 --name redis-3 \\
-v /mydata/redis/node-3/data:/data \\
-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第4个Redis容器
docker run -p 6374:6379 -p 16374:16379 --name redis-4 \\
-v /mydata/redis/node-4/data:/data \\
-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第5个Redis容器
docker run -p 6375:6379 -p 16375:16379 --name redis-5 \\
-v /mydata/redis/node-5/data:/data \\
-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
#第6个Redis容器
docker run -p 6376:6379 -p 16376:16379 --name redis-6 \\
-v /mydata/redis/node-6/data:/data \\
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
或者通过脚本一次性启动6个Redis容器:
for port in $(seq 1 6); \\
do
docker run -p 637$port:6379 -p 1637$port:16379 --name redis-$port \\
-v /mydata/redis/node-$port/data:/data \\
-v /mydata/redis/node-$port/conf/redis.conf:/etc/redis/redis.conf \\
-d --net redis --ip 172.38.0.1$port redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \\
done
执行上述脚本,运行结果如下:
下面进入到redis-1容器中创建集群
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
查看集群信息
redis-cli -c
cluster info
查看节点信息cluster nodes
,可以清楚的看到Redis节点的主从关系。
测试主从复制是否生效,设置一个key,可以看到我们重定向到了Redis-3节点,处理该操作的是Redis-3节点。
新建一个会话,停止Redis-3容器服务,此时重新连接Redis-cli客户端,再次获取k1,重定向到了Redis-3节点的从节点Redis-4中处理。
5.2 SpringBoot项目打包镜像
SpringBoot项目打包镜像分为以下五个步骤
(1)构建SpringBoot项目
在idea中构建一个最简单的Spring Boot项目,写一个接口,启动项目本地测试能否正常调用。
/**
* @author ethan
* @since 2020-04-22 20:26:31
*/
@RestController
public class HelloController
@GetMapping("/hello")
public String hello()
return "Hello World!";
(2)打包应用
使用Maven的package打包Spring Boot项目,生成jar包
(3)编写Dockerfile
编写Dockerfile,内容如下
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
(4)构建镜像
将打包生成的jar包和编写的Dockerfile文件发送到服务器上
使用build命令构建镜像
docker build -t demo .
(5)发布运行
完成镜像的构建后,运行镜像,测试/hello接口能否正常访问。
docker run -d -p 8080:8080 demo
curl localhost:8080/hello
参考:
1.https://www.bilibili.com/video/BV1og4y1q7M4
2.Docker网络详解——原理篇
3.Docker四种网络模式
以上是关于Docker网络详解与实战的主要内容,如果未能解决你的问题,请参考以下文章