Сегодня официально выпустили новую версию Kubernetes — 1.37. Разработчики сфокусировались на трёх основных областях: оптимизация управления жизненным циклом рабочих нагрузок, повышение безопасности кластера и более эффективное распределение ресурсов (прежде всего в механизме DRA). Появились долгожданные фичи: полноценный checkpoint/restore на уровне подов для заморозки состояния приложений, двухэтапный процесс вытеснения (eviction) с кастомными обработчиками, условная авторизация для тонкой настройки прав доступа и возможность задавать флаги (noexec, nodev, nosuid) при монтировании томов.
В нашем обзоре мы разберём 22 новых альфа-фичи — от отлова «тихих» сбоев в хранилищах и нативной аутентификации для admission-вебхуков до вытеснения менее приоритетных подов с целью высвободить ресурсы для более важных подов на том же узле.
Для подготовки статьи мы использовали информацию из блога Kubernetes, таблицы Kubernetes enhancements tracking, CHANGELOG-1.37, а также конкретные issues, pull requests и Kubernetes Enhancement Proposals (KEPs).
Мы разбили все изменения на следующие разделы:
Всего в новом релизе 22 альфа-фичи.
Примечание
Мы сознательно не переводим названия фич на русский. Они в основном состоят из специальной терминологии, с которой инженеры чаще сталкиваются в оригинальной формулировке.
Узлы
Pod-Level Checkpoint/Restore
Feature gate: PodLevelCheckpointRestore, по умолчанию отключён
В Kubernetes поды по умолчанию эфемерны: если под падает или переносится на другой узел, он полностью уничтожается и запускается с нуля. Технология Pod-Level Checkpoint/Restore (KEP-5823) меняет этот подход, добавляя снапшоты для всего пода целиком. Ранее можно было делать только снапшоты отдельных контейнеров (KEP-2008), но это крайне ограничивало спектр применения этой фичи, поскольку контейнеры делят между собой общие сеть и память. Новый механизм позволяет «заморозить» весь под со всей его инфраструктурой (включая состояние в памяти, иерархии процессов, открытые файловые дескрипторы, а также конфигурацию и метаданные на уровне пода), записать текущее состояние в файл, а затем запустить его заново ровно с того момента, на котором он остановился.
KEP помогает решить четыре практические задачи:
Избавляет от ожидания при запуске «тяжёлых» программ (например, Java или ML-нагрузок) — приложение можно запустить один раз, дождаться полной готовности (загрузки в память, прогрева кэша), сделать снимок и затем мгновенно скопировать его на другие узлы при масштабировании.
Выступает резервом для долгоживущих рабочих нагрузок, делая снимки-чекпоинты. В случае падения такой рабочей нагрузки (или если нужно перенести её на другой узел) снимок позволяет продолжить работу с последнего сохранения — так что вам не нужно перезапускать вычисления с нуля.
Упрощает обслуживание серверов: работающие поды можно «поставить на паузу», безопасно перенести на другую машину и продолжить вычисления без потери прогресса.
Облегчает поиск ошибок (для чего, между прочим, в первую очередь и предназначался KEP-2008): можно сделать снимок «зависшего» пода и передать его разработчикам для детального анализа в тестовом окружении, не прерывая работу оригинального приложения.
Сама функциональность реализуется на уровне kubelet'а. Он координирует работу со средой исполнения контейнеров (например, containerd или CRI-O), которая обращается к системной утилите Linux под названием CRIU (Checkpoint/Restore in Userspace). Эта утилита приостанавливает процессы, упаковывает оперативную память, открытые файлы и состояние сетевых соединений в архив. При восстановлении CRIU, используя возможности ядра Linux, воссоздаёт дерево процессов с прежними идентификаторами (PID) и сетевое окружение, строго соблюдая исходные правила безопасности системы.
В альфа-версии API дополняется двумя новыми методами:
CheckpointPod— отвечает за создание чекпоинтов на уровне подов;RestorePod— восстанавливает песочницу пода из сохранённого чекпоинта (для дополнительной информации о том, что такое песочница пода, см. эту статью в блоге Kubernetes).
DRA: Optional Node Preparation
Feature gate: DRAOptionalNodePreparation, по умолчанию отключён
KEP устраняет архитектурное ограничение в механизме динамического выделения ресурсов (Dynamic Resource Allocation, DRA), которое осложняло работу с виртуальными или облачными ресурсами. Ранее kubelet всегда пытался связаться с локальным драйвером через gRPC для подготовки устройства перед запуском контейнера и его очистки после завершения. Из-за этого администраторы были вынуждены развёртывать DaemonSet'ы с драйверами-заглушками на всех узлах кластера, даже если физически настраивать оборудование на серверах не требовалось.
KEP вводит новое булево поле SkipNodeOperations в спецификацию ResourceSliceSpec и структуру DeviceRequestAllocationResult. Если драйвер указывает, что подготовка на стороне узла не нужна, kubelet полностью пропускает этап поиска локального плагина и не совершает лишних gRPC-вызовов. В результате можно беспрепятственно использовать сетевые, виртуальные или удалённые ресурсы без развёртывания сопутствующего ПО на воркерах.
KEP позволяет повысить надёжность кластера (не нужно раскатывать лишних локальных агентов) и упростить его обслуживание. Кроме того, снижается потребление ресурсов процессора/памяти и устраняется риск зависания подов в статусе Terminating из-за сбоев в локальном драйвере. При этом сохраняется полная обратная совместимость для традиционных устройств, которым по-прежнему необходима физическая настройка на узле.
DRA Device Compatibility Groups
Feature gate: DRADeviceCompatibilityGroups, по умолчанию отключён
Раньше совместное использование специализированного оборудования (например, видеокарт) подами через механизм DRA могло приводить к критическим проблемам при планировании. Планировщик Kubernetes видел только общую ёмкость физического устройства и просто вычитал из неё запросы конкретных подов. При этом он не знал, совместимы ли запросы этих подов на физическом уровне. Например, на одном графическом процессоре невозможно одновременно запустить разные профили виртуализации (MIG vs vGPU). В результате, когда планировщик отправлял под на узел с другим подом с несовместимыми запросами, тот зависал в бесконечном цикле ошибок (CrashLoopBackOff или UnexpectedAdmissionError), а Kubernetes не мог автоматически перенести его в другое место.
KEP-5963 добавляет в Kubernetes понятие групп совместимости устройств (Device Compatibility Groups). Теперь планировщик при поиске места для запуска пода учитывает не только свободный объём ресурсов, но и логическую совместимость конфигураций. Если на физическом устройстве уже запущен под, требующий определённого режима работы, планировщик заблокирует размещение на нём другого пода с несовместимым режимом, даже если свободных ресурсов на устройстве хватает.
Для этого API Dynamic Resource Allocation (DRA) был расширен: в описание доступных аппаратных ресурсов (объект ResourceSlice) добавилось новое поле compatibilityGroups — список текстовых меток, генерируемых драйвером конкретного оборудования. При успешном выделении ресурса для пода планировщик копирует текущие метки совместимости в статус выделения (DeviceRequestAllocationResult). При планировании каждого нового пода проверяется, пересекаются ли его группы совместимости с группами уже запущенных на этом устройстве подов. Если пересечения нет, планировщик просто отклоняет этот узел и ищет другой с нужным оборудованием.
Пример
ResourceSlice (ниже) допускает совместное использование GPU как на аппаратном уровне (MIG), так и с гипервизором (vGPU).
apiVersion: resource.k8s.io/v1 kind: ResourceSlice metadata: name: node-1-gpu-0-counters spec: driver: gpu.example.com pool: name: node-1-pool generation: 1 resourceSliceCount: 2 sharedCounters: - name: gpu-0-counters counters: multiprocessors: value: "100" --- apiVersion: resource.k8s.io/v1 kind: ResourceSlice metadata: name: node-1-gpu-0-devices spec: driver: gpu.example.com pool: name: node-1-pool generation: 1 resourceSliceCount: 2 nodeName: node-1 devices: # MIG partitions - name: gpu-0-mig-1g-0 attributes: type: string: "mig-1g" consumesCounters: - counterSet: gpu-0-counters counters: multiprocessors: value: "20" - name: gpu-0-mig-1g-1 attributes: type: string: "mig-1g" consumesCounters: - counterSet: gpu-0-counters counters: multiprocessors: value: "20" # vGPU profiles - name: gpu-0-vgpu-0 attributes: type: string: "vgpu" consumesCounters: - counterSet: gpu-0-counters counters: multiprocessors: value: "50" - name: gpu-0-vgpu-1 attributes: type: string: "vgpu" consumesCounters: - counterSet: gpu-0-counters counters: multiprocessors: value: "50"
ResourceClaims — pod-a запрашивает MIG partition, pod-b запрашивает профиль vGPU:
apiVersion: resource.k8s.io/v1 kind: ResourceClaim metadata: name: pod-a-gpu namespace: default spec: devices: requests: - name: gpu deviceClassName: gpu.example.com selectors: - cel: expression: device.attributes['type'].string == 'mig-1g' --- apiVersion: resource.k8s.io/v1 kind: ResourceClaim metadata: name: pod-b-gpu namespace: default spec: devices: requests: - name: gpu deviceClassName: gpu.example.com selectors: - cel: expression: device.attributes['type'].string == 'vgpu'
Совокупные запросы обоих подов (mig-1g-0 (20 SMs) и vgpu-0 (50 SMs)) меньше доступных ресурсов GPU (20 + 50 < 100), так что планировщик планирует оба пода на один узел. Однако при подготовке устройства его драйвер «падает» из-за несовместимости профилей, и pod-b зависает на этапе подготовки.
Support Default Pod Sysctls in Kubelet
Feature gate: DefaultPodSysctls, по умолчанию отключён
В настоящее время Kubernetes позволяет пользователям задавать параметры sysctl для отдельных подов через поле securityContext.sysctls в спецификации пода.
Однако администраторам узлов часто необходимо единообразно настроить параметры ядра для всех рабочих нагрузок на узле или в пределах пула/группы узлов. Например, высокопроизводительные сетевые приложения могут требовать глобальной настройки определённых значений net.* или kernel.shm* для всех контейнеров на специализированных узлах. Ручная настройка этих параметров для каждого пода неудобна и сопряжена с риском ошибок.
KEP-5996 решает эту проблему, позволяя задавать параметры ядра на уровне узла. Kubelet будет автоматически их применять ко всем запускаемым на узле подам в момент создания песочницы пода.
В структуру KubeletConfiguration добавляется поле DefaultPodSysctls. Оно представляет собой карту map[string]string, содержащую пары «ключ-значение». Kubelet объединяет их со списком из манифеста пода. При этом более высокий приоритет имеют настройки, явно прописанные в spec.securityContext.sysctls самого пода.
Dynamic resize of memory-based volumes
Feature gate: InPlacePodVerticalScalingMemoryBackedVolumes, по умолчанию отключён
В Kubernetes можно примонтировать к поду временное хранилище типа emptyDir размера sizeLimit и указать для него параметр medium: Memory. В этом случае kubelet создаст том tmpfs в оперативной памяти.
Исторически параметр sizeLimit для таких томов был статичным. Чтобы изменить его, приходилось перезапускать под. KEP-6030 позволяет менять размер in-memory томов динамически, без пересоздания пода.
Для этого поле Pod.Spec.Volumes[].EmptyDir.SizeLimit делается изменяемым (mutable), если изменения производятся через ресурс /resize для существующих подов с medium: Memory, а новая подструктура в структуре VolumeStatus позволяет отслеживать фактическое состояние изменения размера тома.
DRA: Standard numaNode Device Attribute
Feature gate: нет, требует DRAListTypeAttributes (см. KEP-5491)
В текущей реализации механизма DRA в Kubernetes компоненты от разных вендоров по-разному публикуют информацию о своей принадлежности к NUMA-узлу. Из-за этого невозможно написать единое правило, ориентируясь на которое планировщик выделял бы ресурсы (например, GPU, NIC, CPU) в пределах одного NUMA-узла для обеспечения максимальной скорости обмена данными.
KEP-6072 решает эту проблему, стандартизируя атрибут resource.kubernetes.io/numaNode. Этот атрибут может публиковаться устройствами в двух форматах. Первый — это простое число (скаляр), ссылающееся на конкретный физический NUMA-узел (работает без feature gate). Второй — это список чисел, который генерируется на основе аппаратных задержек ACPI SLIT (требует активации feature gate DRAListTypeAttributes, см. KEP-5491).
В результате планировщик Kubernetes получает возможность группировать совместимые устройства через пересечение множеств. Например, если процессор жёстко привязан к узлу 4, а сетевая карта сообщает, что ей одинаково подходят узлы [6, 4, 5, 7], планировщик найдёт пересечение — {4}, поймёт, что совпадение есть, и запустит нагрузку вместе на нужном NUMA-узле.
Support TLS Credentials in gRPC Probe
Feature gate: GRPCContainerProbeTLS, по умолчанию отключён
В Kubernetes 1.23 появилась нативная поддержка gRPC для liveness- и readiness-проб. Однако у этой фичи был минус: она не поддерживала TLS. Поскольку многие компании используют внутренние центры сертификации (internal CA) и шифруют весь трафик между микросервисами, gRPC-сервер, настроенный на работу с TLS, не принимал проверки в plaintext от Kubernetes. Разработчикам приходилось продолжать вызывать стороннюю утилиту через exec (см. пример 1 ниже).
KEP-4939 добавляет новое поле mode в спецификацию проб. Оно принимает два значения: Plaintext (по умолчанию) и TLS, при этом валидность сертификата не проверяется.
Пример 1. Настройка проб старым способом (через exec):
livenessProbe: exec: command: - "/bin/grpc_health_probe" # Сторонняя утилита в образе - "-addr=:8443" - "-tls" - "-tls-no-verify"
Пример 2. Настройка проб новым способом:
livenessProbe: grpc: port: 8443 mode: TLS # Режим TLS livenessProbe: grpc: port: 50051 mode: Plaintext # Режим Plaintext
Add bind mount options (noexec, nodev, nosuid) support in volumeMounts
Feature gate: VolumeBindMountOptions, по умолчанию отключён
В Kubernetes хорошей практикой безопасности является запуск контейнеров с файловой системой «только для чтения» (readOnlyRootFilesystem: true). Чтобы приложение могло сохранять временные файлы, для него монтируют пустой том, например, emptyDir в папку /tmp.
По умолчанию Kubernetes монтирует любые тома без ограничений. Злоумышленник может загрузить вредоносный скрипт в доступный для записи /tmp, сделать его исполняемым и запустить. В этом случае файловая система «только для чтения» его не остановит.
KEP-5855 добавляет новое поле bindMountOptions в секцию volumeMounts конкретного контейнера в манифесте пода. В нём можно указать флаги, с которыми в этот контейнер будет монтироваться том:
noexec— запрещает запуск любых программ с этого тома;nosuid— запрещает повышение привилегий через файлы;nodev— запрещает создание и использование файлов устройств.
Фича активируется переключателем функциональности VolumeBindMountOptions.
При этом если kubelet или контейнерный рантайм не поддерживают mount_options, планировщик просто не разместит под с bindMountOptions на этот узел.
Пример
volumes: - name: tmp emptyDir: {} containers: - name: app volumeMounts: - name: tmp mountPath: /tmp bindMountOptions: [noexec, nosuid]
HTTP/2 cleartext (h2c) for container probes
Feature gate: H2CContainerProbe, по умолчанию отключён
KEP-5999 вводит поддержку незашифрованного HTTP/2 (h2c — HTTP/2 cleartext) для проверок состояния контейнеров, а именно для liveness-, readiness- и startup-проб.
Для этого в существующий механизм проб httpGet добавляется поле protocol, которое принимает значения HTTP1, HTTP2 или nil (в этом случае по умолчанию используется HTTP1).
Если в манифесте указано protocol: HTTP2 (при стандартном значении scheme: HTTP), kubelet будет выполнять HTTP GET-запросы с использованием незашифрованного HTTP/2 (h2c).
Пример манифеста пробы
readinessProbe: httpGet: port: 8080 path: /readyz protocol: HTTP2 # Новое поле httpHeaders: - name: Custom-Header value: my-value initialDelaySeconds: 5 periodSeconds: 10
Планировщик
Scheduler Preemption for In-Place Pod Resize
Feature gate: SchedulerPreemptionForPodResize, по умолчанию отключён
Функция In-Place Pod Resize (изменение размера пода «по месту») позволяет изменять запросы ресурсов пода на CPU и память без его перезапуска. Однако если на узле не хватает свободных ресурсов, запрос на их выделение переходит в статус Deferred.
Ранее планировщик не обрабатывал поды с таким статусом. В результате запрос зависал в ожидании, пока ресурсы не освободятся естественным путём (например, из-за удаления другого пода на узле) или администратор не освободит их вручную. Дожидаясь удовлетворения своего запроса, под мог быть «убит» по OOM, хотя на узле были поды с более низким приоритетом, которые можно было бы вытеснить.
KEP-5836 наделяет планировщик Kubernetes способностью вытеснять (preempt) менее приоритетные поды, чтобы высвободить ресурсы для In-Place-увеличения более важных подов на том же узле. При этом при поиске кандидатов на вытеснение планировщик исследует только тот узел, на котором размещён целевой под, игнорируя правила первоначального размещения вроде Node Affinity, Pod Anti-Affinity или Topology Spread. Кроме того, планировщик использует те же параметры PriorityClass и preemptionPolicy, что и при первичном запуске новых подов.
Когда планировщик вытесняет менее важные поды, те возвращаются в общую очередь как Unschedulable. Это создаёт дефицит ресурсов в кластере и активирует Cluster Autoscaler, который заказывает новые узлы у облачного провайдера.
В объект Node добавляется настройка политик вытеснения (podPreemptionPolicy). Она позволяет отключить вытеснение подов с меньшим приоритетом на конкретном узле в тех случаях, когда оно вызвано запросом на изменение размера пода.
DRA: Derived Attributes
Feature gate: DRADerivedAttributes, по умолчанию отключён
KEP-6080 решает проблему ригидности при связывании оборудования в механизме DRA. Сейчас для совместного выделения (co-allocation) устройств — например, GPU и высокоскоростной сетевой карты в пределах одного NUMA-узла — планировщик требует абсолютного совпадения имён и значений атрибутов через механизм matchAttributes. Так как драйверы разных вендоров часто описывают топологию по-разному, связать такое оборудование невозможно без изменения кода драйверов.
Для обхода этого ограничения KEP вводит механизм derivedAttributes на уровне манифестов запроса ресурсов (.devices.requests). Теперь с помощью скриптов CEL можно «на лету» извлекать, парсить и унифицировать метаданные от разных драйверов. Таким способом генерируется общий виртуальный ключ (например, ID NUMA-узла), который затем используется планировщиком для связывания оборудования.
Пример связывания GPU и NIC на одном NUMA-узле
apiVersion: resource.k8s.io/v1 kind: ResourceClaim metadata: name: gpu-numa-alignment-claim spec: devices: requests: - name: gpu exactly: deviceClassName: gpu.nvidia.com count: 8 # [NEW]: Вычисляем виртуальный ключ из драйвера GPU derivedAttributes: - name: shared-numa-node expression: "device.attributes['gpu.nvidia.com'].numa" - name: nic exactly: deviceClassName: dranet count: 1 # [NEW]: Вычисляем такой же ключ из драйвера сетевой карты derivedAttributes: - name: shared-numa-node expression: "device.attributes['dra.net'].numaNode" constraints: # Связываем устройства по общему ключу, используя существующий механизм matchAttribute - matchAttribute: shared-numa-node requests: [gpu, nic]
WAS: Controller Integration APIs
Feature gate: WorkloadWithJob, по умолчанию отключён
Вот уже некоторое время в Kubernetes разрабатывается планирование с учётом специфики рабочих нагрузок (workload-aware scheduling, WAS). Желающим подробнее узнать, что это такое, рекомендую ознакомиться со следующими документами: KEP-4671, KEP-5710, KEP-5732, KEP-6012, KEP-5547.
Однако ресурсы Workload и PodGroup, на которых базируется эта функция, разрабатывались преимущественно как промежуточные API, предназначенные для взаимодействия с планировщиком. На текущий момент отсутствует понимание того, как конечному пользователю контроллеров более высокого уровня (Job, LWS, JobSet, RayJob и др.) формулировать свои требования к планированию напрямую в YAML-манифестах.
KEP-6089 предлагает создать стандартизированный набор переиспользуемых API-компонентов (Reusable API Building Blocks) (структур в scheduling.k8s.io), общую Go-библиотеку workloadbuilder и архитектурные рекомендации. Это упростит владельцам контроллеров интеграцию таких фичей, как Gang Scheduling, Topology-aware Scheduling и Workload-aware Preemption. В результате разработчики смогут предоставлять конечным пользователям единообразный UX — тем не нужно будет писать кастомную логику.
KEP дополняет API Job, позволяя пользователям выражать свои предпочтения по планированию в новом блоке scheduling (при его отсутствии сохраняется стандартное поведение без изменений).
Ниже приведён пример манифеста Job, в котором прописаны предпочтения по совместному планированию (Gang Scheduling), топологии (Zone Topology) и совместному удалению (Atomic Disruption):
apiVersion: batch/v1 kind: Job spec: parallelism: 4 completions: 4 scheduling: # Новый блок policy: gang: {} # MinCount опущено: MinCount по умолчанию для Job = параллелизм (4) constraints: topology: - level: "topology.kubernetes.io/zone" disruption: all: {} # DisruptionMode устанавливается в All (вся группа должна быть погашена одновременно) template: spec: containers: - name: train-node image: training-image:v1
CompositePodGroup API
Feature gate: CompositePodGroup
Как и предыдущий KEP, этот развивает идеи workload-aware scheduling. На этот раз изменения направлены на поддержку сложных, иерархических требований к планированию.
Многие современные рабочие нагрузки в Kubernetes (например, AI training и ML) требуют многоуровневой структуры для обеспечения сложных зависимостей жизненного цикла (например, нагрузка может включать N групп предварительной загрузки (prefill) и M групп декодирования (decode)).
Рабочие нагрузки такого типа часто требуют многоуровневого группового (gang) планирования, то есть родительская группа не может быть запланирована до тех пор, пока не будет запланировано минимальное количество её дочерних групп. Другими словами, элементами планирования теперь стали целые дочерние группы, а не отдельные поды. Увы, существующие API Workload и PodGroup не позволяют выражать многоуровневую иерархию, которая часто характерна для многих out-of-tree API Kubernetes (например, JobSet и LeaderWorkerSet).
Кроме того, на случай частичных сбоев таких многоуровневых рабочих нагрузок нужен механизм реагирования на сбои для разных частей данной конкретной рабочей нагрузки.
KEP-6012 вводит новый API CompositePodGroup для иерархического планирования. Он позволяет строить древовидную структуру, состоящую из корневых групп и конечных элементов — групп подов aka PodGroup (которые уже включают сами поды). В результате планировщик K8s получил возможность учитывать правила на нескольких уровнях одновременно.
Kubernetes теперь может осуществлять многоуровневое:
gang-планирование, когда корневая группа будет допущена к планированию только после того, как готов некий минимум её дочерних групп;
планирование с учётом топологических доменов;
прерывание (Preemption/Disruption): DisruptionMode определяет, могут ли дочерние группы вытесняться или нарушаться независимо (Single) либо должны обрабатываться как единое целое (All) (например, в AI, когда потеря одного worker’а делает дальнейшее выполнение всей training-задачи бессмысленным).
Приложения
Add Recreate Update Strategy to StatefulSet
Feature gate: StatefulSetRecreateStrategy, по умолчанию отключён
StatefulSet'ы в Kubernetes по умолчанию используют стратегию RollingUpdate (постепенного, или скользящего обновления). Она специально заточена под stateful-приложения, для которых критична доступность и сохранность данных, поэтому действует максимально осторожно: контроллер обновляет поды по одному (или с учётом maxUnavailable) и ждёт, пока новый под не перейдёт в состояние Running и Ready.
Проблема этой стратегии в том, что если под зависает при обновлении (например, в состоянии ImagePullBackOff или Pending), весь процесс останавливается. При этом даже устранение проблемы ничего не меняет — контроллер не удаляет блокирующий под самостоятельно. Администратор должен сделать это вручную через kubectl delete pod <имя пода>.
KEP добавляет в StatefulSet'ы стратегию обновления Recreate. С ней StatefulSet:
1. Разом удаляет все поды старой версии (при этом PersistentVolumeClaims не удаляются — данные на томах остаются).
2. Ждёт, пока они завершат свою работу (проверяет deletionTimestamp).
3. Запускает поды новой версии в соответствии с политикой spec.podManagementPolicy.
Поскольку эта стратегия предполагает даунтайм, она подходит не для всех сценариев (см. таблицу сравнения).
Хранилище
Add user fields to atomic write volumes
Feature gate: AtomicWriteVolumeUserFields, по умолчанию отключён
В Kubernetes файлы из томов атомарной записи — ConfigMap, Secret, DownwardAPI и Projected — обычно создаются с владельцем root (UID 0). Многие программы, например MongoDB, предъявляют строгие требования к владельцу файлов и могут отказаться использовать файлы, принадлежащие root, если приложение работает от имени непривилегированного пользователя. Раньше это ограничение обходили с помощью init-контейнеров, которые запускались от имени root: они меняли владельца файлов командой chown.
Однако строгая политика безопасности подов Kubernetes запрещает запуск root-контейнеров — старый метод больше не работает.
KEP-5936 предлагает нативный механизм назначения владельца для файлов в таких томах, добавляя опциональные поля defaultUser и user. Поле defaultUser задаётся на уровне тома и определяет UID владельца по умолчанию для всех его файлов. Поле user задаётся для конкретного элемента и позволяет переопределить UID владельца отдельного файла.
KEP затрагивает только настройку UID для томов атомарной записи. Управление группой (GID) по-прежнему выполняется через существующие механизмы fsGroup и supplementalGroups. Поддержка Windows-контейнеров и постоянных томов (Persistent Volumes) в рамках этого KEP не предусмотрена.
Topology for Volume Snapshots
Feature gate: VolumeSnapshotTopology, по умолчанию отключён
Kubernetes-кластеры часто распределены между физическими зонами доступности и регионами. Обычные тома Kubernetes уже имеют поддержку топологии: система понимает, в каких зонах или регионах можно создать и подключить том. Однако у VolumeSnapshot до сих пор не было аналогичной информации.
KEP добавляет поле nodeAffinity в VolumeSnapshotContent. CSI-драйвер возвращает информацию о топологиях, из которых снапшот доступен, в ответе CreateSnapshot; сайдкар CSI snapshotter преобразует и сохраняет её в этом поле.
Кроме того, администраторы смогут задавать желаемое местоположение при создании снапшотов через VolumeSnapshotClass.allowedTopologies.
Также авторы KEP предлагают механизм восстановления томов из снапшотов с учётом топологии для двух режимов привязки:
WaitForFirstConsumer— отдельный out-of-tree плагин планировщика будет отфильтровывать все узлы, несовместимые с полемNodeAffinityснапшота.Immediate— external-provisioner сравнит, где хранится снапшот и где разрешено размещать диски согласно политикам (StorageClass.allowedTopologies). Диск будет создан только там, где оба этих условия удовлетворяются (в их пересечении). Если это невозможно, провижининг тома завершится сразу с понятной ошибкой о несовместимости топологий.
Таким образом, KEP предотвращает ситуацию, в которой Kubernetes пытается восстановить том из снапшота в зоне, где этот снапшот недоступен.
Add stickyBit support for emptyDir volumes
Feature gate: EmptyDirVolumeMode, по умолчанию отключён
На данный момент тома emptyDir всегда создаются с правами 0777 (чтение, запись и исполнение). Поменять это штатными средствами Kubernetes невозможно.
Это приводит к ряду проблем. Например, если в одном поде работают несколько контейнеров с общим emptyDir, один контейнер может случайно или намеренно удалить файлы, созданные другим контейнером.
KEP-5502 добавляет в API EmptyDirVolumeSource новое опциональное поле mode для томов emptyDir. В нём можно будет указать Unix-права для создаваемой директории в диапазоне от 0000 до 01777.
Пример манифеста
volumes: - name: app-data emptyDir: mode: 0750
Примечания
Обратная совместимость: если
modeне указан, Kubernetes создаст том с правами0777.Windows не поддерживает систему прав Unix (и sticky bit), поэтому на Windows-узлах это поле будет игнорироваться.
Настройки
fsGroupвsecurityContextмогут менять группу и права на томе, поэтому конечный режим может отличаться от заданногоmode.
PV Health Monitor
Feature gate: CSIVolumeHealth, по умолчанию отключён
Сейчас в Kubernetes есть проблема «тихих» сбоев инфраструктуры хранения, например:
Администратор случайно удалил диск в облаке, но Kubernetes думает, что тот всё ещё подключён (потому у PVC статус
Bound).Файловая система на диске повредилась.
Деградировала производительность или отвалился один из путей (
multipath) к диску.
CSI-драйверы часто знают или могут обнаружить такие проблемы, но Kubernetes ранее не имел стандартизированного механизма получения и сохранения этой информации. Без него такие сбои проявляются лишь как зависшие I/O-операции или ошибки монтирования.
KEP-1432 добавляет в спецификацию CSI 4 новых RPC-вызова. С их помощью драйверы хранилищ будут сообщать о здоровье томов и бэкенда. Эти запросы делятся на два уровня.
Уровень контроллера
Специальный sidecar-контейнер (csi-external-health-monitor-controller) отправляет драйверу запросы:
«Дай список всех нездоровых томов» (
ControllerListVolumeHealth);«Каково состояние этого конкретного тома?» (
ControllerGetVolumeHealth).
Результат записывается в PersistentVolumeClaim.status.healthStatus.
Уровень узла
Kubelet на каждом узле опрашивает локальный драйвер:
«Как здоровье этого тома на узле?» (
NodeGetVolumeHealth);«Как себя чувствует бэкенд с точки зрения этого узла?» (
NodeGetStorageHealth).
Результаты записываются соответственно в Pod.Status.VolumeHealth и CSINode.Status.StorageHealth.
KEP стандартизирует набор статусов (Inaccessible, DataLoss, Degraded, StorageUnreachable и StorageDegraded), но не определяет реакцию Kubernetes на них.
Аутентификация и авторизация
API Server Authentication to Webhooks
Feature gate: APIServerWebhookAuthenticationToken, по умолчанию отключён
В настоящее время kube-apiserver по умолчанию не аутентифицирует свои запросы к admission-вебхукам. Это позволяет любой сущности, имеющей доступ к служебной сети, отправлять запросы к вебхуку от имени API-сервера, обходя политики безопасности (см., например, CVE-2025-1974). Существующие механизмы защиты (mTLS, kubeconfig) требуют сложной ручной настройки и перезапуска API-сервера, из-за чего не особо популярны.
KEP-6060 внедряет нативный механизм аутентификации kube-apiserver'а и агрегированных API-серверов перед admission-вебхуками с помощью короткоживущих JWT-токенов.
Подробности реализации
В API TokenRequest добавится поле
AttestationClaims, а также claim webhook-authentication.k8s.io/allowedAPIGroup. Он задаёт группу API, для которой предъявитель токена может направлять AdmissionReview в вебхук.Главный kube-apiserver получит широкий доступ (
allowedAPIGroup: ["*"]), а агрегированные API-серверы смогут запрашивать токены только для тех групп API ресурсов, которыми управляют.Для получения токена необходимо, чтобы у вызывающей сущности было разрешение
createнаserviceaccounts/tokenдля конкретного сервисного аккаунта, а сервисный аккаунт, для которого выпускается токен, мог делатьattestна ресурс webhook-authentication.k8s.io/apigroups и запрошенную группу API.Авторы выпустят официальную Go-библиотеку для доработки вебхуков.
Проверка токена на стороне вебхука (криптографическая подпись, audience, совпадение API-группы) будет происходить локально, без запросов к kube-apiserver'у.
Механизм будет внедряться поэтапно. Существующие вебхуки, не готовые к проверкам, будут просто игнорировать новый заголовок
Authorization. После того как разработчики вебхуков внедрят официальную Go-библиотеку, те станут требовать наличия валидного токена.Предполагается, что существующие механизмы аутентификации на основе kubeconfig продолжат работать как обычно.
KEP относится только к admission-вебхукам. Предложение не охватывает остальные вебхуки (authentication, authorization, audit и conversion). Токены кэшируются и живут не более 10 минут.
Сеть
localhost NodePort userspace proxy for nftables
Feature gate: KubeProxyNFTablesLocalhostNodePorts, по умолчанию отключён
KEP-6032 добавляет в kube-proxy опциональный TCP-прокси для доступа к NodePort-сервисам через localhost (127.0.0.1 и ::1) при использовании бэкенда nftables.
Проблема в том, что IPv4 iptables поддерживает такой сценарий через параметр ядра Linux route_localnet, а у nftables эквивалентного механизма нет. Поскольку nftables планируется сделать бэкендом по умолчанию в Kubernetes, рабочие нагрузки, использующие localhost:<NodePort>, могут «сломаться».
Фича активируется только при явном указании loopback-адресов в --nodeport-addresses, например
--nodeport-addresses=primary,localhost
, или при указании конкретного loopback CIDR. Значение all прокси активировать не будет. Поведение nftables по умолчанию (primary) не меняется.
Прокси поддерживает только TCP-трафик. UDP и SCTP через localhost работать не будут.
Make nftables the default kube-proxy backend
Feature gate: нет (функциональность не добавляется и не удаляется)
KEP-5343 делает nftables бэкендом kube-proxy по умолчанию вместо iptables. Переход планируется провести плавно:
Начиная с Kubernetes 1.37, kube-proxy будет выдавать предупреждения в логах и событиях, если режим не задан явно через
--proxy-modeили полеmodeи поэтому автоматически выбирается iptables.После нескольких релизов с такими предупреждениями значением по умолчанию станет nftables. При этом поддержка iptables сохранится, а пользователи смогут выбирать нужный бэкенд.
API
KEP: Eliminating Internal API Types
Feature gate: нет
Классически каждому ресурсу (например, поду) в Kubernetes соответствует специальная внутренняя реализация (__internal). Дело в том, что у ресурса может быть несколько версий API (v1alpha1, v1beta1, v1). В этом случае API-сервер принимает любой входящий запрос, преобразует объект в версию __internal для обработки (например, для валидации), а затем конвертирует его обратно перед сохранением в etcd или возвратом пользователю.
Раньше такой подход избавлял от необходимости писать логику преобразования между всеми версиями — всё сводилось к конвертации в __internal. Это сильно упрощало архитектуру, поскольку код валидации писался только для типа __internal. Сейчас необходимость в этом отпала, поскольку большинство API уже стабильны (v1), и конвертация из v1 в __internal более не имеет смысла. Кроме того, эти операции ресурсоёмкие, особенно при запросе списка (List) для большого числа подов.
На первом этапе авторы предлагают сделать структуры данных __internal идентичными в памяти (memory identical) текущим стабильным версиям. В результате конвертацию можно будет проводить без поэлементного копирования полей (благодаря возможностям языка Go). По оценкам авторов, при обработке списка из 1000 подов число аллокаций памяти снизится с 21000 до всего лишь 5. Скорость обработки вырастет почти в шесть раз, а потребление RAM уменьшится в три раза.
На втором этапе (начиная с K8s 1.38+) планируется конвертировать типы __internal в алиасы Go для структур v1 (вида type PodSpec = v1.PodSpec). Со временем регистрация типов __internal в runtime.APIVersionInternal прекратится, а основой для конвертации и декодирования станет наиболее стабильная версия ресурса.
Устаревшие или удалённые фичи
Депрекация флага --filename/-f в команде kubectl run
Флаг --filename (или -f) для команды kubectl run больше не поддерживается, поскольку под всегда собирается только из аргументов командной строки вроде NAME и --image (см. Issue).
Статичные поды больше не могут ссылаться на Secret'ы или ConfigMap'ы
Статичные поды не должны иметь возможность напрямую читать API-ресурсы, так как создаются в обход API-сервера. Однако баг в коде позволял им ссылаться на Secret'ы или ConfigMap'ы через поля configMapRef или secretRef. В v1.37 баг исправили — теперь такие ссылки запрещены, а переключатель функциональности, который раньше позволял игнорировать это ограничение, удалён (см. Issue).
Депрекация ipvs-режима в kube-proxy
Поддержку режима ipvs в kube-proxy добавили в v1.8, чтобы решить проблемы с производительностью iptables. Но поскольку сам по себе API ядра для ipvs не способен обеспечить полноценную работу сервисов Kubernetes, режим ipvs продолжает использовать iptables «под капотом» (KEP-3866, "The ipvs mode of kube-proxy will not save us").
В кластерах, где kube-proxy работает в режиме ipvs (или указано mode: ipvs в KubeProxyConfiguration), при запуске теперь будет появляться предупреждение о депрекации.
К версии v1.40 режим ipvs для kube-proxy будет отключён по умолчанию (его ещё можно будет активировать через переключатель функциональности).
К версии v1.43 поддержка режима ipvs будет удалена полностью (см. KEP-5495).
Проверить текущий режим можно командой
kubectl -n kube-system get configmap kube-proxy -o jsonpath='{.data.config\.conf}' | grep 'mode:'
P. S.
Читайте также в нашем блоге:

