🐳 Розділ 14 · Питання #21

Що таке namespace

Namespace — це квартири у великому багатоквартирному будинку:


🟢 Junior Level

30-секундна відповідь

Namespace (Простір імен) — це вбудований механізм Kubernetes для віртуального логічного розділення одного фізичного кластера на ізольовані робочі області для різних команд, проєктів або середовищ (dev, stage, prod).

  • Усунення конфлікту імен: усередині одного namespace імена ресурсів мають бути унікальними, але в різних просторах імен можуть одночасно працювати поди або сервіси з абсолютно однаковими іменами (наприклад, payment-service у dev та payment-service у prod).
  • Розподіл прав доступу (RBAC): адміністратор може надати розробникам повні права в просторі developer-sandbox, але заборонити навіть читання логів у просторі production.
  • Обмеження ресурсів: кожному простору імен можна встановити жорсткий ліміт за сумарними витратами CPU та оперативної пам’яті (ResourceQuota).

Наочна аналогія з життя

Namespace — це квартири у великому багатоквартирному будинку:

  • Сам будинок — це фізичний кластер серверів.
  • Квартири 101, 102 та 103 — це простори імен (dev, stage, prod).
  • У кожній квартирі на кухні стоїть свій холодильник із биркою «Холодильник» (однойменні поди order-db). Мешканці різних квартир не плутають свої холодильники, тому що вони розташовані в ізольованих кімнатах.

Базовий маніфест і команди

apiVersion: v1
kind: Namespace
metadata:
  name: billing-prod
  labels:
    environment: production
    team: billing
# Переглянути список усіх просторів імен у кластері
kubectl get namespaces

# Запустити под у конкретному namespace
kubectl run test-pod --image=nginx -n billing-prod

# Перемкнути робочий namespace за замовчуванням (щоб не писати прапорець -n)
kubectl config set-context --current --namespace=billing-prod

[!WARNING] Namespace — це НЕ фізична і НЕ мережева ізоляція! За замовчуванням у Kubernetes увімкнена пласка мережа: будь-який под із простору dev може вільно надсилати HTTP-запити в базу даних у просторі prod. Для реальної мережевої ізоляції потрібне налаштування NetworkPolicy.


🟡 Middle Level

Розподіл ресурсів: Namespace-Scoped vs Cluster-Scoped

                           [ Kubernetes Кластер ]
                                     │
         ┌───────────────────────────┴───────────────────────────┐
         ▼                                                       ▼
  [ Namespace-Scoped Ресурси ]                            [ Cluster-Scoped Ресурси ]
  Живуть строго всередині простору імен:                  Глобальні для всього кластера:
  - Pod, Deployment, StatefulSet                         - Node (Фізичний сервер)
  - Service, Ingress, EndpointSlice                      - PersistentVolume (PV)
  - ConfigMap, Secret                                    - StorageClass
  - PersistentVolumeClaim (PVC)                          - ClusterRole, ClusterRoleBinding
  - ServiceAccount, Role, RoleBinding                    - Namespace (Сам простір)

Міжмережева взаємодія: CoreDNS та FQDN

Поди з різних просторів імен можуть легко звертатися один до одного мережею з використанням повних доменних імен (FQDN):

http://<service-name>.<namespace-name>.svc.cluster.local:<port>
  • Усередині одного namespace: достатньо короткого імені: http://postgres:5432.
  • З іншого namespace: вимагається FQDN: http://postgres.billing-prod.svc.cluster.local:5432.

Контроль місткості: ResourceQuota vs LimitRange

Для захисту кластера від неконтрольованого споживання ресурсів використовуються два взаємодоповнюючі механізми:

1. ResourceQuota (Сумарна стеля на весь Namespace)

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-quota
  namespace: dev-team
spec:
  hard:
    requests.cpu: "20"        # Сума всіх requests.cpu не може перевищити 20 ядер
    requests.memory: 50Gi     # Сума всіх requests.memory не більше 50 ГБ
    limits.cpu: "40"
    limits.memory: 100Gi
    pods: "30"                # Максимум 30 подів у цьому namespace

2. LimitRange (Правила для кожного окремого контейнера)

apiVersion: v1
kind: LimitRange
metadata:
  name: container-limits
  namespace: dev-team
