Learning Platform
Глоссарий Troubleshooting
Урок 21.04 · 25 мин
Средний
CKADmistakesdebuggingprobeSecurityContextNetworkPolicy

10 типовых ошибок на CKAD — и как их избежать

Каждая из этих ошибок стоила кому-то сертификата. Я собрал их из post-mortem от провалившихся, retake-кандидатов и собственного опыта. Прочитайте — и не повторяйте.


Disk emergency: чеклист при критической проблеме

MISTAKE 1: Забыли переключить context

Каждая задача на экзамене начинается со строки вроде:

kubectl config use-context k8s

Это первое что нужно выполнить перед любым действием в задаче. Если не переключили:

  • Создаёте Pod в кластере mk8s вместо k8s.
  • Проверяющий скрипт ищет Pod в k8s — не находит.
  • Результат — 0 баллов за задачу.

Также часто требуется namespace switch:

kubectl config set-context --current --namespace=target-ns
DANGER

Это самая частая ошибка. Caждая задача — copy-paste context command в первую очередь. Перед submit задачи — kubectl config current-context для проверки.


MISTAKE 2: Wrong probe handler syntax

Probes имеют 3 типа handlers, у каждого свой синтаксис:

# exec — это ARRAY команды, не string
livenessProbe:
  exec:
    command:
      - sh
      - -c
      - "cat /tmp/healthy"
# НЕПРАВИЛЬНО: command: "sh -c cat /tmp/healthy"

# httpGet — path обычно /health или /healthz
readinessProbe:
  httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
      - name: X-Custom
        value: value

# tcpSocket
startupProbe:
  tcpSocket:
    port: 8080
WARNING

exec.command — массив строк, не строка с пробелами. command: "sh -c cmd" создаст Pod, но probe будет fail-иться (kubectl передаёт строку как один argument). Правильно: command: ["sh", "-c", "cmd"].


MISTAKE 3: SecurityContext поля не на том уровне

В SecurityContext есть Pod-level и Container-level. Не все поля доступны на обоих уровнях:

apiVersion: v1
kind: Pod
spec:
  # Pod-level: runAsUser, runAsGroup, fsGroup, supplementalGroups, seccompProfile
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
    - name: app
      # Container-level ONLY: capabilities, allowPrivilegeEscalation,
      # readOnlyRootFilesystem, privileged
      securityContext:
        capabilities:
          add: ["NET_ADMIN"]
          drop: ["ALL"]
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        privileged: false
        runAsUser: 2000  # OVERRIDE Pod-level

Ключевые моменты:

  • fsGroup — только на Pod-level. Применяется ко всем volumes Pod-а.
  • capabilities — только на Container-level. Это привилегия процесса, не Pod.
  • Container может override runAsUser/runAsGroup от Pod-level.

MISTAKE 4: NetworkPolicy без явного policyTypes

# НЕПРАВИЛЬНО: без policyTypes
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-external
spec:
  podSelector:
    matchLabels:
      app: web
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: db

Что произойдёт: К8s применит default policyTypes: [Ingress]. Egress rules игнорируются. Pod может выходить куда угодно — задача не выполнена.

Правильно — явно указать policyTypes:

spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: frontend
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: db
WARNING

Всегда явно указывайте policyTypes даже если кажется очевидным. Default behavior зависит от того, какие rules вы указали — это запутанно и приводит к ошибкам.


MISTAKE 5: ConfigMap mount с subPath не auto-updates

ConfigMap mount имеет 2 варианта:

# Без subPath — kubelet обновляет автоматически (через ~1 минуту)
containers:
  - name: app
    volumeMounts:
      - name: config
        mountPath: /etc/config
volumes:
  - name: config
    configMap:
      name: app-config

# С subPath — FIXED при mount, БЕЗ auto-update
containers:
  - name: app
    volumeMounts:
      - name: config
        mountPath: /etc/app.conf
        subPath: app.conf
volumes:
  - name: config
    configMap:
      name: app-config
DANGER

