[ 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。我们将实例的日志与云控制器的日志关联起来,并整理了一段对话
实例尝试续订 IP。
云控制器收到续订请求并发送响应。
实例“忽略”响应并重新发送续订请求。
云控制器收到第二个请求并发送新的响应。
实例开始将续订请求发送到
255.255.255.255,因为它没有收到云控制器的回复。云控制器收到
255.255.255.255请求并发送第三个响应。实例最终放弃了。
有了这些信息,我们确信问题与 DHCP 有关。我们认为由于某种原因,实例没有获得新的 IP 地址,并且没有 IP,它就从网络中断开了。
快速的 Google 搜索找到了这个:VLAN 模式下的 DHCP 租期错误,这进一步支持了我们的 DHCP 理论。
一个初步的想法是只增加租期时间。如果实例每周只续订一次,发生此问题的可能性将比每分钟小得多。但是,这并没有解决问题,只是掩盖了问题。
我们决定让 tcpdump 运行在这个实例上,看看是否可以再次捕获它。果然,我们做到了。
tcpdump 看起来非常、非常奇怪。简而言之,看起来网络通信在实例尝试续订其 IP 之前就停止了。由于一分钟租期有大量的 DHCP 聊天,因此很难确认,但即使只有几毫秒的差异,如果一个数据包先到达,它就先到达,如果该数据包报告了网络问题,那么它必须在 DHCP 之前发生。
此外,这个实例负责每晚一个非常、非常大的备份作业。虽然“问题”(正如我们现在称之为的那样)并没有发生在备份发生时,但它足够接近(几个小时),以至于我们不能忽视它。
几天过去了,我们越来越多地捕获“问题”。我们发现 dhclient 在“问题”发生后没有运行。现在我们又回到了认为这是一个 DHCP 问题。运行 /etc/init.d/networking restart 使一切恢复运行。
有没有遇到突然得到你正在寻找的 Google 结果的一天?这里发生了同样的情况。我正在寻找关于 dhclient 以及为什么在无法续订租期时它会崩溃的信息,突然我发现了一堆关于 OpenStack 和 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);
删除旧记录后,性能大大提高,我的新部署继续表现良好。