分享

Neutron 系列 (12): Neutron 是如何实现负载均衡器虚拟化的【提高篇】

本帖最后由 eying 于 2016-1-13 17:01 编辑
问题导读:


1.什么是LBaas ?
2.
LBaas HAProxy 如何部署?

3.
LBaas的代码分析?








2. Neutron 中的虚拟负载均衡器

    Neutron LBaas (load-balancer-as-a-service)扩展(extension)提供向在多个 Nova 虚机中运行的应用提供负载均衡的方法。它还提供 API 来快速方便地部署负载均衡器。 它早在 OpenStack 的 Grizzly 版本就集成到 Neutron 中。自集成到 Neutron 以来,LBaaS 经历过几次大的变化。目前,在最新发布的 Kilo 版本中,LBaas 代码从 Neutron 中抽离,直接由独立的项目管理,以及实现了 LBaaS V2。本文是基于 Juno 版本的 LBaas 进行分析。 OpenStack Neutron 默认以 HAProxy 为负载均衡的 driver,同时也支持 A10 network(参考链接)、netscaler、radware (参考文档)等作为 driver。

2.1 LBaas 中的概念

LBaas 可以看做 OpenStack Neutron 对各种物理负载均衡器的虚拟化。它的概念可以和 HAProxy 中的概念进行类比:

HAProxy 的概念LBaas 的概念说明
Driver
LBaas 也是采取 driver 模型来支持多种物理的负载均衡器。LBaas 默认实现了 HAProxy driver,同时,它也支持多个其他 Vendor driver。
FrontendVIP(Virturl IP address)
LBaas 对外提供服务的地址。VIP 有自己的 IP 地址,而且一般都能通过公网进行访问。VIP 负责将网络流量分发到各个 member。
BackendPool代表负载后端的虚拟机池。在以 HAProxy 为 Driver 的情况下,一个 Pool 对应着在一个独立的 network namespace 中运行的 HAProxy 进程所管理的 backend。目前一个 pool 只能有一个 VIP。
Backend serverMemberMember 对应的是 pool 里面处理网络请求的一个 OpenStack Nova 虚机。
Health checkHealth monitor它用来监测 pool 里面 member 的状态,支持 HTTP, TCP, 和 ping 等多种检测方法。在 Nuetron 中这是可选的,如果没有 Health monitor,pool 会一直认为所有的 member 都是 ACTIVE 状态,这样所有的 member 会一直出现在 VIP 的分发列表中,哪怕 member 对应的实例不能响应网络请求。这在实际应用中会造成负载均衡的响应异常。

LBaas driver 模型:

1.jpg
基本概念:
2.jpg

基本概念之间的联系:

3.png

2.2 LBaas HAProxy 部署实例

  OpenStack 直接采用各种开源可用的负载均衡项目来完成负载均衡的任务,默认使用 HAProxy。LBaaS 所做的任务就是根据用户提出的负载均衡要求生成符合要求的HAProxy配置文件并启动 HAProxy,然后由 HAProxy 进行负载均衡。

2.2.1 安装

  不同的 LBaas drive 支持不同的部署模式。社区实现 LBaas HAPorxy driver 只支持 one-arm 模式。你可以部署LBaas Agent 在network 节点上,也可以部署在别的节点上。本实例中,将 neutron-lbaas-agent 安装在 network 节点上。你可以部署多个 LBaas 节点(agent),每个 agent 上运行不同的物理负载均衡器。

4.jpg

网络节点上:

apt-get install neutron-lbaas-agent

apt-get install haproxy

修改 /etc/neutron/lbaas_agent.ini:

interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver

device_driver = neutron.services.loadbalancer.drivers.haproxy.namespace_driver.HaproxyNSDriver

修改/etc/neutron/neutron.conf

service_plugins = router,lbaas

启动 service:

service neutron-lbaas-agent restart

lbaas-agent 进程:

neutron  18890     1  5 06:53 ?        00:00:01 /usr/bin/python /usr/bin/neutron-lbaas-agent --config-file=/etc/neutron/lbaas_agent.ini --config-file=/etc/neutron/neutron.conf --log-file=/var/log/neutron/lbaas-agent.log  

Controller 节点上:

修改/etc/neutron/neutron.conf

service_plugins = router,lbaas

service neutron-server restart

修改/etc/openstack-dashboard/local_settings.py:

OPENSTACK_NEUTRON_NETWORK = {
'enable_router': True,
'enable_quotas': True,
'enable_ipv6': True,
'enable_distributed_router': False,
'enable_ha_router': False,
'enable_lb': True

2.2.2 配置

5.jpg

(1)Create pool

s1@controller:~$ neutron lb-pool-list
+--------------------------------------+-------+----------+-------------+----------+----------------+--------+
| id | name | provider | lb_method | protocol | admin_state_up | status |
+--------------------------------------+-------+----------+-------------+----------+----------------+--------+
| 3b9d8ebd-9eea-4925-b14e-593a6111ff33 | pool1 | haproxy | ROUND_ROBIN | HTTP | True | ACTIVE |
+--------------------------------------+-------+----------+-------------+----------+----------------+--------+

不太理解创建 pool 时候选的 subnet 是什么用途。

(2)Create members

s1@controller:~$ neutron lb-member-list
+--------------------------------------+-------------+---------------+--------+----------------+--------+
| id | address | protocol_port | weight | admin_state_up | status |
+--------------------------------------+-------------+---------------+--------+----------------+--------+
| 1f74a288-937d-4804-9ded-472a5d1110dc | 81.1.180.13 | 80 | 1 | True | ACTIVE |
| 944ff4a0-4070-40e4-8189-20f385755113 | 91.1.180.14 | 80 | 1 | True | ACTIVE |
| c5bd3138-9635-4588-889f-d08fcd364ed4 | 81.1.180.12 | 80 | 1 | True | ACTIVE |
+--------------------------------------+-------------+---------------+--------+----------------+--------+

似乎对 member 自身没什么限制,只要是 pool 所在的 tenant 内的虚机都可以加入。

(3)Create VIP for pool

s1@controller:~$ neutron lb-vip-list
+--------------------------------------+------+-------------+----------+----------------+--------+
| id | name | address | protocol | admin_state_up | status |
+--------------------------------------+------+-------------+----------+----------------+--------+
| 0c32d37d-f84a-4309-9e01-72d9f0bac69e | vip2 | 81.1.180.81 | HTTP | True | ACTIVE |
+--------------------------------------+------+-------------+----------+----------------+--------+

VIP 的 subnet 也可以和 pool 的subnet 不一致,主要是在指定的 subnet 内创建一个 port。

(4)Create health monitor

s1@controller:~$ neutron lb-healthmonitor-list
+--------------------------------------+------+----------------+
| id | type | admin_state_up |
+--------------------------------------+------+----------------+
| df1fcdd1-c0b5-4e14-a2fe-2d0789fc26a5 | PING | True |
+--------------------------------------+------+----------------+

(5) associate health monitor with pool

s1@controller:~$ neutron lb-healthmonitor-associate df1fcdd1-c0b5-4e14-a2fe-2d0789fc26a5 3b9d8ebd-9eea-4925-b14e-593a6111ff33
Associated health monitor df1fcdd1-c0b5-4e14-a2fe-2d0789fc26a5
以上步骤后的 pool 的详细配置:
s1@controller:~$ neutron lb-pool-show 3b9d8ebd-9eea-4925-b14e-593a6111ff33
+------------------------+--------------------------------------------------------------------------------------------------------+
| Field | Value |
+------------------------+--------------------------------------------------------------------------------------------------------+
| admin_state_up | True |
| description | |
| health_monitors | df1fcdd1-c0b5-4e14-a2fe-2d0789fc26a5 |
| health_monitors_status | {"monitor_id": "df1fcdd1-c0b5-4e14-a2fe-2d0789fc26a5", "status": "ACTIVE", "status_description": null} |
| id | 3b9d8ebd-9eea-4925-b14e-593a6111ff33 |
| lb_method | ROUND_ROBIN |
| members | 1f74a288-937d-4804-9ded-472a5d1110dc |
|                | 944ff4a0-4070-40e4-8189-20f385755113 |
|                | c5bd3138-9635-4588-889f-d08fcd364ed4 |
| name      | pool1 |
| protocol   | HTTP |
| provider   | haproxy |
| status      | ACTIVE |
| status_description | |
| subnet_id | 4ec65731-35a5-4637-a59b-a9f2932099f1 |
| tenant_id | 74c8ada23a3449f888d9e19b76d13aab |
| vip_id | 19e16b0b-f48b-4f90-803c-d3afaf26c697 |
+------------------------+--------------------------------------------------------------------------------------------------------+

关于 pool,vip,member 的 subnet,在我的测试环境中,三者之间没什么限制,各自都可以处于不同的 subnet 中,只要subnet 之间配置好了 router。这个ticket 倒是提出来要限制 member 和 vip 都在 pool 的subnet 中,但是有人认为目前没什么必要。但是,lb-pool-create 的 help 信息中,subnet 指 ”The subnet on which the members of the pool will be located.“这个说明就和代码实现就有矛盾了。

2.2.3 验证

在 vm1 81.1.180.12 上,运行 while true; do echo -e 'HTTP/1.0 200 OK\r\n\r\nserver_151' | sudo nc -l -p 80 ; done

在 vm2 81.1.180.13 上,运行 while true; do echo -e 'HTTP/1.0 200 OK\r\n\r\nserver_153' | sudo nc -l -p 80 ; done

在vm3 上使用 wget http://81.1.180.12 和 wget http://81.1.180.13 ,能够返回结果,显示上面的命令成功运行。

再在 vm3 两次使用 wget http://81.1.180.81,结果分别显示 server_153 和 server_151,显示轮询分发成功。

6.jpg


2.2.4 LBaas 的实现

网络节点上,neutron-lbaas-agent:

(1)为每个带有 active VIP 的 pool 创建了一个 network namespace,它以 qlbaas-<pool UUID> 命名:ip netns add qlbaas-3b9d8ebd-9eea-4925-b14e-
593a6111ff33

root@network:/home/s1# ip netns list
qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33

[mw_shl_code=applescript,true]root@network:/home/s1# ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 ip addr
50: tap2d1b74fe-68: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
    link/ether fa:16:3e:7c:e5:ce brd ff:ff:ff:ff:ff:ff
    inet 81.1.180.81/24 brd 81.1.180.255 scope global tap2d1b74fe-68 #该 interface 的IP 地址为 VIP 的 IP 地址
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe7c:e5ce/64 scope link
       valid_lft forever preferred_lft forever[/mw_shl_code]

该 interface 挂载 OVS br-int 上并被打上了它所在的 network(pool -> subnet -> network) 对应的本地 VLAN ID:

[mw_shl_code=applescript,true]Bridge br-int        
        Port "tap2d1b74fe-68"
            tag: 4
            Interface "tap2d1b74fe-68"
                type: internal [/mw_shl_code]

完整的 network namespace 操作过程为:

[mw_shl_code=applescript,true]ovs-vsctl --if-exists del-port tapd9de9e84-23 --add-port br-int tapd9de9e84-23 --set Interface tapd9de9e84-23 type=internal --set Interface tapd9de9e84-23 external-ids:iface-id=d9de9e84-23ad-4f57-b85a-aea99abf409d --set Interface tapd9de9e84-23 external-ids:iface-status=active --set Interface tapd9de9e84-23 external-ids:attached-mac=fa:16:3e:f4:8f:ae
ip link set tapd9de9e84-23 address fa:16:3e:f4:8f:ae

ip netns add qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33  
ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 sysctl -w net.ipv4.conf.all.promote_secondaries=1
ip link set tapd9de9e84-23 netns qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33
ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 ip link set lo up
ip link set tapd9de9e84-23 netns qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33   
ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 ip link set tapd9de9e84-23 up
ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 ip addr show tapd9de9e84-23 permanent scope global
ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 ip -4 addr add 91.1.180.180/24 brd 91.1.180.255 scope global dev tapd9de9e84-23
ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 ip route list dev tapd9de9e84-23 scope link
ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 route add default gw 91.1.180.1
ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 arping -U -I tapd9de9e84-23 -c 3 91.1.180.180  [/mw_shl_code]

(2)生成 haproxy 配置文件

[mw_shl_code=applescript,true]global  #global 中除了 group 都是 hard coded 的
        daemon
        user nobody
        group nogroup  #可以由配置项 user_group 指定,默认为 nogroup
        log /dev/log local0
        log /dev/log local1 notice
        stats socket /var/lib/neutron/lbaas/3b9d8ebd-9eea-4925-b14e-593a6111ff33/sock mode 0666 level user

defaults #都是 hard coded 的
        log global
        retries 3
        option redispatch
        timeout connect 5000
        timeout client 50000
        timeout server 50000

frontend 0c32d37d-f84a-4309-9e01-72d9f0bac69e
        option tcplog
        bind 81.1.180.81:80 #VIP.IP:port
        mode http           #pool1.Protocol
        default_backend 3b9d8ebd-9eea-4925-b14e-593a6111ff33
        maxconn 100         # VIP.ConnectionLimit
        option forwardfor   # 当 mode 为 ”http“时,设置 forwardfor,使得通过 X-Forward-For 头来保存原始的源 IP 地址

backend 3b9d8ebd-9eea-4925-b14e-593a6111ff33
        mode http           #pool.protocol
        balance roundrobin  #pool1.Load_Balancing_Method
        option forwardfor
        timeout check 5s    # Monitor.timeout(5)
        server 1f74a288-937d-4804-9ded-472a5d1110dc 81.1.180.13:80 weight 1 check inter 5s fall 3 #member1 的配置,包括 ip,port,weight;Monitor 的 delay(5),Max Retries (3)
        server 944ff4a0-4070-40e4-8189-20f385755113 91.1.180.14:80 weight 1 check inter 5s fall 3 #member 2 的配置
        server c5bd3138-9635-4588-889f-d08fcd364ed4 81.1.180.12:80 weight 1 check inter 5s fall 3 #member 3 的配置 [/mw_shl_code]

(3)在 network namespace 中 启动了一个 haproxy 进程,使用生成的配置文件:

[mw_shl_code=applescript,true]ip netns exec qlbaas-3b9d8ebd-9eea-4925-b14e-593a6111ff33 haproxy -f /var/lib/neutron/lbaas/3b9d8ebd-9eea-4925-b14e-593a6111ff33/conf -p /var/lib/neutron/lbaas/3b9d8ebd-9eea-4925-b14e-593a6111ff33/pid

root@network:/home/s1# ps -ef | grep haproxy
nobody 22625 1 0 07:29 ? 00:00:02 haproxy -f /var/lib/neutron/lbaas/3b9d8ebd-9eea-4925-b14e-593a6111ff33/conf -p /var/lib/neutron/lbaas/3b9d8ebd-9eea-4925-b14e-593a6111ff33/pid -sf 22365[/mw_shl_code]

(4)在 lbaas 的 interface 上抓包,看看几个机器之间的交互过程

从 81.1.180.14 上 wget 81.1.180.81,VIP 从 81.1.180.12 获取数据

[mw_shl_code=applescript,true]# 81.1.180.14 通过 ARP 得到 81.1.180.81 的 MAC
10:20:33.070752 fa:16:3e:82:37:03 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Ethernet (len 6), IPv4 (len 4), Request who-has 81.1.180.81 tell 81.1.180.14, length 28
10:20:33.070779 fa:16:3e:7c:e5:ce > fa:16:3e:82:37:03, ethertype ARP (0x0806), length 42: Ethernet (len 6), IPv4 (len 4), Reply 81.1.180.81 is-at fa:16:3e:7c:e5:ce, length 28

#发起 81.1.180.14.41882 到 81.1.180.81.80 的 TCP 连接(三次握手)
10:20:33.073829 fa:16:3e:82:37:03 > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 24973, offset 0, flags [DF], proto TCP (6), length 60)
    81.1.180.14.41882 > 81.1.180.81.80: Flags [S], cksum 0x380e (correct), seq 2662307254, win 14600, options [mss 1460,sackOK,TS val 62523 ecr 0,nop,wscale 2], length 0
