菜单

Juning
发布于 2020-05-30 / 1769 阅读
21
0

Docker(六) Docker网络

理解网络

因为原生的Docker for Mac在网络的这一节中有点问题,需要其它的方式才能解决,所以从现在开始在Linux上进行学习和测试,关于这个提及的问题该如何解决,后期再出笔记解决掉

当我们每启动一个docker容器的时候,docker就会给docker容器分配一个IP
我们只要安装了docker,那我们的系统中就会有一张docker0的网卡:

[root@Juning ~]# 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
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:16:3e:0a:c4:31 brd ff:ff:ff:ff:ff:ff
    inet 172.18.83.187/20 brd 172.18.95.255 scope global dynamic noprefixroute eth0
       valid_lft 315359492sec preferred_lft 315359492sec
    inet6 fe80::216:3eff:fe0a:c431/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:86:41:5b:87 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forevers

可以看到docker0的地址172.17.0.1,然后我们启动一个容器看看容器内部的IP是什么样子的:
image.png
tomca01的这个容器里面有一个eth0@if5的网卡,而它的IP地址是172.17.0.2,而我们主机的地址是172.17.0.1;接下载我们看看主机能否ping得通容器:
image.png
OK,没有问题,再看看容器内能否ping通主机:
image.png

得出结论:主机在安装docker的时候自动以桥接的模式安装一张docker0的网卡,这样主机和容器之间就可以相互访问,而这种桥接是采用的一种叫做veth-pair的技术实现的,什么是veth-pair?我们再执行主机和容器里面的ip addr:
主机:image.png
容器:image.png
首先我要注意主机和容器打印网卡时前面的序号:
image.png
主机打印的是:1、2、3、5
容器是:1、4
而且编号4和5是那么的相似,再启动一个容器,然后分别打印一下主机、tomcat01、tomcat02看看:
image.png
主机打印的是:1、2、3、5、7
容器tomcat01是:1、4
容器tomcat02是:1、6
看到这里,我想我明白了,每启动一个容器就会成对创建两个网卡,主机一个容器一个的成对出现,这就是veth-pair技术,就是一对的虚拟设备接口,他们都是成对出现,一段连着协议,一段彼此连接,这样就可以通讯了。
也正是veth-pair技术的这个特性,veth-pair可以充当一个桥梁,连接各种虚拟网络设备,用到veth-pair技术的还有OpenStack、docker容器之间的连接、ovs的连接....
说到这里,容器tomcat01和容器tomcat02相互之间也是可以ping通的:
image.png

总结:tomcat01和tomcat02是公用的一个路由器:docker0
所有的容器在不指定网络的情况下,都是由docker0进行路由,docker会给我们分配一个默认可用的IP
image.png

小结

image.png
docker使用的是Linux的桥接,宿主机中是一个docker容器的网桥docker0,分配的IP数量大约是65535个,docker中所有的网络接口都是虚拟的,为什么是虚拟的呢? 因为虚拟的转发效率高;而且只要容器被删除,对应的网卡也会被删除。

到这里想到一个问题,我们编写了一个微服务,database url=ip。每次启动容器,容器里面的ip就会变动一次,这样的话很麻烦,每次都需要改一次ip,那我们能不能通过容器名字来相互访问呢(就像微服务之间通过注册中心直接用服务名交换地址)?

[root@Juning ~]# docker exec -it tomcat01 ping tomcat02
ping: tomcat02: Name or service not known
# 如何解决呢?
[root@Juning ~]# docker run -d --name tomcat03 --link tomcat02 tomcat
133603ff265c8bc8693cfe65a8009f3c73cb98dd2cbab5837c0168109c6c0b33
[root@Juning ~]# docker exec -it tomcat03 ping tomcat02
PING tomcat02 (172.17.0.3) 56(84) bytes of data.
64 bytes from tomcat02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.107 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.074 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.073 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=4 ttl=64 time=0.072 ms
64 bytes from tomcat02 (172.17.0.3): icmp_seq=5 ttl=64 time=0.074 ms
^C
--- tomcat02 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 126ms
rtt min/avg/max/mdev = 0.072/0.080/0.107/0.013 ms

