一、背景:
想一下这个问题,容器和容器之间是否可以通过网络正常通信?宿主机和容器是否可以通信?如果可以通信,那为什么可以通信。如果不可以通信,如何让他们之间通信。接下来就详细的讲解下 docker 的网络。
二、Docker 网络现状
1、首先删除所有的镜像和容器,如下所示:
# 删除所有容器
docker rm -f $(docker ps -aq)# 删除所有镜像,这个 rmi 是专门删除镜像的命令
docker rmi -f $(docker images -aq)
2、 查看网卡信息,如下所示:
ip addr
如果你出现的是下面的这种多个地址,那么你可以进行 删除 virbr0 和修改 ens33 的操作。
3、docker 的容器和容器之间是如何进行网络访问的?如下所示:
# 运行 tomcat 容器
docker run -d --name tomcat01 tomcat# 进入容器
docker exec -it tomcat01 bash# 下载安装 ip 命令相关的安装包
apt-get update & apt-get install -y iproute2 # 查看ip
ip addr
我们可以发现,172.17.0.2 是容器的地址,我们用宿主机测试下是否可以 ping 通容器的地址,如下所示,发现是可以 ping 通的。
三、原理分析:
1、每启动一个 docker 容器,docker 就会给容器分配一个 IP,只要我们安装了 docker 就会有一个默认的网卡 docker0,使用的是桥接模式,veth-pair (虚拟网线,即一端连着容器,一端连着 docker0 的网卡)技术。
再启动一个 tomcat 容器,命令如下:
# 运行 tomcat 镜像
docker run -d --name tomcat02 tomcat# 进入容器
docker exec -it tomcat02 bash# 下载安装 ip 命令相关的安装包
apt-get update & apt-get install -y iproute2 # 查看ip
ip addr
查看容器和宿主机 ip 如下所示:
我们发现,这个容器带来的网卡都是一对一对的,veth-pair 就是一对虚拟设备接口,他们都是成对出现,一端连着协议,一端彼此相连,veth-pair 充当于一个桥梁,连接各种网络设备。
2、测试 tomcat02 是否可以 ping 通 tomcat01,如下所示:
结论: Tomcat01 和 Tomcat02 是公用的一个路由器 docker0,左右的容器在不指定网络的情况下,都是 docker0 来路由的,docker 会给我们分配一个默认的可用 IP。
Docker 使用的是 Linux 桥接,如下图所示:
四、Docker 的 Link 参数:
1、思考一个场景,我们编写了一个微服务,比如 mysql 的连接地址为:database.url=ip:port ,如果数据库 IP 换掉了,我们想不重启解决这个问题,是否可以通过名字访问容器?
测试是否可以通过容器名称 ping 通另一个容器,如下所示,ping 不通。
我们使用 link 的方式再次启动一个容器 tomcat03 ,再次尝试,如下所示:
# 启动 tomcat03 容器
docker run -d --name tomcat03 --link tomcat01 tomcat# 进入容器
docker exec -it tomcat03 bash# 安装 ping 工具
apt-get update & apt-get install -y iputils-ping# 测试
ping tomcat01
我们发现 tomcat03 是可以 ping 通 tomcat01 的,那么反过来可以吗?显然是不可以的。
因为我们在启动 tomcat03 的时候在本地配置了 tomcat01,如下所示:
--link 参数就是在 hosts 中增加了 172.17.0.2 tomcat01 202aa737790a,但是我们现在已经不推荐这种方式了。即 docker0 不支持自定义网络。
五、自定义网络
1、查看所有的 docker 网络,输入 docker network ls ,如下所示:
bridge:桥接 docker(默认,自己创建也使用 bridge 模式)
none:不配置网络
host:和宿主机共享网络
container:容器网络连通(用的少)
# 这两个命令是一样的,只不过 --net bridge 可以省略,是默认的
# --net bridge, 这个就是我们的 docker0
# docker0 的特点是默认不能访问域名,但是可以通过 --link 打通,但是太麻烦,不推荐
docker run -d --name tomcat01 tomcat = docker run -d --name tomcat01 --net bridge tomcat
2、我们可以自定义一个网络,首先输入 docker network --help 查看需要的参数,如下所示:
接下来执行下面命令,并查看当前的网络信息
# --driver bridge ,桥接的方式
# --subnet 192.168.0.0/16 (16代表255*255个端口=65535) 192.168.0.2 到 192.168.255.255
# --gateway 192.168.0.1,设置网关 ip
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 xhf_net
我们自己的网络就建好了,如下所示:
3、接下来重新创建两个 tomcat 容器,测试是否互通,命令如下:
# 先启动 tomcat_01
docker run -d --name tomcat_01 --net xhf_net tomcat# 进入容器
docker exec -it tomcat_01 bash# 配置 dns,要不无法访问互联网
echo "nameserver 8.8.8.8" >> /etc/resolv.conf# 安装 ping 工具
apt-get update & apt-get install -y iputils-ping# 再启动 tomcat_02
docker run -d --name tomcat_02 --net xhf_net tomcat# 进入容器
docker exec -it tomcat_02 bash# 配置 dns,要不无法访问互联网
echo "nameserver 8.8.8.8" >> /etc/resolv.conf# 安装 ping 工具
apt-get update & apt-get install -y iputils-ping
反向测试也没有问题,网络是互通的,即我们自定义的网络,docker 都已经帮我们维护好了对应关系,推荐这样使用。
4、针对于 mysql 和 redis 集群来说,好处是不同集群使用不同网络,保证集群安全、健康。
5、如果此时我想 tomcat01 和 tomcat_01 互通,也就是和 xhf_net 打通,怎么操作?
# 将 xhf_net 和 tomcat01 打通
docker network connect xhf_net tomcat01
查看 xhf_net 详情,如下所示,发现 xhf_net 的 Containers 中多了一个 tomcat01 容器。
docker network inspect xhf_net
6、假如我要跨网络操作别人,就需要用 docker network connect 来连接。即 docker 通过一个容器两个 IP 的方式,如:阿里云公网 IP 和私网 IP,将两个容器打通。
六、redis 集群实战
我们创建一个 redis 集群,分配同一个网段,测试其中一个节点挂掉之后,其他的节点是否还可以继续工作。
1、创建网卡 redis 和相关的配置文件,如下所示:
# 创建网卡
docker network create redis --subnet 172.38.0.0/16# 使用脚本创建6个配置文件
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/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}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
2、启动六个 redis 容器,如下所示:
# 启动redis-1
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
# 启动redis-2
docker run -p 6372:6379 -p 16372:16379 --name redis-2 \-v /mydata/redis/node-2/data:/data \-v /mydata/redis/node-2/conf/redis.conf:/etc/redis/redis.conf \-d --net redis --ip 172.38.0.12 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf# 启动redis-3
docker run -p 6373:6379 -p 16373:16379 --name redis-3 \-v /mydata/redis/node-3/data:/data \-v /mydata/redis/node-3/conf/redis.conf:/etc/redis/redis.conf \-d --net redis --ip 172.38.0.13 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf# 启动redis-4
docker run -p 6374:6379 -p 16374:16379 --name redis-4 \-v /mydata/redis/node-4/data:/data \-v /mydata/redis/node-4/conf/redis.conf:/etc/redis/redis.conf \-d --net redis --ip 172.38.0.14 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf# 启动redis-5
docker run -p 6375:6379 -p 16375:16379 --name redis-5 \-v /mydata/redis/node-5/data:/data \-v /mydata/redis/node-5/conf/redis.conf:/etc/redis/redis.conf \-d --net redis --ip 172.38.0.15 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf# 启动redis-6
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
3、创建集群,命令如下:
# 随便进入一个容器
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
4、测试主从,当主挂了之后,从是否会成为主
# 进入容器
docker exec -it redis-1 /bin/sh# 进入 redis 命令行模式
redis-cli -c# 查看集群信息
cluster info# 查看节点信息
CLUSTER nodes
# 随便存一个值,发现存在172.38.0.13:6379
set a b# 当172.38.0.13 这台服务挂了之后,测试是否能查询到值,停掉 redis-3
docker stop redis-3# 重新查询
get a# 重新查看节点状态,发现 172.38.0.13:6379 fail, 172.38.0.14 已经成为了 master
CLUSTER nodes