[ English | 日本語 | Deutsch | Indonesia ]

Tales From the Cryp^H^H^H^H Cloud

这里记录了一些 OpenStack 云运维人员的故事。阅读它们,学习他们的智慧。

双 VLAN

我当时在加拿大不列颠哥伦比亚省的凯洛纳现场设置一个新的 OpenStack 云。部署完全自动化:Cobbler 在裸机上部署操作系统,启动它,然后 Puppet 接管。我已经多次在实践中运行过部署场景,并且认为一切都在正常工作。

在我离开凯洛纳的最后一天,我正在酒店参加一个电话会议。在后台,我正在玩弄新的云。我启动了一个实例并登录。一切看起来都很好。出于无聊,我运行了 ps aux,突然实例卡住了。

认为这只是一个一次性的问题,我终止了实例并启动了一个新的实例。这时,电话会议结束了,我前往数据中心。

在数据中心,我正在完成一些任务,并记起了卡住的问题。我登录到新的实例并再次运行 ps aux。它工作了。谢天谢地。我决定再运行一次。它又卡住了。

多次重现问题后,我得出了一个不幸的结论:这个云确实存在问题。更糟糕的是,我在凯洛纳的时间到了,必须返回卡尔加里。

你从哪里开始排查这样的问题?一个实例在发出命令时随机卡住。是镜像吗?不——所有镜像都会发生。是计算节点吗?不——所有节点。实例卡住了吗?没有!新的 SSH 连接可以正常工作!

我们寻求帮助。一位网络工程师建议这是一个 MTU 问题。太好了!MTU!有东西可以开始了!MTU 是什么,为什么会导致问题?

MTU 是最大传输单元。它指定接口每次接受的最大字节数。如果两个接口具有不同的 MTU,字节可能会被截断,并发生奇怪的事情——例如随机会话卡住。

注意

并非所有数据包的大小都是 1500。通过 SSH 运行 ls 命令可能只会创建一个小于 1500 字节的单个数据包。但是,运行带有大量输出的命令,例如 ps aux,需要多个 1500 字节的数据包。

好的,那么 MTU 问题来自哪里?为什么我们在任何其他部署中都没有看到这个问题?有什么新的情况?嗯,新的数据中心,新的上行链路,新的交换机,新的交换机型号,新的服务器,第一次使用这种型号的服务器……基本上所有东西都是新的。太好了。我们在各个区域调整 MTU:交换机、计算节点的 NIC、实例中的虚拟 NIC,甚至让数据中心提高了我们的上行链路接口的 MTU。有些更改有效,有些无效。但是,这种排查方法感觉不对。我们不应该在这些区域更改 MTU。

作为最后的手段,我们的网络管理员(Alvaro)和我坐下来,打开了四个终端窗口、一支铅笔和一张纸。在一个窗口中,我们运行 ping。在第二个窗口中,我们在云控制器上运行 tcpdump。在第三个窗口中,在计算节点上运行 tcpdump。第四个窗口在实例上运行 tcpdump。作为背景,这个云是一个多节点、非多宿主设置。

一个云控制器充当所有计算节点的网关。VlanManager 用于网络配置。这意味着云控制器和所有计算节点为每个 OpenStack 项目都有一个不同的 VLAN。我们使用 -s 选项 ping 更改数据包大小。我们看到有时数据包会完全返回,有时它们只会发出而不返回,有时数据包会在随机点停止。我们将 tcpdump 更改为开始显示数据包的十六进制转储。我们从外部、控制器、计算和实例之间的每种组合进行 ping。

最终,Alvaro 注意到了一些事情。当来自外部的数据包到达云控制器时,它不应该配置 VLAN。我们验证了这是真的。当数据包从云控制器发送到计算节点时,如果它要发送到实例,则应该只具有 VLAN。这仍然是正确的。当 ping 响应从实例发送时,它应该在 VLAN 中。正确。当它返回到云控制器并传送到互联网时,它不应该再具有 VLAN。错误。看起来数据包的 VLAN 部分没有被删除。

这说不通。

在思考这个想法时,我随机在计算节点上输入命令

$ ip a

10: vlan100@vlan20: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br100 state UP

“嘿 Alvaro,你能在 VLAN 之上添加一个 VLAN 吗?”