Если задача “ConfigMap должен auto-обновляться без restart Pod” — НЕ используйте subPath. Если задача “конкретный файл из ConfigMap в существующий конфиг dir” — subPath неизбежен, но потеряете auto-update.


MISTAKE 6: Imperative create + apply конфликт

Конфликт между imperative и declarative подходом:

# Step 1: imperative
kubectl create -f pod.yaml

# Step 2: правите pod.yaml, apply
kubectl apply -f pod.yaml  # CAN FAIL

Что произошло: при kubectl create -f объект создан БЕЗ kubectl.kubernetes.io/last-applied-configuration annotation. kubectl apply использует эту аннотацию для 3-way merge. Без неё — apply путается, либо warning, либо конфликт.

Best practice: всегда использовать kubectl apply -f от начала. Не миксовать.

# Правильно
kubectl apply -f pod.yaml  # creates with annotation
# edit pod.yaml
kubectl apply -f pod.yaml  # 3-way merge works

MISTAKE 7: PVC с RWO и Deployment с replicas > 1

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 2  # ПРОБЛЕМА
  template:
    spec:
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: my-pvc  # RWO PVC

Где PVC:

apiVersion: v1
kind: PersistentVolumeClaim
spec:
  accessModes:
    - ReadWriteOnce  # RWO — только один node может mount

Что произойдёт:

  • Первый Pod успешно создан и mount-ит PVC на ноде A.
  • Второй Pod планируется на ноду B (или A — зависит от scheduler).
  • Если на ноду B: kubelet пытается mount PVC, fails — MultiAttachError. Pod stuck Pending.
  • Если на ту же ноду A: некоторые CSI драйверы поддерживают, но не все.

Решения:

  1. replicas: 1 для Deployment с RWO PVC.
  2. Использовать ReadWriteMany (NFS, CephFS, Azure Files, EFS).
  3. Использовать StatefulSet с volumeClaimTemplates — каждый Pod получает свой PVC.
RWO PVC + replicas > 1 = Pending
PVC RWOReadWriteOnce: только один node может mount этот PV. Это ограничение storage backend — большинство block storage (AWS EBS, GCP PD) только RWO.
Pod 1 на node AПервый Pod планируется на node A. kubelet mount-ит PVC на ноде, контейнер стартует. Volume теперь 'attached' к node A.
Pod 2 на node BВторой Pod планируется на node B. kubelet пытается mount тот же PVC — fails. PVC уже attached к node A. Pod stuck Pending с reason MultiAttachError.

MISTAKE 8: kubectl exec без -it для interactive

# НЕПРАВИЛЬНО для interactive shell
kubectl exec my-pod -- sh
# Запускает sh, но без stdin — мгновенно завершается

# ПРАВИЛЬНО
kubectl exec -it my-pod -- sh
# -i = interactive (stdin attached)
# -t = tty allocated

Также частая ошибка: kubectl exec -it my-pod -c mycontainer -- bash — забыли -c для multi-container Pod, kubectl откажется без default container.


MISTAKE 9: Probes без startupProbe для медленных приложений

# ПРОБЛЕМА: liveness падает на 30s default
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  # initialDelaySeconds: 30 по умолчанию
  # periodSeconds: 10
  # failureThreshold: 3
  # = 30 + 10*3 = 60 сек до считается dead

Если приложение стартует 90 секунд (Spring Boot, JVM warmup) — liveness fail-ится → kubelet restart → loop.

РешениеstartupProbe:

startupProbe:
  httpGet:
    path: /health
    port: 8080
  failureThreshold: 30
  periodSeconds: 10
  # = до 5 минут на startup; liveness не запускается пока startup passes

livenessProbe:
  httpGet:
    path: /health
    port: 8080
TIP

Правило: если приложение стартует > 30 секунд — добавьте startupProbe. Это disable-ит liveness/readiness до тех пор пока startup не passed. Старт защищён, liveness защищает running.


MISTAKE 10: Не использовать kubectl explain

Самая большая трата времени на экзамене — переключение в браузер и поиск по kubernetes.io. Когда забыли точное имя поля YAML, есть быстрее способ:

# Забыли как настроить capabilities?
k explain pod.spec.containers.securityContext.capabilities

