Octavia--Neutron中LBaaS的参考实现
原文链接 https://lingxiankong.github.io/2017-09-13-octavia.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
更新历史:
- 2016.03,初稿完成
- 2017.09,针对 Pike 版本更新
- 2018.01,添加了几句对 lb 中 vip port 的解释
Octavia简介
Octavia主要参与贡献厂商:http://stackalytics.com/?project_type=openstack&metric=commits&module=octavia
Octavia的开发者文档:https://docs.openstack.org/octavia/latest/
因为此前 Rackspace 和 HPE 都部署了 Octavia,并且有工程师团队支持,所以该项目的文档还算是比较完善。
你需要了解的前置知识:
- OpenStack :-)
Haproxy和Keeplived。Octavia说白了,就是将用户的API请求经过逻辑处理,转换成haproxy和keepalived的配置参数,下发到amphorae虚拟机中。关于haproxy和keepalived,可以详细阅读各自的官方文档。同时,推荐如下几篇博客:
- 介绍haproxy+keepalive安装配置,写于06 Nov 2011:http://weizhifeng.net/HA-with-HAProxy-and-KeepAlived.html
- haproxy互为主备, 2013-09-29:http://502245466.blog.51cto.com/7559397/1303506
- Keepalived Check and Notify Scripts, February 21, 2014: https://tobrunet.ch/2013/07/keepalived-check-and-notify-scripts/
- 这个对haproxy的SSL Termination写的比较赞, Jul 10, 2014: https://www.digitalocean.com/community/tutorials/how-to-implement-ssl-termination-with-haproxy-on-ubuntu-14-04,其实作者关于haproxy的整个系列都不错。
TaskFlow。Octavia的内部实现中,逻辑流程的处理主要使用TaskFlow库。关于TaskFlow,请参见官方文档。
从 Pike 版本开始,Octavia 就可以作为独立的Keystone服务而不再是 Neutron 的一个 service plugin。以前在 neutron-lbaas repo 中的 plugin 后续都会被建议移动到 octavia repo 中。同时,CLI 命令也作为 openstack 命令行的一部分,而不是通过 neutron 调用。
$ openstack service list
+----------------------------------+-------------+----------------+
| ID | Name | Type |
+----------------------------------+-------------+----------------+
| 16e8a8e387d24592b417c5374a106c09 | neutron | network |
| 3c33acf1b6f947ba97978eb309c34c83 | nova | compute |
| 5683514d56fe44cb8cc42c6952d049ab | octavia | load-balancer |
| 8c2925f2dca64a6892d7df9f3916dce1 | placement | placement |
| b032bcb879fb4ae3a7159b7086b66db8 | nova_legacy | compute_legacy |
| b76c2014831c49e78d3e9ad993ecabdc | keystone | identity |
| b7d2d6340bc0452f98ab1f5b30438fc5 | glance | image |
| bc469b032c0742e19342e3694c2c572c | cinderv3 | volumev3 |
| c20384962aa84b2d8c45d3636073913a | cinderv2 | volumev2 |
| d7b7a142028e42ec9aa9ed51c3245465 | cinder | volume |
+----------------------------------+-------------+----------------+
$ openstack loadbalancer list --print-empty
+----+------+------------+-------------+---------------------+----------+
| id | name | project_id | vip_address | provisioning_status | provider |
+----+------+------------+-------------+---------------------+----------+
+----+------+------------+-------------+---------------------+----------+
命令行使用流程
如下几个命令创建一个 loadbalancer,一个 listener 一个 pool,同时添加两个 member。
neutron lbaas-loadbalancer-create --name lb1 private-subnet
neutron lbaas-listener-create --loadbalancer lb1 --protocol HTTP --protocol-port 80 --name listener1
neutron lbaas-pool-create --lb-algorithm ROUND_ROBIN --listener listener1 --protocol HTTP --name pool1
neutron lbaas-member-create --subnet private-subnet --address ${IP1} --protocol-port 80 pool1
neutron lbaas-member-create --subnet private-subnet --address ${IP2} --protocol-port 80 pool1
如果使用 openstack CLI,示例:
openstack loadbalancer create --name test_1 --vip-subnet-id f2492f6d-abe8-4600-9620-ea0b30e0f04c
openstack loadbalancer listener create --name test_1 --protocol HTTP --protocol-port 80 a32dfcac-3f65-454d-acb1-fc038434b0ed
openstack loadbalancer pool create --name test_pool_1 --protocol HTTP --listener 7a14d263-d19f-49c6-b7ac-35dc74f95e7e --lb-algorithm ROUND_ROBIN
openstack loadbalancer member create --name member_1 --address 10.0.0.14 --subnet-id f2492f6d-abe8-4600-9620-ea0b30e0f04c --protocol-port 80 1922e4a8-3538-43ff-9489-7cb3b35e6bb0
开发者笔记
Octavia 的 API 框架是 Pecan+WSME,API层的 handler 和 worker 层的 worker 都是可替换的。
创建 loadbalancer 流程
目前支持single和active standby两种模式的loadbalancer,通过配置文件配置(用户无法指定)。测试环境可以是single,但生产环境还是建议active standby。社区正在开发active active,目前(Pike)暂不支持。本文以active standby模式为例。
创建loadbalancer的参数中可以指定 vip 绑定的 port,如果没有指定,octavia 会自动(在 API 层)创建:
port = {'port': {'name': 'octavia-lb-' + load_balancer.id,
'network_id': load_balancer.vip.network_id,
'admin_state_up': False,
'device_id': 'lb-{0}'.format(load_balancer.id),
'device_owner': 'Octavia'}}
普通创建 lb 时,在 API 层会创建 lb 和 vip 的数据库记录,然后把请求交由 worker 处理。
创建loadbalancer,Octavia会创建两个虚拟机。如果配置enable_anti_affinity
,则会针对这个 lb 先在Nova创建ServerGroup(这个ServerGroup的ID会记录在DB中),两个虚拟机就会创建在不同的host上。虚拟机的flavor、image、network、security group、keypair信息都是从配置文件中获取,其中network就是Octavia管理进程与虚拟机通信的管理平面。
Octavia每创建一个 loadbalancer,都会在admin租户下创建包含同样 policy 的 server group。虚拟机以及虚拟机上的port也属于 octavia 租户。即:创建 loadbalancer 的租户只能看到这个 loadbalancer 的信息以及 loadbalancer 所占用的 port(VIP的port)信息,背后的VM、VM的port、SecurityGroup、ServerGroup 都是不可见的。一个 lb 的创建会占用租户 subnet 内的 ip 资源和 port 配额(一般会在 subnet 中创建3个 port)。
有了虚拟机后,同时在入参的subnet下给两个虚拟机分别挂载网卡,并将VIP作为address pair配置到网卡。对这几个port配置相应的安全组规则。
allowed_address_pairs
特性,参见这里.
然后,向虚拟机发送REST API消息 (post vip plug):
POST https://<mgmt-ip>:9876/0.5/plug/vip/<ip address>
参数中有VIP所在 subnet 的 CIDR,网关 IP,vrrp port 的 mac 地址,vrrp port 的 IP 地址等信息。amphora-agent 的处理过程如下:
- 如果vrrp port所在的网卡已经在namespace(amphora-haproxy)中,则直接返回。
- 根据vrrp port的mac地址找到vrrp网卡,也就是对外提供LB服务的网卡(比如eth1, 一般eth0是mgmt-net上的网卡)
- 设置网卡配置文件,将系统/etc/network目录下的文件(除去eth0和openssh相关文件)复制到
/etc/netns/amphora-haproxy/network
目录下。新建/etc/netns/amphora-haproxy/network/interfaces
文件,其内容中source /etc/netns/amphora-haproxy/network/interfaces.d/*.cfg
- 向
/etc/netns/amphora-haproxy/network/interfaces.d/eth1.cfg
中写入网卡配置,eth1是vrrp ip,eth1:0是vip ip - 更新
/var/lib/octavia/plugged_interfaces
,内容{mac_address} {interface}
,据注释说是为了重启时添加到namespace用的 - 创建名为amphora-haproxy的namespace,将eth1添加到namespace下
- 重新激活eth1和eth1:0 (
ip netns exec amphora-haproxy ifdown/ifup eth1
)
总结一下就是,把提供LB服务的网卡放在namespace下
如果是active standby模式,还需要向amphora发送如下消息配置 keepalived 服务:
GET https://<mgmt-ip>:9876/0.5/interface/<vrrp_ip_address>
,找到vrrp网卡名称(通常是eth1)并更新到amphora表的vrrp_interface
字段,在DB添加vrrp_group
表记录PUT https://<mgmt-ip>:9876/0.5/vrrp/upload
,将keepalived配置文件推送到两个虚拟机中(配置文件是/var/lib/octavia/vrrp/octavia-keepalived.conf
),注册octavia-keepalived
服务,keepalived的vrrp check脚本是/var/lib/octavia/vrrp/check_script.sh
,这个脚本会调用/var/lib/octavia/vrrp/check_scripts/
目录下的haproxy_check_script.sh
脚本(该脚本又会调用 octavia 提供的脚本haproxy-vrrp-check
,通过 socket 检测每个 listener 的 status,即:哪怕有一个 listener 状态不对,就会上报 fail)。最后在命名空间中启动虚拟机中的octavia-keepalived
服务PUT https://<mgmt-ip>:9876/0.5/vrrp/start
,启动octavia-keepalived
服务
至此,一个 loadbalancer 就创建结束了。基本上,后面创建listener、pool、member、health monitor,都是围绕这两个虚拟机,对haproxy和keepalived进程进行配置。
换句话说,在 octavia 中,资源之间的映射关系如下:
- lb:就是两个管理员租户的虚拟机
- listener:虚拟机里面的一个 haproxy 进程,frontend 配置
- pool:haproxy 配置中的一个 backend
- member:backend 配置中的一个 member
Amphorae VM
amphorae vm 操作系统支持 Ubuntu 和 Redhat
在虚拟机里面,除了haproxy和 octavia-keepalived 服务,还有最重要的amphora-agent服务。amphora-agent 启动脚本是 octavia repo 中 cmd 目录下的 agent.py。amphora-agent 服务在 Pike 版本是 gunicorn + flask 实现。除了提供 API,amphora-agent 还做一件事,定时向health-monitor发送haproxy的运行时信息,该信息是通过向haproxy进程发送socket查询命令获取到。
创建完 lb,登录 amphorae 验证创建 lb 后的网络配置,可以看到默认只能看到管理 IP,只有在 namespace 中才能看到 vrrp 网卡信息:
(普通用户看到 lb 的信息)
# openstack loadbalancer show a32dfcac-3f65-454d-acb1-fc038434b0ed
+---------------------+--------------------------------------+
| Field | Value |
+---------------------+--------------------------------------+
| admin_state_up | True |
| created_at | 2017-09-12T02:53:41 |
| description | |
| flavor | |
| id | a32dfcac-3f65-454d-acb1-fc038434b0ed |
| listeners | |
| name | test_1 |
| operating_status | ONLINE |
| pools | |
| project_id | e27b1a9084dd4a458a198e70ba0c8e04 |
| provider | octavia |
| provisioning_status | ACTIVE |
| updated_at | 2017-09-12T03:00:12 |
| vip_Address | 10.0.0.12 |
| vip_network_id | 99e7369f-2bf5-4124-b61b-d33f75502d23 |
| vip_port_id | c9ef59e9-b487-4d01-8056-40b486af06a9 |
| vip_subnet_id | f2492f6d-abe8-4600-9620-ea0b30e0f04c |
+---------------------+--------------------------------------+
(管理员租户看到两个虚拟机)
# nova list
+--------------------------------------+----------------------------------------------+--------+------------+-------------+----------------------------------------------------------------------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+----------------------------------------------+--------+------------+-------------+----------------------------------------------------------------------------------+
| 2404547f-23ba-4e30-806e-4a13e00389e7 | amphora-0b29847e-c965-40c3-9fec-0ebd1ab04999 | ACTIVE | - | Running | lb-mgmt-net=192.168.0.3; private=fd00:3cd5:bb0c:0:f816:3eff:febd:4ed5, 10.0.0.5 |
| 6f195ce1-47b0-4d81-ab78-14fd8d1900ef | amphora-53de64ec-0ae7-41bb-b135-695c7eecbd3b | ACTIVE | - | Running | lb-mgmt-net=192.168.0.4; private=fd00:3cd5:bb0c:0:f816:3eff:fe8c:10d6, 10.0.0.11 |
+--------------------------------------+----------------------------------------------+--------+------------+-------------+----------------------------------------------------------------------------------+
(登录任意一个amphorae,因为 devstack 是 all-in-one,而 neutron 又使用 namespace,所以需要在 namespace 中连接 amphorae)
$ NAMESPACE=qdhcp-1bc7cc02-d815-4b9a-87c5-144fe1d21d63
$ MGMT_IP=192.168.0.4
$ ip netns exec $NAMESPACE ssh -i /etc/octavia/.ssh/octavia_ssh_key -o StrictHostKeyChecking=no ubuntu@$MGMT_IP
ubuntu@amphora-0b29847e-c965-40c3-9fec-0ebd1ab04999:~$ sudo -s
# ip netns list
amphora-haproxy
# ifconfig
ens3 Link encap:Ethernet HWaddr fa:16:3e:cf:c0:64
inet addr:192.168.0.3 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::f816:3eff:fecf:c064/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
RX packets:500 errors:0 dropped:0 overruns:0 frame:0
TX packets:643 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:46353 (46.3 KB) TX bytes:85265 (85.2 KB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 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:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
# ip netns exec amphora-haproxy ifconfig
eth1 Link encap:Ethernet HWaddr fa:16:3e:bd:4e:d5
inet addr:10.0.0.5 Bcast:10.0.0.63 Mask:255.255.255.192
inet6 addr: fd00:3cd5:bb0c:0:f816:3eff:febd:4ed5/64 Scope:Global
inet6 addr: fe80::f816:3eff:febd:4ed5/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
RX packets:51 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5986 (5.9 KB) TX bytes:496 (496.0 B)
eth1:0 Link encap:Ethernet HWaddr fa:16:3e:bd:4e:d5
inet addr:10.0.0.12 Bcast:10.0.0.63 Mask:255.255.255.192
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
# curl --insecure https://localhost:9443/0.5/info
{"haproxy_version":"1.6.3-1ubuntu0.1","hostname":"amphora-0b29847e-c965-40c3-9fec-0ebd1ab04999","api_version":"0.5"}
创建 listener 流程
在Octavia中,一个listener就对应amphorae 中一个haproxy进程。
首先生成haproxy配置文件,向amp发送消息:
PUT https://<mgmt-ip>:9876/0.5/listeners/{amphora_id}/{listener_id}/haproxy
haproxy的配置文件在/var/lib/octavia/{listener-id}/haproxy.cfg
,amphora-agent会先调用haproxy -c -L {peer} -f {config_file} -f {haproxy_ug}
校验配置文件,然后生成对应该 listener 的 haproxy 服务脚本。
再次向 amphorae 发送消息启动 haproxy 服务:
PUT https://<mgmt-ip>:9876/0.5/listeners/{listener_id}/start
amphora-agent 的处理:
- 先确定listener的配置目录(
/var/lib/octavia/{listener-id}/
)在不在 - 如果是active standby,更新keepalived对各个haproxy的check脚本,
/var/lib/octavia/vrrp/check_scripts/haproxy_check_script.sh
- 启动haproxy服务,
service haproxy-{listener_id} start
创建 pool
创建 pool 的实现基本跟创建 listener 一致,在 amphorae 中仅仅是在 haproxy 的配置文件增加backend
配置
添加 member 流程
再温习一遍,在添加 member 之前,amphorae 虚拟机上已经有管理 port 和 vrrp port,其中 vrrp port 在命名空间中。
octavia 已经支持直接指定 IP 地址添加 member,而不跟虚拟机绑定,前提是用户自己需要保证 amphorae 能够与这个 IP 地址通信。这种情况最好能在 pool 上添加 health_monitor
来保证 member 的可用性,这样如果 ip 地址不可达时,lb 也不会将消息路由给它。
当然,在 openstack 环境中使用时,在添加 member 时一般都会指定 subnet。
添加member,会在入参中的 subnet 中给 amphora 添加 port(当然,如果 member 所在的 network 与 vrrp port 所在的 network 一样则不会再添加 port),然后向 amphorae 发送消息:
POST https://<mgmt-ip>:9876/0.5/plug/network
,参数是port的信息,处理流程与 plug vip 很类似,就是把通过 nova api 给 amphorae 挂载的网卡移到namespace下。
因为与 member 通信的 port 都在 namespace 中,所以管理 IP 段就可以与租户的网络重叠。
shared pool/l7 policy
什么是共享 pool?
顾名思义,共享 pool 就是在一个 lb 内可以被多个 listener 共享使用的 pool。共享 pool 的意义更多是体现在 layer 7的一些转发规则,比如一个普通 listener上可以配置 l7 policy,将匹配到的请求转发到另一个 pool 里,这个 pool 就可以是共享 pool,不属于任意一个 listener,但却可以接收 lb 中所有的 listener 转发的请求。
创建共享 pool 很简单,参数中指定 lb 即可,共享 pool 一般与 l7 policy 一起使用。示例:
# 在 lb 内创建共享 pool
openstack loadbalancer pool create --name shared_pool_1 --protocol HTTP --loadbalancer a32dfcac-3f65-454d-acb1-fc038434b0ed --lb-algorithm ROUND_ROBIN
# 给 pool 里添加一个 member
openstack loadbalancer member create --address 10.0.0.3 --subnet-id f2492f6d-abe8-4600-9620-ea0b30e0f04c --protocol-port 80 e8c0dad5-ffdb-4da2-a6d9-f23bb02a7deb
# 在 lb 上创建 l7policy,将请求转发到共享 pool
openstack loadbalancer l7policy create --action REDIRECT_TO_POOL --redirect-pool e8c0dad5-ffdb-4da2-a6d9-f23bb02a7deb 7a14d263-d19f-49c6-b7ac-35dc74f95e7e
# 定义 l7policy 的匹配规则
openstack loadbalancer l7rule create --compare-type STARTS_WITH --type PATH --value / f4978c4b-94be-49ee-9e42-e59ac2e29f6d
登录 amphorae 查看 haproxy 的配置,示例如下:
global
daemon
user nobody
log /dev/log local0
log /dev/log local1 notice
stats socket /var/lib/octavia/7a14d263-d19f-49c6-b7ac-35dc74f95e7e.sock mode 0666 level user
defaults
log global
retries 3
option redispatch
timeout connect 5000
timeout client 50000
timeout server 50000
peers 7a14d263d19f49c6b7ac35dc74f95e7e_peers
peer dQBpZZSSPstddYq74BaOxAs9iLc 10.0.0.5:1025
peer HeeZeHoff56dzSzRdgsRaqNIt6s 10.0.0.11:1025
frontend 7a14d263-d19f-49c6-b7ac-35dc74f95e7e
option httplog
bind 10.0.0.12:80
mode http
acl 44f5c464-b7ca-4684-8f15-8f1dbf86185a path -m beg /
use_backend e8c0dad5-ffdb-4da2-a6d9-f23bb02a7deb if 44f5c464-b7ca-4684-8f15-8f1dbf86185a
default_backend 1922e4a8-3538-43ff-9489-7cb3b35e6bb0
backend 1922e4a8-3538-43ff-9489-7cb3b35e6bb0
mode http
balance roundrobin
server 0990eaa2-04db-4a7f-a1f3-d4011f08df7c 10.0.0.14:80 weight 1
backend e8c0dad5-ffdb-4da2-a6d9-f23bb02a7deb
mode http
balance roundrobin
server 5cfb3b1f-e2c0-45c9-8c1e-8e885674da6d 10.0.0.3:80 weight 1
如何运维
安装部署
Pike版本以前,Octavia要与Neutron配合,安装时neutron-lbaas软件包要与Neutron软件包安装在同一位置,在python路径中,neutron-lbaas目录与neutron目录同级。Octavia几个服务进程可以不与Neutron服务进程部署在同一host,因为neutron plugin与octavia是通过rest api通信。其实可以通过octavia中的devstack脚本了解其配置和安装过程。
从 Pike 开始,Octavia 可以作为独立的 Keystone 服务部署,不再是 Neutron 的 service plugin。
当然,如果是生产环境要用,还要考虑其与周边安装工具的配合是否完备。比如是否有debian包、是否支持ansible安装、是否支持puppet安装?关于ansible的安装,社区正在做:
- https://specs.openstack.org/openstack/openstack-ansible-specs/specs/mitaka/lbaasv2.html
- https://blueprints.launchpad.net/openstack-ansible/+spec/lbaasv2
上面的BP负责人回答:LBaaS v2 with agent (not octavia) is in Mitaka now, but the Liberty backport is still getting reviewed, octavia support is on hold until we can get something to make the LB mgmt network easier.
所以,截止Mitaka,如果要使用octavia,只有devstack是可用的。octavia社区正在完善installation文档。
创建 amphorae 镜像
在部署完 octavia 真正运行服务之前,最重要的一个工作就是准备镜像。这个镜像是用来创建 amphorae service 虚拟机。
octavia repo 中提供了创建 haproxy 镜像的脚本可以直接使用:
$ ./diskimage-create/diskimage-create.sh -s 2
By default, the script will pull a new base image from ubuntu images website(see the default configuration below), and install necessary packages into it. If you want to customize the base image, you can modify env variable in your loalhost as the following:
export DIB_CLOUD_IMAGES=http://cloud-images.ubuntu.com
export DIB_RELEASE=trusty
export BASE_IMAGE_FILE=trusty-server-cloudimg-amd64-root.tar.gz
By default, the code branch of amphora-agent installed into the image is master, if your Octavia code version in not master but stable/mitaka branch, you MUST modify as the following, in order to create a corresponding amphora image that can communicate with Octavia services:
sed -i 's:octavia:octavia stable/mitaka:' $OCTAVIA_DIR/elements/amphora-agent/source-repository-amphora-agent
The default image file generated by the script will locate in $OCTAVIA_DIR/diskimage-create/amphora-x64-haproxy.qcow2. Upload image to Glance is very straight forward, but before that, don't forget source admin credentials:
$ glance image-create amphora-x64-haproxy --is-public True --container-format bare --disk-format qcow2 --file $OCTAVIA_DIR/diskimage-create/amphora-x64-haproxy.qcow2
One more thing to do with amphora image is to add a tag which Octavia will search by:
$ glance image-tag-update <OCTAVIA_AMP_IMAGE_ID> amphora
At the same time, make sure the following config options are correct in Octavia configuration file:
[controller_worker]
amp_image_tag = amphora
# amp_image_id =
更新 amphorae 镜像
在大规模分布式环境中,作为运维人员,给服务器打补丁是一个很普通的日常工作,比如升级内核或打安全补丁。对于 octavia 来说,amphorae 作为租户不可见的 service VM,运维人员应当将其作为普通服务器来对待。但因为 amphorae 在本质上还是虚拟机,所以它又有一些特殊性。
考虑到生产环境的 lb 应该至少是master-slave 模式部署,而且 octavia 本身也提供了功能支持(health manager),所以升级 amphorae 镜像在流程上就比较简单。只需注意几点:
- 实际操作中要考虑用户的 SLA
- 如果使用了 spare pool,要注意与 spare pool 的配合
- 更新镜像应该是实际运维活动中的一个环节,要注意与其他环节(比如镜像生成)在自动化流程上打通
可以参考 octavia 官方文档,Rotating the Amphora Images
Octavia与Neutron的关系
Pike 之前
作为Neutron中LBaaS的参考实现,与其说Octavia与Neutron的关系,还不如说Octavia与Neutron中LBaaS service plugin的关系。之前有人在网上问过同样的问题,一位Octavia core reviewer给出如下答案:
lbaas v1: This is the original Neutron LBaaS, and what you see in Horizon or in the neutron CLI as “lb-*”. It has an haproxy backend, and a few vendors supporting it. Feature-wise, it’s basically a byte pump.
lbaas v2: This is the “new” Neutron LBaaS, and is in the neutron CLI as “lbaas-*” (it’s not yet in Horizon.) It first shipped in Kilo. It re-organizes the objects, and adds TLS termination support, and has L7 plus other new goodies planned in Liberty. It similarly has an haproxy reference backend with a few vendors supporting it.
octavia: Think of this as a service vm framework that is specific to lbaas, to implement lbaas via nova VMs instead of “lbaas agents". It is expected to be the reference backend implementation for neutron lbaasv2 in liberty. It could also be used as its own front-end, and/or given drivers to be a load balancing framework completely outside neutron/nova, though that is not the present direction of development.
总结一下就是,Neutron中有LBaaS service plugin,在LBaaS plugin中有很多provider或者叫driver(比如开源的haproxy driver或其他很多网络设备厂家实现的driver),其中一个叫octavia driver,该driver做的事情就是调用Octavia的API,与Octavia服务交互。
还有一点值得注意,使用Octavia时,在Neutron和Octavia中都会有database,会重复记录一些资源信息。Octavia只是作为LBaaS的service provider之一,所以,Neutron DB中同样会记录loadbalancer与provider的对应关系。因为octavia从一开始就朝着standalone设计,所以有自己的db是最基本的要求。因此,在生产环境下,建议自己实现一致性检查。
neutron与octavia通信时,使用admin token,但会把实际租户的project id设置到操作对象结构体中,octavia会把project id记录到DB。octavia的API处理目前是没有鉴权的,生产环境部署时,建议与neutron-server部署在一起,并通过本地地址访问。
通过neutron api使用lb资源时,get和list的操作都会使用neutron自己的db信息。
Pike 之后
从 Pike 版本开始,octavia 可以作为一个独立服务部署而不依赖于 neutron,neutron 对于 octavia 开说只是下层的一个服务而已。所以,也就不会有两份数据库记录,不用担心数据不一致的问题。
逻辑架构图
下图是通过octavia创建一个loadbalancer之后的逻辑图。
octavia中有个叫health-monitor的进程,其任务之一是监听来自amphorae虚拟机发送的运行状态数据,以此更新lb、listener、pool、member的状态,最重要的是更新amphora_health
表。同时,更新listener_statistics
表,这个表的数据可以作为计费依据。
health-monitor进程的任务之二,是根据amphora_health
表,找到异常状态的amphorae虚拟机,对该虚拟机进行更换操作。即删除旧的虚拟机,创建并配置新的amphorae虚拟机。
需要注意的是,health-monitor进程的监听IP和端口,必须在lb-mgmt-net内,以便接收amphorae的消息。可以查看devstack安装脚本了解该步骤的初始化过程。
octavia还有个进程叫house-keeping,主要任务是确保空闲的amphorae池大小、定期清理db中已删除的amphorae记录、定期更新amphorae中的证书。
Failover流程和实验
health-monitor根据amphora_health
表检测到有amphorae状态异常时,就会触发failover流程 (health-monitor每次运行时仅会failover一个amphora,如果是更换image等maintainance操作,需要考虑适当调整)。
- 将amphorae db中的status设置为pending delete
- 设置amphora-health db中的busy字段为True,这样该amp就不会被health monitor重复检测
- 调用Nova接口删除amphorae虚拟机(按照Nova的设计,删除虚拟机,不会删除虚拟机以port形式挂载的网卡,但会删除使用network创建的port)
- 等待Neutron将amphora vm上挂载的port的信息(比如device-id)重置(不考虑mgmt network上的port)
- 删除amphora-health db中该amp的记录
- 将amphorae db中的status设置为deleted
- 创建新的amphorae虚拟机,此时虚拟机只挂载了mgmt network的网卡
- 用旧的amphorae db的一些信息覆盖新的amphorae db记录,vrrp ip, ha ip, vrrp port id, ha port id等
- 调用一次ListenersUpdate,没想明白是何意?
- 给个虚拟机挂载vip port,vrrp port,以及与member互通的port,并到虚拟机里配置相应的网卡
- 在amphora db中设置role,到虚拟机里处理keepalived相关的配置
- 最后调用了一次ListenersStart
我的环境中已经创建如下资源:
vagrant@octavia:~/devstack$ source openrc demo demo
vagrant@octavia:~/devstack$ neutron lbaas-loadbalancer-list
+--------------------------------------+-------+-------------+---------------------+----------+
| id | name | vip_address | provisioning_status | provider |
+--------------------------------------+-------+-------------+---------------------+----------+
| 38307452-d6bd-488b-af29-3032fff65fe1 | my_lb | 10.0.0.11 | ACTIVE | octavia |
+--------------------------------------+-------+-------------+---------------------+----------+
vagrant@octavia:~/devstack$ source openrc admin admin
vagrant@octavia:~/devstack$ nova list
+--------------------------------------+----------------------------------------------+--------+------------+-------------+-----------------------------------------------------------------------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+----------------------------------------------+--------+------------+-------------+-----------------------------------------------------------------------------------+
| e356a373-2fed-4228-a695-fec797a42a9e | amphora-b07c2493-32de-4af0-bf88-abe3c54355ce | ACTIVE | - | Running | lb-mgmt-net=192.168.0.10; private=fd36:53a8:d962:0:f816:3eff:feeb:ad76, 10.0.0.12 |
+--------------------------------------+----------------------------------------------+--------+------------+-------------+-----------------------------------------------------------------------------------+
vagrant@octavia:~/devstack$ neutron port-list -- --device-id e356a373-2fed-4228-a695-fec797a42a9e
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
| id | name | mac_address | fixed_ips |
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
| 31289554-d738-4df3-b9be-85214ba7039b | | fa:16:3e:67:fe:6e | {"subnet_id": "06e5e04f-a92c-43bb-ba2e-349016e4ff21", "ip_address": "192.168.0.10"} |
| fbdca2b6-03c6-4775-b72c-340d55be0aa3 | | fa:16:3e:eb:ad:76 | {"subnet_id": "c43853c0-84a8-4a29-96f0-edbc387ae996", "ip_address": "10.0.0.12"} |
| | | | {"subnet_id": "a0a34d85-7327-4173-9ccc-865b9c06f54f", "ip_address": "fd36:53a8:d962:0:f816:3eff:feeb:ad76"} |
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
vagrant@octavia:~/devstack$ neutron port-show 31289554-d738-4df3-b9be-85214ba7039b
+-----------------------+--------------------------------------------------------------------------------------------------------------+
| Field | Value |
+-----------------------+--------------------------------------------------------------------------------------------------------------+
| admin_state_up | True |
| allowed_address_pairs | |
| binding:host_id | octavia |
| binding:profile | {} |
| binding:vif_details | {"port_filter": true, "ovs_hybrid_plug": true} |
| binding:vif_type | ovs |
| binding:vnic_type | normal |
| device_id | e356a373-2fed-4228-a695-fec797a42a9e |
| device_owner | compute:None |
| dns_assignment | {"hostname": "host-192-168-0-10", "ip_address": "192.168.0.10", "fqdn": "host-192-168-0-10.openstacklocal."} |
| dns_name | |
| extra_dhcp_opts | |
| fixed_ips | {"subnet_id": "06e5e04f-a92c-43bb-ba2e-349016e4ff21", "ip_address": "192.168.0.10"} |
| id | 31289554-d738-4df3-b9be-85214ba7039b |
| mac_address | fa:16:3e:67:fe:6e |
| name | |
| network_id | f280060d-d473-4ca1-8004-5ac880db7556 |
| port_security_enabled | True |
| security_groups | 74a0601b-29c9-4ea8-9eba-604fb35a090f |
| status | ACTIVE |
| tenant_id | 40621451e5b04ddcb96764022afb5a01 |
+-----------------------+--------------------------------------------------------------------------------------------------------------+
mysql> select * from amphora \G;
*************************** 1. row ***************************
id: b07c2493-32de-4af0-bf88-abe3c54355ce
compute_id: e356a373-2fed-4228-a695-fec797a42a9e
status: ALLOCATED
load_balancer_id: 38307452-d6bd-488b-af29-3032fff65fe1
lb_network_ip: 192.168.0.10
vrrp_ip: 10.0.0.12
ha_ip: 10.0.0.11
vrrp_port_id: fbdca2b6-03c6-4775-b72c-340d55be0aa3
ha_port_id: d2c26a5d-51b9-4487-bd16-5438218ad8ee
role: STANDALONE
cert_expiration: 2018-04-09 09:48:49
cert_busy: 0
vrrp_interface: NULL
vrrp_id: 1
vrrp_priority: NULL
为了启动failover流程,只需把amp虚拟机在lb-mgmt-net上的port状态down掉即可:
vagrant@octavia:~/devstack$ neutron port-update 31289554-d738-4df3-b9be-85214ba7039b --admin-state-up False
health-monitor进程会检测到该amp虚拟机异常,会替换该虚拟机,但会保留该虚拟机上所有非lb-mgmt-net上的port:
vagrant@octavia:~/devstack$ nova list
+--------------------------------------+----------------------------------------------+--------+------------+-------------+-----------------------------------------------------------------------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+----------------------------------------------+--------+------------+-------------+-----------------------------------------------------------------------------------+
| 9396f601-ed7b-481e-a56b-a1b27ed3e609 | amphora-57865deb-b8b6-45bc-a2d2-d231c7e011ae | ACTIVE | - | Running | lb-mgmt-net=192.168.0.11; private=fd36:53a8:d962:0:f816:3eff:feeb:ad76, 10.0.0.12 |
+--------------------------------------+----------------------------------------------+--------+------------+-------------+-----------------------------------------------------------------------------------+
vagrant@octavia:~/devstack$ neutron port-list -- --device-id 9396f601-ed7b-481e-a56b-a1b27ed3e609
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
| id | name | mac_address | fixed_ips |
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
| a4d27c16-b25e-44f2-a595-fd5fd066c2c4 | | fa:16:3e:b5:3a:e4 | {"subnet_id": "06e5e04f-a92c-43bb-ba2e-349016e4ff21", "ip_address": "192.168.0.11"} |
| fbdca2b6-03c6-4775-b72c-340d55be0aa3 | | fa:16:3e:eb:ad:76 | {"subnet_id": "c43853c0-84a8-4a29-96f0-edbc387ae996", "ip_address": "10.0.0.12"} |
| | | | {"subnet_id": "a0a34d85-7327-4173-9ccc-865b9c06f54f", "ip_address": "fd36:53a8:d962:0:f816:3eff:feeb:ad76"} |
+--------------------------------------+------+-------------------+-------------------------------------------------------------------------------------------------------------+
mysql> select * from amphora \G;
*************************** 1. row ***************************
id: 57865deb-b8b6-45bc-a2d2-d231c7e011ae
compute_id: 9396f601-ed7b-481e-a56b-a1b27ed3e609
status: ALLOCATED
load_balancer_id: 38307452-d6bd-488b-af29-3032fff65fe1
lb_network_ip: 192.168.0.11
vrrp_ip: 10.0.0.12
ha_ip: 10.0.0.11
vrrp_port_id: fbdca2b6-03c6-4775-b72c-340d55be0aa3
ha_port_id: d2c26a5d-51b9-4487-bd16-5438218ad8ee
role: STANDALONE
cert_expiration: 2018-04-09 10:04:58
cert_busy: 0
vrrp_interface: NULL
vrrp_id: 1
vrrp_priority: NULL
注意事项:使用spare pool可以加快failover的流程;但使用spare pool将不会遵从anti-affinity(如果配置的话)规则。
在failover流程中,amphorae虚拟机中的amphora-agent.log日志如下(我加了步骤名称),从日志中可以看到failover时,对新虚拟机的配置过程:
# ListenersUpdate
2016-04-13 23:36:35.265 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:36:35] "PUT /0.5/listeners/d5716c03-e269-4d13-9d03-8ddc21a0dcc4/816339a8-2e17-4b61-8825-c6856d7b7f2e/haproxy HTTP/1.1" 202 -
2016-04-13 23:36:35.500 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:36:35] "GET /0.5/listeners/816339a8-2e17-4b61-8825-c6856d7b7f2e HTTP/1.1" 200 -
2016-04-13 23:36:37.495 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:36:37] "PUT /0.5/listeners/816339a8-2e17-4b61-8825-c6856d7b7f2e/start HTTP/1.1" 202 -
# AmphoraPostVIPPlug
2016-04-13 23:40:44.096 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:40:44] "POST /0.5/plug/vip/10.0.0.6 HTTP/1.1" 202 -
# AmphoraPostNetworkPlug
2016-04-13 23:41:41.996 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:41:41] "POST /0.5/plug/network HTTP/1.1" 202 -
# AmphoraUpdateVRRPInterface
2016-04-13 23:41:46.481 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:41:46] "GET /0.5/interface/10.0.0.7 HTTP/1.1" 200 -
# AmphoraVRRPUpdate
2016-04-13 23:41:50.277 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:41:50] "PUT /0.5/vrrp/upload HTTP/1.1" 200 -
# AmphoraVRRPStart
2016-04-13 23:41:58.807 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:41:58] "PUT /0.5/vrrp/start HTTP/1.1" 202 -
# ListenersStart
2016-04-13 23:42:10.131 416 INFO werkzeug [-] 192.168.0.3 - - [13/Apr/2016 23:42:10] "PUT /0.5/listeners/816339a8-2e17-4b61-8825-c6856d7b7f2e/start HTTP/1.1" 202 -
AWS Elastic Loadbalancing
分析任何OpenStack中项目时,都会不由自主的看一看老大哥AWS的类似服务的文档,毕竟OpenStack很多project都是照着AWS实现的。对于LBaaS来讲也不例外,AWS中就有一个Elastic Loadbalancing服务,提供的功能比 octavia 要强大很多,详细信息可以参考如下几个链接:
- https://aws.amazon.com/articles/1636185810492479
- https://aws.amazon.com/elasticloadbalancing/details/
- https://aws.amazon.com/documentation/elastic-load-balancing/
- https://mp.weixin.qq.com/s/kslbNq7-zZ2VnqIAqJA-KQ
这里我针对某些特性只做一些总结:
- 支持三种 LB 类型:Application Load Balancer(类似于 octavia 提供的layer 7 LB)、Network Load Balancer(高性能的Layer 4 LB)、Classic Load Balancer(针对传统虚拟机网络)
- AWS 使用自己的 DNS 服务来提供 ELB 的访问,这样就可以利用 DNS 实现 lb 的自动扩展,也就是说,ELB 默认就是 active-active 模式并能做到根据流量自动伸缩
- ELB 后端可以是 EC2 VM、container 或 IP 地址,VM 可以来自不同 AZ,其中对 IP 地址的支持有助于应用向云上迁移或混合云场景
- 可以与 Auto Scaling 结合,保证后端服务的高可用
FAQ
添加member时,如果参数中member所在的subnet与loadbalancer的subnet不一致,会怎样?
Octavia允许member的IP地址范围与loadbalancer的subnet范围不同,内部实现时,会在member的IP地址范围内给amphora虚拟机添加网卡,以便与member通信。从另一种意义上来说,其实是对member所在subnet的IP地址空间的占用,而该subnet的租户是不感知的。
相似的问题,添加member时,如果member所在的subnet与lb-mgmt-net有地址重叠该怎么办?此时,amphorae会有多个网卡属于同一网段。
在amphorae内使用namespace的方式对tenant network与lb-mgmt-net进行隔离。
创建member时,如果address不在subnet范围内,会怎么样?
我在阅读代码时发现了这个问题,并没有测试。按照代码逻辑,这个操作不会有什么异常。但loadbalancer肯定无法使用,因为amphora虚拟机根本访问不到member的IP地址。碰巧,我在review一个patch涉及此问题,我把问题抛给了patch的作者。patch链接:https://review.openstack.org/292614
如何升级amphora虚拟机的内核?
这个问题是团队内部考量是否部署octavia时,operator提出的一个很现实的问题。根据我对octavia的代码理解以及与octavia社区core的交流,我给出的答复如下:
There is a process called octavia-health-manager in octavia, it will check the amphora vm status and do failover. For example, we have amp1(master) and amp2(backup) for a loadbalancer in active-standby mode, when we want to patch the vm, we need to do following things:
- we need to make a new image included patches we need using diskimage-builder tool.
- update image data in Glance.
- Mark down the management port of any one of the two vms(amp2 is recommended).
- the octavia-health-manager process will know that, and it will replace the vm with a new one, using new image.
- repeat for the other vm.
其实一开始我的答案中第三步是删除虚拟机,但octavia社区目前仍有一个相关的bug待解决,所以octavia社区建议down掉虚拟机的lb管理面port即可。bug链接在这里。
我的升级虚拟机内核的脚本在这里。
高版本的octavia可否与低版本的neutron(lbaas v2)兼容?
经过测试,mitaka octavia完全兼容liberty neutron/neutron-lbaas,但在读代码时发现在mitaka中octavia driver会将project id传递给octavia服务,这在liberty版本的octavia driver中是没有的。需要进一步研究一下没有project id会产生什么影响。
但需要注意,liberty horizon与mitaka neutron-lbaas-v2(lbaas v2的第一个release)无法兼容,会出现如下错误:
on line 24 of dashboard/project/lbaasv2/lbaasv2.scss
Traceback:
File "/home/lingxiankong/Envs/horizon/local/lib/python2.7/site-packages/scss/calculator.py", line 141, in evaluate_expression
return ast.evaluate(self, divide=divide)
File "/home/lingxiankong/Envs/horizon/local/lib/python2.7/site-packages/scss/ast.py", line 346, in evaluate
raise SyntaxError("Undefined variable: '%s'." % self.name)
SyntaxError: Undefined variable: '$gray-light'.
octavia会依赖babican么?
在octavia的安装和使用中,我看到了octavia使用babican来做证书管理。但如果不使用octavia提供的TLS Termination的功能,就可以不使用babican。
有趣的是,neutron-lbaas和octavia中都使用了插件机制来提供证书管理,在实际使用过程中,需要两者的配置保持一致。虽然目前neutron-lbaas中支持barbican和local两种方式,但local的方式仅仅提供开发测试使用。
用户通过barbican创建或上传自己的证书,用barbican中的container id作为参数创建listener,octavia就会从barbican获取用户的证书和密钥文件,并上传到amphorae中配置haproxy ssl。