“如果你这样做,你会向数据包添加额外的 4 个字节……”

然后一切都说通了……

$ grep vlan_interface /etc/nova/nova.conf
vlan_interface=vlan20

nova.conf 中,vlan_interface 指定 OpenStack 应该将所有 VLAN 附加到哪个接口。正确的设置应该是

vlan_interface=bond0

这将是服务器的绑定 NIC。

vlan20 是数据中心为我们提供的用于传出互联网访问的 VLAN。这是一个正确的 VLAN,也附加到 bond0。

我不小心将 OpenStack 配置为将所有租户 VLAN 附加到 vlan20 而不是 bond0,从而将一个 VLAN 堆叠在另一个 VLAN 之上。这给每个数据包添加了额外的 4 个字节,并导致发送一个 1504 字节的数据包,这会导致到达只能接受 1500 的接口时出现问题。

一旦修复了此设置,一切就正常工作了。

“问题”

2012 年 8 月底,加拿大阿尔伯塔省的一所高等院校将其基础设施迁移到 OpenStack 云。幸运的是,在运行的第一天或两天内,他们的服务器之一从网络中消失了。砰。没了。

重新启动实例后,一切都恢复运行。我们查看了日志,发现网络通信在某个时刻停止,然后一切都处于空闲状态。我们认为这是一个随机事件。

几天后,它再次发生了。

我们查看了两组日志。最突出的事情是 DHCP。当时,OpenStack 默认将 DHCP 租期设置为一分钟(现在是两分钟)。这意味着每个实例都会联系云控制器(DHCP 服务器)以续订其固定 IP。由于某种原因,此实例无法续订其 IP。我们将实例的日志与云控制器的日志关联起来,并整理了一段对话

  1. 实例尝试续订 IP。

  2. 云控制器收到续订请求并发送响应。

  3. 实例“忽略”响应并重新发送续订请求。

  4. 云控制器收到第二个请求并发送新的响应。

  5. 实例开始将续订请求发送到 255.255.255.255,因为它没有收到云控制器的回复。

  6. 云控制器收到 255.255.255.255 请求并发送第三个响应。

  7. 实例最终放弃了。

有了这些信息,我们确信问题与 DHCP 有关。我们认为由于某种原因,实例没有获得新的 IP 地址,并且没有 IP,它就从网络中断开了。

快速的 Google 搜索找到了这个:VLAN 模式下的 DHCP 租期错误,这进一步支持了我们的 DHCP 理论。

一个初步的想法是只增加租期时间。如果实例每周只续订一次,发生此问题的可能性将比每分钟小得多。但是,这并没有解决问题,只是掩盖了问题。

我们决定让 tcpdump 运行在这个实例上,看看是否可以再次捕获它。果然,我们做到了。

tcpdump 看起来非常、非常奇怪。简而言之,看起来网络通信在实例尝试续订其 IP 之前就停止了。由于一分钟租期有大量的 DHCP 聊天,因此很难确认,但即使只有几毫秒的差异,如果一个数据包先到达,它就先到达,如果该数据包报告了网络问题,那么它必须在 DHCP 之前发生。

此外,这个实例负责每晚一个非常、非常大的备份作业。虽然“问题”(正如我们现在称之为的那样)并没有发生在备份发生时,但它足够接近(几个小时),以至于我们不能忽视它。

几天过去了,我们越来越多地捕获“问题”。我们发现 dhclient 在“问题”发生后没有运行。现在我们又回到了认为这是一个 DHCP 问题。运行 /etc/init.d/networking restart 使一切恢复运行。

有没有遇到突然得到你正在寻找的 Google 结果的一天?这里发生了同样的情况。我正在寻找关于 dhclient 以及为什么在无法续订租期时它会崩溃的信息,突然我发现了一堆关于 OpenStack 和 dnsmasq 的讨论,这些讨论与我们看到的问题完全相同!

重负载网络 IO 和 Dnsmasq 的问题.

实例在运行时丢失 IP 地址,因为没有 DHCPOFFER.

认真地说,Google。

这个错误报告是关键:KVM 镜像与桥接网络失去连接

阅读报告很有趣。它充满了人们遇到一些奇怪的网络问题,但没有以相同的方式解释它。

所以这是一个 qemu/kvm 错误。

