Docker网络讲解 及实验redis集群部署

理解docker0

  • 准备工作:清空所有的容器,清空所有的镜像
docker rm -f $(docker ps -a -q)	# 删除所有容器
docker rmi -f $(docker images -qa)	# 删除全部镜像

Docker的网络也是十分重要的一个点

查看本地IP

ip a

# 有三个网络:
lo	127.0.0.1	# 本机回环地址
eth0	172.17.90.138	# 阿里云的私有IP
docker0	172.18.0.1	# docker网桥
# 问题:Docker 是如何处理容器网络访问的?

测试

docker会给每个容器都分配一个ip,且容器和容器之间是可以互相访问的

# 启动tomcat01
[root@zheng ~]# docker run -d -P --name tomcat01 tomcat

# 查看tomcat01的ip地址,docker会给每个容器都分配一个ip!
[root@zheng ~]# docker exec -it tomcat01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group
default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
122: eth0@if123: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
state UP group default
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever

# linux服务器是否可以ping通容器内的tomcat ?
[root@zheng ~]# ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.070 ms 
# 可以ping通!

原理

  • 1,每一个安装了Docker的linux主机都有一个docker0的虚拟网卡。这是个桥接网卡,使用了veth-pair技术!
    下面总结有理解veth-pair技术
# 我们再次查看主机的 ip addr 
[root@zheng ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:30:27:f4 brd ff:ff:ff:ff:ff:ff
inet 172.17.90.138/20 brd 172.17.95.255 scope global dynamic eth0 valid_lft 310954997sec preferred_lft 310954997sec
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:bb:71:07:06 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0 valid_lft forever preferred_lft forever
123: vethc8584ea@if122: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
noqueue master docker0 state UP group default
link/ether 0a:4b:bb:40:78:a7 brd ff:ff:ff:ff:ff:ff link-netnsid 0

# 发现:本来我们有三个网络,我们在启动了个tomcat容器之后,多了一个!123的网络!
  • 2,每启动一个容器,linux主机就会多了一个虚拟网卡
# 我们启动了一个tomcat01,主机的ip地址多了一个	123: vethc8584ea@if122
# 然后我们在tomcat01容器中查看容器的ip是	122: eth0@if123

# 我们再启动一个tomcat02观察
[root@zheng ~]# docker run -d -P --name tomcat02 tomcat

# 然后发现linux主机上又多了一个网卡	125: veth021eeea@if124:
# 我们看下tomcat02的容器内ip地址是	124: eth0@if125
[root@zheng ~]# docker exec -it tomcat02 ip addr

# 观察现象:
# tomcat --- linux主机 vethc8584ea@if122 ---- 容器内 eth0@if123
# tomcat --- linux主机 veth021eeea@if124 ---- 容器内 eth0@if125
# 相信到了这里,大家应该能看出点小猫腻了吧!只要启动一个容器,就有一对网卡

# veth-pair 就是一对的虚拟设备接口,它都是成对出现的。一端连着协议栈,一端彼此相连着。
# 正因为有这个特性,它常常充当着一个桥梁,连接着各种虚拟网络设备!
# “Bridge、OVS 之间的连接”,“Docker 容器之间的连接” 等等,以此构建出非常复杂的虚拟网络结构,比如 OpenStack Neutron。
  • 3,测试下tomcat01和tomcat02容器间是否可以互相ping通
[root@zheng ~]# docker exec -it tomcat02 ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.110 ms

# 结论:容器和容器之间是可以互相访问的。
  • 4,网络模型图

    结论:tomcat1和tomcat2共用一个路由器。是的,他们使用的一个,就是docker0。任何一个容器启动默认都是docker0网络。
    docker默认会给容器分配一个可用ip。

总结

Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容
器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,
这样容器之间就能够通过容器的Container-IP直接通信。

Docker容器网络就很好的利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth-pair);
Docker中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,
无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。

容器互联--Link

思考一个场景,我们编写一个微服务,数据库连接地址原来是使用ip的,如果ip变化就不行了,那我们能不能使用服务名访问呢?
jdbc:mysql://mysql:3306,这样的话哪怕mysql重启,我们也不需要修改配置了!docker提供了 --link 的操作!

# 我们使用tomcat02,直接通过容器名ping tomcat01,不使用ip
[root@zheng ~]# docker exec -it tomcat02 ping tomcat01
ping: tomcat01: Name or service not known 
# 发现ping不通