# Все поля resource limits?
k explain pod.spec.containers.resources --recursive

# Структура NetworkPolicy?
k explain networkpolicy.spec --recursive

Это:

  • Мгновенно — без переключения вкладок.
  • Точно — выдаёт ровно те поля что доступны для вашей kubectl/cluster version.
  • С описанием--recursive опускает описания (только schema), без --recursive — каждое поле с docs string.
TIP

Тренируйтесь использовать kubectl explain дома. Через 2 недели у вас будет рефлекс — “забыл поле → explain”. Это экономит 30 секунд каждый раз. За экзамен — 5+ минут.


Bonus: 5 микро-ошибок

  • YAML с табами вместо пробелов. Vim без set et — катастрофа. Set expandtab обязательно.
  • kubectl apply без -f. kubectl apply pod.yaml — error. Нужно kubectl apply -f pod.yaml.
  • Resource без namespace в задаче где требуется. Создали в default — задача провалена.
  • Не проверили статус. Создали ресурс, перешли к следующей задаче — а он Pending. kubectl get для подтверждения.
  • Запутались в kind. pod vs Pod (kind case sensitive в YAML), pods vs pod в kubectl (оба работают как resource).

Killer-моменты ошибок

  1. Context switch обязателен для каждой задачи.
  2. exec.command — массив, не строка.
  3. fsGroup только на Pod-level, capabilities только на Container-level.
  4. policyTypes явно в NetworkPolicy.
  5. subPath blockирует auto-update ConfigMap.
  6. Не миксовать create -f и apply -f.
  7. RWO PVC + replicas > 1 = Pending.
  8. exec -it для interactive shell.
  9. startupProbe для slow apps.
  10. kubectl explain быстрее docs.

Проверка знанийKnowledge check
NetworkPolicy блокирует Ingress трафик, но egress правило написано но не работает. Что забыли?
ОтветAnswer
policyTypes: [Ingress, Egress]. По умолчанию K8s применяет policyTypes только для тех типов rules где есть spec. Если в spec есть только ingress rules — egress остаётся неограниченным. Если хотите управлять egress — явно укажите policyTypes: [Ingress, Egress] и добавьте egress rules. Это самая частая ошибка с NetworkPolicy.
Проверка знанийKnowledge check
Deployment с replicas=3 использует PVC с accessMode ReadWriteOnce. Что произойдёт?
ОтветAnswer
Только один Pod будет Running. Остальные 2 — stuck Pending с MultiAttachError. RWO PVC может быть attached только к одной ноде одновременно. Решения: (1) replicas=1, (2) переключиться на ReadWriteMany (NFS/CephFS/EFS), (3) StatefulSet с volumeClaimTemplates — каждый Pod получает свой PVC. Это типичная ошибка миграции stateful workload в Deployment.
Проверка знанийKnowledge check
ConfigMap mounted с subPath: 'app.conf'. Обновили ConfigMap через kubectl edit. Pod видит изменения?
ОтветAnswer
НЕТ. subPath mount — это FIXED при создании контейнера, без auto-update. kubelet обновляет файлы ConfigMap mount автоматически каждые ~60 секунд, НО только когда subPath НЕ используется. С subPath файл становится 'snapshot' на момент mount. Чтобы изменения применились — нужно restart Pod (kubectl rollout restart deploy). Альтернатива — mount без subPath в директорию /etc/config, чтобы получить auto-update.
Проверка знанийKnowledge check
capabilities должны быть указаны на Pod-level или Container-level securityContext? А fsGroup?
ОтветAnswer
capabilities — только Container-level. fsGroup — только Pod-level. Это разделение по природе: capabilities — атрибут процесса контейнера (Linux capabilities for execve), fsGroup — атрибут файловой системы Pod-а (применяется ко всем volumes Pod-а). runAsUser/runAsGroup доступны на обоих уровнях — Container override-ит Pod-level. Помнить разделение критично для всех security задач на CKAD.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. NetworkPolicy с подselector matching app=web, есть egress rule на DNS, но запросы блокируются. Что забыли?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5