在找到错误报告的同时,我的同事能够成功重现“问题”!他是怎么做到的?他使用 iperf 向一个实例发送了大量的带宽。在 30 分钟内,该实例从网络中消失了。

配备了修补的 qemu 和重现方法,我们开始看看是否终于解决了“问题”。在连续 48 小时内对实例进行带宽轰炸后,我们有信心了。剩下的就是历史了。你可以在错误报告中搜索“joe”来找到我的评论和实际测试。

消失的镜像

在 2012 年底,Cybera(一个非营利组织,其任务是监督加拿大阿尔伯塔省网络基础设施的发展)为其 DAIR 项目 部署了一个更新的 OpenStack 云。生产运行几天后,一个计算节点卡住了。重新启动节点后,我检查了该节点上托管的实例,以便代表客户启动它们。幸运的是,只有一个实例。

nova reboot 命令不起作用,所以我使用了 virsh,但它立即返回一个错误,说它无法找到备份磁盘。在这种情况下,备份磁盘是 Glance 镜像,当镜像首次使用时复制到 /var/lib/nova/instances/_base。为什么找不到它?我检查了目录,果然它消失了。

我查看了 nova 数据库,看到了实例在 nova.instances 表中的条目。实例使用的镜像与 virsh 报告的匹配,所以没有不一致之处。

我检查了 Glance,注意到这个镜像是一个用户创建的快照。至少这是个好消息——只有这个用户会受到影响。

最后,我检查了 StackTach 并查看了用户的事件。他们创建和删除了几个快照——很可能是在进行实验。虽然时间戳不匹配,但我的结论是他们启动了实例,然后删除了快照,并且它不知何故从 /var/lib/nova/instances/_base 中删除了。这都没有道理,但这是我能想到的最好的解释。

事实证明,这个计算节点崩溃的原因是硬件问题。我们将其从DAIR云中移除,并致电Dell进行维修。Dell到达并开始工作。不知怎么的(或者是不小心),另一个计算节点被碰到了并重启了。太好了。

当这个节点完全启动后,我重新执行了相同的场景,查看正在运行的实例,以便将它们重新启动。总共有四个。三个启动了,一个报错。错误与之前相同:无法找到后端磁盘。说真的,什么?

再次,事实证明该镜像是一个快照。成功启动的另外三个实例是标准的云镜像。是快照的问题吗?这说不通。

关于DAIR架构的一点说明:/var/lib/nova/instances 是一个共享的NFS挂载点。这意味着所有计算节点都可以访问它,包括 _base 目录。另一个集中区域是云控制器的 /var/log/rsyslog。这个目录收集来自所有计算节点的OpenStack日志。我想知道是否有关于 virsh 报告的文件条目

dair-ua-c03/nova.log:Dec 19 12:10:59 dair-ua-c03
2012-12-19 12:10:59 INFO nova.virt.libvirt.imagecache
[-] Removing base file:
/var/lib/nova/instances/_base/7b4783508212f5d242cbf9ff56fb8d33b4ce6166_10

啊哈!所以OpenStack正在删除它。但是为什么?

Essex版本中引入了一个特性,可以定期检查是否有未使用的 _base 文件。如果有,OpenStack Compute会删除它们。这个想法听起来无害,并且有一些优点。但是这个特性是如何被开启的?在Essex版本中默认是禁用的。理所当然。它被决定在Folsom版本中开启。我无法强调的是

删除事物的操作不应该默认启用。

现在磁盘空间很便宜。数据恢复却不便宜。

其次,DAIR共享的 /var/lib/nova/instances 目录也导致了问题。由于所有计算节点都可以访问此目录,因此所有计算节点都会定期检查 _base 目录。如果只有一个实例使用镜像,并且该实例所在的节点关闭了几分钟,它将无法标记该镜像为仍在被使用。因此,该镜像看起来没有被使用并被删除。当计算节点重新上线时,在该节点上托管的实例将无法启动。

情人节计算节点大屠杀

虽然这个故事的标题比实际事件要戏剧化得多,但我认为,或者希望,我不会有机会在标题中使用“情人节大屠杀”这个词了。

这个情人节,我收到一个警报,通知我云中的一个计算节点不再可用——这意味着,

$ openstack compute service list

显示该节点处于关闭状态。