spec:
  limits:
  - default:                  # Ліміт за замовчуванням, якщо розробник забув вказати
      cpu: "1"
      memory: "1Gi"
    defaultRequest:           # Реквест за замовчуванням
      cpu: "200m"
      memory: "256Mi"
    max:                      # Заборонити контейнери понад 4 CPU
      cpu: "4"
      memory: "4Gi"
    type: Container

🔴 Senior Level

Ілюзія безпеки: чому Namespace не є межею довіри

На архітектурних співбесідах Senior-рівня поширене запитання:

«Чи безпечно розміщувати в одному кластері в різних Namespace застосунки публічного контуру та закриті банківські бази даних?»

Відповідь: НІ, якщо не вжито додаткових заходів. Namespace ізолює лише видимість через API Server, але:

  1. Спільне ядро Linux (Shared Kernel): поди з dev та prod можуть бути запущені шедулером на одній і тій самій фізичній ноді. Вразливість ядра Linux (наприклад, Dirty Pipe, Dirty COW або баги в підсистемі eBPF) дозволяє процесу з ненадійного контейнера отримати root-доступ до хоста й захопити сусідні контейнери prod.
  2. Пласка мережа (Flat Network): якщо в кластері не увімкнено CNI з підтримкою NetworkPolicy (Cilium або Calico) і не оголошено політику «заборонити все за замовчуванням» (Default Deny), контейнери бачать трафік один одного.
  3. Рішення для строгого Multi-Tenancy: фізичне розділення на різні кластери серверів, або ізоляція середовищ виконання на базі віртуалізації рівня гіпервізора (Kata Containers, gVisor).

Зависання Namespace у статусі Terminating (Finalizers Trap)

Класична аварійна ситуація: адміністратор виконує kubectl delete namespace staging, але видалення зависає в статусі Terminating на кілька днів.

Механізм зависання:

  1. Контролер NamespaceLifecycle встановлює статус Terminating та запускає каскадне видалення всіх вкладених ресурсів.
  2. У секції spec.finalizers у простору імен за замовчуванням стоїть блокувальник kubernetes.
  3. Якщо хоча б один вкладений ресурс містить незавершений фіналізатор (наприклад, PVC очікує відповіді від відключеного хмарного сховища AWS EBS, або користувацький CRD від оператора не може завершити graceful-очищення), видалення блокується назавжди.

Алгоритм реанімації:

# 1. Знайти ресурс, що блокує видалення
kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n staging

# 2. Очистити фіналізатори на проблемному об'єкті
kubectl patch pvc stuck-pvc -n staging -p '{"metadata":{"finalizers":null}}' --type=merge

# 3. Крайній хірургічний захід: пряме видалення фіналізатора через Raw API
kubectl get namespace staging -o json | jq '.spec.finalizers=[]' | kubectl replace --raw "/api/v1/namespaces/staging/finalize" -f -

Pod Security Standards (PSA) на рівні Namespace

У Kubernetes 1.25+ застарілі PodSecurityPolicy замінені на вбудований Admission Controller Pod Security Admission (PSA), який налаштовується простими лейблами на самому Namespace:

apiVersion: v1
kind: Namespace
metadata:
  name: secure-prod
  labels:
    # Заборонити запуск root-контейнерів, hostPath, privileged mode
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/warn: restricted

API Server автоматично відхилить створення будь-якого пода, що порушує стандарти безпеки CIS Benchmark!


4 Tricky Questions

1. Що станеться при спробі створити Deployment без блоку resources.requests, якщо в даному Namespace активний об’єкт ResourceQuota з обмеженням за CPU?

Відповідь: API-сервер негайно відхилить створення пода з помилкою валідації admission-контролера:

Error from server (Forbidden): pods "order-app-xxx" is forbidden: failed quota: compute-quota: must specify cpu for: order-app

Якщо в ResourceQuota задано обмеження на сумарний обсяг requests.cpu, контролер квот вимагає, щоб абсолютно кожен контейнер у цьому namespace мав явно оголошений requests.cpu. Єдиний спосіб уникнути цієї помилки без ручного прописування ресурсів у кожному маніфесті — створити в цьому ж просторі імен об’єкт LimitRange із секцією defaultRequest, який автоматично підставлятиме дефолтні значення в усі нові контейнери на етапі мутації маніфесту.


