Routing Mesh 的理解与应用

前言

在之前的学习中,我们学会了部署更新应用,那么swarm的应用怎么对外发布? 外部又如何访问呢?为什么swarm node 任意节点都可以访问service?

本章节就来详细说明

概述

Routing Mesh是docker swarm提供的确保service在多节点网络上可用的集群网络机制。其分为两类:

  • Internal Routing Mesh
  • Ingress Routing Mesh

Internal 容器与容器之间通过overlay网络进行访问。通过 service name 进行通信 (eg: 容器内 通过 ping service name ), 但是 service name 所对应的 ip 不是 真实 ip 而是 VIP (Virtual ip)

Ingress 如果service 有端口绑定,则可以通过swarm 任意节点的相应端口访问service

Internal

1、创建overlay网络

首先在Swarm的集群中创建一个overlay的network

$ docker network create -d overlay demo

查看

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
...
748vwsjqoqz7        demo                overlay             swarm
...

2、启动service

创建一个 whoami 的service ,使用demo网络

$ docker service create --name whoami -p 8000:8000 --network demo -d jwilder/whoami

当我们启动这个服务后,可以尝试去访问它:

$ curl http://127.0.0.1:8000
I'm 6fafaed61f64

我们再开启一个服务

$ docker service create --name test -d --network demo busybox:1.28.3 /bin/sh -c "while true;do sleep 3600;done"

busybox 1.29开始 nslookup 解析有改动,使用1.28版本

或者使用新版 busybox,命令改用 -type=a

此时我们2个服务是在不同节点上运行的

$ docker service ps whoami
ID                  NAME                IMAGE                   NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
yq3zung8aoum        whoami.1            jwilder/whoami:latest   node3               Running             Running 8 minutes ago                       
$ docker service ps test
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
c3b8p5xqfu9k        test.1              busybox:latest      node1               Running             Running 43 seconds ago

3、不同节点服务之间通信

  • 进入test 容器访问 whoami
[root@node1] $ docker exec -it 67b6 /bin/sh
/ # ping whoami
PING whoami (10.0.4.2): 56 data bytes
64 bytes from 10.0.4.2: seq=0 ttl=64 time=0.206 ms
64 bytes from 10.0.4.2: seq=1 ttl=64 time=0.102 ms

可以看到,网络访问是通的,并且返回的地址是10.0.4.2,这是不是就说明了whoami容器的ip是这个呢?我们查看一下。

  • 查看whoami 容器ip
[root@node3 ~]$ docker exec -it 6faf ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:00:04:03  
          inet addr:10.0.4.3  Bcast:10.0.4.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

发现ip是10.0.4.3,为什么呢?

我们知道scale参数可以将容器水平扩展,不同的容器都有一个ip,为了保证这些服务的ip不变,这里使用了虚拟的ip,这个ip地址是不会改变的

  • 创建多个whoami服务的容器

    $ docker service scale whoami=3
    
  • 再次测试

docker exec -it  67b6 ping whoami
/ # ping whoami
PING whoami (10.0.4.2): 56 data bytes
64 bytes from 10.0.4.2: seq=0 ttl=64 time=0.094 ms
64 bytes from 10.0.4.2: seq=1 ttl=64 time=0.096 ms

可以看到虽然whoami服务已经启动了3个容器,但是这个虚拟的ip还是没有变化,那么怎么查看这个虚拟ip对应的真实容器是哪个呢?可以通过以下命令查看:

$ docker exec -it 1a24 nslookup whoami
Server:    127.0.0.11
Address 1: 127.0.0.11

Name:      whoami
Address 1: 10.0.4.2 bogon

$ docker exec -it 1a24 nslookup tasks.whoami
Server:    127.0.0.11
Address 1: 127.0.0.11

Name:      tasks.whoami
Address 1: 10.0.4.3 whoami.1.yq3zung8aoumd72b4l06zwd54.demo
Address 2: 10.0.4.8 whoami.2.px32xnis2hnx8huukoegltrtj.demo
Address 3: 10.0.4.9 whoami.3.uuu0dvphuqzl7kav9yk6gm86b.demo

也可以这样去验证,只需要在本地上去访问这个服务,看它返回的主机信息:

[root@node1 ~]$ curl http://127.0.0.1:8000
I'm d32684a5c6b6
[root@node1 ~]$ curl http://127.0.0.1:8000
I'm 5080f4b56526
[root@node1 ~]$ curl http://127.0.0.1:8000
I'm 6fafaed61f64

Ingress

概述

当在 任何 一个 swarm 节点去访问 端口服务的时候 会通过 本节点 的 IPVS ( ip virtual service ) 到 真正的 swarm 节点上。

功能

  • 外部访问的均衡负载
  • 服务端口被暴露到各个swarm节点
  • 内部通过 IPVS 进行均衡负载

原理分析

  • 转发规则

用上一节使用whoami service 来查看