我登录到云控制器,能够同时 ping 和 SSH 进入有问题的计算节点,这看起来非常奇怪。通常,如果我收到这种类型的警报,计算节点会完全崩溃并且无法访问。

经过几分钟的故障排除,我看到了以下细节

  • 一位用户最近尝试在该节点上启动一个CentOS实例

  • 这位用户是该节点上的唯一用户(新节点)

  • 负载在收到警报之前飙升至8

  • 绑定的10gb网络设备(bond0)处于DOWN状态

  • 1gb网卡仍然存活并处于活动状态

我查看了绑定对中两个网卡的的状态,发现两者都无法与交换机端口通信。考虑到绑定中的每个网卡都连接到不同的交换机,我认为两个交换机同时端口失效的可能性很小。我得出结论,10gb双端口网卡已损坏,需要更换。我为该节点托管的数据中心硬件支持部门创建了一个工单。我很幸运这是一个新节点,还没有其他人在上面托管。

一个小时后,我收到了相同的警报,但针对另一个计算节点。糟糕。好吧,现在肯定有问题了。就像原始节点一样,我能够通过SSH登录。bond0网卡处于DOWN状态,但1gb网卡处于活动状态。

最棒的是:同一个用户刚刚尝试创建一个CentOS实例。什么?

我完全困惑了,所以我给我们的网络管理员发短信,看看他是否可以提供帮助。他登录到两个交换机,并立即发现了问题:交换机检测到来自两个计算节点的生成树协议数据包,并立即关闭端口以防止生成树环路

Feb 15 01:40:18 SW-1 Stp: %SPANTREE-4-BLOCK_BPDUGUARD: Received BPDU packet on Port-Channel35 with BPDU guard enabled. Disabling interface. (source mac fa:16:3e:24:e7:22)
Feb 15 01:40:18 SW-1 Ebra: %ETH-4-ERRDISABLE: bpduguard error detected on Port-Channel35.
Feb 15 01:40:18 SW-1 Mlag: %MLAG-4-INTF_INACTIVE_LOCAL: Local interface Port-Channel35 is link down. MLAG 35 is inactive.
Feb 15 01:40:18 SW-1 Ebra: %LINEPROTO-5-UPDOWN: Line protocol on Interface Port-Channel35 (Server35), changed state to down
Feb 15 01:40:19 SW-1 Stp: %SPANTREE-6-INTERFACE_DEL: Interface Port-Channel35 has been removed from instance MST0
Feb 15 01:40:19 SW-1 Ebra: %LINEPROTO-5-UPDOWN: Line protocol on Interface Ethernet35 (Server35), changed state to down

他重新启用了交换机端口,两个计算节点立即恢复了正常。

不幸的是,这个故事有一个开放式的结局……我们仍在调查为什么CentOS镜像会发送生成树协议数据包。此外,我们正在研究一种适当的方法来减轻这种情况的发生。这是一个比人们想象的更大的问题。虽然交换机防止生成树环路非常重要,但当发生这种情况时,整个计算节点被切断网络是非常令人头疼的。如果一个计算节点托管100个实例,其中一个实例发送生成树协议数据包,那么该实例实际上对其他99个实例进行了DDoS攻击。

这在网络领域是一个持续且热门的话题——尤其是随着虚拟化和虚拟交换机的兴起。

掉进兔子洞

用户能够从正在运行的实例中检索控制台日志,这对于支持来说是一大福音——很多时候他们可以弄清楚实例内部发生了什么,并在不打扰你的情况下修复问题。不幸的是,有时对故障的过度记录也会导致问题。

收到一份报告:虚拟机启动缓慢,或者根本无法启动。启动标准的检查——Nagios上没有问题,但是我们RabbitMQ集群的主节点出现了网络峰值。开始调查,但很快其他队列集群的部分开始像筛子一样泄漏内存。然后警报来了——主Rabbit服务器崩溃,连接故障转移到从服务器。

当时,我们的控制服务由另一个团队托管,我们没有太多的调试信息来确定主节点发生了什么,而且我们无法重启它。该团队指出它在没有警报的情况下失败,但设法重启了它。一个小时后,集群恢复到正常状态,我们回家了。