10:20:33.073884 fa:16:3e:7c:e5:ce > fa:16:3e:82:37:03, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    81.1.180.81.80 > 81.1.180.14.41882: Flags [S.], cksum 0x0a91 (incorrect -> 0xc0ae), seq 1950187908, ack 2662307255, win 28960, options [mss 1460,sackOK,TS val 19810369 ecr 62523,nop,wscale 7], length 0
10:20:33.078375 fa:16:3e:82:37:03 > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 24974, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.14.41882 > 81.1.180.81.80: Flags [.], cksum 0x5257 (correct), seq 1, ack 1, win 3650, options [nop,nop,TS val 62525 ecr 19810369], length 0

# HTTP 数据从 81.1.180.14.41882 发到 81.1.180.81.80
10:20:33.079796 fa:16:3e:82:37:03 > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 140: (tos 0x0, ttl 64, id 24975, offset 0, flags [DF], proto TCP (6), length 126)
    81.1.180.14.41882 > 81.1.180.81.80: Flags [P.], cksum 0x1a1e (correct), seq 1:75, ack 1, win 3650, options [nop,nop,TS val 62525 ecr 19810369], length 74
10:20:33.079902 fa:16:3e:7c:e5:ce > fa:16:3e:82:37:03, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 51655, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.81.80 > 81.1.180.14.41882: Flags [.]^C, cksum 0x0a89 (incorrect -> 0x5f6a), seq 1, ack 75, win 227, options [nop,nop,TS val 19810371 ecr 62525], length 0