# 反向可以ping通吗?
[root@Juning ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known

# 反向的不能ping通,这是为什么呢?有两点:
[root@Juning ~]# docker inspect tomcat03
  1. image.png
  2. image.png
    总结:--link首先是在links里面做了标记,然后在hosts文件里面直接打上了tomcat02的ip,但是这种方式非常不灵活,配置起来也麻烦,所以不推荐使用

那我们开发应该用什么呢? --link不行,docker0的网桥因为不能直接通过容器名称直接访问,也不够用;
所以用什么呢?————自定义网络!

自定义网络

查看所有的docker网络

image.png
NAME表达的是网络模式:

  1. bridge:桥接
  2. none:不配置网络
  3. host:和宿主机共享网络
  4. container:容器网络连通(用的少,局限性很大)

测试:

# 我们直接启动的命令docker run -d --name tomcat01 tomcat 其实默认带 --net bridge,而这个就是docker0
docker run -d --name tomcat01 --net bridge tomcat

# docker0特点,默认,容器名不能访问,--link可以打通但是比较麻烦
# 所以可以自定义网络

# --driver bridge 桥接
# --subnet 192.168.0.0/16  
# --gateway 192.168.0.1
[root@Juning ~]# docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
5d0ddb14ff64590113d643b5b09410947f02fedef01ebd7cff1b6837d3080b8d

[root@Juning ~]# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
f2626a88b650        bridge              bridge              local
01b2e7e552dd        host                host                local
5d0ddb14ff64        mynet               bridge              local
1a90343a609c        none                null                local

到这里,我们自己的网络就创建好了,以后我们就可以使用我们自己创建的网络了
image.png
启动两个容器试试,可以看到两个容器已经在使用我们自己的网络了:

[root@Juning ~]# docker run -d --name tomcat-net-01 --net mynet tomcat
b349f91222f9a35d9b60cc1e080ab07c0b5b7239f1c66f6a4c004040c99d24c3
[root@Juning ~]# docker run -d --name tomcat-net-02 --net mynet tomcat
1d1a94427e37320a6c79feb580cda1dbfad844cf44e43803ea12b271f9931490
[root@Juning ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "5d0ddb14ff64590113d643b5b09410947f02fedef01ebd7cff1b6837d3080b8d",
        "Created": "2020-05-30T23:44:57.633030828+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": {
            "1d1a94427e37320a6c79feb580cda1dbfad844cf44e43803ea12b271f9931490": {
                "Name": "tomcat-net-02",
                "EndpointID": "6eab1133f55f2905a11606ab1720a559ce7b915aefa2ad18f3805112794372d0",
                "MacAddress": "02:42:c0:a8:00:03",
                "IPv4Address": "192.168.0.3/16",
                "IPv6Address": ""
            },
            "b349f91222f9a35d9b60cc1e080ab07c0b5b7239f1c66f6a4c004040c99d24c3": {
                "Name": "tomcat-net-01",
                "EndpointID": "f8f407bb17bcaac43d8934faa85cae2d0ce1ed00f883802cef0111a30afc7976",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

docker自定义网络十分完善,先测一测:

# 先试用ip ping一下,可以ping通
[root@Juning ~]# 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.160 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.093 ms
64 bytes from 192.168.0.3: icmp_seq=3 ttl=64 time=0.090 ms
64 bytes from 192.168.0.3: icmp_seq=4 ttl=64 time=0.092 ms
^C
--- 192.168.0.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 53ms
rtt min/avg/max/mdev = 0.090/0.108/0.160/0.032 ms
# 在不使用--link的情况下直接ping容器名,达到了我们想要的效果!
[root@Juning ~]# 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.066 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=2 ttl=64 time=0.077 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=3 ttl=64 time=0.085 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=4 ttl=64 time=0.086 ms
64 bytes from tomcat-net-02.mynet (192.168.0.3): icmp_seq=5 ttl=64 time=0.079 ms
^C
--- tomcat-net-02 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 116ms
rtt min/avg/max/mdev = 0.066/0.078/0.086/0.012 ms

我们自定义的网络docker都已经帮我们维护好了,推荐我们平时使用这样的网络!
好处:不同的集群使用不同的网络,保证集群是安全和健康的

网络连通

在同一个网段上容器可以相互ping通,那如果不在呢?比如下图的tomcat01想连通tomcat-net-01
image.png

先试试直接ping:

[root@Juning ~]# docker exec tomcat01 ping tomcat-net-01
ping: tomcat-net-01: Name or service not known

怎么打通呢?

docker network connect mynet tomcat01

image.png
连通之后就是将tomcat01放到了mynet网络下,官方叫法:一个容器两个ip,也就是说网卡跟网卡之间不能打通,但是容器跟网卡之间却可以

测试:
image.png

结论:假设要跨网络操作,就需要使用docker network connect连通!

实战:部署Redis集群

image.png
如上图所示,我们先要创建六个Redis,然后让它们实现高可用,如果master3挂掉了,slave3就会顶替master3。
由于需要创建自定义网络和六个Redis,所以我们直接用脚本:

# 创建网卡
docker network create redis --subnet 172.38.0.0/16

# 创建六个Redis配置
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

image.png

# 启动六个Redis
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

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

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

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

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

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

# 启动好之后进入容器配置集群
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

image.png
创建好之后就可以玩玩了:
集群信息:image.png
节点信息:image.png
设置一个值看看:image.png
可以看到刚刚设置的a:b是ip尾号13结尾的master节点处理的,那么按照道理,它的从库也会有它的值,就算13的节点挂了,13的从库也会代替他
我们玩点比较过分的,从外部直接停掉13看看:

[root@Juning ~]# docker stop redis-3
redis-3

# 然后看看能不能获取到a的值:
/data # redis-cli -c
127.0.0.1:6379> get a
-> Redirected to slot [15495] located at 172.38.0.14:6379
"b"

查看集群节点信息:
image.png


评论