第二天早上继续诊断,被另一个完全相同的故障所启动。我们迅速让消息队列再次运行,并试图弄清楚为什么Rabbit遭受了如此多的网络流量。在nova-api上启用调试日志很快就带来了理解。一个 tail -f /var/log/nova/nova-api.log 滚动得比我们见过的任何时候都快。CTRL+C停止它,我们就可以清楚地看到一个系统日志一遍又一遍地喷出错误——来自我们用户实例的一个系统日志。

找到实例ID后,我们前往 /var/lib/nova/instances 查找 console.log

adm@cc12:/var/lib/nova/instances/instance-00000e05# wc -l console.log
92890453 console.log
adm@cc12:/var/lib/nova/instances/instance-00000e05# ls -sh console.log
5.5G console.log

果然,用户一直在刷新仪表板上的控制台日志页面,而5G的文件正在遍历Rabbit集群以到达仪表板。

我们打电话给他们,让他们停止一段时间,他们很高兴放弃这个严重损坏的虚拟机。在那之后,我们开始监控控制台日志的大小。

到今天,该问题还没有永久的解决方案,但我们期待在下一次峰会上进行讨论。

哈瓦那被死者缠绕

台湾学术中心网格计算中心的Felix Lee贡献了这个故事。

我刚刚使用RDO仓库将OpenStack从Grizzly升级到Havana 2013.2-2,一切都运行得很好——除了EC2 API。

我注意到API会遭受重负载,并且对某些EC2请求(例如 RunInstances)响应缓慢。

来自 /var/log/nova/nova-api.log 的输出在 Havana

2014-01-10 09:11:45.072 129745 INFO nova.ec2.wsgi.server
[req-84d16d16-3808-426b-b7af-3b90a11b83b0
0c6e7dba03c24c6a9bce299747499e8a 7052bd6714e7460caeb16242e68124f9]
117.103.103.29 "GET
/services/Cloud?AWSAccessKeyId=[something]&Action=RunInstances&ClientToken=[something]&ImageId=ami-00000001&InstanceInitiatedShutdownBehavior=terminate...
HTTP/1.1" status: 200 len: 1109 time: 138.5970151

这个请求花费了超过两分钟才能处理,但在使用相同硬件和系统配置的另一个共存Grizzly部署中执行速度很快。

来自 /var/log/nova/nova-api.log 的输出在 Grizzly

2014-01-08 11:15:15.704 INFO nova.ec2.wsgi.server
[req-ccac9790-3357-4aa8-84bd-cdaab1aa394e
ebbd729575cb404081a45c9ada0849b7 8175953c209044358ab5e0ec19d52c37]
117.103.103.29 "GET
/services/Cloud?AWSAccessKeyId=[something]&Action=RunInstances&ClientToken=[something]&ImageId=ami-00000007&InstanceInitiatedShutdownBehavior=terminate...
HTTP/1.1" status: 200 len: 931 time: 3.9426181

在监控系统资源时,我注意到EC2 API处理此请求时内存消耗显著增加。我认为它没有正确处理内存——可能没有释放内存。如果API收到几个这样的请求,内存消耗会迅速增长,直到系统耗尽RAM并开始使用交换空间。每个节点有48 GB的RAM,并且“nova-api”进程会在几分钟内消耗所有内存。一旦发生这种情况,整个系统就会变得无法使用,直到我重新启动nova-api服务为止。

所以,我一直在想Havana上的EC2 API中发生了什么变化,可能导致这种情况发生。是错误还是我现在需要解决的正常行为?

在深入研究nova(OpenStack Compute)代码后,我注意到 api/ec2/cloud.py 中有两个区域可能影响我的系统

instances = self.compute_api.get_all(context,
                                     search_opts=search_opts,
                                     sort_dir='asc')

sys_metas = self.compute_api.get_all_system_metadata(
    context, search_filts=[{'key': ['EC2_client_token']},
                           {'value': [client_token]}])

由于我的数据库包含许多记录——超过100万条元数据记录和超过30万条处于“已删除”或“出错”状态的实例记录——每次搜索都需要很长时间。我决定通过首先存档备份副本,然后使用MySQL客户端执行一些删除来清理数据库。例如,我运行以下SQL命令来删除超过一年的已删除实例的行

mysql> delete from nova.instances where deleted=1 and terminated_at < (NOW() - INTERVAL 1 YEAR);

删除旧记录后,性能大大提高,我的新部署继续表现良好。