2. Чому PersistentVolume (PV) є об’єктом кластерного рівня (Cluster-Scoped), у той час як PersistentVolumeClaim (PVC) прив’язаний до конкретного Namespace?

Відповідь: Це фундаментальне архітектурне розділення зон відповідальності:

  • PersistentVolume (PV) являє собою фізичний дисковий ресурс (LUN у СЗД, том AWS EBS, локальний NVMe SSD). Ним розпоряджається адміністратор інфраструктури кластера. Цей ресурс глобальний і не повинен бути прив’язаний до ізольованих проєктів.
  • PersistentVolumeClaim (PVC) — це користувацька заявка на ресурс від конкретного застосунку розробника (наприклад: «Мені в проєкті billing потрібно 50 ГБ SSD»). Вона створюється розробником у межах свого Namespace.
  • Kubernetes знаходить відповідний вільний глобальний PV і монопольно пов’язує (bind) його з локальним PVC. При видаленні Namespace видаляється лише заявка (PVC), а доля фізичного тома (PV) визначається його глобальною політикою reclaimPolicy (Retain або Delete).

3. Яким чином зловмисник, що скомпрометував под у Namespace development, може отримати доступ до бази даних у Namespace production при стандартній конфігурації Kubernetes?

Відповідь: При розгортанні кластера «з коробки»:

  1. Мережевий рівень: мережева модель Kubernetes CNI за замовчуванням є абсолютно пласкою та повністю дозвільною (Allow-All). Зловмисник із шелла пода в dev може запустити curl http://db.production.svc.cluster.local:5432 і отримати доступ до відкритого сокета бази даних.
  2. Рівень DNS: CoreDNS кластера резольвить імена сервісів із будь-яких просторів імен для всіх клієнтів без обмежень. Захист: обов’язкове створення маніфесту NetworkPolicy з типом Ingress, що дозволяє підключення до БД у production лише від подів, які мають строго визначений namespaceSelector.

4. Чи можна перенести вже працюючий Pod або Service з одного Namespace до іншого за допомогою команди kubectl?

Відповідь: Ні, перенести працюючий об’єкт між просторами імен неможливо. Поле metadata.namespace є незмінним (Immutable) з моменту створення об’єкта. У базі даних etcd ключ ресурсу жорстко включає ім’я namespace: /registry/pods/<namespace>/<pod-name>. Щоб перемістити сервіс, необхідно:

  1. Вивантажити його маніфест: kubectl get deployment my-app -n old-ns -o yaml > app.yaml.
  2. Змінити в маніфесті поле metadata.namespace: new-ns.
  3. Застосувати його в новому просторі: kubectl apply -f app.yaml.
  4. Видалити старий ресурс у колишньому просторі імен.

🎯 Шпаргалка для інтерв’ю

Головне про Namespace

  • Суть: віртуальне логічне розділення кластера; дозволяє використовувати однакові імена ресурсів у різних середовищах.
  • Головний міф: Namespace НЕ забезпечує фізичну та мережеву ізоляцію за замовчуванням.
  • Міжмережевий DNS: service-name.namespace-name.svc.cluster.local.
  • ResourceQuota: сумарний ліміт CPU/RAM/Pods на весь простір імен.
  • LimitRange: дефолтні та граничні значення CPU/RAM на один контейнер.
  • Зависання в Terminating: викликане блокуванням фіналізаторами (spec.finalizers) на дочірніх ресурсах (PVC, CRD).
  • Безпека: Pod Security Admission (restricted) налаштовується через лейбли на Namespace.

Типи ресурсів за областю видимості

| Область видимості | Приклади ресурсів | | :— | :— | | Namespaced | Pod, Service, Deployment, PVC, ConfigMap, Secret, ServiceAccount, Role | | Cluster-wide | Node, PersistentVolume, StorageClass, ClusterRole, Namespace |

Червоні прапорці на співбесіді (Чого говорити не можна)

  • ❌ «Namespace ізолює мережу, тому поди з dev не можуть підключитися до сервісів у prod» — мережа пласка, для ізоляції обов’язково потрібен NetworkPolicy.
  • ❌ «Ми зберігаємо dev, stage та production в одному namespace default» — антипатерн, що призводить до хаосу та ризику випадкового видалення даних.
  • ❌ «Щоб перенести Pod в інший namespace, достатньо змінити його через kubectl edit pod» — поле namespace незмінне в etcd.

Пов’язані питання