Сбой Reddit 14 марта 2023 года: как обновление Kubernetes до 1.24 положило сайт на 314 минут
Команда KAI Lab · · 9 мин чтения
14 марта 2023 года, в Пи-день, Reddit был недоступен 314 минут после обновления одного из важнейших кластеров Kubernetes с версии 1.23 до 1.24. Причина — метка узлов node-role.kubernetes.io/master: при обновлении до 1.24 kubeadm снимает её с управляющих узлов, а по ней сетевой плагин Calico выбирал route reflectors, и когда метка исчезла, маршруты между подами пропали во всём кластере.
Во время сбоя причину так и не нашли. Кластер восстановили из бэкапа с откатом на старую версию, а связь с меткой один из инженеров вспомнил уже при разборе. Настройку route reflectors сделали годами раньше, и её не было ни в одном репозитории.
Главное о сбое в одной таблице
| Вопрос | Ответ |
|---|---|
| Когда | 14 марта 2023, обновление запущено сразу после 19:00 UTC, сайт встал примерно через 2 минуты |
| Сколько длился | 314 минут |
| Что обновляли | кластер с легаси-частью стека Reddit, собранный kubeadm, с 1.23 до 1.24 |
| Причина | kubeadm upgrade apply в 1.24 снимает метку node-role.kubernetes.io/master, а селекторы route reflectors в Calico искали узлы по ней |
| Что видели дежурные | пропали метрики кластера, не работал DNS, API-сервер сыпал таймаутами вебхука OPA |
| Как чинили | восстановили управляющий слой из бэкапа на старой версии, трафик возвращали ступенями от 1% до 100% |
| Когда нашли причину | после восстановления, при разборе |
| Разбор | 21 марта 2023, пост «You Broke Reddit: The Pi-Day Outage» в r/RedditEng |
Хронология сбоя Reddit
| Время (UTC) | Что произошло |
|---|---|
| сразу после 19:00 | Инженер запускает обновление кластера с 1.23 до 1.24. Около двух минут всё нормально |
| через ~2 минуты | Сайт встаёт целиком |
| +3 минуты | Команда в звонке. Первое, что видят: кластер потерял все метрики, DNS не работает, записи из Consul не резолвятся |
| далее | В логах API-сервера — таймауты вызовов admission-вебхуков. Единственный вебхук, который смотрит на все ресурсы, — Open Policy Agent. Его удаляют: таймауты исчезают мгновенно, но кластер не оживает |
| через 2+ часа | Идей не осталось. Решают восстанавливать кластер из бэкапа. Удаление всех рабочих узлов занимает около 20 минут |
| восстановление | Два управляющих узла удаляют, третий откатывают и восстанавливают на нём данные, потом добавляют новые управляющие узлы. По ходу чинят подключение к etcd и TLS-сертификаты, которые не подходят по имени хоста |
| финал | Трафик возвращают ступенями: 1%, 5%, 10%, 20%, 35%, 55%, 80%, 100%. Итого 314 минут простоя |
Детали первых секунд восстановили уже при разборе. В 19:04:49 UTC объём логов API-сервера резко вырос. Примерно за 5 секунд до этого замолчали логи OPA. А за 2 секунды до всплеска calico-node на всех узлах начал удалять маршруты: сначала к первому обновлённому управляющему узлу, потом ко всем узлам.
Что такое route reflectors и при чём здесь метка
Calico раздаёт маршруты к подам по BGP: каждый узел сообщает соседям, какие подсети подов у него. По умолчанию узлы соединяются «каждый с каждым». В большом кластере это дорого: число соединений растёт квадратично. Поэтому, по словам Reddit, начиная примерно со 100 узлов рекомендуют route reflectors: несколько узлов обмениваются маршрутами со всеми, а остальные — только с ними. У Reddit, как пишут авторы разбора, к этой сотне надо «добавить нули».
Reflectors у Reddit были на управляющих узлах, а выбирались они селекторами в ресурсе Calico BGPPeer. В разборе прямо сказано: nodeSelector и peerSelector route reflectors указывали на метку node-role.kubernetes.io/master. Примерно так это выглядит (иллюстрация по документации Calico, не конфиг Reddit):
apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
name: peer-with-route-reflectors
spec:
nodeSelector: all()
peerSelector: has(node-role.kubernetes.io/master)С версии 1.20 Kubernetes переходит с термина master на control-plane. В заметках к kubeadm 1.24 изменение помечено как ACTION REQUIRED: новые кластеры больше не получают метку node-role.kubernetes.io/master, а kubeadm upgrade apply снимает её с существующих управляющих узлов. Обновление сняло метку, селектор перестал находить reflectors, и узлы потеряли маршруты друг к другу. Сеть подов умерла, а вместе с ней DNS и всё, что ходит по сети внутри кластера.
Почему причину не нашли во время сбоя
Конфигурации не было в коде. Route reflectors настроила команда-предшественница нынешней команды Compute несколько лет назад. Часть настройки Calico нужно было скачать через его собственную CLI, поправить руками и загрузить обратно. В итоге, как пишет Reddit, конфигурация не была закоммичена нигде, и инженерам не за что было зацепиться.
Симптомы вели в сторону. Сначала DNS, потом таймауты вебхука OPA. Удаление вебхука убрало таймауты, но не вернуло сеть.
Метка — не API. Удалённые версии API находят утилиты вроде pluto и kubent. Метку в селекторе стороннего custom resource не найдёт никто, кроме вас.
После разбора мейнтейнеры Calico открыли issue #7497. Они признали, что их документация по route reflectors отправляла пользователей в calicoctl, а это лишняя зависимость для команд инфраструктуры, и пообещали показывать в документации управление через kubectl.
Почему восстановление заняло часы
- У Kubernetes нет поддерживаемого даунгрейда. При обновлении он сам мигрирует схемы и данные, обратного пути не предусмотрено. Оставался бэкап.
- Процедура восстановления устарела. Её много раз проверяли на пилотных кластерах, но не обновляли вслед за изменениями. Она была написана под версию Kubernetes, у которой уже закончилась поддержка, и ещё до перехода на CRI-O.
- Кластеры были «питомцами». Reddit в облаке с 2009 года и рано взял Kubernetes. Кластеры собирали kubeadm без общего шаблона, и они слишком велики для управляемых облачных сервисов. В разборе это сказано так: вырастили стайку питомцев, а не управляли стадом.
Уроки для DevOps
1. Метки и аннотации — такой же контракт, как API
Перед обновлением читайте заметки к релизу, особенно пункты ACTION REQUIRED, и ищите каждое изменённое имя во всём, что на него опирается:
# какие узлы несут старую и новую метку
kubectl get nodes -l node-role.kubernetes.io/master
kubectl get nodes -l node-role.kubernetes.io/control-plane
# где старая метка упоминается в манифестах и чартах
grep -rn "node-role.kubernetes.io/master" ./deploy ./charts
# и в custom resources: kubectl get all их не показывает
calicoctl get bgppeers -o yaml | grep -n "node-role.kubernetes.io/master"Если после обновления первая команда отвечает No resources found, всё, что выбирало узлы по этой метке, осталось без узлов.
2. Всё, что настроено руками, должно жить в Git
Конфигурация, которой нет в репозитории, существует только в работающем кластере и в памяти людей. Люди уходят. Если инструмент требует CLI, выгрузите результат в YAML и держите его в Git вместе с манифестами.
3. Staging ценен ровно настолько, насколько он похож на прод
Reddit проверял обновления на выделенных кластерах и выкатывал их от менее критичных к более критичным. Но любая «ручная» особенность прода, которой нет в тестовой среде, проверку обходит. Инвентаризируйте отличия прода от staging.
4. Процедура восстановления стареет вместе с кластером
Бэкап, который восстанавливали по инструкции под старую версию, — наполовину бэкап. Восстановление надо прогонять после каждого крупного обновления. Тот же урок дал сбой GitLab 2017 года, где пять механизмов бэкапа не сработали.
5. Возвращайте трафик ступенями
Reddit поднимал трафик с 1% до 100% за восемь шагов. Восстановленный после аварии кластер — непроверенная система, и полная нагрузка сразу может уронить его снова.
6. Сначала симптомы, потом гипотезы
DNS не работает, метрики пропали, вебхук тормозит — это симптомы одного отказа сети. Когда отказывает всё сразу, ищите общий нижний слой. Как разбирать сам DNS, мы писали в статье DNS не резолвит в Linux.
Что проверить у себя завтра:
- есть ли в кластере ресурсы, которые меняли через CLI и не сохранили в Git;
- кто последний раз прогонял восстановление кластера и на какой версии;
- что в вашей инфраструктуре выбирает узлы или поды по меткам, которые может поменять обновление.
Реши сам
Два вопроса по этому сбою. Ответы — под спойлером.
Вопрос 1. Почему обновление до 1.24 сломало сеть между подами?
- В 1.24 удалили поддержку Calico
- Обновление сняло метку master с управляющих узлов, а по ней Calico выбирал route reflectors
- Закончилось место на управляющих узлах
- Истёк сертификат etcd
Показать ответ
Верный ответ — 2. Селекторы nodeSelector и peerSelector указывали на node-role.kubernetes.io/master. В 1.24 kubeadm upgrade apply снимает эту метку, селектор возвращает пустой список, и узлы теряют маршруты друг к другу.
Вопрос 2. Почему Reddit восстанавливал кластер из бэкапа, а не просто откатил версию?
- Откат занял бы больше времени
- У Kubernetes нет поддерживаемого даунгрейда: при обновлении автоматически мигрируют схемы и данные
- Бэкап был свежее
- Так требовали правила компании
Показать ответ
Верный ответ — 2. Обратного пути для миграций, которые Kubernetes делает при обновлении, не предусмотрено. Поэтому откат управляющего слоя — это восстановление данных из бэкапа, и процедуру надо держать актуальной.
Частые вопросы
Сколько длился сбой Reddit 14 марта 2023 года? 314 минут — Reddit сам обыграл совпадение с числом пи. Обновление запустили сразу после 19:00 UTC, сайт встал примерно через две минуты.
Почему метка master исчезла при обновлении? Kubernetes с версии 1.20 уходит от слова master. В kubeadm 1.24 новые кластеры метку node-role.kubernetes.io/master не получают, а kubeadm upgrade apply снимает её с существующих управляющих узлов. Изменение было помечено в заметках к релизу как ACTION REQUIRED.
Почему Reddit не нашёл причину сразу? Настройку route reflectors сделали годы назад, её не было ни в коде, ни в документации. Симптомы — DNS и таймауты вебхука OPA — уводили в другую сторону. Связь с меткой нашли только при разборе.
Поможет ли pluto или kubent найти такую проблему? Нет. Они ищут удалённые версии API в манифестах. Метка в селекторе стороннего ресурса — не версия API, её находят только поиском по репозиториям и по ресурсам в кластере.
Пройти похожий сбой самому
В симуляторе DevOps KAI Lab седьмой месяц посвящён переезду в Kubernetes. В нём есть разбор сбоя Reddit и тикеты про обслуживание нод без простоя и про релиз, который завис на середине. Похожая история с автоматическим обновлением и сетью узлов случилась у Datadog, её разбор — в статье сбой Datadog 2023. Все разборы собраны в подборке 18 сбоев, которые должен знать DevOps-инженер.
Начать можно бесплатно: тест навыков и первые задания первого месяца.
Источники
- Reddit Engineering, u/grumpimusprime. You Broke Reddit: The Pi-Day Outage, 21.03.2023 — хронология, причина, восстановление, цитаты.
- Kubernetes. kubeadm: apply "master" label/taint migration for 1.24, PR #107533 — заметка к релизу ACTION REQUIRED о снятии метки.
- Calico. Configure BGP peering — route reflectors,
BGPPeer,nodeSelectorиpeerSelector. - Calico. Improvements to Calico Configuration, issue #7497, 23.03.2023 — реакция мейнтейнеров на разбор Reddit.