#发起 81.1.180.81.49215 > 81.1.180.12.80 的连接
10:20:33.080106 fa:16:3e:7c:e5:ce > fa:16:3e:2b:3e:2a, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 49199, offset 0, flags [DF], proto TCP (6), length 60)
    81.1.180.81.49215 > 81.1.180.12.80: Flags [S], cksum 0x0a8f (incorrect -> 0xb04a), seq 2295826540, win 29200, options [mss 1460,sackOK,TS val 19810371 ecr 0,nop,wscale 7], length 0
10:20:33.080936 fa:16:3e:2b:3e:2a > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 74: (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    81.1.180.12.80 > 81.1.180.81.49215: Flags [S.], cksum 0xeeb5 (correct), seq 1301661959, ack 2295826541, win 14480, options [mss 1460,sackOK,TS val 2352200 ecr 19810371,nop,wscale 2], length 0
10:20:33.081056 fa:16:3e:7c:e5:ce > fa:16:3e:2b:3e:2a, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 49200, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.81.49215 > 81.1.180.12.80: Flags [.], cksum 0x0a87 (incorrect -> 0x5528), seq 1, ack 1, win 229, options [nop,nop,TS val 19810371 ecr 2352200], length 0

##传送 HTTP 数据
#数据从 81.1.180.81.49215 发到 81.1.180.12.80
10:20:33.081264 fa:16:3e:7c:e5:ce > fa:16:3e:2b:3e:2a, ethertype IPv4 (0x0800), length 170: (tos 0x0, ttl 64, id 49201, offset 0, flags [DF], proto TCP (6), length 156)
    81.1.180.81.49215 > 81.1.180.12.80: Flags [P.], cksum 0x0aef (incorrect -> 0x06d5), seq 1:105, ack 1, win 229, options [nop,nop,TS val 19810371 ecr 2352200], length 104
10:20:33.093515 fa:16:3e:2b:3e:2a > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 11584, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.12.80 > 81.1.180.81.49215: Flags [.], cksum 0x4781 (correct), seq 1, ack 105, win 3620, options [nop,nop,TS val 2352200 ecr 19810371], length 0

#数据从 81.1.180.12.80 发到 81.1.180.81.49215
10:20:33.110793 fa:16:3e:2b:3e:2a > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 96: (tos 0x0, ttl 64, id 11585, offset 0, flags [DF], proto TCP (6), length 82)
    81.1.180.12.80 > 81.1.180.81.49215: Flags [P.], cksum 0xcc98 (correct), seq 1:31, ack 105, win 3620, options [nop,nop,TS val 2352207 ecr 19810371], length 30
10:20:33.110904 fa:16:3e:7c:e5:ce > fa:16:3e:2b:3e:2a, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 49202, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.81.49215 > 81.1.180.12.80: Flags [.], cksum 0x0a87 (incorrect -> 0x5494), seq 105, ack 31, win 229, options [nop,nop,TS val 19810378 ecr 2352207], length 0

# HTTP 数据从 81.1.180.81.80 发到 81.1.180.14.41882
10:20:33.111083 fa:16:3e:7c:e5:ce > fa:16:3e:82:37:03, ethertype IPv4 (0x0800), length 96: (tos 0x0, ttl 64, id 51656, offset 0, flags [DF], proto TCP (6), length 82)
    81.1.180.81.80 > 81.1.180.14.41882: Flags [P.], cksum 0x0aa7 (incorrect -> 0xe480), seq 1:31, ack 75, win 227, options [nop,nop,TS val 19810379 ecr 62525], length 30
10:20:33.124295 fa:16:3e:82:37:03 > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 24976, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.14.41882 > 81.1.180.81.80: Flags [.], cksum 0x51db (correct), seq 75, ack 31, win 3650, options [nop,nop,TS val 62535 ecr 19810379], length 0

#关闭 81.1.180.12.80 > 81.1.180.81.49215 和 81.1.180.81.80 > 81.1.180.14.41882 的连接 (三次挥手。本来是四次,第四次可能有延迟)
10:20:33.124322 fa:16:3e:2b:3e:2a > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 11586, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.12.80 > 81.1.180.81.49215: Flags [F.], cksum 0x4751 (correct), seq 31, ack 105, win 3620, options [nop,nop,TS val 2352210 ecr 19810378], length 0
10:20:33.124444 fa:16:3e:7c:e5:ce > fa:16:3e:82:37:03, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 51657, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.81.80 > 81.1.180.14.41882: Flags [F.], cksum 0x0a89 (incorrect -> 0x5f36), seq 31, ack 75, win 227, options [nop,nop,TS val 19810382 ecr 62535], length 0
10:20:33.173331 fa:16:3e:7c:e5:ce > fa:16:3e:2b:3e:2a, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 49203, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.81.49215 > 81.1.180.12.80: Flags [.], cksum 0x0a87 (incorrect -> 0x5480), seq 105, ack 32, win 229, options [nop,nop,TS val 19810394 ecr 2352210], length 0
10:20:33.173567 fa:16:3e:82:37:03 > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 24977, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.14.41882 > 81.1.180.81.80: Flags [F.], cksum 0x51cd (correct), seq 75, ack 32, win 3650, options [nop,nop,TS val 62544 ecr 19810382], length 0
10:20:33.173756 fa:16:3e:7c:e5:ce > fa:16:3e:82:37:03, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 51658, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.81.80 > 81.1.180.14.41882: Flags [.], cksum 0x0a89 (incorrect -> 0x5f20), seq 32, ack 76, win 227, options [nop,nop,TS val 19810394 ecr 62544], length 0
10:20:33.176286 fa:16:3e:7c:e5:ce > fa:16:3e:2b:3e:2a, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 49204, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.81.49215 > 81.1.180.12.80: Flags [F.], cksum 0x0a87 (incorrect -> 0x547e), seq 105, ack 32, win 229, options [nop,nop,TS val 19810395 ecr 2352210], length 0
10:20:33.190055 fa:16:3e:2b:3e:2a > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 66: (tos 0x0, ttl 64, id 11587, offset 0, flags [DF], proto TCP (6), length 52)
    81.1.180.12.80 > 81.1.180.81.49215: Flags [.], cksum 0x4731 (correct), seq 32, ack 106, win 3620, options [nop,nop,TS val 2352224 ecr 19810395], length 0[/mw_shl_code]

看看具体的数据包:

[mw_shl_code=applescript,true]# 81.1.180.14 发送 GET 命令到 81.1.180.81
11:04:16.282603 fa:16:3e:82:37:03 > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 140: (tos 0x0, ttl 64, id 53145, offset 0, flags [DF], proto TCP (6), length 126)
    81.1.180.14.41883 > 81.1.180.81.80: Flags [P.], cksum 0xd178 (correct), seq 1:75, ack 1, win 3650, options [nop,nop,TS val 718394 ecr 20466170], length 74
        0x0000:  4500 007e cf99 4000 4006 607e 5101 b40e  E..~..@.@.`~Q...
        0x0010:  5101 b451 a39b 0050 e1b8 39b0 359e 1dfd  Q..Q...P..9.5...
        0x0020:  8018 0e42 d178 0000 0101 080a 000a f63a  ...B.x.........:
        0x0030:  0138 49fa 4745 5420 2f20 4854 5450 2f31  .8I.GET./.HTTP/1
        0x0040:  2e31 0d0a 486f 7374 3a20 3831 2e31 2e31  .1..Host:.81.1.1
        0x0050:  3830 2e38 310d 0a55 7365 722d 4167 656e  80.81..User-Agen
        0x0060:  743a 2057 6765 740d 0a43 6f6e 6e65 6374  t:.Wget..Connect
        0x0070:  696f 6e3a 2063 6c6f 7365 0d0a 0d0a       ion:.close....

#81.1.180.81 转发上面的 GET 请求到 91.1.180.14,可见 Host 已经换成 VIP 的 IP 了,原始 IP 保存在X-Forwarded-For 中。
11:04:16.283954 fa:16:3e:7c:e5:ce > fa:16:3e:87:40:f3, ethertype IPv4 (0x0800), length 170: (tos 0x0, ttl 64, id 19645, offset 0, flags [DF], proto TCP (6), length 156)
    81.1.180.81.46000 > 91.1.180.14.80: Flags [P.], cksum 0x14f1 (incorrect -> 0x41ae), seq 1:105, ack 1, win 229, options [nop,nop,TS val 20466172 ecr 17195073], length 104
        0x0000:  4500 009c 4cbd 4000 4006 d93c 5101 b451  E...L.@.@..<Q..Q
        0x0010:  5b01 b40e b3b0 0050 bc0d 0541 897a 262f  [......P...A.z&/
        0x0020:  8018 00e5 14f1 0000 0101 080a 0138 49fc  .............8I.
        0x0030:  0106 6041 4745 5420 2f20 4854 5450 2f31  ..`AGET./.HTTP/1
        0x0040:  2e31 0d0a 486f 7374 3a20 3831 2e31 2e31  .1..Host:.81.1.1
        0x0050:  3830 2e38 310d 0a55 7365 722d 4167 656e  80.81..User-Agen
        0x0060:  743a 2057 6765 740d 0a43 6f6e 6e65 6374  t:.Wget..Connect
        0x0070:  696f 6e3a 2063 6c6f 7365 0d0a 582d 466f  ion:.close..X-Fo
        0x0080:  7277 6172 6465 642d 466f 723a 2038 312e  rwarded-For:.81.
        0x0090:  312e 3138 302e 3134 0d0a 0d0a            1.180.14....

#91.1.180.14 发回回复消息给 81.1.180.81
11:04:16.288376 fa:16:3e:87:40:f3 > fa:16:3e:7c:e5:ce, ethertype IPv4 (0x0800), length 96: (tos 0x0, ttl 63, id 18031, offset 0, flags [DF], proto TCP (6), length 82)
    91.1.180.14.80 > 81.1.180.81.46000: Flags [P.], cksum 0x0676 (correct), seq 1:31, ack 105, win 3620, options [nop,nop,TS val 17195074 ecr 20466172], length 30
        0x0000:  4500 0052 466f 4000 3f06 e0d4 5b01 b40e  E..RFo@.?...[...
        0x0010:  5101 b451 0050 b3b0 897a 262f bc0d 05a9  Q..Q.P...z&/....
        0x0020:  8018 0e24 0676 0000 0101 080a 0106 6042  ...$.v........`B
        0x0030:  0138 49fc 4854 5450 2f31 2e32 2032 3030  .8I.HTTP/1.2.200
        0x0040:  204f 4b0d 0a0d 0a73 6572 7665 725f 3135  .OK....server_15
        0x0050:  320a                                     2.

#81.1.180.81 将上面的消息转发到 81.1.180.14                               2.
11:04:16.288639 fa:16:3e:7c:e5:ce > fa:16:3e:82:37:03, ethertype IPv4 (0x0800), length 96: (tos 0x0, ttl 64, id 21511, offset 0, flags [DF], proto TCP (6), length 82)
    81.1.180.81.80 > 81.1.180.14.41883: Flags [P.], cksum 0x0aa7 (incorrect -> 0x9ae0), seq 1:31, ack 75, win 227, options [nop,nop,TS val 20466173 ecr 718394], length 30
        0x0000:  4500 0052 5407 4000 4006 dc3c 5101 b451  E..RT.@.@..<Q..Q
        0x0010:  5101 b40e 0050 a39b 359e 1dfd e1b8 39fa  Q....P..5.....9.
        0x0020:  8018 00e3 0aa7 0000 0101 080a 0138 49fd  .............8I.
        0x0030:  000a f63a 4854 5450 2f31 2e32 2032 3030  ...:HTTP/1.2.200
        0x0040:  204f 4b0d 0a0d 0a73 6572 7665 725f 3135  .OK....server_15
        0x0050:  320a                                     2.[/mw_shl_code]

3. Neutron LBaas 代码分析

代码在 https://github.com/openstack/neutron-lbaas/tree/stable/juno。代码本身相对简单。

代码分布:

7.jpg    

  • Extension:负责处理 REST API 请求,并进行 schedule,然后将 REST API 请求转发到 schedule 过程选出的 agent 上的 Plugin。
  • Plugin:处理核心逻辑,管理 DB 的读写。
  • Agent:处理并响应由 Plugin 发来的请求,管理 Driver。OpenStack Neutron 默认采用 HAProxy 作为 driver。

除此以外,LBaas 还提供提供CLI 和 Horizon 中的 GUI。

控制节点上的 Neutron server:

1. 根据配置文件,导入LBaas extension(service_plugins = *,lbaas,而 lbaas = neutron.services.loadbalancer.plugin:LoadBalancerPlugin)

2. 接到 lb-pool-create REST API 调用:

POST /v2.0/lb/pools.json HTTP/1.1

Route path: '/lb/pools.:(format)', defaults: {'action': u'create', 'controller': <wsgify at 139957175473808 wrapping <function resource at 0x7f4a51b962a8>>}
Match dict: {'action': u'create', 'controller': <wsgify at 139957175473808 wrapping <function resource at 0x7f4a51b962a8>>, 'format': u'json'}
Request body:  {u'pool': {u'subnet_id': u'4ac56c61-84f3-4d00-b87a-1ab2441e8437', u'lb_method': u'ROUND_ROBIN', u'protocol': u'HTTP', u'name': u'test2', u'admin_state_up': True}}

3. 根据 Scheduler 算法找到一个放置该 pool 的 LBaas agent

Pool cc719e63-0e84-4c0f-9254-49ba7c67e86d is scheduled to lbaas agent 90c87c01-1cd1-48b0-8369-30f44c058574 schedule

4. 进入 LoadBalancerAgentApi,通过 RPC 调用该 agent 所在节点上的 LBaas Plugin

cast called with arguments (<neutron.context.Context object at 0x7f4a51498590>, {'args': {'driver_name': 'haproxy_ns', 'pool': {'status': 'PENDING_CREATE', 'lb_method': u'ROUND_ROBIN', 'protocol': u'HTTP', 'description': '', 'health_monitors': [], 'members': [], 'status_description': None, 'id': 'cc719e63-0e84-4c0f-9254-49ba7c67e86d', 'vip_id': None, 'name': u'test2', 'admin_state_up': True, 'subnet_id': u'4ac56c61-84f3-4d00-b87a-1ab2441e8437', 'tenant_id': u'74c8ada23a3449f888d9e19b76d13aab', 'health_monitors_status': [], 'provider': 'haproxy'}}, 'namespace': None, 'method': 'create_pool'}) {'topic': u'n-lbaas_agent.network', 'version': None}

关于 Agent scheduler:

一个 OpenStack 环境中可以有多个 LBaas agent,每个 agen 可能支持不同的物理负载均衡器,那么将新建的 pool 对应的物理负载均衡器部署到哪个 Agent 上就需要一个 Schedule 过程。你可以查看pool 所在的 agent:

s1@controller:~$ neutron lb-agent-hosting-pool 3b9d8ebd-9eea-4925-b14e-593a6111ff33
+--------------------------------------+---------+----------------+-------+
| id | host | admin_state_up | alive |
+--------------------------------------+---------+----------------+-------+
| 90c87c01-1cd1-48b0-8369-30f44c058574 | network | True | :-) |
+--------------------------------------+---------+----------------+-------+

Neutron 默认只实现了ChanceScheduler: loadbalancer_pool_scheduler_driver = neutron.services.loadbalancer.agent_scheduler.ChanceScheduler。你也可以实现你的 Scheduler。默认的 ChanceScheduler 从活动的、支持该 Pool Provider(比如 haproxy)的所有 agent 中随机选择一个。

网络节点上的 LBaas Plugin (class LoadBalancerPlugin):

0. 初始化:从配置文件 device_driver 中读取所有drivers
1. 接到 REST API 调用后:
1.1 执行 DB 操作
1.2. 调用 driver 操作

网络节点上的 HAProxy LBaas driver:

1. 接收 RPC 通知,在 vip,pool,member,monitor 有变化时刷新 haproxy 进程
2. 通过 RPC 获取该 pool 的所有逻辑配置
3. 根据逻辑配置生成 haproxy 配置文件
4. 在network namespace 中启动 haproxy 进程



相关内容:


neutron系列:Neutron 所实现的虚拟化网络(1)


neutron系列:Neutron 所实现的虚拟化网络(2)

neutron系列:使用 Open vSwitch + VLAN 组网(3)

neutron系列:使用 Open vSwitch + VLAN 组网(4)

Neutron系列 : 使用Open vSwitch + GRE/VxLAN 组网 (5)

Neutron系列 : 使用Open vSwitch + GRE/VxLAN 组网(6)

Neutron系列 : Neutron OVS OpenFlow 流表 和 L2 Population(7)

Neutron系列 : Neutron OVS OpenFlow 流表 和 L2 Population(8)

Neutron系列 : Neutron 是如何向 Nova 虚机分配固定IP地址的(9)

Neutron系列 : Neutron 是如何向 Nova 虚机分配固定IP地址的(10)

Neutron 系列 (11): Neutron 是如何实现负载均衡器虚拟化的【基础篇】

Neutron 系列 (12): Neutron 是如何实现负载均衡器虚拟化的【提高篇】

Neutron 系列 (13): Neutron 是如何实现虚机防火墙的 【上】

Neutron 系列 (14): Neutron 是如何实现虚机防火墙的 【下】

Neutron 系列 (15): OpenStack 是如何实现 Neutron 网络 和 Nova虚机 防火墙的

Neutron 系列(16):虚拟专用网(VPN)虚拟化

Neutron 系列 (17): Neutron 分布式虚拟路由【上】

Neutron 系列 (18): Neutron 分布式虚拟路由【下】

Neutron系列(19):Neutron L3 Agent HA 之 虚拟路由冗余协议(VRRP)【上】

Neutron系列(20):Neutron L3 Agent HA 之 虚拟路由冗余协议(VRRP)【下】

Neutron系列(21):OpenStack 高可用和灾备方案 [OpenStack HA and DR]【上】

Neutron系列(22):OpenStack 高可用和灾备方案 [OpenStack HA and DR]【下】




已有(2)人评论

跳转到指定楼层
xuezhiji 发表于 2016-5-6 15:46:03
不错,学习了
回复

使用道具 举报

sheep113 发表于 2016-6-30 14:52:57
Controller 节点上:
修改/etc/neutron/neutron.conf
service_plugins = router,lbaas
重启neutron-server后,执行nova list,报错:
ERROR (ClientException): The server has either erred or is incapable of performing the requested operation. (HTTP 500) (Request-ID: req-3f690ef3-4a8d-4ea0-a9d3-d0e8bc7512ec)
改回service_plugins = router 就OK了,但dashboard没有出现“负载均衡器”
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条