Сбой Windows Azure 29 февраля 2012 года: как дата «через год» сломала облако
Команда KAI Lab · · 8 мин чтения
29 февраля 2012 года облако Windows Azure потеряло часть вычислительных сервисов из-за ошибки в дате: агент внутри виртуальной машины выпускал сертификат сроком действия «та же дата через год» и получал 29 февраля 2013 года — дату, которой не существует. Сбой начался в 00:00 UTC, автоматика приняла программную ошибку за отказ железа и разнесла её по кластерам, а поспешное исправление отключило сеть ещё в семи кластерах.
Затронуты были Compute и зависящие от него сервисы: Access Control Service, Service Bus, портал SQL Azure и Data Sync. Хранилище и сами базы SQL Azure работали. Все сервисы объявили здоровыми 1 марта в 02:15 по тихоокеанскому времени, а клиентам дали кредит 33% за весь затронутый расчётный месяц.
Главное о сбое в одной таблице
| Вопрос | Ответ |
|---|---|
| Когда | 29 февраля 2012, 00:00 UTC (28 февраля, 16:00 по тихоокеанскому времени) |
| Причина | срок действия сертификата считали, прибавляя к году единицу: 29.02.2012 + 1 год = 29.02.2013 |
| Почему разрослось | агент хоста принимал сбой агента ВМ за отказ железа, контроллер переносил машины на другие серверы, где ошибка повторялась |
| Что задело | Compute, Access Control Service, Service Bus, портал SQL Azure, Data Sync Services |
| Что не задело | Windows Azure Storage и базы SQL Azure |
| Вторая волна | «ускоренный» фикс отключил сеть у всех ВМ в семи кластерах |
| Когда вернулись | кластеры в основном работали к 08:00 PST 29 февраля, все сервисы здоровы 1 марта в 02:15 PST |
| Компенсация | кредит 33% клиентам Compute, Access Control, Service Bus и Caching за весь затронутый месяц |
Хронология сбоя Azure
| Время (PST) | Время (UTC) | Что произошло |
|---|---|---|
| 28 февраля, 16:00 | 29 февраля, 00:00 | Агенты в новых ВМ начинают падать при создании сертификата |
| 18:38 | 02:38 | Найдена ошибка в коде |
| 18:55 | 02:55 | Управление сервисами отключено во всех кластерах мира |
| 23:20 | 07:20 | Исправление готово к тестам |
| 29 февраля, 02:11 | 10:11 | Исправление работает на первом кластере, его начинают выкатывать на остальные |
| 02:47 | 10:47 | В семи кластерах все ВМ теряют сеть — побочный эффект «ускоренной» выкатки |
| 05:40 | 13:40 | Выкатывают исправленное исправление |
| 08:00 | 16:00 | Кластеры в основном работают, но часть серверов в повреждённом состоянии, их чинят вручную |
| 1 марта, 02:15 | 1 марта, 10:15 | Все сервисы Azure объявлены здоровыми |
Как одна дата уронила агента
В каждой виртуальной машине Azure работал Guest Agent (GA), на физическом сервере — Host Agent (HA). При старте ВМ агент GA создавал так называемый transfer certificate для защищённого обмена с агентом хоста. Дата начала действия — полночь по UTC текущего дня, дата окончания — через год.
«Через год» считали самым простым способом: брали текущую дату и прибавляли единицу к году. С 00:00 UTC 29 февраля 2012 года это давало 29 февраля 2013 года. Такой даты нет, сертификат не создавался, и агент завершал работу.
Та же ошибка легко воспроизводится в любом языке:
>>> from datetime import date
>>> date(2012, 2, 29).replace(year=2013)
Traceback (most recent call last):
...
ValueError: day is out of range for monthGNU date такую дату тоже не примет. Но если прибавить год средствами самой библиотеки дат, результат нормализуется, а не падает:
$ LC_ALL=C date -d 2013-02-29
date: invalid date '2013-02-29'
$ date -d "2012-02-29 +1 year" +%F
2013-03-01Работающие машины ошибка не трогала: сертификат создаётся при инициализации. Падало всё, что стартовало после полуночи UTC: новые развёртывания, масштабирование, перезапуски и обновления ОС.
Как автоматика разнесла ошибку по кластерам
Здесь начинается самое поучительное. Агент хоста ждёт весточки от агента ВМ 25 минут. Если чистая ВМ три раза подряд не выходит на связь, HA решает, что виноват сервер, и сообщает об отказе железа. Контроллер фабрики (Fabric Controller) переводит сервер в состояние Human Investigate (HI) — «нужен человек» — и запускает service healing: переносит ВМ на другие серверы.
На новых серверах машины стартовали заново, снова создавали сертификат и снова падали. Механизм, который защищал клиентов от сломанного железа, стал размножать программную ошибку. У контроллера был порог: когда серверов в HI набиралось слишком много, автоматическое восстановление в кластере останавливалось. Это единственный предохранитель, который сработал сам.
В 18:55 PST Microsoft отключила управление сервисами во всех кластерах мира. Причина — не дать клиентам самим добить свои приложения: любое развёртывание или масштабирование создавало новые ВМ, а те падали на сертификате.
Как фикс сломал ещё семь кластеров
Исправление GA готовили и тестировали на текущей версии агентов. Но семь кластеров в этот момент были посреди выкатки новой версии HA. Для них решили вернуть старую версию HA вместе с исправленным GA.
В разборе Microsoft честно пишет: уверенные в тестах, они выбрали не обычную выкатку, а blast — обновление HA на всех серверах одновременно. При этом упустили, что в пакет со старым HA попал сетевой плагин, написанный для нового HA, и эти версии несовместимы. В 02:47 PST все ВМ в семи кластерах потеряли сеть, включая те, что до этого работали нормально. Исправленный пакет выкатили в 05:40 PST, а часть серверов потом ещё чинили вручную.
Уроки для DevOps
1. Даты — отдельный класс ошибок
Високосные годы, конец месяца, переход на летнее время, часовые пояса, високосные секунды — всё это нужно тестировать явно. Пишите тесты с «неудобными» датами и гоняйте их с подменой времени: 28 и 29 февраля, 31 декабря, 23:59:59. Считайте интервалы средствами библиотеки, а не арифметикой над полями даты. Похожий класс ошибок через четыре месяца, 30 июня 2012 года, ударил по Linux: разбор — в статье про високосную секунду 2012 года.
2. Автоматика должна уметь отличать программную ошибку от отказа железа
Service healing предполагал, что если ВМ не отвечает, виноват сервер. Когда ошибка в софте, перенос машин только раздувает аварию. Нужны пороги («если за 10 минут отказало больше N серверов, остановись и позови человека») и рубильник, который выключает автоматическое восстановление целиком.
3. Срочный фикс идёт через ту же поэтапную выкатку
Первая авария длилась часы из-за ошибки в дате. Вторая случилась из-за того, что выкатку ускорили и обошли стандартный процесс. В Ansible защита от этого — serial и max_fail_percentage, в Kubernetes — maxUnavailable. Даже в аварии сначала один кластер, потом остальные.
4. Проверяйте весь пакет, а не только изменённый файл
Исправляли агент, а сломался сетевой плагин, который поехал в том же пакете. Сравнивайте состав артефакта с тем, что сейчас работает на сервере, а не только строчку, которую поменяли.
5. Всё, что истекает, должно быть в инвентаре
Срок действия сертификата — часовой механизм. В Azure он сработал в момент выпуска, у Ericsson в 2018 году — через годы, когда истёк встроенный сертификат: разбор — в статье про сбой Ericsson 2018. Как проверять сроки у своих сертификатов, мы разбирали в статье истёк SSL-сертификат.
Что проверить у себя завтра:
- есть ли в коде вычисления вида «год + 1», «месяц + 1», «день + 30»;
- есть ли у автоматического восстановления порог и рубильник;
- идёт ли срочный фикс через ту же поэтапную выкатку, что и обычный релиз.
Реши сам
Два вопроса по этому сбою. Ответы — под спойлером.
Вопрос 1. Почему ошибка в агенте одной ВМ затронула целые кластеры?
- Все серверы стояли в одном дата-центре
- Агент хоста принимал сбой за отказ железа, и контроллер переносил ВМ на другие серверы, где ошибка повторялась
- Злоумышленники воспользовались уязвимостью
- На серверах кончилось место
Показать ответ
Верный ответ — 2. Service healing создан для отказов железа. Против программной ошибки, которая повторяется на любом сервере, он работает как усилитель.
Вопрос 2. Почему исправление вызвало второй сбой?
- Исправление не прошло тесты
- Его выкатили одновременно на все серверы, а в пакет попал сетевой плагин, несовместимый со старой версией агента хоста
- Клиенты перезапустили свои ВМ
- Истёк ещё один сертификат
Показать ответ
Верный ответ — 2. Microsoft выбрала blast-обновление вместо обычной выкатки. Поэтапная выкатка показала бы несовместимость на первом кластере, а не на семи сразу.
Частые вопросы
Что случилось с Windows Azure 29 февраля 2012 года? Агент в виртуальных машинах создавал сертификат со сроком «через год» и получал несуществующую дату 29 февраля 2013 года. Новые ВМ не запускались, автоматика переносила их с сервера на сервер, и сбой разошёлся по кластерам.
Какие сервисы Azure пострадали? Windows Azure Compute и зависящие от него Access Control Service, Service Bus, портал SQL Azure и Data Sync Services. Windows Azure Storage и базы SQL Azure не пострадали.
Сколько длился сбой? Сбой начался в 16:00 PST 28 февраля (00:00 UTC 29 февраля). К 08:00 PST 29 февраля кластеры в основном работали, все сервисы объявили здоровыми 1 марта в 02:15 PST.
Какую компенсацию получили клиенты? Кредит 33% всем клиентам Windows Azure Compute, Access Control, Service Bus и Caching за весь затронутый расчётный месяц.
Пройти похожий сбой самому
В симуляторе DevOps KAI Lab пятый месяц посвящён Ansible. В нём есть разбор этого сбоя и тикет «Выкатка на серверы по одному, а не всем сразу» — ровно тот навык, которого не хватило Azure во второй половине ночи. Все разборы собраны в подборке 18 сбоев, которые должен знать DevOps-инженер.
Начать можно бесплатно: тест навыков и первые задания первого месяца.
Источники
- Microsoft Azure Blog. Summary of Windows Azure Service Disruption on Feb 29th, 2012, 09.03.2012 — причина, хронология, service healing, blast-обновление, затронутые сервисы, кредит 33%.
- Python. datetime — Basic date and time types —
date.replace()и проверка допустимых дат.