我们可以看看worker节点上没有存在的服务它是怎么访问的,如果访问它内部是怎么转发的。

$ iptables -L -n -t nat     
...
Chain DOCKER-INGRESS (2 references)
target     prot opt source               destination         
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000 to:172.18.0.2:8000
RETURN     all  --  0.0.0.0/0            0.0.0.0/0  
...

可以看到iptables 会把请求转发到172.18.0.2:8000这个地址上去,那么我们看看本机的ip:

$ ip a
...
4: docker_gwbridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:64:49:83:64 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global docker_gwbridge
       valid_lft forever preferred_lft forever
    inet6 fe80::42:64ff:fe49:8364/64 scope link 
       valid_lft forever preferred_lft forever
...

没有找到172.18.0.2,但是找到了docker_gwbridge,可以看到两个ip处于同一网段,那么172.18.0.2应该也连接上docker_gwbridge。查看下桥接配置

$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242abd9beea       no
docker_gwbridge 8000.024264498364       no              veth103b8e6
                                                        vetha3f1870
                                                        vethb8ad1cd

看到它有多个interface,但是哪一个才是 172.18.0.2使用的呢?查看下 docker_gwbridge

$ docker network inspect docker_gwbridge
...
        "Containers": {
            "890cd1e1521eaadd16ff81441c9682abcf8fa72750964084d2c82cb20d5059b4": {
                "Name": "gateway_e5100e10eae9",
                "EndpointID": "677710cde7562a42806011867919a2c413d0656158f5ca9abd8b48b0ef50bbd2",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            },
            "d32684a5c6b6da417980f46be89e3bd97b99c399271bb56c3c28a61cbcb45efe": {
                "Name": "gateway_efca9cc9bb58",
                "EndpointID": "84cea76a6f86c7cbbbf23d7fc9ecca2d9fce10160a66bad6a7a58bc9ddaf647e",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "ingress-sbox": {
                "Name": "gateway_ingress-sbox",
                "EndpointID": "8c140d8c03087243a6b899018c7577f850bd8c3ddd5d1fa96a07b96def786b07",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": 
...

可以发现gateway_ingress-sbox 的容器ip 就是我们需要找的。ingress-sbox就是 网络命名空间(network namespace)。

  • 进入ingress-sbox

我们可以先查看docker中所有的Network Namespace:

$ ls /var/run/docker/netns
1-748vwsjqoq  1-dmmsdyma59  e5100e10eae9  efca9cc9bb58  ingress_sbox  lb_748vwsjqo

然后通过下面的命令进入ingress_sbox:

nsenter --net=/var/run/docker/netns/ingress_sbox

查看ip:

$ ip a
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
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
    link/ether 02:42:0a:00:00:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.0.7/24 brd 10.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 10.0.0.20/32 brd 10.0.0.20 scope global eth0
       valid_lft forever preferred_lft forever
30: eth1@if31: <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 1
    inet 172.18.0.2/16 brd 172.18.255.255 scope global eth1
       valid_lft forever preferred_lft forever

这个namespace中的ip就是我们要找的

当请求被转发到这个namespace后它又是怎么做的呢?我们先看看它的转发规则:

$ iptables -nL -t mangle

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
MARK       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8000 MARK set 0x117

可以看到,当转发8000端口时MARK set 0x117,接下来执行如下的命令:

$ ipvsadm -l

IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
FWM  279 rr
  -> 10.0.0.22:0                  Masq    1      0          0         
  -> 10.0.0.23:0                  Masq    1      0          0         
  -> 10.0.0.24:0                  Masq    1      0          0

随便找个whoami容器,查看ip

$ docker exec -it d326 ifconfig
...
eth1      Link encap:Ethernet  HWaddr 02:42:0A:00:00:16  
          inet addr:10.0.0.22  Bcast:10.0.0.255  Mask:255.255.255.0
...

可以看出 与 ipvsadm 对应上了

总结

原理如图

当我们访问任一节点的8000端口时,只要我们这个节点处于Swarm集群中,不管服务是否部署到这个节点都能访问,只要端口相同即可。我们本地的请求会被转发到Ingress_sbox这个Network Namespace中,在这个名称空间中再通过lvs转发到具体服务容器的ip和8000端口中去。

配置一个外部负载均衡

真正的生产环境一般是把代理服务器服务器暴露出去。这里使用haproxy来举例

这个是官方示例图。我们这里 还是用 whoami作为后端服务

可以在任一swarm 节点,或者 单独的机器安装haproxy,并配置

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

frontend http_front
   bind *:80
   stats uri /haproxy?stats
   default_backend http_back

backend http_back
    balance     roundrobin
    server  node1 172.16.35.13:8000 check
    server  node2 172.16.35.10:8000 check
    server  node3 172.16.35.11:8000 check

当访问HAProxy负载均衡的80端口时,它会把你的请求转发到swarm节点。然后swarm节点的routing mesh把请求路由到一个可用的服务上

results matching ""

    No results matching ""