# 我们再启动一个tomcat03,但是启动的时候连接tomcat02
[root@zheng ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
a3a4a17a2b707766ad4f2bb967ce1d94f658cd4cccef3bb8707395cdc71fa1e7

# 这个时候,我们就可以使用tomcat03 ping通tomcat02 了
[root@zheng ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.18.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.18.0.3): icmp_seq=1 ttl=64 time=0.093 ms
64 bytes from tomcat02 (172.18.0.3): icmp_seq=2 ttl=64 time=0.066 ms

# 再来测试,tomcat03 是否可以ping tomcat01	失败
[root@zheng ~]# docker exec -it tomcat03 ping tomcat01
ping: tomcat01: Name or service not known

# 再来测试,tomcat02 是否可以ping tomcat03	发现也ping不通
[root@zheng ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known
  • 思考,这个原理是什么呢?我们进入tomcat03中查看下host配置文件
[root@zheng ~]# docker exec -it tomcat03 cat /etc/hosts
127.0.0.1	localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.18.0.3 tomcat02 b80da266a3ad # 发现tomcat2直接被写在这里
172.18.0.4 a3a4a17a2b70

# 所以这里其实就是配置了一个 hosts 地址而已!
# 原因:--link的时候,直接把需要link的主机的域名和ip直接配置到了hosts文件中了。

--link早都过时了,我们不推荐使用!我们可以使用自定义网络的方式

自定义网络

基本命令查看

docker network --help

  • 查看所有网络
[root@zheng ~]#	docker network ls		
NETWORK ID	NAME	DRIVER	SCOPE
4eb2182ac4b2	bridge	bridge	local
ae2b6209c2ab	host	host	local
c037f7ec7e57	none	null	local
  • 网络驱动及选用原则
#1、网络驱动
Docker 网络子系统使用可插拔的驱动,默认情况下有多个驱动程序,并提供核心联网功能。

(1)bridge:桥接网络,这是默认的网络驱动程序(不指定驱动程序创建的容器默认是bridge驱动)。
(2)host:主机网络。消除容器和主机的网络隔离,直接使用主机的网络。
(3)overlay:覆盖网络。可以将多个Docker守护进程连接,实现跨主机容器通讯(swarm集群)。
(4)macvlan:将MAC地址分配给容器,使容器作为网络上的物理设备。不通过Docker主机网络栈进行路由,直接通过MAC地址路由到容器。
(5)none:表示关闭容器的所有网络连接。常与自定义网络驱动一起使用,不适用于swarm。
(6)网络插件:可以通过Docker安装和使用第三方网络插件。

#2、Docker 网络驱动选用原则
(1)bridge桥接网络:最适合用于同一个Docker主机上运行的多个容器之间需要通信的场景。(单主机)
(2)host主机网络:最适用于当网络栈不能与Docker主机隔离,而容器的其他方面需要被隔离的场景。(解除容器和主机隔离)
(3)overlay网络:适用于不同Docker主机上运行的容器需要通信的场景,或者多个应用程序通过Swarm集群服务一起工作的场景。(多主机、集群)
(4)macvlan网络:适用于从虚拟机迁移过来的场景,或者容器需要像网络上的物理机一样,拥有独立MAC地址的场景。(容器需要mac)
(5)第三方网络插件适用于将Docker与专用网络栈进行集成的场景。(订制化)
  • 所有网络模式

  • 查看一个具体的网络的详细信息
#创建一个网络
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
--driver bridge #桥接模式
--subnet 192.168.0.0/16 #子网地址 
--gateway 192.168.0.1 #网关

#查看创建的网络
docker network ls

#查看详细信息
docker network inspect mynet(或ID)

自定义网卡

1、删除原来的所有容器

[root@zheng ~]# docker rm -f $(docker ps -aq)

# 恢复到了最开始的样子
[root@zheng ~]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group
default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:30:27:f4 brd ff:ff:ff:ff:ff:ff
inet 172.17.90.138/20 brd 172.17.95.255 scope global dynamic eth0 valid_lft 310951436sec preferred_lft 310951436sec
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:bb:71:07:06 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/16 brd 192.168.255.255 scope global docker0valid_lft forever preferred_lft forever

2、创建容器,但是默认创建的容器都是docker0网卡的

# 默认我们不配置网络,也就相当于默认值 --net bridge 使用的docker0
docker run -d -P --name tomcat01 --net bridge tomcat

# docker0网络的特点
1.它是默认的
2.域名访问不通
3.--link 域名通了,但是删了又不行

3、可以在容器创建的时候使用自定义网络

# 自定义创建的默认default "bridge"
# 自定义创建一个网络网络
[root@zheng ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
09bd09d8d3a6b33e6d19f49643dab551e5a45818baf4d5328aa7320c6dcfc236

# 查看确认下
[root@zheng ~]# docker network ls
NETWORK ID	NAME	DRIVER	SCOPE
4eb2182ac4b2	bridge	bridge	local
ae2b6209c2ab	host	host	local
09bd09d8d3a6	mynet	bridge	local
c037f7ec7e57	none	null	local
[root@zheng ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id":
"09bd09d8d3a6b33e6d19f49643dab551e5a45818baf4d5328aa7320c6dcfc236",
"Created": "2020-05-13T13:29:33.568644836+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",	
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

# 我们来启动两个容器测试,使用自己的 mynet!
[root@zheng ~]# docker run -d -P --name tomcat-net-01 --net mynet tomcat
065f82e947c760c63539ab4c0de0d683787ec7ac6d0dcaa71f64e191319f9fe7

[root@zheng ~]# docker run -d -P --name tomcat-net-02 --net mynet tomcat
2e85d71afe87c87166786b0bbae2d90eefb969d716fcd78a21173add5956cb12

[root@zheng ~]# docker ps
CONTAINER ID	IMAGE	PORTS	NAMES
2e85d71afe87	tomcat	0.0.0.0:32772->8080/tcp	tomcat-net-02
065f82e947c7	tomcat	0.0.0.0:32771->8080/tcp	tomcat-net-01

# 再来查看下
[root@zheng ~]# docker network inspect mynet
[
{
"Name": "mynet",
"Id":
"09bd09d8d3a6b33e6d19f49643dab551e5a45818baf4d5328aa7320c6dcfc236",
............
"Containers": {

"065f82e947c760c63539ab4c0de0d683787ec7ac6d0dcaa71f64e191319f9fe7": {
"Name": "tomcat-net-01",
"EndpointID":
"d61cef1bc294d7f10fb6d9b728735fc87bed79e4e02f5298374f0fab3e9b2da6",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},

"2e85d71afe87c87166786b0bbae2d90eefb969d716fcd78a21173add5956cb12": {
"Name": "tomcat-net-02",
"EndpointID":
"adbc37a20526c2985c3589382998a3d106ef722662c7b296a57d8a7c8f449f38",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

# 我们来测试ping容器名和ip试试,都可以ping通
[root@zheng ~]# docker exec -it tomcat-net-01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.093 ms

[root@zheng ~]# docker exec -it tomcat-net-01 ping tomcat-net-02
PING tomcat-net-02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=1 ttl=64
time=0.063 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64
time=0.066 ms

# 发现,我们自定义的网络docker都已经帮我们维护好了对应的关系
# 所以我们平时都可以这样使用网络,不使用--link效果一样,所有东西实时维护好,直接域名 ping通

好处:
redis 不同的集群使用不同的网络,保证集群是安全和健康的
mysql 不同的集群使用不同的网络,保证集群是安全和健康的
互相隔离的

网络连通


docker0和自定义网络肯定不通,我们使用自定义网络的好处就是网络隔离:

大家公司项目部署的业务都非常多,假设我们有一个商城,我们会有订单业务(操作不同数据),会有订单业务购物车业务(操作不同缓存)。如果在一个网络下,有的程序猿的恶意代码就不能防止了,所以我们就在部署的时候网络隔离,创建两个桥接网卡,比如订单业务(里面的数据库,redis,mq,全部业务都在order-net网络下)其他业务在其他网络。
那关键的问题来了,如何让 tomcat-net-01 访问 tomcat1?

# 启动默认的容器,在docker0网络下
[root@zheng ~]# docker run -d -P --name tomcat01 tomcat
bcd122e0dcf6bf8c861eaa934911f98a5497a4954f3fde9575e496160bd23287

[root@zheng ~]# docker run -d -P --name tomcat02 tomcat
6183aaeca003a3e5a3549a37f9c1040551320ae358807b4aaad547a986afb887

# 查看当前的容器
[root@zheng ~]# docker ps
CONTAINER ID	IMAGE	PORTS	NAMES
6183aaeca003	tomcat	0.0.0.0:32774->8080/tcp	tomcat02
bcd122e0dcf6	tomcat	0.0.0.0:32773->8080/tcp	tomcat01
2e85d71afe87	tomcat	0.0.0.0:32772->8080/tcp	tomcat-net-02
065f82e947c7	tomcat	0.0.0.0:32771->8080/tcp	tomcat-net-01

# 我们来查看下network帮助,发现一个命令 connect
[root@zheng ~]# docker network --help
Commands:
connect	Connect a container to a network 	# 连接一个容器到一个网络
create	Create a network
disconnect Disconnect a container from a network
inspect	Display detailed information on one or more networks
ls	List networks
prune	Remove all unused networks
rm	Remove one or more networks

# 我们来测试一下!打通mynet-docker0
# 命令 docker network connect [OPTIONS] NETWORK CONTAINER

[root@zheng ~]# docker network connect mynet tomcat01
[root@zheng ~]# docker network inspect mynet
[
{
......
"Containers": {

"065f82e947c760c63539ab4c0de0d683787ec7ac6d0dcaa71f64e191319f9fe7": {
"Name": "tomcat-net-01",
"EndpointID":
"d61cef1bc294d7f10fb6d9b728735fc87bed79e4e02f5298374f0fab3e9b2da6",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},

"2e85d71afe87c87166786b0bbae2d90eefb969d716fcd78a21173add5956cb12": {
"Name": "tomcat-net-02",
"EndpointID":
"adbc37a20526c2985c3589382998a3d106ef722662c7b296a57d8a7c8f449f38",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
// 发现我们的tomcat01就进来这里了,tomcat01拥有了双ip

"bcd122e0dcf6bf8c861eaa934911f98a5497a4954f3fde9575e496160bd23287": {
"Name": "tomcat01",
"EndpointID":
"b2bf2342948e17048d872a4d5603c77e90d0e032439d510e86c10a1acc3928d9",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
			}
		},
		......
	}
}

# tomcat01 可以ping通了
[root@zheng ~]# docker exec -it tomcat01 ping tomcat-net-01 PING tomcat-net-01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=1 ttl=64 time=0.071 ms
64 bytes from tomcat-net-01.mynet (192.168.0.2): icmp_seq=2 ttl=64 time=0.067 ms

# tomcat02 依旧ping不通
[root@zheng ~]# docker exec -it tomcat02 ping tomcat-net-01 ping: tomcat-net-01: Name or service not known

结论:如果要跨网络操作别人,就需要使用docker network connect [OPTIONS] NETWORK CONTAINER连接

实战:部署一个Redis集群

# 创建网卡
docker network create redis --subnet 172.38.0.0/16
docker network ls 即可看到redis

# 通过脚本创建六个redis配置(直接复制进命令行)
for port in $(seq 1 6); \	#for循环循环六次
do \
mkdir -p /mydata/redis/node-${port}/conf	#创建redis的一些配置文件
touch /mydata/redis/node-${port}/conf/redis.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}	#连接具体IP
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done

cd /mydata/redis/
ls 即可看到六个节点
cat node-1/conf 可看见配置文件内容,即配置成功

#启动示例
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; \

# 启动6个容器
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
#-d 后台运行;redis网络;5.0.9的版本;通过redis.conf来启动

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

# 进入一个redis,注意这里是 sh命令
#redis镜像里没有bash命令;但有sh命令;redis-1就是进入第一个节点
docker exec -it redis-1 /bin/sh

# 进入之后 创建集群
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
输入yes
#redis-cli通过--cluster集群方式连接 创建一个集群;--cluster-replicas 1 进行分片

# 连接集群
redis-cli -c #-c是集群;不要-c是单机

# 查看集群信息
cluster info

# 查看节点(三主三从)
cluster nodes

#通过IP可以发现哪个节点在处理这个值
set a b

# 打开另一个标签页并停止到存值的容器
docker stop redis-3

# 然后再次get a,发现依旧可以获取值
#因为此时从机已经替代了主机
get a
# 查看节点,发现高可用完全没问题
#redis-3显示fail,master被代替
posted @ 2022-04-25 17:59  郑琰  阅读(439)  评论(0编辑  收藏  举报
#

# #