Docker网络详解与实战

1. Docker默认的网络模式

使用以下命令查看所有的Docker网络:

docker network ls

在这里插入图片描述

Docker默认提供了四个网络模式,说明:

  1. bridge:容器默认的网络是桥接模式(自己搭建的网络默认也是使用桥接模式,启动容器默认也是使用桥接模式)。此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。

  2. none:不配置网络,容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,配置IP等。

  3. host:容器和宿主机共享Network namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。

  4. 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网络配置的过程如下:

  1. 在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。
  2. Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0。另一端放在主机中,以veth65f9这样类似的名字命名,并将这个网络设备加入到docker0网桥中。
  3. 从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四种网络模式

猜你喜欢

转载自blog.csdn.net/huangjhai/article/details/120425457