目录
- docker网络介绍
- docker网络模式
- 常用命令
- docker网络原理
- 自定义网络
- 实战:redis主从部署
docker网络介绍
实际只要我们启动docker,那么主机上就会产生一个名字为docker0的虚拟网桥。它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
那么容器内部的网络是怎么样的?
下面启动一个tomcat容器看下
docker run -d -P --name tomcat01 tomcat
查看容器内的网络地址
# mac上
docker exec -it tomcat01 /bin/bash
# 基础镜像发现没有ip命令
# 使用一下命令安装
apt update && apt install -y iproute2
# 在执行ip addr
root@8dcc12b24d79:/usr/local/tomcat# 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: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000
link/tunnel6 :: brd :: permaddr b2db:d9a1:ad63::
26: eth0@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
发现容器启动的时候有一个eth0@if27的ip地址,是docker分配的。
主机能否ping通容器内部?
test@testdeMacBook-Pro lib % ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
可以发现,主机能ping通容器内部。
docker网络模式
查看docker内的网络:
test@testdembp ~ % docker network ls
NETWORK ID NAME DRIVER SCOPE
3a36663866ac bridge bridge local
5f7722055c12 host host local
e5ffa769c939 none null local
docker内的网络模式有4种:
- bridge模式
使用–network bridge指定。docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0 。具体原理见下面的docker网络原理。
- host模式
使用–network host指定。直接使用宿主机的 IP 地址与外界进行通信,不再需要额外进行NAT 转换。
容器将不会获得一个独立的Network Namespace, 而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
- none模式
使用–network none指定。禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)
- container模式
使用–network container:NAME或者容器ID指定。
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
常用命令
可以使用docker network --help查看网络相关的命令
est@testdembp ~ % docker network --help
Usage: docker network COMMAND
Manage networks
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
常用如下:
1.docker network ls
查看所有网络
test@testdembp ~ % docker network ls
NETWORK ID NAME DRIVER SCOPE
3a36663866ac bridge bridge local
5f7722055c12 host host local
a78f84325c81 mynet bridge local
e5ffa769c939 none null local
a2eb36c948fa redis bridge local
2.docker network inspect
查看某个网络的信息
test@testdembp ~ % docker network inspect mynet
[
{
"Name": "mynet",
"Id": "a78f84325c81a5298c1ab2db019c2ead540d02c56bfc332c1f7146fb4927ceec",
"Created": "2021-11-28T06:38:40.652573879Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
3.docker network rm
删除某个网络
4.docker network create
创建一个信息网络
docker网络原理
docker网络原理是怎么样的呢?
还是以上面启动的tomcat容器为例。
1.每启动一个docker容器,docker就会为容器分配一个ip,只要安装了docker,就会有一个docker0桥接模式,使用的是evth-pair技术。
测试主机ip addr
ip addr
会发现主机多了一个网卡,与上面容器内的网卡正好组成了一对。这就是evth-pair技术实现的。
2.再启动一个tomcat容器
docker run -d -P --name tomcat02 tomcat
测试网卡
docker exec -it tomcat02 ip addr
root@8e66c1e052ef:/usr/local/tomcat# 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: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000
link/tunnel6 :: brd :: permaddr 5a34:21f1:af79::
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
再查看主机网络
ip addr
发现有又多了一对,且主机也可以ping通。
evth-pair就是一对虚拟设备接口,他们是成对实现的,一段连着协议,一段彼此相连。正因为这个特性,evth-pair充当一个桥梁,连接各种虚拟设备。
3.容器间也是可以ping通
测试tomcat01是否可以ping通tomcat02
# 进入容器后如果ping命令没有,先安装
apt install iputils-ping
# 再ping
docker exec -it tomcat02 /bin/bash
root@8e66c1e052ef:/usr/local/tomcat# ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
原理:
1.Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
2.网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配,如下图。
2.1 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);
2.2 每个容器实例内部也有一块网卡,每个接口叫eth0;
2.3 docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。
注意删除容器,虚拟网卡就自动删除。
但是有一个问题是当容器停止或删除后,再次启动,分配的ip地址会变,那么容器之间不能再使用原来的ip互通。所以实际生产中容器间使用服务名进行互通。下面介绍如何实现。
自定义网络
要实现容器间使用服务名可以互通,可以使用–link,但是已经不推荐这种用法,推荐使用自定义网络。
创建网络
以前直接启动会有默认的网络参数,–net bridge就是我们的docker0
docker run -d -P --name tomcat01 tomcat
# 等价于下面的
docker run -d -P --name tomcat01 --net bridge tomcat
下面手动添加网络
docker network create --driver bridge --subnet 172.18.0.0/16 --gateway 172.18.0.1 mynet
再查看创建的网络
test@testdeMacBook-Pro ~ % docker network inspect m
ynet
[
{
"Name": "mynet",
"Id": "a78f84325c81a5298c1ab2db019c2ead540d02c56bfc332c1f7146fb4927ceec",
"Created": "2021-11-28T06:38:40.652573879Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
使用我们创建的网络
docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat
查看下网络信息,发现Containers里有容器使用
test@testdeMacBook-Pro ~ % docker network inspect mynet
[
{
"Name": "mynet",
"Id": "a78f84325c81a5298c1ab2db019c2ead540d02c56bfc332c1f7146fb4927ceec",
"Created": "2021-11-28T06:38:40.652573879Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"244dd1957186e55324a4a14960b0a25f2dc99fe124dbee0df8924f3d5bb59159": {
"Name": "tomcat-net-01",
"EndpointID": "54eae3c3e6fc73ada997d1b9db83b81c7f1f14c4ae34489c84755ba2a9d50bd3",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"339b2cb0674b9002334fb537d360ebd217da99fc4b8397e81ce8857ef3693d36": {
"Name": "tomcat-net-02",
"EndpointID": "9c866dec91bca04404c84f3b161df4d082bd4b482b972be274e54f7dec082615",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
再次测试,现在容器就可以使用服务名互相ping通。推荐使用这种方式。
实战:redis主从部署
先创建一个网卡
docker network create redis --subnet 172.19.0.0/16
查看网卡信息
test@testdeMacBook-Pro ~ % docker network inspect redis
[
{
"Name": "redis",
"Id": "a2eb36c948faf3d39fbee5dd229b629bd815ba05f282937e955eede47e00bbf2",
"Created": "2021-11-28T07:06:45.298375548Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
通过shell脚本创建6个redis配置
for port in $(seq 1 6);
do
mkdir -p /Users/fltech/docker/redis/mydata/redis/node-${port}/conf
touch /Users/fltech/docker/redis/mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/Users/fltech/docker/redis/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.19.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
创建6个redis容器
docker run -p 6371:6379 -p 16371:16379 --name redis-1 -v /Users/fltech/docker/redis/mydata/redis/node-1/data:/data -v /Users/fltech/docker/redis/mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf -d --net redis --ip 172.19.0.11 redis redis-server /etc/redis/redis.conf
docker run -p 6372:6379 -p 16372:16379 --name redis-2 -v /Users/fltech/docker/redis/mydata/redis/node-2/data:/data -v /Users/fltech/docker/redis/mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf -d --net redis --ip 172.19.0.12 redis redis-server /etc/redis/redis.conf
docker run -p 6373:6379 -p 16373:16379 --name redis-3 -v /Users/fltech/docker/redis/mydata/redis/node-3/data:/data -v /Users/fltech/docker/redis/mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf -d --net redis --ip 172.19.0.13 redis redis-server /etc/redis/redis.conf
docker run -p 6374:6379 -p 16374:16379 --name redis-4 -v /Users/fltech/docker/redis/mydata/redis/node-4/data:/data -v /Users/fltech/docker/redis/mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf -d --net redis --ip 172.19.0.14 redis redis-server /etc/redis/redis.conf
docker run -p 6375:6379 -p 16375:16379 --name redis-5 -v /Users/fltech/docker/redis/mydata/redis/node-5/data:/data -v /Users/fltech/docker/redis/mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf -d --net redis --ip 172.19.0.15 redis redis-server /etc/redis/redis.conf
docker run -p 6376:6379 -p 16376:16379 --name redis-6 -v /Users/fltech/docker/redis/mydata/redis/node-6/data:/data -v /Users/fltech/docker/redis/mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf -d --net redis --ip 172.19.0.16 redis redis-server /etc/redis/redis.conf
查看下
test@testdeMacBook-Pro ~ % docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c3e6e4d05259 redis "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:6376->6379/tcp, 0.0.0.0:16376->16379/tcp redis-6
91153e95aba7 redis "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:6375->6379/tcp, 0.0.0.0:16375->16379/tcp redis-5
fe8c01799026 redis "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:6374->6379/tcp, 0.0.0.0:16374->16379/tcp redis-4
2b6741ccfc74 redis "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:6373->6379/tcp, 0.0.0.0:16373->16379/tcp redis-3
441a19ba8a5a redis "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6372->6379/tcp, 0.0.0.0:16372->16379/tcp redis-2
2571675ef6c0 redis "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:6371->6379/tcp, 0.0.0.0:16371->16379/tcp redis-1
进入容器
docker exec -it redis-1 /bin/sh
创建集群
redis-cli --cluster create 172.19.0.11:6379 172.19.0.12:6379 172.19.0.13:6379 172.19.0.14:6379 172.19.0.15:6379 172.19.0.16:6379 --cluster-replicas 1
查看下集群是否创建成功
redis-cli -c
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:149
cluster_stats_messages_pong_sent:159
cluster_stats_messages_sent:308
cluster_stats_messages_ping_received:154
cluster_stats_messages_pong_received:149
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:308
127.0.0.1:6379> cluster nodes
027d5116005261c1828b656fe7c137943eb20181 172.19.0.15:6379@16379 slave 1ce779755e15cefdfbba326736cf27d1428b90d5 0 1638088882480 1 connected
ca1f943de9b98dc4cf23702c153d1caf22bf4289 172.19.0.12:6379@16379 master - 0 1638088881971 2 connected 5461-10922
0e09fa327ce99c3d5fb55d8fea7353f150b8060f 172.19.0.13:6379@16379 master - 0 1638088883000 3 connected 10923-16383
1ce779755e15cefdfbba326736cf27d1428b90d5 172.19.0.11:6379@16379 myself,master - 0 1638088882000 1 connected 0-5460
674eaa35be97425af235aade149810a2d558047a 172.19.0.16:6379@16379 slave ca1f943de9b98dc4cf23702c153d1caf22bf4289 0 1638088883501 2 connected
a814ffb7f0a39091d6506b52b1496894dc6866b8 172.19.0.14:6379@16379 slave 0e09fa327ce99c3d5fb55d8fea7353f150b8060f 0 1638088881456 3 connected
测试主从是否成功:
先set一个值
127.0.0.1:6379> set a b
-> Redirected to slot [15495] located at 172.19.0.13:6379
OK
172.19.0.13:6379> get a
"b"
发现设置在了172.19.0.13:6379这个redis里,也就是我们的redis-3
现在把redis-3容器停止,在取值试下,仍然可以成功,则主从设置成功:
docker stop redis-3
重启redis集群客户端再取:
# redis-cli -c
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.19.0.14:6379
"b"
可以发现再次取值从从机也就是redis-4成功取到值。
在查看节点信息:
172.19.0.14:6379> cluster nodes
674eaa35be97425af235aade149810a2d558047a 172.19.0.16:6379@16379 slave ca1f943de9b98dc4cf23702c153d1caf22bf4289 0 1638089504534 2 connected
0e09fa327ce99c3d5fb55d8fea7353f150b8060f 172.19.0.13:6379@16379 master,fail - 1638089193079 1638089190526 3 connected
027d5116005261c1828b656fe7c137943eb20181 172.19.0.15:6379@16379 slave 1ce779755e15cefdfbba326736cf27d1428b90d5 0 1638089505559 1 connected
ca1f943de9b98dc4cf23702c153d1caf22bf4289 172.19.0.12:6379@16379 master - 0 1638089506000 2 connected 5461-10922
a814ffb7f0a39091d6506b52b1496894dc6866b8 172.19.0.14:6379@16379 myself,master - 0 1638089505000 7 connected 10923-16383
1ce779755e15cefdfbba326736cf27d1428b90d5 172.19.0.11:6379@16379 master - 0 1638089506578 1 connected 0-5460
可以看到redis-3 fail,并且redis-4变成主机了。