Kubernetes CKA: Архитектура, администрирование и подготовка к сертификации

Углубленный курс-интенсив для систематизации знаний по K8s, ориентированный на подготовку к экзамену CKA и техническим интервью. Программа охватывает внутреннюю логику компонентов, управление ресурсами, сетевую безопасность и включает разбор 30 практических задач.

Архитектура Kubernetes и глубокий разбор компонентов Control Plane

Архитектура Kubernetes и глубокий разбор компонентов Control Plane

Парадокс Kubernetes заключается в том, что сам оркестратор не запускает ни одного контейнера и не маршрутизирует ни одного байта пользовательского трафика. По своей сути Kubernetes — это гигантская, распределенная машина состояний. Вы просто описываете желаемое состояние системы (Desired State), а компоненты кластера бесконечно сверяют его с текущим состоянием (Actual State) и пытаются устранить разницу.

Для успешной сдачи CKA и прохождения технических интервью недостаточно знать, как создать Pod. Вы должны понимать, кто именно в кластере принимает решение о его создании, где хранится информация об этом и как компоненты общаются между собой.

Две половины мозга: Control Plane и Worker Nodes

Архитектуру Kubernetes можно разделить на две фундаментальные части.

  1. Control Plane (Управляющий слой) — мозг кластера. Он принимает глобальные решения (например, на каком узле запустить приложение), обнаруживает события и реагирует на них.
  2. Worker Nodes (Рабочие узлы) — мускулы кластера. Это серверы, на которых физически работают ваши контейнеры.

Чтобы кластер работал как единый механизм, компоненты должны иметь строгую специализацию. Давайте разберем Control Plane под микроскопом.

Анатомия Control Plane

В классическом кластере (созданном через kubeadm) компоненты Control Plane запускаются в виде статических подов (Static Pods) в пространстве имен kube-system.

CKA Cheat: Манифесты статических подов Control Plane по умолчанию лежат в директории /etc/kubernetes/manifests/. Если вы измените YAML-файл в этой папке, kubelet автоматически перезапустит соответствующий компонент. Это ключевой навык для траблшутинга на экзамене.

1. kube-apiserver (Центральный коммутатор)

kube-apiserver — это единственный компонент кластера, с которым взаимодействуете вы (через kubectl) и все остальные компоненты системы.

Ключевые особенности:

  • Stateless (не хранит состояние): Сам API-сервер не запоминает данные. Он масштабируется горизонтально простым добавлением новых реплик за балансировщиком нагрузки.
  • Единая точка входа: Ни один компонент кластера (кроме API-сервера) не имеет прямого доступа к базе данных etcd.
  • Функции: Аутентификация, авторизация (RBAC), валидация запросов (Admission Control) и маршрутизация манифестов.

2. etcd (Абсолютная память)

etcd — это высокодоступное распределенное хранилище пар «ключ-значение» (key-value store). Это единственный stateful-компонент в Control Plane.

Если kube-apiserver — это коммутатор, то etcd — это единственный источник истины (Single Source of Truth). В нем хранятся все конфигурации, секреты и состояния объектов.

Что нужно помнить для CKA:

  • etcd использует алгоритм консенсуса Raft. Для обеспечения отказоустойчивости кластер etcd должен состоять из нечетного количества узлов (обычно 3 или 5).
  • Если данные в etcd потеряны, кластер Kubernetes фактически перестает существовать, даже если контейнеры все еще работают на узлах.
  • На экзамене CKA обязательно будет задача на резервное копирование и восстановление (Backup & Restore) etcd с использованием утилиты etcdctl.

3. kube-scheduler (Распределитель ресурсов)

Многие думают, что scheduler запускает поды на узлах. Это ошибка, на которой часто ловят на интервью.

kube-scheduler ничего не запускает. Его единственная задача — заметить новый Pod без назначенного узла (nodeName пустое), проанализировать доступные ресурсы кластера и выбрать оптимальный узел для этого пода.

Процесс принятия решения состоит из двух этапов:

  1. Filtering (Фильтрация): Отсеиваются узлы, которые не подходят аппаратно (не хватает CPU/RAM) или логически (Taints, Node Selectors).
  2. Scoring (Оценка): Оставшимся узлам выставляются баллы по ряду критериев (например, приоритет отдается узлу, где уже есть нужные образы контейнеров). Узел с максимальным баллом побеждает.

После выбора узла scheduler просто отправляет запрос в kube-apiserver с сообщением: "Привяжи Pod X к Узлу Y".

4. kube-controller-manager (Неутомимый надзиратель)

Это набор фоновых процессов (контроллеров), которые непрерывно следят за состоянием кластера через kube-apiserver. Их задача — приводить Actual State к Desired State.

Внутри kube-controller-manager работают десятки контроллеров. Вот основные:

  • Node Controller: Следит за тем, живы ли рабочие узлы.
  • ReplicaSet Controller: Гарантирует, что работает ровно столько реплик пода, сколько указано в конфигурации.
  • EndpointSlice Controller: Связывает сервисы с подами (заполняет списки IP-адресов).
  • ServiceAccount & Token Controllers: Создают стандартные аккаунты и токены доступа для новых пространств имен.

Сводная таблица компонентов

Компонент Роль в кластере Порт по умолчанию Состояние
kube-apiserver Интерфейс взаимодействия и валидации 6443 Stateless
etcd База данных, источник истины 2379, 2380 Stateful
kube-scheduler Выбор узла для запуска Pod 10259 Stateless
kube-controller-manager Поддержание желаемого состояния 10257 Stateless

Коротко о Worker Nodes

Хотя фокус этой статьи — Control Plane, для понимания общей картины необходимо упомянуть агентов, работающих на рабочих узлах:

  • kubelet: "Капитан" узла. Он получает от kube-apiserver спецификации подов (PodSpecs) и приказывает Container Runtime запустить или остановить контейнеры. Также он постоянно рапортует API-серверу о состоянии узла.
  • kube-proxy: Сетевой прокси-сервер. Он обновляет правила маршрутизации операционной системы (через iptables или IPVS), чтобы сетевой трафик правильно доходил до подов.
  • Container Runtime: Программное обеспечение, ответственное за непосредственный запуск контейнеров (containerd, CRI-O).

Жизненный цикл создания Pod: Как они общаются?

Понимание того, как компоненты взаимодействуют между собой — это ключ к успешному траблшутингу. Рассмотрим классический сценарий: вы выполняете команду kubectl apply -f pod.yaml.

Вот текстовая расшифровка этого процесса (идеальный ответ на техническом интервью):

  1. kubectl валидирует YAML локально и отправляет HTTP POST запрос в kube-apiserver.
  2. kube-apiserver аутентифицирует вас, проверяет права (RBAC), валидирует манифест и записывает объект Pod в etcd со статусом Pending (узел еще не назначен).
  3. kube-apiserver подтверждает kubectl, что Pod успешно создан в базе.
  4. kube-scheduler непрерывно опрашивает API-сервер. Он замечает новый Pod без привязки к узлу.
  5. kube-scheduler вычисляет лучший узел для пода и отправляет запрос Binding обратно в kube-apiserver.
  6. kube-apiserver обновляет запись в etcd, добавляя имя узла в спецификацию пода.
  7. kubelet на выбранном узле (который тоже непрерывно слушает API-сервер) замечает, что ему назначен новый Pod.
  8. kubelet обращается к Container Runtime (например, containerd) с командой скачать образ и запустить контейнер.
  9. После успешного запуска kubelet отправляет статус Running обратно в kube-apiserver, который финально обновляет данные в etcd.

В этой цепочке никто не общается друг с другом напрямую. Все коммуникации происходят исключительно через подписки (watches) на изменения в kube-apiserver.

Понимание этой архитектуры позволит вам на экзамене CKA быстро локализовать проблему. Если Pod висит в статусе Pending, проблема, скорее всего, в scheduler или нехватке ресурсов. Если Pod создается, но контейнер не стартует (статус ContainerCreating или CrashLoopBackOff), проблема на стороне kubelet или Container Runtime на конкретном узле.

Объекты API и управление рабочими нагрузками: Pods, Deployments, StatefulSets

Объекты API и управление рабочими нагрузками: Pods, Deployments, StatefulSets

Контейнер смертен, и чаще всего — внезапно. Если запустить «голый» Docker-контейнер на сервере, то при сбое узла он исчезнет навсегда. Kubernetes не работает с контейнерами напрямую именно по этой причине: ему нужны более высокие уровни абстракции, которые позволяют описывать желаемое состояние системы (Desired State) и автоматически восстанавливать его при сбоях.

В этой статье мы разберем иерархию рабочих нагрузок (Workloads) — от атомарной единицы до сложных контроллеров, управляющих приложениями без сохранения состояния (Stateless) и с сохранением состояния (Stateful).

Pod: Атомарная единица Kubernetes

Pod (Под) — это минимальный развертываемый объект в Kubernetes. Вы не можете развернуть половину пода или отдельный контейнер через API.

Почему Kubernetes использует поды, а не контейнеры? Под создает «логический хост» для одного или нескольких контейнеров, обеспечивая им общее пространство имен (Namespaces). Все контейнеры внутри одного пода делят:

  1. Сетевое пространство (Network): у них один IP-адрес и один набор портов. Они могут общаться друг с другом через localhost.
  2. Хранилище (Storage): они могут монтировать одни и те же тома (Volumes) и обмениваться файлами.
  3. IPC (Inter-Process Communication): могут использовать общую разделяемую память.

