Что такое сервис в Kubernetes
Что проверяют
Понимаете ли вы сервис со стороны приложения, которое в этом кластере живёт. Определение перескажут все, а вот вопрос, почему после масштабирования нагрузка не разъезжается по новым подам, отделяет тех, кто писал сервисы на Go под Kubernetes, от тех, кто про них читал.
Как отвечать
Сервис это стабильное имя и виртуальный адрес для набора подов, отобранных по меткам. Адреса живут в EndpointSlice, перенаправление делает kube-proxy правилами на узле, поэтому балансировка идёт по соединениям транспортного уровня, а не по запросам.
Дальше уместно перейти к последствиям для клиента на Go. http.Transport кэширует соединения и переиспользует их, а правило раскладывает только новые, поэтому пул долгих соединений так и висит на тех подах, что были живы в момент установки. Лечится это либо ограничением времени жизни соединений через IdleConnTimeout и MaxConnsPerHost, либо безголовым сервисом и балансировкой на стороне клиента.
С gRPC ситуация жёстче: все вызовы мультиплексируются в одно соединение, и без резолвера поверх безголового сервиса и политики round_robin балансировки нет вообще.
Третье, что стоит назвать, это завершение работы. Удаление пода из списка адресов и обновление правил на узлах идут асинхронно с остановкой контейнера, поэтому нужны обработка SIGTERM, Server.Shutdown с дренажом и небольшая пауза перед остановкой.
Где спотыкаются
Считают, что раз запросы идут через сервис, балансировка получается сама. Она получается сама ровно для коротких соединений, а типичный клиент на Go с постоянными соединениями получает залипание на подмножестве подов.
Делают один обработчик и на готовность, и на живость. Тогда перегруженный под либо перезапускается, хотя достаточно было убрать его из списка адресов, либо продолжает принимать трафик, который не может обслужить.
Не дренируют соединения при выкатке. Приложение закрывает слушающий сокет в ту же секунду, что получило сигнал, часть запросов уже летит по старому правилу, и каждый деплой даёт всплеск ошибок у клиентов.