很早就接触过Docker容器相关的概念,之前还使用过Jenkins提供的k8s插件进行容器编排。但说实话,自己工作还是生活中使用的比较少,基础处于我知道是怎么样的,但缺乏实践经验。
搭建xxl-job-admin docker container 遇到的疑问
在阅读xxl-job的文档时,发现其提供Docker容器部署的方式,于是就在本机装好了Docker环境,实践一番。提前说明一下,本次实验使用的Docker版本为19.03.8。
xxl-job-admin的容器部署很简单,一共就分两步:
- 先部署一个MySql,部署完需要执行好xxl-job相应的SQL脚本
- 再部署xxl-job-admin,其中MySql链接指向刚刚部署好的MySql实例
部署命令也很简单,一共就3条:
1 | docker network create simple-network |
我们在结尾再解释为什么是这样写的,我先提出我的困惑。和单机部署应用进程都处在同一个网络环境中不同的是,在同一台主机上的两个容器,他们的网络环境是不是隔离的?如果不是隔离的,容器之间是怎么通信的,容器和宿主机之间又是怎么通行的呢?
Docker network drivers 介绍
在解答疑惑之前,我们需要先了解一下Docker的network驱动,通常情况下,默认是以下几种:
- bridge:默认网络驱动模式,通常如果不特别指定别的网络驱动模式,一般默认就是bridge。
- host:在独立容器中,使用host网络驱动会移除容器和容器主机之间的网络隔离性。也就是说,这个容器和主机的网络环境完全一样。不过目前只在Docker 17.06以上版本支持,且不支持Docker Desktop for Mac, Docker Desktop for Windows, or Docker EE for Windows Server.
- overlay:overlay 网络将多个 Docker 守护进程连接在一起,并使集群服务能够相互通信。您还可以使用 overlay 网络来实现 swarm 集群和独立容器之间的通信,或者不同 Docker 守护进程上的两个独立容器之间的通信。该策略实现了在这些容器之间进行操作系统级别路由的需求。[^cnkirito_footnote]
- macvlan:Macvlan 网络允许为容器分配 MAC 地址,使其显示为网络上的物理设备。 Docker 守护进程通过其 MAC 地址将流量路由到容器。对于希望直连到物理网络的传统应用程序而言,使用 macvlan 模式一般是最佳选择,而不应该通过 Docker 宿主机的网络进行路由。[^cnkirito_footnote]
- none:对于此容器,禁用所有联网。通常与自定义网络驱动程序一起使用。none 模式不适用于集群服务。[^cnkirito_footnote]
对于运行在一台主机的单独容器而言,只需要关心以上的bridge、host、none。
查看当前容器下已有的网络驱动
1 | ➜ ~ docker network ls |
可以看到,默认在一个Docker容器中包含三种网络驱动,其中SCOPE
指的是网络范围,可以是local或swarm范围。区别在于local是在主机范围内提供连接和网络服务(例如DNS或IPAM)。swarm可跨群群集提供连接和网络服务。swarm网络在整个群集中具有相同的网络ID,而local范围网络在每个主机上具有唯一的网络ID。
接下来我们主要介绍host并着重介绍bridge网络驱动。
Docker Host Network Driver
如果在创建容器时指定--net=host
,host网络中的所有容器都可以在宿主机上相互通信。从网络角度来看,这等于在没有容器的主机上运行的多个进程。因为它们使用相同的主机环境,所以没有两个容器能够绑定到相同的TCP端口,如果在同一主机上调度了多个容器,则可能出现端口冲突的情况。
1 | #Create containers on the host network |
可见,C1和宿主机的公用一套网络环境,所以eth0网卡的IP均为172.31.21.213
。在host网络下,容器网络环境和宿主机网络没有任何隔离性,容器和容器之间,容器和宿主机之间的网络通信不存在任何障碍。
Docker Bridge Network Driver
容器创建时倘若没有特别指定其他网络驱动,则默认使用的是Bridge 桥接网络驱动。而在docker中,bridge分为系统默认和用户自定义的两种;
Default Docker Bridge Network
可以看到上面执行执行docker network ls 时,第一行展示的network
就是一个名叫Bridge的Bridge网络驱动。
比如启动一个名叫bb-default-bridge的容器,在容器内部,其IP为172.17.0.2
1 | ➜ ~ docker run --rm -it --name bb-default-bridge busybox sh |
那如何反映,该容器使用的是默认的Bridge网络驱动呢,docker提供了docker network inspect命令展现使用该网络驱动下的所有容器。
1 | ➜ ~ docker network inspect bridge |
可见,你同时可以发现,在Containers.IPv4Address的内容和容器内获取到的IP是一致的,且com.docker.network.bridge.name所指是网桥名称为docker0。
实验宿主机的IP为192.168.1.10,那在bb-default-bridge中能不能访问到宿主机呢
1 | / # ping 192.168.1.105 |
结果发现是可以的,反过来宿主机ping容器则ping不通(是不是有点类似Windows中的NAT网络类型)。如果宿主机需要访问容器时,可以在创建容器时使用 -p 参数将宿主机端口和容器端口映射。
假如再创建一个bb-default-bridge-1,依旧使用的是默认的Bridge网络驱动。
1 | "Containers": { |
会发现,两者均可以互相ping通。也就说,在同一个宿主机的不同容器之间,使用默认Bridge network driver 是可以通过IP相互访问对方的。
User-Defined Bridge Networks
用户自定的Bridge网络驱动和默认的Bridge网络驱动类似,那么如何创建一个用户自定义的Bridge网络驱动呢。最简单的方法就是docker network create 语句,更多用法可以参照docker network Command-line reference。
比如创建一个叫lazyallen的User-Defined Bridge Network,其中-d是drive的缩写,且是可选项,若不指定默认创建的是Bridge类型的网络驱动。
1 | ➜ ~ docker network create -d bridge lazyallen |
可见,的确出现了一个叫lazyallen的Bridge Network。和默认的Bridge网络驱动相比,在用法上的差别就是 User-Defined Bridge Networks 提供了容器间自动的DNS解析。
什么意思呢?我们同样创建两个容器,让他们都使用lazyallen Bridge Driver,使用 –network
可指定network driver
1 | docker run --rm -it --name bb-user-defined-bridge-0 --network lazyallen busybox sh |
容器在网络通信时,会将容器名直接DNS解析为目标容器IP。
1 | PING bb-user-defined-bridge-0 (172.18.0.2): 56 data bytes |
这样最大的好处就是,容器之间不用再使用IP作为hardcode嵌入到配置中,只要使用容器名称作为引用即可,就算因为网络环境导致容器的IP动态变化,也不用再修改配置。
同样,下面这张图片就可以很好解释Bridge network driver
回过头解释
1 | docker network create simple-network |
通过以上解释,这里就显而易见地做了3件事
- 创建了一个叫simple-network的自定义Bridge 网络驱动
- 创建两个容器时均指定simple-network网络驱动
- 后端应用的配置文件将MySql的地址使用xxl-mysql引用
参考链接
- Use bridge networks
- Docker Swarm Reference Architecture: Exploring Scalable, Portable Docker Container Networks
- Docker Network—Bridge 模式
[^cnkirito_footnote]: 引用自文章「Docker Network—Bridge 模式」的翻译