Паттерн Sidecar (Многоконтейнерный под)

На экзамене CKA часто встречается задача на добавление второго контейнера в существующий под. Это классический паттерн Sidecar (прицепная коляска). Основной контейнер выполняет бизнес-логику (например, веб-сервер Nginx), а вспомогательный — расширяет его функционал (например, агент Fluentd, собирающий логи Nginx из общей папки и отправляющий их на сервер).

Пример манифеста многоконтейнерного пода:

apiVersion: v1
kind: Pod
metadata:
  name: web-app-pod
spec:
  containers:
  - name: main-nginx
    image: nginx:1.24
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  - name: sidecar-log-collector
    image: busybox
    command: ["/bin/sh", "-c", "tail -f /var/log/nginx/access.log"]
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  volumes:
  - name: shared-logs
    emptyDir: {}

Жизненный цикл пода (Pod Phases)

Для успешного траблшутинга критически важно понимать фазы, через которые проходит под:

  • Pending: API-сервер принял манифест, но под еще не запущен (например, планировщик ищет узел или скачивается тяжелый образ).
  • Running: Под привязан к узлу, все контейнеры созданы, минимум один работает.
  • Succeeded: Все контейнеры успешно завершили работу (код возврата 00) и не будут перезапущены.
  • Failed: Все контейнеры завершили работу, но хотя бы один завершился с ошибкой (код возврата 0\neq 0).
  • CrashLoopBackOff (статус, а не фаза): Контейнер постоянно падает при старте, и kubelet увеличивает паузу между попытками перезапуска.

ReplicaSet: Страж количества

Сам по себе Pod уязвим. Если узел упадет, под умрет вместе с ним. Чтобы приложение было отказоустойчивым, подами управляют контроллеры. Базовый контроллер — ReplicaSet.

Его единственная задача — поддерживать математическое равенство: Desired=ActualDesired = Actual (Желаемое количество реплик равно фактическому).

ReplicaSet не привязан к подам жестко. Он находит «свои» поды с помощью Label Selectors (селекторов меток). Если вы вручную создадите под с меткой app: frontend, а ReplicaSet настроен на поддержание 3 подов с такой меткой, он посчитает ваш под своим. Если подов станет 4, он безжалостно удалит один случайный.

Примечание: В современной практике ReplicaSet почти никогда не создают напрямую. Ими управляет контроллер более высокого уровня — Deployment.

Deployment: Эволюция Stateless-приложений

Deployment — это декларативный способ управления ReplicaSet и подами. Он предназначен для Stateless приложений (веб-серверы, API-шлюзы, микросервисы), где все реплики абсолютно идентичны и взаимозаменяемы.

«Stateless-приложения — это стадо (cattle). Если одна овца заболела, вы не везете ее к ветеринару. Вы избавляетесь от нее и покупаете новую. Deployments автоматизируют этот процесс».

Главная суперсила Deployment — безопасное обновление версий приложения (Rollout) и откат назад (Rollback).

Стратегии обновления

  1. Recreate: Сначала удаляются все старые поды, затем создаются новые. Плюс: нет конфликтов версий. Минус: простой (Downtime) приложения.
  2. RollingUpdate (по умолчанию): Плавная замена старых подов на новые.

При RollingUpdate используются два важных параметра:

  • maxUnavailable: сколько подов от желаемого количества NN могут быть недоступны во время обновления (по умолчанию 25%).
  • maxSurge: на сколько подов можно превысить желаемое количество NN в процессе обновления (по умолчанию 25%).

Шпаргалка CKA: Управление Deployment

На экзамене вам часто придется обновлять образы и откатывать изменения. Делайте это императивными командами для экономии времени:

# Обновить образ контейнера 'nginx' на версию 1.25
kubectl set image deployment/frontend nginx=nginx:1.25

# Проверить статус раскатки
kubectl rollout status deployment/frontend

# Посмотреть историю обновлений (ревизии)
kubectl rollout history deployment/frontend

# Откатиться на предыдущую рабочую версию (Rollback)
kubectl rollout undo deployment/frontend

StatefulSet: Укрощение состояния

Если Deployments управляют «стадом», то StatefulSet предназначен для «домашних питомцев» (pets) — баз данных (PostgreSQL, MongoDB), брокеров сообщений (Kafka) и систем кэширования (Redis).

Для базы данных поды не взаимозаменяемы. Базе данных критически важно знать, кто является Master-узлом, а кто Replica. У них должны быть постоянные имена и постоянные диски.

Ключевые отличия StatefulSet:

  1. Стабильная сетевая идентичность: Поды получают предсказуемые имена с порядковыми индексами от 00 до N1N-1. Если StatefulSet называется mysql, поды будут называться mysql-0, mysql-1, mysql-2. При перезапуске имя сохраняется.
  2. Строгий порядок запуска и удаления: Под mysql-1 не начнет создаваться, пока mysql-0 не перейдет в состояние Running. Удаление происходит в обратном порядке: от N1N-1 до 00.
  3. Стабильное хранилище (VolumeClaimTemplates): Каждый под получает свой собственный независимый Persistent Volume. Если под mysql-0 падает и пересоздается на другом узле, StatefulSet гарантирует, что к нему будет подключен тот же самый диск с его старыми данными.

Сравнение Deployment и StatefulSet

Характеристика Deployment (Stateless) StatefulSet (Stateful)
Имена подов Случайный хэш (web-7b9c...) Индексные (db-0, db-1)
Порядок создания Параллельно (все сразу) Строго последовательно (0,1,2...0, 1, 2...)
Хранилище данных Общее или эфемерное Индивидуальное для каждого пода (через PVC)
Сетевой доступ Балансировка через обычный Service Прямой доступ к подам через Headless Service
Типичный сценарий Nginx, Node.js, Spring Boot PostgreSQL, Elasticsearch, RabbitMQ

О том, как именно StatefulSet обеспечивает прямой сетевой доступ к конкретному поду через Headless Service, мы подробно поговорим в следующей главе, посвященной сетевому взаимодействию.

Сетевое взаимодействие, сервисы и маршрутизация трафика в кластере

Сетевое взаимодействие, сервисы и маршрутизация трафика в кластере

Представьте мегаполис, в котором дома постоянно перемещаются, исчезают и отстраиваются заново на других улицах, но почтальон всегда доставляет письма точно по адресу. Это ваш кластер Kubernetes. Поды эфемерны: они создаются, умирают (CrashLoopBackOff) и пересоздаются контроллерами (Deployments) с совершенно новыми IP-адресами. Если IP-адреса постоянно меняются, как компоненты микросервисной архитектуры находят друг друга?

В этой статье мы разберем, как Kubernetes решает проблему сетевой связности, превращая хаос эфемерных подов в надежную и предсказуемую систему маршрутизации.

Базовая сетевая модель Kubernetes (CNI)

Прежде чем говорить о сервисах, необходимо понять фундамент. Kubernetes не реализует сеть самостоятельно. Он делегирует эту задачу плагинам CNI (Container Network Interface) — таким как Calico, Flannel или Cilium.

Независимо от выбранного плагина, сетевая модель K8s диктует три непреложных правила:

  1. Каждый Pod получает собственный уникальный IP-адрес в рамках кластера.
  2. Все Pods могут общаться друг с другом напрямую без использования NAT (Network Address Translation), даже если они находятся на разных узлах (Worker Nodes).
  3. Агенты узла (kubelet) могут общаться со всеми подами на этом узле.

Сетевая модель K8s устраняет необходимость проброса портов (port mapping) между контейнером и хостом, как это делалось в чистом Docker. Внутри кластера сеть выглядит как огромный плоский коммутатор.

Service: Стабильный якорь в море хаоса

Поскольку напрямую обращаться к IP-адресу пода бессмысленно (он изменится при следующем перезапуске), Kubernetes предлагает абстракцию — объект Service.

Service предоставляет единый, статический IP-адрес (VIP — Virtual IP) и DNS-имя для набора подов. Связь между Service и подами осуществляется через уже знакомый нам механизм Label Selectors. Service постоянно отслеживает поды с нужными метками и автоматически обновляет свой список конечных точек (Endpoints).

Основной манифест Service

apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: my-backend # Ищет поды с такой меткой
  ports:
    - protocol: TCP
      port: 80        # Порт самого Service (стабильный)
      targetPort: 8080 # Порт внутри контейнера Pod'а

Типы Service: От внутреннего к внешнему

В зависимости от того, откуда должен приходить трафик, Service может принимать один из четырех типов.

Тип Service Зона видимости Как работает Применение
ClusterIP (по умолчанию) Только внутри кластера Выдает внутренний статический IP. Недоступен извне. Связь между микросервисами (например, Backend \rightarrow Database).
NodePort Внешний доступ Открывает статический порт (диапазон 30000-32767) на каждом Worker Node. Прямой доступ к приложению извне (чаще для отладки).
LoadBalancer Внешний доступ Автоматически заказывает балансировщик нагрузки у облачного провайдера (AWS, GCP). Продакшн-доступ в публичных облаках.
ExternalName Внешний ресурс Работает как CNAME-запись в DNS, перенаправляя трафик на внешнее доменное имя. Доступ из кластера к внешней managed-базе данных.

Headless Service (Обещание из прошлой главы)

В прошлой статье мы упоминали, что для контроллера StatefulSet (базы данных, брокеры сообщений) требуется особый подход. Базам данных часто нужно знать точные адреса своих соседей для репликации, а не общаться через единый балансировщик.

