Задача
Кластер делят три команды, у каждой свой namespace. Выкатка одной команды приводит к деградации сервисов двух других: поды соседей начинают вытесняться, часть уходит в Pending.
Разработчики недоумевают: «Мы же в своём namespace, как мы вообще могли задеть чужие сервисы?»
Вопрос: что именно namespace не изолирует и какой минимум ограничений закрывает эту дыру?
Что кажется очевидным
«Namespace — это изоляция. Разные namespace — разные песочницы».
Это самое живучее заблуждение про Kubernetes.
Как это работает на самом деле
Namespace — это область имён и точка приложения политик, а не граница изоляции. Он даёт:
- уникальность имён объектов внутри себя;
- удобную единицу для RBAC, квот и сетевых политик;
- логическую группировку.
Чего namespace не делает сам по себе:
| Ресурс | Изолирован namespace’ом? |
|---|---|
| Имена объектов | ✅ Да |
| CPU и память нод | ❌ Нет — общий пул на весь кластер |
| Сеть | ❌ Нет — под из A по умолчанию достучится до пода в B |
| Ноды | ❌ Нет — поды разных команд едут на одни и те же ноды |
| Ядро ОС | ❌ Нет — контейнеры делят ядро ноды |
| Cluster-scoped объекты | ❌ Нет — узлы, PV, CRD, ClusterRole общие |
| etcd и API server | ❌ Нет — общий control plane |
В нашей задаче произошло типичное: команда выкатила Deployment без resources (или с большими значениями) и заняла на нодах столько памяти, что kubelet начал вытеснять чужие поды — приоритетно те, что в BestEffort и Burstable (см. задачу 3). Ноды-то общие.
Ответ
Namespace не изолирует вычислительные ресурсы, сеть и control plane. Сосед по кластеру может навредить как минимум четырьмя способами: съесть ресурсы нод, засыпать API server запросами, достучаться до чужих подов по сети, занять cluster-scoped ресурсы.
Минимальный набор, который это закрывает: ResourceQuota + LimitRange + NetworkPolicy + PriorityClass, а для по-настоящему недоверенных соседей — разные пулы нод.
Что с этим делать
1. ResourceQuota — потолок на namespace
Ограничивает суммарное потребление команды:
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: team-a
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
persistentvolumeclaims: "20"
count/deployments.apps: "50"
pods: "100"Важный побочный эффект: как только в namespace появляется квота на requests.cpu/memory, каждый под обязан их указывать, иначе он не будет создан. Это само по себе дисциплинирует.
2. LimitRange — значения по умолчанию
Квота задаёт потолок на namespace, LimitRange — правила для отдельного контейнера:
apiVersion: v1
kind: LimitRange
metadata:
name: team-a-limits
namespace: team-a
spec:
limits:
- type: Container
default: # limits, если не указаны
cpu: "500m"
memory: 512Mi
defaultRequest: # requests, если не указаны
cpu: "100m"
memory: 128Mi
max:
cpu: "4"
memory: 8GiЭто страховка от «забыли указать ресурсы» и от «один под попросил 200 ГиБ».
3. NetworkPolicy — сеть по умолчанию закрыта
Без политик под из любого namespace достучится до любого пода в кластере. Базовый приём — запретить всё входящее, потом открыть нужное:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: team-a
spec:
podSelector: {}
policyTypes: [Ingress]Помните: NetworkPolicy работает, только если CNI её поддерживает (Calico, Cilium и т. п.). С плагином без поддержки манифест применится и не будет делать ничего — проверьте это явно.
4. PriorityClass — кого вытеснять первым
Чтобы выкатка одной команды не выбивала критичные сервисы другой, задайте приоритеты:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: production-critical
value: 1000000
globalDefault: false
description: "Критичные production-сервисы"Поды с более высоким приоритетом при нехватке места вытесняют менее приоритетные, а не наоборот.
5. Разделение нод — когда соседи недоверенные
Всё вышеперечисленное защищает от случайного вреда, но не от намеренного: контейнеры делят ядро ОС, и эскалация из контейнера — реальный класс атак. Если команды не доверяют друг другу (или это внешние арендаторы):
- отдельные пулы нод с
taints/tolerationsиnodeSelector; Pod Security Admissionв режимеrestricted;- в пределе — отдельные кластеры. Это дороже в обслуживании, но единственная настоящая граница безопасности.
Быстрая проверка
# Есть ли квоты во всех namespace
kubectl get resourcequota -A
# Namespace без сетевых политик
kubectl get networkpolicy -A
# Поды без requests — потенциальные нарушители
kubectl get pods -A -o json | jq -r '
.items[] | select(.spec.containers[].resources.requests == null)
| "\(.metadata.namespace)/\(.metadata.name)"'Дальше: Задача 5. OOMKilled: кто и что на самом деле считает.