1.前言
本系列文章记录了从0开始学习Docker的过程,Docker系列历史文章:
(一)基本概念与安装使用
(二)如何使用Docker发布一个SpringBoot服务
(三)使用registry远程镜像仓库管理镜像
(四)使用volume持久化Docker容器中的Redis数据
(五)使用bind mounts修改Docker容器中的Nginx配置
在前边介绍了,得益于使用dockerfile
创建镜像文件,registry
仓库管理镜像文件,我们可以仅通过docker run
这个简单的指令,就能创建容器并启动一个服务,减少了繁琐的安装部署流程。容器之所以能够这么强大,除了上述的镜像文件管理以外,还得意于Docker
的network
机制。
可以想象一下,我们可以将已经创建好的容器看作一个轻量级的OS,如果没有网络通信,这个容器就被变成一个孤岛,没有什么存在的意义。所以在容器与宿主机之间、容器与容器之间,通过network
建立了网络通信。
在network
机制中,提供了容器间的网络通信API,这个API封装了对操作系统网络的操作,我们在使用Docker的时候可以不用在意操作系统层面的网络实现细节。
接下来会介绍如何使用这些API,并以Nginx
反向代理为例讲述自定义的network
存在的意义。
2.network的驱动类型
Docker中的network
实际上是使用的插件机制,即提供核心接口,让不同的插件去实现细节,我们一般将这种实现细节的插件叫做驱动。由用户根据需要选择不同的驱动处理不同的业务场景。
Docker官方提供了几个默认的驱动,下面是不同驱动使用场景:
bridge
:用在单机多容器需要互相通信的场景,是docker的默认网络驱动。overlay
:用于多机多容器的网络通信场景,更多的使用在docker swarm中。- host:也是用在单机场景,与bridge的区别在于,host会直接使用宿主机的ip和端口。
- ipvlan:用于用户需要自行操作ip的场景。
- macvlan:用于用户需要自行操作mac地址的场景。
- none:禁用网络,自己玩。
一般来说常用的就是bridge
和overlay
驱动,如果上述默认的驱动类型还不满足要求的话,也可以在Docker Hub上找自定义的驱动【自定义网络驱动地址】。
3.网络驱动的使用
3.1.bridge模式
使用docker network
指令可以管理网络驱动【network指令文档地址】,在docker安装完毕之后,会创建几个默认的网络驱动,如下:
docker network ls
NETWORK ID NAME DRIVER SCOPE
18baf7cfd864 bridge bridge local
06fe786c0c48 host host local
6844c7218d3f none null local
我们在输入docker run
运行容器,如果不显示的指定驱动,则会使用默认的bridge
,但是我们在实际使用时,一般不会选择默认的驱动,而是自行创建一个。
docker network create -d bridge my-bridge
这里的-d
是指定驱动类型,默认就是bridge
,如果想创建一个bridge
类型的驱动的话,指令可以简化为:
docker network create my-bridge
此时再查看network
列表,就能看到刚刚创建的驱动了。
docker network ls
NETWORK ID NAME DRIVER SCOPE
18baf7cfd864 bridge bridge local
06fe786c0c48 host host local
8e449b8833ab my-bridge bridge local
6844c7218d3f none null local
为什么要使用自定义的bridge
而不使用默认的呢?
自定义bridge
有以下的几点优势:
- 提供了DNS服务
可以将容器名映射到容器的ip和端口,使用容器名进行网络访问。 - 可以更好的做到容器隔离
可以将一组应用放到同一个bridge
中,另一组应用放到另一个bridge
中,两组应用互不影响。 - 可以动态的添加和移除网络驱动
在容器运行的时候,可以通过docker network connect/disconnect
指令,连接或移除某一个自定义的网络驱动,这种方式是不需要重启容器的。如果使用默认的bridge
则需要重新创建容器。
接下来,以nginx
做服务的反向代理的示例,来感受一下自定义network
的优势。
在上一篇《使用bind mounts修改Docker容器中的Nginx配置》 中,已经详细的讲述了如何创建nginx
容器,并将配置文件挂接到宿主机中,不知道如何在Docker中使用nginx
的同学,可以看一下这篇Nginx配置部分,非常简单。在这个例子中,配置了两个SpringBoot的服务,配置如下:
upstream hello-boot {
server 192.168.200.101:8080;
server 192.168.200.101:8081;
}
server {
listen 80;
listen [::]:80;
server_name 192.168.200.101;
location /hello {
proxy_pass http://hello-boot;
}
access_log /var/log/nginx/hello-boot.access.log main;
}
配置中的ip地址192.168.200.101
是宿主机所在的ip,两个SpringBoot服务所在的容器,映射了宿主机的端口8080
和8081
,此时我们访问nginx
时,就可以将请求转发到任意一个服务中。
这种方式虽然可以正常使用,但是占用了宿主机的端口,并且还可能存在端口冲突的问题,所以最好是在upstream
中配置容器的ip而不是宿主机的ip,而容器间需要网络通信的话,需要将所有的容器都连接到同一个网络驱动中,现在将Nginx和两个SpringBoot的服务都连接到my-bridge
。
上面提到了,自定义的bridge
可以动态的连接或断开连接,所以可以直接使用connect
指令,不需要重新创建容器。
docker network connect my-bridge my-nginx
docker network connect my-bridge hello-1
docker network connect my-bridge hello-2
然后使用inspect
指令查看容器的网络情况。
docker network inspect my-bridge
可以看到my-bridge
为各个容器分配的ip地址,我们将hello-1
与hello-2
的ip填充到nginx
的配置文件中:
upstream hello-boot {
server 172.18.0.3:8080;
server 172.18.0.4:8080;
}
这样就不会占用宿主机的端口了,但又引入了新的问题,这里的ip是由my-bridge
动态分配的,在hello-1
或hello-2
重新发布时,有可能会被其他的应用占用ip,导致访问异常。
上面我们提到了,自定义的bridge
自带了DNS服务,所以我们可以使用容器名代替ip地址,最终的配置文件修改如下:
upstream hello-boot {
server hello-1:8080;
server hello-2:8080;
}
server {
listen 80;
listen [::]:80;
server_name 192.168.200.101;
access_log /var/log/nginx/hello-boot.access.log main;
location /hello {
proxy_pass http://hello-boot;
}
}
重新加载Nginx并尝试访问:
docker exec -it my-nginx nginx -s reload
curl http://192.168.200.101/hello/sayHello?name=挥之以墨
sayHello
方法只做了一个返回,输入了name
是什么就返回什么,此时会打印出:
hello 挥之以墨
3.2. host模式
host
模式创建的容器,不能指定映射的端口,会使用镜像中已经指定好的端口,例如创建一个host
模式的nginx。
docker run -d --network=host --name nginx-host nginx
此时,将会占用宿主机的80端口,尝试访问下:
4.结语
本篇主要讲述了bridge
类型的网络驱动是如何使用的,自定义的bridge
相对于默认的bridge
的优势所在,即:
- 自带DNS服务
- 可以动态的连接容器或断开容器而不需要重启
- 可以有效的隔离不同的应用组
而对于host
类型的网络驱动来说,由于或占用主机的端口,而且不能动态指定端口,很容易端口冲突,我们一般不选择这种使用方式。
overlay
类型的驱动,主要是用在多机多容器的网络通信场景,由于管理比较复杂,一般是配合多机容器编排机制 Docker Swarm来使用的,在后续的博客中会继续讲述。
下一篇会讲Docker的单机容器编排技术,Docker Compose。
如果觉得本文对你有所帮助,可以帮忙点点赞哦!你的支持是我更新最大的动力!