Как кэш controller-runtime на самом деле работает, и почему ваш контроллер не обрушивает API-сервер
Контроллеры Kubernetes, обычно написанные на Go с использованием kubebuilder и controller-runtime, обеспечивают эффективную разработку распределенных рабочих нагрузок. Однако по мере увеличения нагрузки понимание внутреннего устройства controller-runtime становится критически важным для предотвращения производственных проблем. Основная концепция заключается в том, что r.Get() и r.List() в реконсилере не обращаются напрямую к API-серверу; вместо этого они получают доступ к локальному кэшу в памяти. Этот кэш изначально заполняется операцией списка, а затем поддерживается с помощью отслеживания (watches).Такая конструкция делает локальные чтения исключительно быстрыми, избегая нагрузки на управляющий узел. Однако компромиссом является то, что локальный кэш может потреблять значительный объем памяти и возвращать устаревшие данные. Записи, напротив, всегда направляются на API-сервер, минуя кэш. Размер этого локального кэша и стратегии индексирования напрямую влияют на использование памяти. Неэффективная операция List() может непреднамеренно привести к медленному линейному сканированию большого количества объектов.Цикл реконсиляции непрерывно сравнивает желаемые состояния с фактическими, чтобы привести их в соответствие. События ставятся в очередь, что приводит к функции реконсиляции, которая считывает текущее состояние, определяет необходимые действия и потенциально генерирует новые события. Основная цель кэша — обеспечить быстрое и актуальное представление состояний объектов для этих циклов реконсиляции. Эта модель отслеживания, унаследованная от client-go, предотвращает постоянное опрашивание API, поддерживая одно долгоживущее соединение для обновлений.controller-runtime абстрагирует сложности низкоуровневых компонентов client-go, таких как Reflector, DeltaFIFO и Indexer. Менеджер (Manager) оркестрирует общий кэш, контроллеры и другие службы. Информатор (Informer) отслеживает определенный GVK, поддерживая локальное хранилище (Indexer) и отправляя события подписчикам. ResourceEventHandlers обрабатывают эти события, обновляя хранилище, которое затем отражается в представлении контроллера. Очередь задач (workqueue) управляет ключами объектов для обработки рабочими, а предикаты (Predicates) фильтруют события перед постановкой в очередь.Reflector является единственным компонентом, напрямую взаимодействующим с API-сервером, выполняя первоначальный список, а затем устанавливая отслеживание. Он использует resourceVersion для возобновления отслеживания и обеспечения того, чтобы ни одно событие не было пропущено. Если соединение отслеживания теряется или API-сервер указывает устаревший resourceVersion, выполняется повторный список (relist). DeltaFIFO действует как упорядоченный буфер для изменений, группируя дельты по ключу объекта. Он предоставляет все накопленные изменения для объекта с момента последнего вызова Pop(), обеспечивая упорядоченную пакетную обработку. Однако он не сворачивает последовательные события "Добавлено" или "Обновлено", что может привести к нескольким раундам обработки для быстро меняющихся объектов.
r.Get()иr.List()в реконсилере не обращаются напрямую к API-серверу; вместо этого они получают доступ к локальному кэшу в памяти. Этот кэш изначально заполняется операцией списка, а затем поддерживается с помощью отслеживания (watches).Такая конструкция делает локальные чтения исключительно быстрыми, избегая нагрузки на управляющий узел. Однако компромиссом является то, что локальный кэш может потреблять значительный объем памяти и возвращать устаревшие данные. Записи, напротив, всегда направляются на API-сервер, минуя кэш. Размер этого локального кэша и стратегии индексирования напрямую влияют на использование памяти. Неэффективная операцияList()может непреднамеренно привести к медленному линейному сканированию большого количества объектов.Цикл реконсиляции непрерывно сравнивает желаемые состояния с фактическими, чтобы привести их в соответствие. События ставятся в очередь, что приводит к функции реконсиляции, которая считывает текущее состояние, определяет необходимые действия и потенциально генерирует новые события. Основная цель кэша — обеспечить быстрое и актуальное представление состояний объектов для этих циклов реконсиляции. Эта модель отслеживания, унаследованная от client-go, предотвращает постоянное опрашивание API, поддерживая одно долгоживущее соединение для обновлений.controller-runtime абстрагирует сложности низкоуровневых компонентов client-go, таких как Reflector, DeltaFIFO и Indexer. Менеджер (Manager) оркестрирует общий кэш, контроллеры и другие службы. Информатор (Informer) отслеживает определенный GVK, поддерживая локальное хранилище (Indexer) и отправляя события подписчикам. ResourceEventHandlers обрабатывают эти события, обновляя хранилище, которое затем отражается в представлении контроллера. Очередь задач (workqueue) управляет ключами объектов для обработки рабочими, а предикаты (Predicates) фильтруют события перед постановкой в очередь.Reflector является единственным компонентом, напрямую взаимодействующим с API-сервером, выполняя первоначальный список, а затем устанавливая отслеживание. Он используетresourceVersionдля возобновления отслеживания и обеспечения того, чтобы ни одно событие не было пропущено. Если соединение отслеживания теряется или API-сервер указывает устаревшийresourceVersion, выполняется повторный список (relist). DeltaFIFO действует как упорядоченный буфер для изменений, группируя дельты по ключу объекта. Он предоставляет все накопленные изменения для объекта с момента последнего вызоваPop(), обеспечивая упорядоченную пакетную обработку. Однако он не сворачивает последовательные события "Добавлено" или "Обновлено", что может привести к нескольким раундам обработки для быстро меняющихся объектов.