本文最后更新于 319 天前,其中的信息可能已经过时,如有错误请发送邮件到wuxianglongblog@163.com
docker管理容器之间的互联
一.跨容器通讯概述
1.多容器互联概述
在学习本章节之间,我们所学习的容器都有一个特点,就是所有的程序都被我们封装在同一个容器了,我们时常说"all-in-one"也不为过哈。
但在实际工作中,难免会遇到多容器相互通信的场景,比如单节点多容器通信,跨主机的多容器通信等。这又该如何是好呢?接下来我们就一起来探究一下多容器互联的案例。
2.容器通信面临哪些难题
如下图所示,容器在通信时,并不能固定IP地址,当我们重新启动新容器时,不难发现,IP地址会发生改变哟~
3.跨主机通信案例
如下图所示,启动容器时,我们可以使用"--link"选项来关联指定的容器,本质上是将关联容器的名称的机器IP地址添加到容器的"/etc/hosts"解析中。
使用"--link"有一个美中不足的情况,就是要求被关联的容器必须处于运行状态!否则就会抛出"Cannot link to a non running container"异常。
值得一提的是,"--link"参数只是单向的配置,本质上是你在某个容器的"/etc/hosts"添加了相应的解析,对其它容器的"/etc/hosts"并没有任何影响哟~
温馨提示:
关于"--link"的使用了解即可,如果有别人这样写你要明白啥意思,我们在生产环境中建议大家为各个服务使用自定义网络。
二.docker单机容器网络通信概述:star:
1.docker五种基础网络类型
bridge:
默认类型,桥接到宿主机docker0的网络,类似于VM的Nat模式。
host:
host类型,使用宿主机网络,网络性能最高。
container:
容器类型,使用其它容器的共享网络,这在K8S中频繁使用。比如同一个Pod中可以有多个容器共存。
none:
没有网络,该容器不能上外网。
network:
自定义网络,我们可以使用docker network create创建自定义网络。
温馨提示:
(1)从严格意义上来说docker就只有3种基础网络类型,分别为bridge,host和none;
(2)其中container和network本质上都是基于上述3种基础网络类型哟;
2.创建brigde网络类型的容器
[root@docker201.oldboyedu.com ~]# docker container run --network bridge -it -d alpine:latest
d3191721357a44e8898f0ab7f5605dfb7a6cd71c1ee6f4a4311fe46f5b212fa1
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d3191721357a alpine:latest "/bin/sh" 5 seconds ago Up 4 seconds condescending_chatterjee
fa9573e66949 alpine:latest "/bin/sh" About a minute ago Up About a minute loving_poincare
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container inspect --format='{{.NetworkSettings.Networks}}' condescending_chatterjee
map[bridge:0xc00015c000]
[root@docker201.oldboyedu.com ~]#
温馨提示:
如下图所示,创建容器时,如果创建容器时不显式指定网络,则默认的网络类型为bridge。
3.创建host网络类型的容器
[root@docker201.oldboyedu.com ~]# docker container run --network host -it -d alpine:latest
1102bf99d3356ab9bb2068a7ce6878883a4022eda87bf42799f3d251275c4f66
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1102bf99d335 alpine:latest "/bin/sh" 3 seconds ago Up 2 seconds jolly_neumann
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container inspect --format='{{.NetworkSettings.Networks}}' jolly_neumann
map[host:0xc00015c000]
[root@docker201.oldboyedu.com ~]#
温馨提示:
由于容器和宿主机使用相同的网络,那会存在当容器占用了特定的网络端口时,宿主机将无法在再次占用该端口哟~比如我们可以运行两个nginx镜像用来测试。
4.创建none网络类型的容器
[root@docker201.oldboyedu.com ~]# docker container run --network none -it -d alpine:latest
818507229a523c69a9c6d1f4a8bb4b791ed56eec9aff03e7ee457dc664bcad49
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
818507229a52 alpine:latest "/bin/sh" 2 seconds ago Up 1 second nifty_lovelace
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container inspect --format='{{.NetworkSettings.Networks}}' nifty_lovelace
map[none:0xc000574000]
[root@docker201.oldboyedu.com ~]#
温馨提示:
生产环境中,我们很少会遇到容器的网络类型为"none",除非专门给客户用来测试的环境,该容器仅能做一些基础性的实验,因为该容器无法联网。
5.创建container网络类型的容器
[root@docker201.oldboyedu.com ~]# docker container run --network container:oldboyedu_kod -d -it alpine:latest
e6341f990e6f99da3da16c9377312968a5b97415de86cc2cc86301707b327753
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e6341f990e6f alpine:latest "/bin/sh" 3 seconds ago Up 3 seconds cranky_nobel
e0b18ced7796 oldboyedu_kod:v3 "/bin/bash /start_in…" 38 seconds ago Up 37 seconds 22/tcp, 80/tcp oldboyedu_kod
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container inspect --format='{{.NetworkSettings.Networks}}' cranky_nobel
map[]
[root@docker201.oldboyedu.com ~]#
温馨提示:
这种网络容器在Kubernetes中频繁使用,还记得同一个Pod资源是可以容纳多个容器吗?这些容器在同一个Pod资源共享的是相同的网络类型。
6.创建自定义网络
[root@docker201.oldboyedu.com ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
16a2b2b8a5b9 bridge bridge local
6f73cd7e0326 host host local
da59e2f161a4 none null local
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker network create -d bridge --subnet 172.28.0.0/16 --gateway 172.28.0.254 jasonyin2020
696e38de3b8ef925813497ebeb216999b7bc0f6a770cd30ca47e601e5b842cd5
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
16a2b2b8a5b9 bridge bridge local
6f73cd7e0326 host host local
696e38de3b8e jasonyin2020 bridge local
da59e2f161a4 none null local
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container run -it -d --name oldboyedu_kod --network jasonyin2020 -p 80:80 oldboyedu_kod:v3
bb74bf2310d664a98294f0cee846ce1df08992db89ce5e58d5acf061cd774b51
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# docker container run -it --network jasonyin2020 oldboyedu_linux:v1
温馨提示:
(1)如下图所示,我们创建容器时,可以使用"--network"选项为该容器指定所述网络类型;
(2)关于创建容器时的相关参数说明,请参考"docker network create --help";
7.自定义网络分配IP地址案例
(1)创建自定义网络
docker network create -d bridge --gateway 172.26.1.254 --subnet 172.26.1.0/24 --ip-range 172.26.1.100/28 oldboyedu2021
(2)使用自定义网络
docker container run -itd --network oldboyedu2021 --rm alpine
温馨提示:
有关子网掩码计算可以借助网上的工具即可,如下图所示.
三.docker单机容器网络通信案例-基于专用的网络实现zabbix的容器间通信
1.zabbix架构图
如下图所示,是一个简单的zabbix架构图,此处我并没有考虑zabbix-proxy角色。
基于docker环境部署最新版本的zabbix:
https://www.zabbix.com/documentation/current/manual/installation/containers
2.创建专用于Zabbix组件容器的网络
docker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 zabbix-net
3.启动空的MySQL服务器实例(该步骤有个小坑,请参考提示第三点)
docker run --name mysql-server -t \
-e MYSQL_DATABASE="zabbix" \
-e MYSQL_USER="zabbix" \
-e MYSQL_PASSWORD="zabbix_pwd" \
-e MYSQL_ROOT_PASSWORD="root_pwd" \
--network=zabbix-net \
-d mysql:8.0 \
--restart unless-stopped \
--character-set-server=utf8 --collation-server=utf8_bin \
--default-authentication-plugin=mysql_native_password
温馨提示:(以上代码摘自官方,以下注解为我个人见解)
(1)使用相关的参数:
"-e":
是"--env"的简称,表示启动容器时传入一个环境变量;
"--network":
表示使用的网络组件为"zabbix-net";
"-t":
为容器单独分配一个终端。
"-d":
表示后台运行。
"--restart":
当容器退出时要应用的重新启动策略,默认值为"no"。
如果设置为"unless-stopped",表示服务停止会自动触发重启操作。
"--character-set-server":
指定服务端的字符集。
"--default-authentication-plugin":
指定默认的认证插件,注意MySQL5.7和MySQL8.0默认插件有所不同哟。
(2)如果本地没有镜像,则自动会去官网下载镜像:
如下图所示,没有镜像会有长时间的下载镜像过程,因此建议大家使用我准备好的镜像。
(3)实际运行MySQL实例时请删除"--restart"选项,因为会导致容器无法启动。
4.启动Zabbix Java网关实例
docker run --name zabbix-java-gateway -t \
--network=zabbix-net \
--restart unless-stopped \
-d zabbix/zabbix-java-gateway:alpine-5.4-latest
温馨提示:
如下图所示,没有镜像会有长时间的下载镜像过程,因此建议大家使用我准备好的镜像。
5.启动Zabbix服务器实例并将该实例与创建的MySQL服务器实例链接
docker run --name zabbix-server-mysql -t \
-e DB_SERVER_HOST="mysql-server" \
-e MYSQL_DATABASE="zabbix" \
-e MYSQL_USER="zabbix" \
-e MYSQL_PASSWORD="zabbix_pwd" \
-e MYSQL_ROOT_PASSWORD="root_pwd" \
-e ZBX_JAVAGATEWAY="zabbix-java-gateway" \
--network=zabbix-net \
-p 10051:10051 \
--restart unless-stopped \
-d zabbix/zabbix-server-mysql:alpine-5.4-latest
6.启动 Zabbix web 界面并将实例链接到创建的 MySQL 服务器和 Zabbix 服务器实例
docker run --name zabbix-web-nginx-mysql -t \
-e ZBX_SERVER_HOST="zabbix-server-mysql" \
-e DB_SERVER_HOST="mysql-server" \
-e MYSQL_DATABASE="zabbix" \
-e MYSQL_USER="zabbix" \
-e MYSQL_PASSWORD="zabbix_pwd" \
-e MYSQL_ROOT_PASSWORD="root_pwd" \
--network=zabbix-net \
-p 80:8080 \
--restart unless-stopped \
-d zabbix/zabbix-web-nginx-mysql:alpine-5.4-latest
7.访问zabbix的登录页面
如下图所示,访问宿主机的80端口,并输入zabbix-web的默认用户名("Admin")和密码("zabbix")进行登录即可。
8.zabbix页面登录成功
如下图所示,就是咱们自信版本的zabbix版本哟~
9.补充内容(了解即可)
Docker容器的重启(--restart)策略如下:
no:
默认策略,在容器退出时不重启容器
on-failure:
在容器非正常退出时(退出状态非0),才会重启容器
on-failure:3:
在容器非正常退出时重启容器,最多重启3次
always:
在容器退出时总是重启容器
unless-stopped:
在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器.
温馨提示:
(1)手动使用"docker container stop|kill"指令始终不会重启容器;
(2)但是手动在宿主机kill相关的容器经常的确会重启,或者是重启操作系统也会重启哟.
推荐阅读:
https://docs.docker.com/engine/reference/run/#restart-policies---restart
四.小规模跨宿主机容器网络通信案例-macvlan
1.相同macvlan网络之间的通信原理
macvlan是Linux kernel支持的新特性,支持的版本有v3.9-3.19和 4.0+,比较稳定的版本推荐 4.0+。
如下图所示,它一般是以内核模块的形式存在,我们可以通过以下方法判断当前系统是否支持。
如果第一个命令报错,或者第二个命令没有返回,说明当前系统不支持 macvlan,需要升级内核。
根据macvlan子接口之间的通信模式,macvlan有四种网络模式,分别为private,vepa,bridge,passthru。如果这些网络感兴趣的小伙伴可自行查阅相关资料,但我并不太建议大家在这里花费太多时间。
在Docker中,macvlan只是众多Docker网络模型中的一种,并且是一种跨主机的网络模型,作为一种驱动(driver)启用(使用"-d"参数指定),可惜呀,Docker macvlan只支持bridge模式。
相同macvlan网络之间的通信如下图所示。
2.不同 macvlan 网络之间的通信
由于macvlan网络会独占物理网卡,也就是说一张物理网卡只能创建一个macvlan网络,如果我们想创建多个macvlan网络就得用多张网卡,但主机的物理网卡是有限的,怎么办呢?
好在macvlan网络也是支持VLAN子接口的,所以,我们可以通过VLAN技术将一个网口划分出多个子网口,这样就可以基于子网口来创建 macvlan网络了。感兴趣的小伙伴可自行查阅相关资料。
3.自定义macvlan网络
(1)在docker201.oldboyedu.com节点创建macvlan网络
docker network create -d macvlan --subnet=172.29.0.0/16 --gateway=172.29.0.254 -o parent=eth0 oldboyedu_macvlan
(2)在docker202.yinzhengjie.com节点创建macvlan网络
docker network create -d macvlan --subnet=172.29.0.0/16 --gateway=172.29.0.254 -o parent=ens33 oldboyedu_macvlan
4.基于自定义的macvlan网络创建容器
(1)在docker201.oldboyedu.com节点创建容器时手动分配IP地址并指定自定义的macvlan网络
docker container run -it -d --name oldboyedu_kod2021 --network oldboyedu_macvlan --ip 172.29.0.201 oldboyedu_kod:v3
(2)在docker202.yinzhengjie.com节点创建容器时手动分配IP地址并指定自定义的macvlan网络
docker container run -it --name oldboyedu_linux2021 --network oldboyedu_macvlan --ip 172.29.0.202 jasonyin2020/oldboyedu_linux:v1
5.macvlan网络的优缺点
优点:
(1)docker原生支持,无需安装额外插件,配置起来相对简单。
(2)适合小规模docker环境,例如只有1-3台,如果服务器过多,手动分配IP地址可能会无形之间增加工作量;
缺点:
(1)需要手动分配IP地址,如果让其自动分配IP地址可能会存在多个主机自动分配的IP地址冲突的情况,到时候还需要人工介入维护;
(2)本机相同网络(本案例为"oldboyedu_macvlan")的容器之间相互通信没问题,跨主机之间的容器进行通信也没问题,但容器无法与宿主机之间进行通信,也无法连接到外网;
五.大规模跨宿主机容器网络通信案例-overlay
1.什么是overlay网络
overlay翻译为中文名称为"重叠网络",底层是基于vxlan技术实现的。
2.什么是vlan
3.什么是vxlan
4.部署consul键值对数据库
简而言之,consul是类似于zookeeper,etcd一样的键值对数据库,我们用它来存储已分配的IP地址。如下图所示,我们成功的部署了2台docker环境指向了同一个consul服务。
(1)加载consul进行
[root@harbor250.oldboyedu.com ~]# docker image pull consul:latest
(2)运行容器
[root@harbor250.oldboyedu.com ~]# docker container run -it -d -p 10.0.0.101:8500:8500/tcp --restart=always consul:latest
(3)修改所有客户端的配置文件(我这里仅演示了一台,你可以根据自己的情况做相关的微调即可)
[root@docker201.oldboyedu.com ~]# vim /etc/docker/daemon.json
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# cat /etc/docker/daemon.json
{
"registry-mirrors": ["https://tuv7rqqq.mirror.aliyuncs.com"],
# "insecure-registries": ["docker201.oldboyedu.com:5000"],
# "data-root":"/oldboyedu/docker",
"cluster-store": "consul://10.0.0.101:8500",
"cluster-advertise": "10.0.0.101:6666"
}
[root@docker201.oldboyedu.com ~]#
[root@docker201.oldboyedu.com ~]# systemctl restart docker
[root@docker201.oldboyedu.com ~]#
参数说明:
data-root:
指定docker的数据存储目录。
cluster-store:
配置的是nginx监听的地址。
cluster-advertise:
指定的是docker的宿主机注册到consul中的指定网卡中的ip,也可以写ip和端口号,端口只要没被占用就可以了。
5.在任意客户端节点创建overlay类型网络(只需要在一个节点操作即可,其他节点会通过consul服务进行同步)
docker network create -d overlay --subnet 172.30.0.0/16 --gateway 172.30.0.254 oldboyedu_overlay
温馨提示:
如下图所示,我们只需要在任意一个节点只需该命令即可,无需在所有节点上执行,因为凡是注册在consul服务中的所有docker实例均为同一个集群,会有它自动帮咱们同步网络哟~
6.各节点基于自定义的overlay类型网络创建容器
(1)在docker202.yinzhengjie.com节点创建容器:
docker container run -it -d --name linux01 --network oldboyedu_ol01 oldboyedu_linux:v1
(2)在docker201.oldboyedu.com节点创建容器:
docker container run -it -d --name linux02 --network oldboyedu_ol01 alpine:latest
温馨提示:
(1)如下图所示,跨主机的两个容器是可以相互ping通啦~
(2)
7.overlay组件的工作原理
如上图所示,不难发现容器内存在两块网卡,分别对应eth0和eth1,我们的跨主机容器之间不仅仅能够实现互通,各个容器还能实现外网的访问。
如下图所示,为overlay网络组件的工作原理。假设linux01容器发起ping请求,目标IP为linux02的地址172.29.0.2。其工作流程如下所示:
(1)该请求的流量通过连接到网桥(Bridge)交换机的veth5855590接口发出。虚拟交换机并不知道将包发送到哪里,因为在虚拟交换机的MAC地址映射表(ARP 映射表)中并没有与当前目的IP对应的MAC地址;
(2)所以虚拟网桥(Bridge)交换机会将该包发送到其上的全部端口。连接到网桥(Bridge)交换机的VTEP接口知道如何转发这个数据帧,所以会将自己的MAC地址返回。这就是一个代理ARP响应,并且虚拟网桥(Bridge)交换机根据返回结果学会了如何转发该包。接下来虚拟交换机会更新自己的ARP映射表,将172.29.0.2映射到本地VTEP的 MAC地址上。
(3)现在虚拟网桥(Bridge)交换机已经学会如何转发目标为linux02的流量,接下来所有发送到linux02的包都会被直接转发到VTEP接口。
(4)交换机会将包转发到VTEP接口,VTEP完成数据帧的封装,这样就能在底层网络传输。具体来说,封装操作就是把VXLAN Header信息添加以太帧当中。
(5)VXLAN Header信息包含了VXLAN网络ID(VNID),其作用是记录 VLAN到VXLAN的映射关系。每个VLAN都对应一个VNID,以便包可以在解析后被转发到正确的VLAN。
(6)封装的时候会将数据帧放到UDP包中,并设置UDP的目的IP 字段为docker202.yinzhengjie.com节点的VTEP的IP地址,同时设置 UDP Socket端口为4789。这种封装方式保证了底层网络即使不知道任何关于VXLAN的信息,也可以完成数据传输。
(7)当包到达docker202.yinzhengjie.com之后,内核发现目的端口为UDP端口4789,同时还知道存在VTEP接口绑定到该 Socket。所以内核将包发给VTEP,由VTEP读取VNID,解压包信息,并根据 VNID发送到本地网桥(Bridge)交换机的连接到VLAN的交换机。在该交换机上,包被发送给容器linux02。
以上大体介绍了 Docker 覆盖网络是如何利用 VXLAN 技术的。
参考连接:
http://c.biancheng.net/view/3198.html
六.课堂练习
(1)自定义docker网络,要求网段为172.30.1.0/24,网关为172.30.1.254,网络名称为oldboyedu-linux77
(2)基于上一步的网络创建容器,容器名称为"oldboyedu-network"
(3)自定义docker网络,要求网段为172.31.100.0/24,网关为172.31.100.254,网络名称为oldboyedu-linux78
(4)将"oldboyedu-network"容器添加到oldboyedu-linux78网络,并查看其网络信息,截图;
(5)删除自定义网络;
七.可能会遇到的故障
1.Cannot link to a non running container
故障原因:
如下图所示,说明该容器未正常运行,因此不会输出。
解决方案:
将需要使用"--link"的容器手动启动起来。
2.Pool overlaps with other one on this address space
故障原因:
创建网络时,指定的地址池网段与已有的网络地址池冲突。
解决方案:
修改地址池网段,避免网段冲突即可。
3.network with name oldboyedu_macvlan already exists
故障原因:
创建的网络名称已存在。
解决方案:
修改网络的名称即可,避免网络名称冲突。
4. Error response from daemon: endpoint with name linux01 already exists in network oldboyedu_ol01.
故障原因:
在同一个overlay中,创建了想用容器名称。
解决方案:
修改容器名称重新创建即可。