en ru

Пример отладки экземпляра Deployment в Kubernetes

2018-09-09

Не так давно я просматривал список багрепортов в репозитории Kubernetes. Искал что бы такое починить в целях повышения собственной заметности в сообществе. И наткнулся на отчёт, который следовало бы озаглавить как «Таинственное обновление ReplicaSet в Deployment, которое его ломает».

Сейчас причина сбоя видится тривиальной, если подумать, а не использовать накопившиеся условные рефлексы. Но в тот момент задача показалась не столь простой, а от того - интересной. И я решил ею заняться.

Раз отчёт об ошибке появился на багтрекере Кубернетиса, то и проблема скорее всего в самом Кубернетисе, наивно подумал я. Но как вообще люди решают подобные задачи? Нет ли уже готовой методологии для этого? Лично я ничего подобного не встречал, но, похоже, не я один задаюсь такими вопросами. А потому я решил скопировать свой комментарий на эту тему из обсуждения отчёта сюда. Так мне его потом проще будет найти. И, может быть, кому-то ещё пригодится.

Мой способ отладки это путь проб и ошибок прежде всего, слегка направляемый некоторым моим прошлым опытом. Но общий алгоритм таков:

  1. Воспроизвести проблему локально. Как правило этот шаг занимает больше всего времени.

  2. Посмотреть во все доступные логи на предмет странного. К сожалению, у всех людей разное представление о странном, сильно зависящее от опыта. Чем больше опыт, тем очевиднее странное. Мой крохотный опыт не позволил найти ничего странного на этом шаге.

  3. Попробовать собрать старую версию Кубернетиса. Чем старее, тем лучше. Если проблема не воспроизводится, то дальнейшие шаги сводятся к последовательным запусками git bisect с целью найти коммит, который привносит проблему. Но в данном случае проблема воспроизводилась даже в Kubernetes v1.8.0, а значит это не обычная регрессия, и надо копать дальше.

  4. Изучить содержимое проблемных объектов (ReplicaSet) посредством комманды kubectl describe .... Как объекты одного и того же типа отличаются друг от друга? Если какой-то объект меняется с течением времени, то следует присмотреться, что именно в нём меняется. На этом шаге обнаружилось, что изменяется объект Deployment - в нём исчезают “tolerations”.

  5. Найти код, который исполняется в время изменения Deployment и сделай логгирование по-многословнее. Тут оказалось, что искомый код находится в kube-contoller-manager, а установка вот такой переменной окружения повышает словоохотливость логов

    LOG_SPEC=deployment_controller*=5,replica_set*=5,sync*=5,recreate*=5
    

    Однако единственным результатом этого шага стало подтверждение, что объект Deployment действительно кем-то изменяется.

  6. Изменить обработчик обновления объектов Deployment, чтобы в логах было видно, что конкретно изменяется помимо “tolerations” (например, с помощью в этой библиотеки). Тут могут быть сюрпризы… Но не в этот раз.

  7. Поискать код, который непосредственно меняет объекты Deployment через apiserver при помощи вот такой команды.

    $ git grep Deployments | grep "Update("
    

    В коде Кубернетиса есть всего несколько мест, где Deployment обновляются, но ни одно из них не выглядит подозрительным. Это, наконец-то, приводит нас к давно напрашивающемуся выводу о том, что источник изменений объекта находится где-то за пределами кода самого Кубернетиса.

  8. Посмотреть на YAML-файлы. Нет ли там чего-нибудь интересного. Однако в этих файлах интересного слишком много - глаза разбегаются. А значит следует

  9. Попробовать найти минимальную конфигурацию, при которой проблема всё ещё воспроизводится. Здесь обнаружилось, что простые маленькие изменения в разных местах файлов конфигураций приводят к тому, что проблема исчезает, как, впрочем, и работа конфигурации.

  10. Тогда ничего не остаётся кроме как изучить файлы конфигураций строчку за строчкой в надежде найти хоть какую-то зацепку. Хоть какая-то зацепка оказалась тем, что сервис работает под учётной записью kube-state-metrics, которая запрашивает разрешение на изменения объектов типа Deployment.

  11. Логично попробовать запретить этой учётной записи изменять Deployment‘ы. Это подтвердит, что объекты Deployment больше не изменяются.

  12. Заглянуть в логи изучаемого пода. Вот оно! В логах контейнера addon-resizer есть сообщение о невозможности изменить Deployment.

  13. Найти исходники для образа addon-resizer:1.0. Это оказалось непростой задачей, поскольку версия 1.0 ну очень уж старая и даже недоступна в текущем репозитории исходников, из которых строится этот образ. Очевидно причина проблем в том, что Kubernetes API эволюционировал слишком сильно со времён версии 1.0. Беглый взгляд на PodSpec, который используется в addon-resizer:1.0 подтверждает эту гипотезу.

Итак, проблема в слишком старом образе контейнера данной конфигурации - quay.io/coreos/addon-resizer:1.0.

Внутри этого контейнера запускается клиент, который по каким-то своим соображениям изменяет конфигурацию сервиса, но делает это используя устаревший API, в котором нет поля Tolerations в определении PodSpec. В итоге наше “развёртывание” теряет “tolerations” в шаблоне пода. Такая потеря приводит к необходимости контроллеру “развёртываний” (deployment controller) обновить объект Deployment, а значит и пересоздать ReplicaSet. Управляемый новым объектом ReplicaSet под не имеет установленных “tolerations”, а потому не может стартовать на узле, помеченном как мастер-узел.