Для этого используется Headless Service. В манифесте указывается clusterIP: None. Такой сервис не получает виртуального IP и не балансирует трафик. Вместо этого при DNS-запросе к имени сервиса CoreDNS возвращает список A-записей — реальных IP-адресов всех подов, попадающих под селектор.

Под капотом: Как работает kube-proxy

Service — это не физический маршрутизатор и не отдельный контейнер. Это абстракция, реализованная компонентом kube-proxy, который работает на каждом Worker Node.

Kube-proxy слушает Control Plane (kube-apiserver) на предмет появления новых Service и Endpoints, а затем конфигурирует сетевые правила операционной системы для перехвата трафика.

Существует два основных режима работы kube-proxy:

  1. iptables (по умолчанию): Kube-proxy создает правила iptables. Трафик перехватывается ядром Linux и перенаправляется на один из подов случайным образом. Проблема в том, что правила обрабатываются последовательно. При тысячах сервисов время поиска нужного правила растет линейно: O(n)\mathcal{O}(n), где nn — количество правил.
  2. IPVS (IP Virtual Server): Более современный режим, использующий хэш-таблицы в ядре Linux. Время маршрутизации остается константным: O(1)\mathcal{O}(1). Это критически важно для производительности в огромных кластерах.

Ingress: Умная маршрутизация L7 (HTTP/HTTPS)

NodePort и LoadBalancer работают на 4-м уровне модели OSI (L4 — транспортный). Они умеют маршрутизировать только TCP/UDP трафик по IP и портам.

Если у вас 10 веб-приложений, использование 10 LoadBalancer будет стоить дорого, а использование NodePort потребует от клиентов указывать нестандартные порты (например, example.com:31204).

Решение — Ingress. Это объект API, который управляет внешним доступом к сервисам в кластере, обычно по HTTP/HTTPS (L7 — прикладной уровень). Ingress позволяет настроить маршрутизацию на основе URL-путей или доменных имен.

Чтобы объект Ingress заработал, в кластере должен быть установлен Ingress Controller (например, NGINX Ingress Controller, Traefik или HAProxy). Контроллер — это под, который принимает весь внешний трафик на стандартные порты (80 и 443) и проксирует его нужным Service согласно правилам.

Пример манифеста Ingress (маршрутизация по пути)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
spec:
  rules:
  - host: my-company.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service # Трафик /api пойдет сюда
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service # Остальной трафик пойдет сюда
            port:
              number: 80

Service Discovery: Роль CoreDNS

Как поды узнают IP-адрес нужного Service? Через встроенный в кластер DNS-сервер — CoreDNS.

При создании Service, CoreDNS автоматически генерирует для него DNS-запись по строгому шаблону: <название-сервиса>.<пространство-имен>.svc.cluster.local

Например, если вы создали Service с именем db-master в namespace production, любой под в кластере сможет обратиться к нему по адресу: db-master.production.svc.cluster.local

Если обращающийся под находится в том же namespace (production), он может использовать короткое имя: просто db-master.

Шпаргалка для траблшутинга (CKA): Если поды не могут связаться по DNS-имени, первым делом проверьте статус подов CoreDNS в пространстве имен kube-system: kubectl get pods -n kube-system -l k8s-app=kube-dns.

Управление хранилищами данных: Persistent Volumes и механизмы CSI

Управление хранилищами данных: Persistent Volumes и механизмы CSI

Если контейнер внутри пода аварийно завершит работу, kubelet перезапустит его, но все файлы, созданные или измененные за время его жизни, исчезнут. Контейнер запустится с «чистого листа». В мире stateless приложений это норма, но для баз данных, очередей сообщений и файловых хранилищ потеря состояния — это катастрофа. Kubernetes решает эту проблему, вынося хранение данных за пределы жизненного цикла пода.

Эволюция хранения: от Volumes к PV и PVC

Базовый объект Volume (том) решает проблему обмена данными между контейнерами в одном поде (например, через emptyDir), но его существование жестко привязано к жизни самого пода. Если под удаляется, удаляется и том.

Для независимого хранения данных Kubernetes ввел абстракцию Persistent Volume (PV) и Persistent Volume Claim (PVC). Этот паттерн разделяет ответственность:

  1. Администратор кластера (или автоматика) управляет физическими дисками и создает объекты PersistentVolume.
  2. Разработчик приложения запрашивает нужный объем и тип доступа через PersistentVolumeClaim, не вникая в то, какой именно массив хранения (NFS, AWS EBS, Ceph) используется под капотом.

PersistentVolume (PV) — это ресурс кластера (не привязан к namespace), представляющий собой физический или виртуальный диск.

PersistentVolumeClaim (PVC) — это запрос на выделение хранилища (привязан к namespace), который делает приложение.

Жизненный цикл тома (Статическое выделение)

При статическом подходе администратор заранее создает пул PV. Когда разработчик создает PVC, Kubernetes ищет подходящий PV и связывает их. Этот процесс называется Binding.

Логика связывания (Binding)

Контроллер PersistentVolumeController непрерывно следит за новыми PVC. Чтобы PVC успешно связался с PV, должны совпасть следующие условия:

  • Емкость (Capacity): Объем PV должен быть больше или равен запрошенному в PVC (VpvVpvcV_{pv} \geq V_{pvc}).
  • Режим доступа (Access Modes): PV должен поддерживать режимы, запрошенные в PVC.
  • Класс хранилища (StorageClassName): Если в PVC указан класс, PV должен иметь такой же.
  • Метки (Label Selectors): Если в PVC указан селектор, PV должен иметь соответствующие метки.

Связь PV и PVC эксклюзивна: один PV может быть привязан только к одному PVC.

Режимы доступа (Access Modes)

Режим доступа определяет, как именно узлы кластера могут монтировать том.

Режим Аббревиатура Описание Типичный Use-Case
ReadWriteOnce RWO Том монтируется на чтение и запись только одним узлом (Node). Базы данных (AWS EBS, GCE PD).
ReadOnlyMany ROX Том монтируется только для чтения множеством узлов. Раздача статики, конфигураций.
ReadWriteMany RWX Том монтируется на чтение и запись множеством узлов. Файловые серверы (NFS, CephFS).
ReadWriteOncePod RWOP Том монтируется на чтение и запись строго одним подом (появилось в K8s 1.22+). Максимальная изоляция данных пода.

Политики возврата (Reclaim Policy)

