Как EKS съедает приватные IP адреса

Итак, недавно случилась ситуация. Большой Kubernetes кластер на EKS. К нему привязана VPC и три private subnet в разных availability zone. Сеть с маской 22. То есть, 3066 доступных IP адресов. Должно быть. А в один прекрасный день Kubernetes отказался запускать pod под предлогом того, что свободных IP адресов в сети нет.

Народ в отчаянии собирается поднимать новый кластер с большим количеством приватных адресов и перевозить туда старый. Фронт работ оценивается минимум в неделю.

Простым подсчетом выясняю, что ресурсов, требующих IP адресов насобирается едва на их половину. А где же прячутся остальные?

А ларчик открывался просто…

Использование IP адресов внутри EKS кластера регулирует Amazon VPC Container Network Interface (CNI) plugin для Kubernetes. Он работает внутри кластера, в виде DeamonSet по имени aws-node. И состоит он из двух компонентов:

  • L-IPAM daemon (ipamD) — ответственный за создание сетевых интерфесов и присоединение их к EC2 instance ноды. а еще он отвечает за «горячий резерв» или «warm pool» IP адресов для назначения их подам на каждой ноде.
  • CNI plugin — отвечает за подключение хост-сети (например, настройку сетевых интерфейсов и виртуального Ethernet) и добавление правильного сетевого интерфейса в пространство имен (namespace) пода.

Как я уже писал выше, ipamD обслуживает «горячий резерв» IP адресов следующим образом, когда количество подов, запущенных на ноде, превышает количество IP адресов, которые могут быть назначены сетевому интерфейсу ноды, ipamD создает еще один сетевой интерфейс, при условии что еще не подключено максимально допустимое количество интерфейсов для инстанса.

Но есть переменная окружения (WARM_ENI_TARGET), которая может определять количество свободных сетевых интерфейсов (и все их доступные IP адреса), которые демон ipamD должен попытаться сохранить доступными для назначения пода на ноде. По умолчанию, значение этой переменной равно «1», что означает что ipamD будет держать один интерфейс в резерве. Но когда интрфейс в резерве, туда же попадают все количество адресов, которые могут быть назначены этому типу интерфейса. А это зависит от типа EC2 инстанса. К примеру, инстанс типа m4.large может иметь до 10 IP на интерфейс. И как только на ноде появится пять подов, еще один интерфейс станет в «горячий резерв». А так же в резерв попадут 15 IP адресов. Пять от существующего интерфейса и десять от нового.

Теперь о том, как этого избежать.

Есть еще одна переменная — WARM_IP_TARGET. Переменная определяет количество IP адресов (а не интерфейсов), которые должны быть зарезервированы. К примеру, если значение WARM_IP_TARGET равно «10», это значит что ipamD вместо резервирования интерфейсов, будет резервировать адреса. И только если нельзя назначать IP адреса существующему интерфейсу, будет создан новый.

И что самое интересное, если задана переменная WARM_IP_TARGET, то WARM_ENI_TARGET игнорируется.

Фикс, который освободил мне почти половину сети, закключался в запуске одной команды, определяющей значение WARM_IP_TARGET:

kubectl -n kube-system set env daemonset aws-node WARM_IP_TARGET=2

Что означает следующее использование IP адресов — один для ноды, по одному для каждого пода и два в «горячий резерв».

И все. Половина 22 сети вернулась из резерва. Фикс, заменяющий по меньшей мере, неделю работы.