Docker进阶:网络篇

理解Docker0

查看本地ip ip addr

[root@VM-0-6-centos ~]# 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
#阿里云的私有IP
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:9a:88:4d brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.6/20 brd 172.17.15.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe9a:884d/64 scope link 
       valid_lft forever preferred_lft forever
# docker网桥
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:d1:ba:72:7a 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
    inet6 fe80::42:d1ff:feba:727a/64 scope link 
       valid_lft forever preferred_lft forever

Docker 是如何处理容器网络访问的?

我们之前安装ES的时候,留过一个问题,就是安装Kibana的问题,Kibana得指定ES的地址!或者我们实际场景中,我们开发了很多微服务项目,那些微服务项目都要连接数据库,需要指定数据库的url地址,通过ip。但是我们用Docker管理的话,假设数据库出问题了,我们重新启动运行一个,这个时候数据库的地址就会发生变化,docker会给每个容器都分配一个ip,且容器和容器之间是可以互相访问的。 我们可以测试下容器之间能不能ping通过。

[root@VM-0-6-centos ~]# docker run -d -P --name tomcat01 tomcat
# 查看tomcat01的ip地址,docker会给每个容器都分配一个ip!
[root@VM-0-6-centos ~]#  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
24: eth0@if25: <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
# 可以ping通!
[root@VM-0-6-centos ~]# ping 172.18.0.2

原理

每一个安装了 Docker 的 linux 主机都有一个 docker0 的虚拟网卡。这是个桥接网卡,使用了 veth-pair 技术!

  • 再次查看主机的 ip addr :本来有三个网络,启动tomcat容器之后,多了一对网络!

  • 每启动一个容器,linux主机就会多一对虚拟网卡。

    #启动一个tomcat01,主机的ip地址多了个  
    25: veth2b7cb71@if24
    #然后我们在tomcat01容器中查看容器的ip 
    24: eth0@if25
    
    #我们再启动一个tomcat02观察
    [root@VM-0-6-centos ~]# docker run -d -P --name tomcat02 tomcat
    
    # 然后发现linux主机上又多了一个网卡	
    27: veth4d2bd95@if26
    
    # 我们看下tomcat02的容器内ip地址是	 
    [root@VM-0-6-centos ~]# docker exec -it tomcat02 ip addr
     26: eth0@if27
    

    可以发现:只要启动一个容器,本机上就会生成一对网卡。使用到的技术是veth-pair ,veth-pair 就是虚拟设备接口,它是成对出现的。一端连着协议栈,一端彼此相连着。

    正因为有这个特性,它常常充当着一个桥梁,连接着各种虚拟网络设备!

我们来测试下 tomcat01 和 tomcat02 容器间是否可以互相 ping 通

[root@VM-0-6-centos ~]# 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.095 ms

测试结果容器和容器之间是可以互相访问的。

结论:tomcat1 和 tomcat2 共用一个路由器docker0。任何一个容器启动默认都是 docker0网段下的 , docker 会给容器分配一个可用 ip ,因此容器都在同一个网段下,所以能够互相访问。

小结

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

image-20210615232726561

Docker 容器网络就很好的利用了 Linux 虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫 veth pair);

Docker 中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。

Link参数(了解)

思考一个场景,我们编写一个微服务,数据库连接地址原来是使用ip的,如果ip变化就不行了,那我们能不能使用服务名访问呢?

就像 jdbc:mysql://mysql:3306,这样的话哪怕 mysql 重启,我们也不需要修改配置了!

docker提供了 --link 的操作!

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

#我们再启动一个tomcat03,但是启动的时候连接tomcat02
[root@VM-0-6-centos ~]# docker run -d -P --name tomcat03 --link tomcat02 tomcat
80ed9c4e1f9428598a91c727ed13b7d0534d86d569855d4fb8739baabe5d6b91

#这个时候,我们就可以使用tomcat03 ping通 tomcat02 了
[root@VM-0-6-centos ~]#  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.092 ms

# tomcat3 ping不通 tomcat1
[root@VM-0-6-centos ~]# docker exec -it tomcat03 ping tomcat01
ping: tomcat01: Name or service not known
# tomcat2 ping不通 tomcat3 反向也ping不通
[root@VM-0-6-centos ~]# docker exec -it tomcat02 ping tomcat03
ping: tomcat03: Name or service not known

这是为什么呢?

#进入tomcat03中查看下host配置文件
[root@VM-0-6-centos ~]# 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
#发现tomcat2直接被写在这里
172.18.0.3	tomcat02 cff25f666b32
172.18.0.4	80ed9c4e1f94

所以这里其实就是配置了一个 hosts 地址而已!

原因:--link 的时候,直接把需要 link 的主机的域名和 ip 直接配置到了 hosts 文件中了

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

自定义网络(重要)

基本概念

指令

[root@VM-0-6-centos ~]# 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

Run 'docker network COMMAND --help' for more information on a command.

查看所有网络