Что происходит с диском (PV), когда пользователь удаляет заявку (PVC)? Это определяется политикой persistentVolumeReclaimPolicy:

  1. Retain (Сохранение): PV переходит в статус Released, но данные остаются нетронутыми. Том нельзя использовать повторно до ручной очистки администратором.
  2. Delete (Удаление): Удаляется и объект PV в Kubernetes, и сам физический диск во внешнем хранилище (например, удаляется EBS-том в AWS).
  3. Recycle: Устаревший метод (выполнял rm -rf /thevolume/*). Сейчас заменен динамическим выделением.

Пример статического манифеста (для экзамена CKA)

На экзамене часто просят создать локальный PV (тип hostPath). Помните, что hostPath подходит только для тестов на одном узле.

# 1. Создаем PersistentVolume (ресурс кластера)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-local-data
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: "/mnt/data" # Директория на Worker Node
---
# 2. Создаем заявку PVC (в конкретном namespace)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-app-data
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi # Запрашиваем 2Gi (свяжется с PV на 5Gi, так как 5 >= 2)

Container Storage Interface (CSI) и Динамическое выделение

Статическое выделение не масштабируется. В крупных кластерах администраторы не создают диски вручную. Здесь на сцену выходит Dynamic Provisioning (динамическое выделение) и объект StorageClass.

Что такое CSI?

Исторически код для интеграции с облачными дисками (AWS, GCP, VMware) был вшит прямо в исходный код Kubernetes (in-tree плагины). Это замедляло разработку и раздувало ядро системы.

CSI (Container Storage Interface) — это стандартный API, который позволяет провайдерам хранилищ писать свои драйверы как независимые приложения (out-of-tree), разворачиваемые в кластере в виде обычных подов (часто через DaemonSet).

StorageClass: автоматизация создания дисков

StorageClass — это профиль хранилища, который описывает, какой CSI-драйвер использовать и с какими параметрами (например, тип диска SSD или HDD, зона доступности).

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: ebs.csi.aws.com # Указание на CSI-драйвер
parameters:
  type: gp3
  iopsPerGB: "50"
volumeBindingMode: WaitForFirstConsumer

Важное свойство: volumeBindingMode: WaitForFirstConsumer. По умолчанию диск создается в момент создания PVC. Но если кластер размазан по нескольким зонам доступности (AZ), диск может создаться в Zone A, а планировщик (kube-scheduler) решит запустить под в Zone B. Возникнет конфликт. WaitForFirstConsumer заставляет контроллер хранилища ждать, пока планировщик не выберет узел для пода, и только тогда создает диск в нужной зоне.

Шпаргалка администратора: Траблшутинг хранилищ

На экзамене CKA и в реальной работе вы часто будете сталкиваться с проблемами монтирования.

1. Проверка статусов:

kubectl get pv,pvc,sc

Идеальное состояние PVC — Bound. Если PVC висит в статусе Pending, значит контроллер не может найти подходящий PV или StorageClass не может его создать.

2. Анализ причин Pending:

kubectl describe pvc <pvc-name>

Ищите секцию Events. Типичные ошибки:

  • FailedBinding: Нет PV с нужным capacity или accessModes.
  • WaitForFirstConsumer: Это не ошибка. PVC ждет, пока вы создадите Pod, который использует этот PVC.

3. Безопасное удаление: Если вы удалите PVC, который сейчас смонтирован в работающий Pod, Kubernetes включит защиту Storage Object in Use Protection. PVC перейдет в статус Terminating и зависнет в нем, пока вы не удалите Pod.

Конфигурация приложений: использование ConfigMaps, Secrets и переменных окружения

Конфигурация приложений: использование ConfigMaps, Secrets и переменных окружения

Представьте ситуацию: вы собрали идеальный Docker-образ вашего приложения, развернули его в кластере, и всё работает. Но вдруг администратор базы данных сообщает, что пароль или URL для подключения изменились. Если эти данные «зашиты» внутри образа, вам придется пересобрать образ, перетестировать его и заново развернуть. Это долго, неэффективно и нарушает один из главных принципов методологии Twelve-Factor App: строгое разделение кода и конфигурации.

В Kubernetes эта проблема решается элегантно: контейнеры остаются неизменными (stateless-шаблонами), а конфигурация внедряется в них на этапе запуска (в момент создания Pod) с помощью специальных объектов API — ConfigMaps и Secrets.

ConfigMap: хранилище открытых данных

ConfigMap — это объект Kubernetes, предназначенный для хранения неконфиденциальных данных в формате «ключ-значение» (key-value).

Чаще всего в ConfigMap хранят:

  • Переменные окружения (например, LOG_LEVEL=DEBUG).
  • Конфигурационные файлы целиком (например, nginx.conf или application.properties).
  • Аргументы командной строки для запуска контейнера.

Важный инсайт для CKA: Максимальный размер одного ConfigMap (как и Secret) ограничен 1 МБ1 \text{ МБ}. Это связано с ограничениями хранилища etcd. Если ваша конфигурация больше, её следует хранить во внешнем хранилище или разбивать на части.

Декларативный и императивный подходы

Вы можете создать ConfigMap через YAML-манифест:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  database_url: "jdbc:postgresql://db-master:5432/main"
  log_level: "INFO"

Однако на экзамене CKA время ограничено. Намного быстрее использовать императивные команды:

# Создание из литералов
kubectl create configmap app-config --from-literal=log_level=INFO --from-literal=database_url=jdbc...

# Создание из файла (имя файла станет ключом, а его содержимое — значением)
kubectl create configmap nginx-config --from-file=nginx.conf

Secrets: работа с чувствительными данными

Secret работает точно так же, как ConfigMap, но предназначен для хранения конфиденциальной информации: паролей, OAuth-токенов, SSH-ключей и TLS-сертификатов.

Главное отличие на уровне манифеста заключается в том, что данные в Secret (в секции data) должны быть закодированы в формате Base64.

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=        # "admin" в base64
  password: c3VwZXJzZWNyZXQ= # "supersecret" в base64

Разрушение мифа о безопасности Secrets

Base64 — это кодирование, а не шифрование. Любой, кто имеет доступ к манифесту Secret, может декодировать его значения командой echo "YWRtaW4=" | base64 --decode.

Почему же тогда используется Base64? Исключительно для того, чтобы безопасно передавать бинарные данные (например, сертификаты) или спецсимволы внутри текстовых форматов JSON/YAML, с которыми работает API Kubernetes.

По умолчанию объекты Secret хранятся в etcd в виде обычного (незашифрованного) текста. Для реальной защиты на уровне кластера администраторы настраивают Encryption at Rest (шифрование данных в покое для etcd) и строго ограничивают доступ к ресурсам Secret через RBAC.

Способы доставки конфигурации в Pod

Создать ConfigMap или Secret — это только половина дела. Их нужно передать внутрь контейнера. Kubernetes предлагает два основных механизма: переменные окружения и монтирование томов (Volumes).

1. Переменные окружения (Environment Variables)

Идеально подходят для одиночных значений (URL, порты, флаги).

Точечное внедрение (valueFrom): Вы можете выбрать конкретный ключ из ConfigMap/Secret и назначить его переменной окружения.

env:
  - name: DB_USER
    valueFrom:
      secretKeyRef:
        name: db-credentials
        key: username

Массовое внедрение (envFrom): Если в ConfigMap 10 ключей, описывать каждый долго. Можно импортировать их все разом. Ключи из ConfigMap станут именами переменных окружения.

envFrom:
  - configMapRef:
      name: app-config

2. Монтирование как файлы (Volumes)

Этот способ обязателен, если вам нужно передать целый файл конфигурации (например, prometheus.yml или tls.crt). Kubernetes создает временный том, заполняет его данными из ConfigMap/Secret и монтирует в файловую систему контейнера. Каждый ключ становится именем файла, а значение — содержимым этого файла.

volumes:
  - name: config-volume
    configMap:
      name: nginx-config
containers:
  - name: nginx
    volumeMounts:
      - name: config-volume
        mountPath: /etc/nginx/conf.d

Динамическое обновление: Env vs Volumes

Это один из самых частых вопросов на технических интервью: «Что произойдет с приложением, если я изменю ConfigMap, который уже используется запущенным подом?»

Ответ зависит от способа доставки:

Характеристика Переменные окружения (env) Тома (volumeMounts)
Обновление при изменении источника НЕТ. Процессы в Linux читают переменные окружения только при старте. ДА. kubelet периодически синхронизирует примонтированные файлы.
Требуется ли рестарт Pod? Да, Pod нужно пересоздать, чтобы он подхватил новые значения. Нет, файл обновится «на лету» (но само приложение должно уметь перечитывать файл, например, по Webhook или inotify).

Неизменяемые конфигурации (Immutable)

С версии Kubernetes 1.21 появилась возможность помечать ConfigMaps и Secrets как неизменяемые, добавив поле immutable: true в манифест.

Зачем это нужно? Когда конфигурация монтируется как том, kubelet на каждой Worker Node вынужден регулярно опрашивать API-сервер, чтобы узнать, не изменился ли ConfigMap. В кластерах с тысячами подов это создает колоссальную нагрузку на kube-apiserver. Установка immutable: true отключает этот поллинг (опрос), радикально снижая нагрузку на Control Plane. Если конфигурацию нужно изменить, создается новый ConfigMap (например, app-config-v2), и Deployment обновляется для его использования.

Резюме для подготовки к CKA

  1. Используйте императивные команды (kubectl create configmap ..., kubectl create secret generic ...) для экономии времени.
  2. Помните синтаксис проброса: env -> valueFrom -> configMapKeyRef / secretKeyRef.
  3. Четко понимайте разницу: переменные окружения статичны после запуска пода, файлы в томах обновляются динамически силами kubelet.
  4. Base64 в Secrets — это не безопасность, это кодирование для передачи данных.

Управление доступом (RBAC), безопасность контекста и сетевые политики

Управление доступом (RBAC), безопасность контекста и сетевые политики

Представьте себе крепость с титановой входной дверью, внутри которой все межкомнатные двери открыты настежь, а каждый гость носит в кармане универсальную отмычку. Именно так выглядит кластер Kubernetes «из коробки». API-сервер надежно аутентифицирует входящие запросы, но по умолчанию любой под может отправить сетевой запрос любому другому поду, а контейнеры часто запускаются с правами root. В этой статье мы разберем три фундаментальных механизма, которые превращают эту открытую систему в безопасную среду с нулевым доверием (Zero Trust).

1. RBAC: Кто, Как и Что может делать

Управление доступом на основе ролей (Role-Based Access Control) в Kubernetes решает проблему авторизации. После того как пользователь или приложение прошли аутентификацию, RBAC определяет, разрешен ли им конкретный API-запрос.

Архитектура RBAC строится на трех китах:

  1. Subject (Субъект)Кто запрашивает доступ (User, Group или ServiceAccount).
  2. Role (Роль)Что разрешено делать (набор правил).
  3. Binding (Привязка)Как они связаны (назначение роли субъекту).

Если бы мы назначали права каждому пользователю напрямую, сложность системы росла бы лавинообразно по формуле C=U×PC = U \times P, где UU — количество пользователей, а PP — количество разрешений. Ролевая модель снижает эту сложность, группируя разрешения.

Пространство имен (Namespace) против Кластера (Cluster)

Объекты RBAC строго делятся по области видимости. Это частая ловушка на экзамене CKA.

Область действия Роль Привязка Применение
Namespace Role RoleBinding Доступ к ресурсам внутри одного неймспейса (Pods, Services, PVC).
Cluster ClusterRole ClusterRoleBinding Доступ к кластерным ресурсам (Nodes, PV) или ко всем неймспейсам сразу.

Экзаменационный лайфхак (CKA): Вы можете использовать RoleBinding в конкретном неймспейсе, чтобы привязать субъекта к ClusterRole. Это даст пользователю права, описанные в ClusterRole, только в рамках этого неймспейса. Это избавляет от необходимости дублировать одинаковые Role в каждом неймспейсе.

Практика: Манифесты и проверка прав

Рассмотрим создание сервисного аккаунта для CI/CD системы, которой нужно только читать логи подов в неймспейсе backend.

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cicd-viewer
  namespace: backend
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: backend
rules:
- apiGroups: [""] # Пустая строка означает core API group
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-binding
  namespace: backend
subjects:
- kind: ServiceAccount
  name: cicd-viewer
  namespace: backend
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Для проверки прав на экзамене и в жизни используется императивная команда auth can-i. Она позволяет задать вопрос API-серверу от лица любого субъекта:

# Может ли текущий пользователь удалять деплойменты?
kubectl auth can-i delete deployments

# Может ли ServiceAccount 'cicd-viewer' читать поды в неймспейсе 'backend'?
kubectl auth can-i get pods --as=system:serviceaccount:backend:cicd-viewer -n backend

2. Security Context: Ограничение привилегий контейнера

Если RBAC защищает API Kubernetes, то Security Context защищает узлы кластера (Worker Nodes) от вредоносного кода внутри контейнеров.

По умолчанию процесс в контейнере запускается от имени пользователя root (UID 0). Если злоумышленник найдет уязвимость выхода из контейнера (container breakout), он получит root-доступ к самому узлу.

Security Context можно применять на двух уровнях:

  • Pod level (spec.securityContext): применяется ко всем контейнерам и томам в поде.
  • Container level (spec.containers[].securityContext): переопределяет настройки пода для конкретного контейнера.

Ключевые параметры

  • runAsUser / runAsGroup: Принудительный запуск процессов от непривилегированного пользователя (например, UID 1000).
  • runAsNonRoot: Запрещает запуск контейнера, если образ требует root-прав.
  • privileged: Если true, контейнер получает почти все права хостовой машины (опасно!).
  • capabilities: Управление возможностями ядра Linux. Вместо выдачи полных root-прав, можно точечно добавить или забрать системные вызовы.

Пример защиты пода с веб-сервером:

apiVersion: v1
kind: Pod
metadata:
  name: secure-nginx
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000 # Права на смонтированные тома
  containers:
  - name: nginx
    image: nginx
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL # Отбираем все root-привилегии ядра
        add:
        - NET_BIND_SERVICE # Разрешаем только биндинг портов < 1024

3. Network Policies: Внутренний файрвол кластера

Как мы помним из главы про сетевое взаимодействие, CNI-плагины по умолчанию реализуют плоскую сеть: любой под может установить соединение с любым другим подом.

NetworkPolicy — это объект API, который позволяет ограничить сетевой трафик на уровнях L3 (IP-адреса) и L4 (TCP/UDP порты).

Важно: NetworkPolicy работает только если ваш CNI-плагин (например, Calico, Cilium, Weave Net) поддерживает их. Если вы создадите политику в кластере с Flannel, API-сервер ее примет, но трафик фильтроваться не будет.

Логика работы и Default Deny

Политики оперируют двумя направлениями:

  • Ingress (входящий трафик к поду).
  • Egress (исходящий трафик от пода).

По умолчанию в неймспейсе действует правило Default Allow. Но как только вы применяете хотя бы одну NetworkPolicy, которая выбирает под (через podSelector), для этого пода включается режим изоляции, и весь трафик, не описанный явно в правилах, отбрасывается.

Лучшая практика информационной безопасности — создание политики Default Deny для всего неймспейса:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {} # Пустой селектор выбирает ВСЕ поды в неймспейсе
  policyTypes:
  - Ingress
  - Egress

После применения этого манифеста неймспейс production становится полностью изолированным.

Анатомия правил (Матрица доступа)

Рассмотрим классическую задачу CKA: разрешить доступ к поду базы данных (порт 3306) только от подов с меткой app=backend из неймспейса с меткой env=prod.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-access
  namespace: database
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          env: prod
      podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 3306

Осторожно, частая ошибка! Обратите внимание на структуру массива from в YAML: Если namespaceSelector и podSelector находятся под одним дефисом (-), они работают по логике AND (И). Трафик разрешен только если под соответствует И метке, И находится в нужном неймспейсе. Если бы они были с разными дефисами, они бы работали по логике OR (ИЛИ), разрешая доступ всем подам из нужного неймспейса ПЛЮС любым подам с нужной меткой из текущего неймспейса.

Выводы для подготовки к CKA

На экзамене от вас потребуется быстро находить дыры в безопасности. Запомните алгоритм:

  1. Проверьте права через kubectl auth can-i.
  2. Изучите securityContext в манифесте пода на предмет privileged: true или запуска от root.
  3. Убедитесь, что в неймспейсе есть NetworkPolicy, ограничивающая трафик к критичным компонентам.

Планирование ресурсов, квоты и стратегии распределения нагрузки

Планирование ресурсов, квоты и стратегии распределения нагрузки

Представьте ситуацию: вы разворачиваете в кластере небольшой вспомогательный скрипт. Через час на этом же узле внезапно падает production-база данных, а сам узел переходит в статус NotReady. Причина банальна — скрипт содержал утечку памяти, а Kubernetes позволил ему забрать всю доступную оперативную память узла. Чтобы кластер оставался стабильным, предсказуемым и отказоустойчивым, необходимо жестко управлять двумя аспектами: сколько ресурсов потребляет приложение и где именно оно должно работать.

Управление ресурсами Pod: Requests и Limits

В Kubernetes управление вычислительными ресурсами строится вокруг двух фундаментальных понятий, которые часто путают, но они служат абсолютно разным целям.

  • Requests (Запросы) — это гарантированный минимум ресурсов, необходимый контейнеру. Эта цифра используется только kube-scheduler на этапе планирования (Binding). Если на узле нет свободного объема ресурсов, равного сумме Requests всех подов плюс новый под, планировщик туда его не назначит.
  • Limits (Лимиты) — это жесткий максимум, который контейнер не может превысить. За соблюдением лимитов следит kubelet (через механизмы cgroups в Linux ядра) уже во время выполнения.

Математика ресурсов

В манифестах ресурсы измеряются в специфических единицах:

  • CPU (Процессорное время): Измеряется в ядрах или милликорах (millicores). 1 CPU=1000 m1 \text{ CPU} = 1000 \text{ m} Где 1 CPU1 \text{ CPU} эквивалентен одному гиперпотоку (vCPU) на облачной ВМ или одному физическому ядру на bare-metal. Значение 500 m500 \text{ m} означает половину ядра.
  • Memory (Память): Измеряется в байтах, но обычно используются мебибайты (Mi) или гибибайты (Gi). 1 Mi=1024 Ki1 \text{ Mi} = 1024 \text{ Ki} Важно не путать с мегабайтами (MB, где множитель 1000). В K8s стандарт — степени двойки.

Сжимаемые и несжимаемые ресурсы

Разница между CPU и RAM критична при достижении лимитов:

  1. CPU — сжимаемый ресурс (Compressible). Если контейнер пытается превысить свой CPU Limit, он не падает. Kubelet просто начинает искусственно замедлять его работу (CPU Throttling). Приложение работает медленнее, но продолжает жить.
  2. Memory — несжимаемый ресурс (Incompressible). Память нельзя «замедлить». Если контейнер превышает Memory Limit, ядро Linux немедленно убивает процесс. Под переходит в состояние OOMKilled (Out Of Memory).

Классы обслуживания (QoS Classes)

На основе того, как вы задаете Requests и Limits, Kubernetes автоматически присваивает поду класс обслуживания (Quality of Service). Это критически важно в ситуации, когда узел исчерпал ресурсы и kubelet должен решить, кого «убить» первым (Eviction).

Класс QoS Условие присвоения Приоритет выживания
Guaranteed Requests строго равны Limits для CPU и RAM во всех контейнерах пода. Самый высокий. Убиваются последними.
Burstable Заданы Requests, но Limits либо больше Requests, либо не заданы вообще. Средний. Убиваются, если нет BestEffort.
BestEffort Не заданы ни Requests, ни Limits. Самый низкий. Первые кандидаты на удаление.

Правильно настроенные классы QoS — основа стабильности. Критичные компоненты БД всегда должны быть Guaranteed, а фоновые задачи по обработке логов можно оставить Burstable.

Контроль на уровне Namespace: LimitRange и ResourceQuota

Если вы администратор кластера (CKA), вы не можете доверять разработчикам ручное прописывание лимитов. Люди склонны либо забывать о них (создавая BestEffort поды), либо запрашивать слишком много. Для защиты неймспейсов используются два объекта.

LimitRange: Политики по умолчанию для одного Pod

LimitRange позволяет задать значения по умолчанию для контейнеров, а также минимальные и максимальные границы. Если разработчик деплоит под без ресурсов, K8s автоматически подставит значения из LimitRange.

apiVersion: v1
kind: LimitRange
metadata:
  name: default-mem-cpu
  namespace: development
spec:
  limits:
  - default: # Применится как Limit, если не указано
      cpu: 500m
      memory: 512Mi
    defaultRequest: # Применится как Request, если не указано
      cpu: 100m
      memory: 256Mi
    type: Container

ResourceQuota: Глобальный лимит на Namespace

Если LimitRange контролирует каждый отдельный под, то ResourceQuota ограничивает суммарное потребление всего неймспейса.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "4" # Суммарно не более 4 ядер
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "10" # Не более 10 подов суммарно

Важный нюанс для экзамена: Если в неймспейсе создана ResourceQuota на CPU/RAM, создание любого пода без явного указания ресурсов (или без LimitRange в неймспейсе) будет отклонено API-сервером.

Продвинутый шедулинг: Taints и Tolerations

До этого мы обсуждали ресурсы. Теперь поговорим о размещении. Механизм Taints (пятна) и Tolerations (допуски) работает по принципу отторжения.

Узел получает Taint («запах» или «замок»). Поды по умолчанию избегают таких узлов. Чтобы под смог запуститься на узле с Taint, у него должен быть соответствующий Toleration («противогаз» или «ключ»).

Эффекты Taints

При установке Taint указывается эффект, определяющий строгость правила:

  1. NoSchedule: Жесткий запрет. Новые поды без Toleration не будут запланированы на узел. Уже запущенные поды продолжат работать.
  2. PreferNoSchedule: Мягкий запрет. Планировщик постарается не помещать сюда поды, но если других вариантов нет — поместит.
  3. NoExecute: Безжалостный запрет. Новые поды не планируются, а уже запущенные поды без Toleration будут немедленно выселены (Evicted) с узла.

Императивная команда установки Taint: kubectl taint nodes node1 gpu=true:NoSchedule

Декларативный Toleration в спецификации Pod:

tolerations:
- key: "gpu"
  operator: "Equal"
  value: "true"
  effect: "NoSchedule"

Инсайт архитектуры: Control Plane узлы по умолчанию имеют Taint node-role.kubernetes.io/control-plane:NoSchedule. Именно поэтому обычные поды разработчиков там не запускаются.

Притяжение рабочих нагрузок: Affinity и Anti-Affinity

Если Taints отталкивают поды, то механизмы Affinity (родство) их притягивают.

Node Affinity (Родство к узлам)

Это эволюция простого nodeSelector. Позволяет использовать логические операторы (In, NotIn, Exists) и задавать мягкие или жесткие правила.

  • requiredDuringSchedulingIgnoredDuringExecutionЖесткое правило (Hard). Если подходящего узла нет, под останется в состоянии Pending.
  • preferredDuringSchedulingIgnoredDuringExecutionМягкое правило (Soft). Планировщик попытается найти узел, но если не найдет, запустит под где-нибудь еще.

(Обратите внимание на суффикс IgnoredDuringExecution — если метка узла изменится после того, как под уже запущен, под не будет выселен).

Pod Affinity и Anti-Affinity (Родство между подами)

Часто нужно планировать поды не относительно свойств узлов, а относительно других подов.

  • Pod Affinity: "Размести этот под там же, где работает под X". Пример: веб-сервер и локальный кэш должны быть на одном узле для минимальной сетевой задержки.
  • Pod Anti-Affinity: "Никогда не размещай этот под там, где уже работает под X". Пример: разнесение реплик базы данных по разным серверам для отказоустойчивости.

Магия topologyKey

Ключевой параметр в правилах Pod (Anti)Affinity — это topologyKey. Он определяет границы домена отказа (failure domain).

Если topologyKey: kubernetes.io/hostname, планировщик будет разносить поды по разным физическим серверам. Если topologyKey: topology.kubernetes.io/zone, планировщик будет разносить поды по разным дата-центрам (зонам доступности), даже если в одной зоне много свободных узлов. Это основа построения Multi-AZ (Availability Zone) кластеров.

# Пример Pod Anti-Affinity
affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: app
          operator: In
          values:
          - web-store
      topologyKey: "kubernetes.io/hostname"
      # Не ставить два пода app=web-store на один и тот же узел

Комбинируя Requests/Limits (защита от перегрузки), LimitRange/ResourceQuota (административный контроль) и Taints/Affinity (интеллектуальное размещение), администратор Kubernetes создает самобалансирующуюся систему, устойчивую к отказам оборудования и ошибкам в коде приложений.

Методология траблшутинга, логирование и мониторинг состояния кластера

Методология траблшутинга, логирование и мониторинг состояния кластера

Представьте: вы на экзамене CKA, таймер неумолимо отсчитывает время, а развернутое приложение упорно выдает 502 Bad Gateway. Паника — ваш главный враг в этой ситуации. Хаотичный ввод команд в терминал редко приводит к успеху. Эффективный администратор Kubernetes отличается от новичка не тем, что знает наизусть все возможные ошибки, а тем, что владеет строгим алгоритмом поиска первопричины.

В этой статье мы разберем системный подход к диагностике кластера, научимся читать «следы», которые оставляют компоненты, и рассмотрим инструменты мониторинга.

Воронка траблшутинга: принцип Top-Down

Главное правило диагностики Kubernetes: двигайтесь от общего к частному. Никогда не начинайте с чтения логов приложения, если вы не убедились, что узел, на котором оно запущено, вообще жив.

Этот подход можно визуализировать как воронку, где на каждом этапе мы сужаем область поиска.

Уровень Что проверяем Ключевые команды Типичные проблемы
1. Cluster / Node Состояние узлов кластера, системные сервисы kubectl get nodes, systemctl status kubelet Узел в статусе NotReady, исчерпаны ресурсы диска, упал kubelet.
2. Pod Статус пода, события планировщика kubectl get pods, kubectl describe pod Pending, ImagePullBackOff, CrashLoopBackOff, ошибки монтирования томов.
3. Container Логи самого приложения внутри пода kubectl logs Ошибки в коде приложения, неверные переменные окружения, падение процесса.
4. Network Маршрутизация, DNS, Endpoints kubectl get svc, kubectl get endpoints Сервис не видит поды, не работает CoreDNS, блокировка NetworkPolicy.

Рассмотрим каждый уровень подробно, так как именно по этому сценарию строится большинство задач на траблшутинг в CKA.

Уровень 1: Диагностика Worker Node и Kubelet

Если kubectl get nodes показывает, что один из узлов находится в статусе NotReady, планировщик (kube-scheduler) перестанет назначать на него новые поды.

Первый шаг — посмотреть события узла:

kubectl describe node <node-name>

Ищите секцию Conditions (MemoryPressure, DiskPressure, PIDPressure, Ready). Если причина не очевидна, проблема почти всегда кроется в агенте узла — kubelet.

Поскольку kubelet работает как обычный системный сервис Linux (systemd), а не как контейнер, диагностировать его нужно средствами ОС:

# Подключаемся к проблемному узлу по SSH
ssh node-01

# Проверяем статус сервиса
systemctl status kubelet

# Читаем системный журнал (логи kubelet)
journalctl -u kubelet -f

Частые причины падения kubelet: неверный путь к сертификатам, ошибка конфигурации в /var/lib/kubelet/config.yaml или выключенный swap (если не задан флаг --fail-swap-on=false).

Уровень 2: Жизнь, смерть и события Pod'ов

Если узлы здоровы, но приложение не работает, переходим на уровень Pod. Статус пода — ваш главный индикатор.

Механика CrashLoopBackOff

Один из самых раздражающих статусов — CrashLoopBackOff. Он означает, что контейнер запускается, сразу же падает (завершается с ошибкой), kubelet его перезапускает, и цикл повторяется.

Чтобы не перегружать систему (например, если приложение падает из-за недоступности БД), Kubernetes использует экспоненциальную задержку между перезапусками.

Формула задержки выглядит так:

Tdelay=min(102n1,300)T_{delay} = \min(10 \cdot 2^{n-1}, 300)

Где TdelayT_{delay} — время ожидания перед следующим запуском в секундах, nn — порядковый номер перезапуска, а 300300 — жесткий лимит в 5 минут (максимальная задержка).

Использование Events (событий)

События (Events) — это хроника того, что Control Plane делал с вашим объектом. Если под висит в статусе Pending или ImagePullBackOff, логи контейнера вам не помогут (контейнер еще не создан!). Вам нужны события.

kubectl describe pod <pod-name>

В самом низу вывода будет таблица Events. Именно здесь вы увидите сообщения от default-scheduler (например, нет узлов, удовлетворяющих nodeAffinity) или от kubelet (ошибка скачивания образа, ошибка монтирования Secret).

Связь с предыдущими темами: В главе 7 мы упоминали статус OOMKilled. Если под был убит ядром Linux за превышение лимита памяти, в kubectl describe pod в секции Containers -> State вы увидите Reason: OOMKilled.

Уровень 3: Логирование контейнеров

Когда под находится в статусе Running, но ведет себя некорректно (например, отдает HTTP 500), мы спускаемся на уровень приложения.

# Просмотр логов пода
kubectl logs <pod-name>

# Просмотр логов в реальном времени (tail)
kubectl logs -f <pod-name>

Особенность многоконтейнерных подов: Если в поде работает паттерн Sidecar (например, основное приложение и сборщик метрик), команда kubectl logs выдаст ошибку, попросив указать конкретный контейнер:

kubectl logs <pod-name> -c <container-name>

Если контейнер упал и перезапустился, вы можете посмотреть логи его предыдущего (мертвого) инстанса, добавив флаг -p (previous):

kubectl logs <pod-name> -p

Уровень 4: Сетевая связность и Service Discovery

Сценарий: поды Running, логи чистые, но веб-интерфейс недоступен. Проблема на уровне сети.

Самая частая ошибка новичков — неправильные Label Selectors в манифесте Service. Service ищет поды по меткам. Если он их находит, он создает объект Endpoints.

Золотое правило сетевого траблшутинга: Всегда проверяйте Endpoints. Если у Service нет Endpoints, трафик никуда не пойдет.

# Проверяем, нашел ли сервис наши поды
kubectl get endpoints <service-name>

Если список IP-адресов пуст, проверьте совпадение selector в Service и labels в Pod.

Если Endpoints есть, но связи по DNS-имени нет, проверяем работу CoreDNS. Для этого на экзамене удобно запустить временный под с утилитами nslookup или dig:

kubectl run -i --tty --rm debug-pod --image=busybox --restart=Never -- sh
# Внутри пода выполняем:
nslookup <service-name>

Базовый мониторинг: Metrics Server

Для принятия решений о масштабировании и планировании ресурсов (Requests/Limits, которые мы разбирали ранее) кластеру необходимо знать текущее потребление CPU и памяти.

Сам по себе kube-apiserver эти метрики не хранит. Для этого требуется установить аддон Metrics Server. Он собирает данные с kubelet каждого узла и предоставляет их через Metrics API.

Без Metrics Server команды семейства kubectl top работать не будут.

# Посмотреть потребление ресурсов узлами
kubectl top nodes

# Посмотреть потребление ресурсов подами с сортировкой по CPU
kubectl top pods --sort-by=cpu

Знание этих команд критически важно для CKA: вас могут попросить найти под, потребляющий больше всего памяти, и записать его имя в файл.

Справочник по командам kubectl и шаблоны ключевых YAML-манифестов

Справочник по командам kubectl и шаблоны ключевых YAML-манифестов

На экзамене Certified Kubernetes Administrator (CKA) у вас есть ровно 120120 минут на решение 152015-20 практических задач. Это означает, что на одну задачу отводится в среднем 686-8 минут. Если вы начнете писать YAML-манифесты с чистого листа, вспоминая правильные отступы и названия полей, шансы на успешную сдачу стремятся к нулю. Главный навык администратора в стрессовой ситуации — умение заставить кластер написать базовый код за вас.

В этой статье мы систематизируем инструментарий kubectl, превратив его из простого клиента в мощный генератор кода и поисковую систему по кластеру.

Философия скорости: Императивный подход для декларативных целей

Хотя Kubernetes построен на декларативной парадигме (мы описываем желаемое состояние в YAML, а кластер его достигает), на экзамене и в траблшутинге правит императивный подход.

Ваш главный инструмент — связка флагов --dry-run=client и -o yaml.

Золотое правило CKA Никогда не пишите манифесты с нуля. Генерируйте каркас императивной командой, сохраняйте его в файл, а затем редактируйте только специфические требования задачи.

Как это работает на практике:

  1. Вы отдаете команду создать объект, но добавляете --dry-run=client. Это говорит kubectl: «Сформируй запрос, но не отправляй его на API-сервер».
  2. Флаг -o yaml приказывает вывести сформированный объект в консоль в формате YAML.
  3. Оператор > перенаправляет этот вывод в файл.
# Генерация идеального каркаса пода за 2 секунды
kubectl run nginx-pod --image=nginx:alpine --dry-run=client -o yaml > pod.yaml

Навигация и контекст: Защита от фатальных ошибок

На экзамене вы будете работать с несколькими кластерами. Выполнение правильной команды в неправильном контексте (кластере) оценивается в 00 баллов за задачу.

Задача Команда Комментарий
Узнать текущий контекст kubectl config current-context Выполняйте перед каждой новой задачей.
Посмотреть все контексты kubectl config get-contexts Показывает кластеры, пользователей и неймспейсы.
Сменить контекст kubectl config use-context <name> Имя контекста всегда дается в начале задания.
Задать неймспейс по умолчанию kubectl config set-context --current --namespace=<ns> Избавляет от необходимости писать -n <ns> в каждой команде.

Императивные генераторы: Шпаргалка

Ниже собраны команды, которые мгновенно создают нужные ресурсы. Запомните глаголы: run (только для подов), create (для большинства объектов) и expose (для сервисов).

1. Управление рабочими нагрузками (Workloads)

# Pod с указанием лейблов
kubectl run my-pod --image=nginx --labels="app=frontend,env=prod"

# Deployment с указанием количества реплик
kubectl create deployment my-dep --image=redis --replicas=3

# Job (одноразовая задача)
kubectl create job my-job --image=busybox -- date

# CronJob (задача по расписанию)
kubectl create cronjob my-cron --image=busybox --schedule="*/1 * * * *" -- date

2. Сеть и сервисы (Networking)

Команда expose берет существующий объект (Pod или Deployment) и автоматически создает Service, копируя селекторы.

# ClusterIP сервис для пода (порт кластера 80, порт контейнера 8080)
kubectl expose pod my-pod --port=80 --target-port=8080 --name=my-svc

# NodePort сервис для Deployment
kubectl expose deployment my-dep --port=80 --type=NodePort

# Создание сервиса без привязки к объекту (императивно через create)
kubectl create service clusterip my-svc --tcp=80:8080

3. Конфигурация и доступы (Config & RBAC)

# ConfigMap из литералов
kubectl create configmap app-config --from-literal=DB_HOST=localhost --from-literal=DB_PORT=5432

# Secret из файла
kubectl create secret generic app-secret --from-file=ssh-privatekey=~/.ssh/id_rsa

# Role (разрешение на чтение подов)
kubectl create role pod-reader --verb=get,list,watch --resource=pods

# RoleBinding (привязка роли к ServiceAccount)
kubectl create rolebinding read-pods --role=pod-reader --serviceaccount=default:my-sa

Искусство выборки: JSONPath и Custom Columns

Часто в задачах просят найти узел с наибольшим потреблением ресурсов или извлечь конкретный IP-адрес пода и записать его в файл. Искать глазами в выводе kubectl describe — долго и чревато ошибками.

Для точной выборки используется флаг -o jsonpath.

Синтаксис базового запроса выглядит так: -o jsonpath='{.путь.к.полю}'. Если элементов несколько (список), используются скобки [*].

Примеры применения на экзамене:

# Получить IP-адрес конкретного пода
kubectl get pod nginx -o jsonpath='{.status.podIP}'

# Получить имена всех образов контейнеров в кластере
kubectl get pods -A -o jsonpath='{.items[*].spec.containers[*].image}'

# Вывести список узлов и их InternalIP (формат: ИмяУзла IP)
# Используем custom-columns для красивой таблицы
kubectl get nodes -o custom-columns=NODE:.metadata.name,IP:.status.addresses[?(@.type=="InternalIP")].address

Документация под рукой: kubectl explain

На экзамене у вас есть доступ к официальной документации (kubernetes.io/docs), но поиск по сайту занимает время. Встроенная команда kubectl explain — это ваш оффлайн-справочник по структуре любого YAML-манифеста.

Если вы забыли, куда вписать securityContext или как настроить livenessProbe, используйте:

# Узнать, какие поля есть внутри spec контейнера
kubectl explain pod.spec.containers

# Рекурсивный вывод всей структуры (добавляем grep для поиска)
kubectl explain deployment --recursive | grep -A 5 strategy

Каркасы ключевых YAML-манифестов

Даже с императивными командами некоторые вещи (например, монтирование томов или сложный nodeAffinity) придется дописывать руками. Ниже представлены минималистичные, очищенные от лишнего мусора шаблоны, которые нужно уметь быстро модифицировать.

1. Pod с ресурсами и томами

Этот шаблон покрывает 80%80\% задач на модификацию подов: лимиты, переменные окружения и базовые тома.

apiVersion: v1
kind: Pod
metadata:
  name: complex-pod
spec:
  containers:
  - name: app
    image: nginx
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
    env:
    - name: ENV_VAR_NAME
      value: "some-value"
    volumeMounts:
    - name: data-vol
      mountPath: /usr/share/nginx/html
  volumes:
  - name: data-vol
    emptyDir: {} # Или hostPath/persistentVolumeClaim

2. NetworkPolicy (Default Deny & Allow Specific)

Сетевые политики нельзя сгенерировать императивно. Их структуру нужно понимать. Этот шаблон закрывает неймспейс и открывает только порт 8080 для подов с меткой role: frontend.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: backend # К кому применяется политика
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          role: frontend # Кто имеет доступ
    ports:
    - protocol: TCP
      port: 80

3. PersistentVolume (PV) и PersistentVolumeClaim (PVC)

Связка хранилища. Запомните: PV — это ресурс кластера (нет namespace), PVC — ресурс проекта (есть namespace).

# --- PV ---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: hostpath-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data/db

---
# --- PVC ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi

Освоив генерацию каркасов и точечную навигацию по документации через explain, вы сэкономите десятки минут на экзамене и сможете сфокусироваться на архитектурной логике задач, а не на борьбе с синтаксисом YAML.

Практикум: 30 типовых экзаменационных вопросов с подробными решениями

Практикум: 30 типовых экзаменационных вопросов с подробными решениями

Экзамен Certified Kubernetes Administrator (CKA) длится 2 часа и состоит исключительно из практических задач в "живой" консоли. У вас не будет времени вспоминать структуру YAML-файлов или искать базовые концепции в документации — здесь решает мышечная память. Технические же интервью, напротив, проверяют глубину понимания: почему система ведет себя именно так, а не иначе.

В этой финальной статье курса мы объединим оба подхода. Мы разберем 30 типовых вопросов, разделив их на архитектурные (для интервью) и практические (для экзамена).

1. Архитектура и Control Plane (Вопросы для интервью)

На интервью от вас ждут понимания механики работы кластера под капотом.

1. Что произойдет с подами, если Worker Node внезапно выйдет из строя (отключится питание)? Сразу — ничего. API-сервер потеряет связь с kubelet этого узла. Спустя время (по умолчанию 40 секунд) узел перейдет в статус NotReady. Еще через 5 минут (параметр pod-eviction-timeout) kube-controller-manager начнет процесс вытеснения (eviction). Поды получат статус Terminating, а ReplicaSet создаст новые поды на здоровых узлах.

2. Как etcd защищен от проблемы "Split-Brain" (расщепления мозга) при сетевом разделении? etcd использует алгоритм консенсуса Raft. Для записи данных требуется подтверждение от большинства узлов кластера (кворума). Формула кворума: Q=N2+1Q = \lfloor \frac{N}{2} \rfloor + 1, где NN — количество узлов etcd. Если кластер из 3 узлов разделяется на 2 и 1, часть с двумя узлами сохраняет кворум (222 \geq 2) и продолжает работу, а изолированный узел перестает принимать запись.

3. В чем фундаментальная разница между обычным Pod и Static Pod? Обычный Pod создается через API-сервер, сохраняется в etcd и планируется kube-scheduler. Static Pod управляется исключительно локальным kubelet на конкретном узле. API-сервер лишь отображает его статус ("зеркальный под"), но не может его удалить или изменить.

4. Как kube-proxy маршрутизирует трафик к подам при обращении к Service? По умолчанию kube-proxy работает в режиме iptables. Он следит за объектами Service и Endpoints через API-сервер и создает правила брандмауэра Linux. Когда пакет обращается к виртуальному IP сервиса (VIP), iptables перехватывает его и с помощью вероятностного алгоритма подменяет IP-адрес назначения (DNAT) на IP-адрес одного из реальных подов.

5. Зачем нужны Custom Resource Definitions (CRD)? CRD позволяют расширить стандартный API Kubernetes. Если вам не хватает встроенных объектов (Pod, Deployment), вы можете создать свой (например, Database или SSLConfig). После применения CRD API-сервер начинает принимать YAML-манифесты с вашим новым kind, а для управления их жизненным циклом пишутся кастомные контроллеры (Operators).

2. Экзаменационная среда и контексты (CKA)

На экзамене CKA вы работаете не с одним, а с несколькими кластерами. Самая частая ошибка — выполнить правильную задачу не в том кластере.

Перед каждым заданием на экзамене вам будет дана команда для переключения контекста. Никогда не игнорируйте ее.

kubectl config use-context k8s-c1-H

6. Как посмотреть все доступные контексты и текущий кластер?

kubectl config get-contexts
kubectl config current-context

7. Как найти узел с ролью Control Plane и узнать его версию ОС?

kubectl get nodes -o wide
# В выводе ищите узел с меткой/ролью control-plane и смотрите колонку OS-IMAGE.

3. Быстрые императивные команды (CKA)

На экзамене нет времени писать YAML с нуля. Вы должны генерировать их императивно. Ниже — таблица-шпаргалка с типовыми задачами (Вопросы 8–15).

Задача (Вопрос) Решение (Команда)
8. Создать Pod nginx с меткой env=prod kubectl run nginx --image=nginx --labels=env=prod
9. Сгенерировать YAML для Deployment без его создания kubectl create deploy web --image=nginx --dry-run=client -o yaml > deploy.yaml
10. Масштабировать Deployment api до 5 реплик kubectl scale deploy api --replicas=5
11. Обновить образ в Deployment и записать это в историю kubectl set image deploy/api nginx=nginx:1.19.2 --record
12. Проверить статус раскатки (rollout) kubectl rollout status deploy/api
13. Откатить Deployment на предыдущую версию kubectl rollout undo deploy/api
14. Создать Service типа NodePort для пода kubectl expose pod nginx --port=80 --type=NodePort
15. Извлечь IP-адреса всех подов (JSONPath) kubectl get pods -o jsonpath='{.items[*].status.podIP}'

4. Управление рабочими нагрузками и YAML (CKA)

Здесь проверяется умение комбинировать объекты и редактировать манифесты.

16. Создайте многоконтейнерный Pod (паттерн Sidecar). Основной контейнер busybox пишет логи в файл, второй busybox читает их. Решение: Генерируем базовый под: kubectl run multi --image=busybox --dry-run=client -o yaml > pod.yaml. Затем редактируем YAML, добавляя emptyDir и второй контейнер:

apiVersion: v1
kind: Pod
metadata:
  name: multi
spec:
  volumes:
  - name: log-volume
    emptyDir: {}
  containers:
  - name: app
    image: busybox
    command: ["/bin/sh", "-c", "while true; do echo 'Logging' >> /var/log/app.log; sleep 5; done"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log
  - name: sidecar
    image: busybox
    command: ["/bin/sh", "-c", "tail -f /var/log/app.log"]
    volumeMounts:
    - name: log-volume
      mountPath: /var/log

17. Запустите Pod на конкретном узле node02. Решение: В спецификацию пода (spec) нужно добавить nodeName.

spec:
  nodeName: node02
  containers:
  # ...

18. Найдите под, потребляющий больше всего CPU, и запишите его имя в файл. Решение: Используем Metrics Server.

kubectl top pods -A --sort-by=cpu | head -n 2 > /opt/highest_cpu.txt

5. Хранилище: PV и PVC (CKA)

19. Создайте PersistentVolume pv-log объемом 1Gi, режимом ReadWriteMany, используя hostPath /opt/volume. Решение: PV нельзя создать императивно одной строкой. Пишем YAML (можно подсмотреть структуру через kubectl explain pv.spec или в документации).

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-log
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: /opt/volume

20. Создайте PVC, который свяжется с PV из предыдущего шага, и подключите его к поду. Решение: Создаем PVC с совпадающими требованиями (1Gi, ReadWriteMany).

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-log
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

В поде монтируем так:

spec:
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: pvc-log
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - name: data
          mountPath: /var/www/html

6. Безопасность, Конфигурация и RBAC (CKA)

21. Создайте Secret из литерала и пробросьте его в Pod как переменную окружения DB_PASS. Решение: Шаг 1: kubectl create secret generic db-secret --from-literal=password=supersecret Шаг 2: В манифесте пода:

    env:
      - name: DB_PASS
        valueFrom:
          secretKeyRef:
            name: db-secret
            key: password

22. Создайте ServiceAccount deployer, роль deploy-role (с правом создавать deployments) и свяжите их в неймспейсе dev. Решение: Это легко делается императивно.

kubectl create namespace dev
kubectl create serviceaccount deployer -n dev
kubectl create role deploy-role --verb=create --resource=deployments -n dev
kubectl create rolebinding deploy-binding --role=deploy-role --serviceaccount=dev:deployer -n dev

23. Проверьте, может ли созданный ServiceAccount удалять поды? Решение: Используем механизм auth can-i.

kubectl auth can-i delete pods --as=system:serviceaccount:dev:deployer -n dev
# Вывод должен быть: no

24. Настройте NetworkPolicy, которая запрещает весь входящий трафик в неймспейсе secure, кроме трафика на порт 80 от подов с меткой access=true. Решение:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-specific
  namespace: secure
spec:
  podSelector: {} # Применяется ко всем подам в неймспейсе
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"
    ports:
    - protocol: TCP
      port: 80

7. Обслуживание узлов и Траблшутинг (CKA)

Это самые "дорогие" по баллам задания на экзамене.

25. Сделайте резервную копию базы данных etcd и сохраните ее в /opt/etcd-backup.db. Решение: Для работы с etcd нужен клиент etcdctl и сертификаты Control Plane.

ETCDCTL_API=3 etcdctl snapshot save /opt/etcd-backup.db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

26. Восстановите etcd из созданного бэкапа в директорию /var/lib/etcd-restore. Решение:

ETCDCTL_API=3 etcdctl snapshot restore /opt/etcd-backup.db \
  --data-dir=/var/lib/etcd-restore

Важно: После восстановления нужно изменить путь к data-dir в манифесте статического пода /etc/kubernetes/manifests/etcd.yaml.

27. Выведите узел node01 из эксплуатации для обслуживания, убедившись, что поды безопасно перенесены. Решение: Используем команду drain. Флаг --ignore-daemonsets обязателен, так как DaemonSet нельзя вытеснить обычным способом.

kubectl drain node01 --ignore-daemonsets --force

28. Верните узел node01 обратно в работу. Решение:

kubectl uncordon node01

29. Узел node02 находится в статусе NotReady. Как найти причину? Решение: Воронка траблшутинга.

  1. Проверяем статус узла: kubectl describe node node02 (смотрим секцию Conditions).
  2. Заходим на сам узел по SSH: ssh node02.
  3. Проверяем статус агента: systemctl status kubelet.
  4. Читаем логи агента: journalctl -u kubelet -f. Обычно проблема кроется в неверной конфигурации или упавшем Container Runtime.

30. Обновите кластер (kubeadm) с версии 1.28.0 до 1.28.1. Решение (краткий алгоритм для Control Plane):

  1. Обновляем пакетный менеджер: apt-get update && apt-get install -y kubeadm=1.28.1-00.
  2. Планируем апгрейд: kubeadm upgrade plan.
  3. Применяем апгрейд: kubeadm upgrade apply v1.28.1.
  4. Дрейним узел: kubectl drain <node-name> --ignore-daemonsets.
  5. Обновляем компоненты узла: apt-get install -y kubelet=1.28.1-00 kubectl=1.28.1-00.
  6. Перезапускаем агент: systemctl daemon-reload && systemctl restart kubelet.
  7. Возвращаем узел в работу: kubectl uncordon <node-name>.

Главный совет на экзамен: Не застревайте на одном вопросе. Если траблшутинг занимает больше 7-10 минут, ставьте флажок (Flag) и идите дальше. Сдают те, кто быстро забирает легкие баллы с императивными командами и RBAC.