[root@VM-0-6-centos ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
4b873066daf7   bridge    bridge    local
86da632adad6   elastic   bridge    local
64b606f257e7   host      host      local
34ab4fdb73c0   none      null      local

网络模式

网络模式 配置 说明
bridge模式 --net=bridge 默认值,在 Docker 网桥 docker0 上为容器创建新的网络栈
none模式 --net=none 不配置网络,用户可以稍后进入容器,自行配置
container 模式 -- net=container:name/id 容器和另外一个容器共享Network namespace。 kubernetes中的pod就是多个容器共享一个Network namespace。
host模式 --net=host 容器和宿主机共享Network namespace
用户自定义 --net=自定义网络 用户自己使用network相关命令定义网络,创建容器的 时候可以指定为自己定义的网络,就像 elastic

查看一个具体的网络的详细信息

[root@VM-0-6-centos ~]# docker network inspect 4b873066daf7
[
    {
        "Name": "bridge",
        "Id": "4b873066daf7eca3fd7a79ce17b46dff17a89368cb7f43d01c88dd9ee08d9407",
        "Created": "2021-06-11T18:42:36.937993641+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                //默认 docker0 是管理这个子网范围内的。0~16,
                //也就是 255*255,去掉0个255,我们有65534可以分配的ip
                //所以 docker0 网络默认可以支持创建6万多个容器ip不重复
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "220fac5f16f3cf7f2619131502cb6bb4004f334c3b501a0ceff8804c361cf027": {
                "Name": "tomcat01",
                "EndpointID": "d54c9b71aff1843c3a1609de8eaa85785ebddd8a419ea5fae346fb538568946f",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "80ed9c4e1f9428598a91c727ed13b7d0534d86d569855d4fb8739baabe5d6b91": {
                "Name": "tomcat03",
                "EndpointID": "a6dd8dd8ba2b5b341cbed8318a2463a4e28f1059cc848504a409fbf75ae21f4f",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            },
            "cff25f666b32df808923a51e14f2f2686fc9aff161e07c188c28ce15d0b38401": {
                "Name": "tomcat02",
                "EndpointID": "b5b18a038166b64a0308cc4a506f543b92d35b9fc50f5758f8d5daccc1df87bd",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

自定义网卡

1 - 删除之前创建的测试 tomcat 容器

docker rm -f $(docker ps -aq)

2 - 自定义网卡

docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
#默认值 --net bridge 使用的docker0
[vagrant@bogon ~]$ sudo docker run -d -P --name tomcat01 --net bridge tomcat
f572d585355b1670f4ae7915bf05c5ae47ce3e1cf55e4b120d7284961a85371a #创建成功返回网卡id

[vagrant@bogon ~]$ sudo docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
174c4fb32e49   bridge    bridge    local
3898bcc133c6   host      host      local
f572d585355b   mynet     bridge    local #自定义的网卡
4b78e6954228   none      null      local
[root@VM-0-6-centos ~]# docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "7f9fbfea6931271e917c7a932c47d05f311f2fe6f1e694b95e4ef3fcf060446e",
        "Created": "2021-06-16T15:56:58.841772626+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": {}
    }
]

3 - 创建容器并指定网络

我们来启动两个容器测试,使用自己的 mynet

docker run -d -P --name tomcat-net-01 --net mynet tomcat
docker run -d -P --name tomcat-net-02 --net mynet tomcat

查看网络使用情况

[vagrant@bogon ~]$ sudo docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "f572d585355b1670f4ae7915bf05c5ae47ce3e1cf55e4b120d7284961a85371a",
        "Created": "2021-11-09T08:14:19.702564797Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.168.0.0/16",
                    "Gateway": "172.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": { #在该网络下运行的容器
            "2b1e895bacfac82ad364b4df70caceab3598b8c3b39680457dbced5efb889993": {
                "Name": "tomcat-net-01",
                "EndpointID": "328fac3336aa765ba1b0db605bc6d0bdac37e84ea6dd89b5c049acacd31819b9",
                "MacAddress": "02:42:ac:a8:00:02",
                "IPv4Address": "172.168.0.2/16",
                "IPv6Address": ""
            },
            "7f5c073265d10bccf94514d5ed2e289d008968c463d4f8f705fff7a633236f1e": {
                "Name": "tomcat-net-02",
                "EndpointID": "f5c2f4414da41b1f87c13c9c41f672898bc1b587a16c8041e4da51b7891a4888",
                "MacAddress": "02:42:ac:a8:00:03",
                "IPv4Address": "172.168.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

来测试ping容器名和ip试试

# 都可以ping通
[root@VM-0-6-centos ~]# 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.118 ms
[root@VM-0-6-centos ~]# 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.059 ms

发现,我们自定义的网络docker都已经帮我们维护好了对应的关系。实际开发中也是用自定义网卡,而且是不同的模块都拥有各自网卡,避免一个模块出现错误影响其他模块。

网络连通

思考:我们已经知道自定义网络下的两个tomcat可以互相访问,那docker0网络下的tomcat可以访问自定义网络中的tomcat容器吗?

答案是不可以。

docker0和自定义的网络使用的不是同一个网桥,肯定不通,我们使用自定义网络的好处就是网络隔离。

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

那关键的问题来了,如何让两个不同的网络互相访问呢,如何让 tomcat-net-01 访问 tomcat1?

image-20210616162056466

# 在docker0网络下创建tomcat容器
docker run -d -P --name tomcat01 tomcat
docker run -d -P --name tomcat02 tomcat

有个命令 connect 用来连接网络

# 我们来测试一下!打通mynet-docker0
[root@VM-0-6-centos ~]$ docker network connect mynet tomcat01
[root@VM-0-6-centos ~]$ docker network inspect mynet
[
    {
        "Name": "mynet",
        "Id": "7f9fbfea6931271e917c7a932c47d05f311f2fe6f1e694b95e4ef3fcf060446e",
        "Created": "2021-06-16T15:56:58.841772626+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": {
            "52395d45d1fcad0170da201db471ea6ac75c25c9f7d91d10b6260dce2739fd54": {
                "Name": "tomcat-net-01",
                "EndpointID": "3c1aea820c4276b0d0dbe249ebd6f43547baceb7c3e774f8ee4b61b0e4b0b11f",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            },
            #发现我们的tomcat01就进来这里了
            "c159a99201d5b3f0f6be065d562c1a0e6439b316084361937f9eda9a22e997ab": {
                "Name": "tomcat01",
                "EndpointID": "e11948dcd704e50b8008ee41546ff7c9f506b636f41fb6e6697081fd9d398dc5",
                "MacAddress": "02:42:c0:a8:00:04",
                "IPv4Address": "192.168.0.4/16",
                "IPv6Address": ""
            },
            "f2106d157b5ea6c5bdb87e04b9dc18be2b2e25a41d61e25851ea0afbf1e9ea39": {
                "Name": "tomcat-net-02",
                "EndpointID": "3cb8b199df4fca69e1a6428ae0536ff3291f9215f8b9d9303a2ec3402bdc704f",
                "MacAddress": "02:42:c0:a8:00:03",
                "IPv4Address": "192.168.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

tomcat01 可以ping通了

[root@VM-0-6-centos ~]# 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.075 ms

tomcat02 依旧ping不通,因为之前只连接了tomcat01

[root@VM-0-6-centos ~]#  docker exec -it tomcat02 ping tomcat-net-01
ping: tomcat-net-01: Name or service not known

结论:

  • 不同的网络无法联通,例如无法直接联通docker0和mynet,但是可以让容器直接联通其他的网络
  • 如果要跨网络操作别人,就需要使用 docker network connect [OPTIONS] NETWORK CONTAINER 连接

实战测试:redis集群

清楚之前创建的测试容器

docker rm -f $(docker ps -aq)

创建redis集群环境的专属网络

docker network create redis --subnet 172.38.0.0/16

拉取redis镜像

docker pull redis

通过脚本创建六个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
[root@bogon /]$ cd /mydata/
[root@bogon mydata]$ ls
redis
[root@bogon mydata]$ cd redis/
[root@bogon redis]$ ls
node-1  node-2  node-3  node-4  node-5  node-6

启动6个容器

for port in $(seq 1 6); \
do \
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 redis-server /etc/redis/redis.conf; \
done

创建集群

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
#进入集群 -c表示cluster集群模式
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:252
cluster_stats_messages_pong_sent:263
cluster_stats_messages_sent:515
cluster_stats_messages_ping_received:258
cluster_stats_messages_pong_received:252
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:515

#查看各节点信息
127.0.0.1:6379> cluster nodes
c00a3c14f541f50e812e6c9f737ab96986136cad 172.38.0.11:6379@16379 myself,master - 0 1636525137000 1 connected 0-5460
1b4e812a81ba094b8dae72954da0dc221b3b283c 172.38.0.13:6379@16379 master - 0 1636525138000 3 connected 10923-16383
492553c112cd060c7b38c2de88f73055bba9e332 172.38.0.15:6379@16379 slave c00a3c14f541f50e812e6c9f737ab96986136cad 0 1636525138583 1 connected
c8a458cf8932c0787090017a387da5d6c36ffaab 172.38.0.12:6379@16379 master - 0 1636525138000 2 connected 5461-10922
de5f9fb914c3c95387abc96a2c05e8287b1ad570 172.38.0.14:6379@16379 slave 1b4e812a81ba094b8dae72954da0dc221b3b283c 0 1636525137000 3 connected
f84fd372d60818e518296df5256643f030b72905 172.38.0.16:6379@16379 slave c8a458cf8932c0787090017a387da5d6c36ffaab 0 1636525138583 2 connected

测试添加一个数据

127.0.0.1:6379> set name birdy
-> Redirected to slot [5798] located at 172.38.0.12:6379
OK

可以看到在172.38.0.12主机上添加了一个key

把172.38.0.12主机停掉后再次进入集群查询

#关闭redis-2容器
docker stop redis-2

#启动并进入容器
docker exec -it redis-1 /bin/sh

#进入集群
redis-cli -c
127.0.0.1:6379> get name
-> Redirected to slot [5798] located at 172.38.0.16:6379
"birdy"

172.38.0.12主机挂掉后在172.38.0.16上仍然可以查到,高可用性实现!

posted @ 2021-10-22 10:02  至安  阅读(279)  评论(0编辑  收藏  举报