Troubleshooting — Kubernetes (CKAD)
База знаний типичных ошибок курса Kubernetes (CKAD).
Категория
Симптомы
- Pod в статусе Pending или Running, но container в Waiting с reason ImagePullBackOff или ErrImagePull. kubectl describe pod показывает Events типа 'Failed to pull image ...: not found' / 'pull access denied' / 'manifest unknown'. Pod не стартует, restartCount растёт.
Причина
Опечатка в image name (registry/repo:tag) — типичные nginx:latests, niginx, registry typo Image не существует в registry — забыли push после build, или tag другой Private registry — нет imagePullSecret или token истёк / неверный Rate limit Docker Hub anonymous (100 pulls/6h), нужен авторизованный pull Network blocked: NetworkPolicy egress блокирует registry, корпоративный proxy, firewall Платформа image не совпадает с node (arm64 image на amd64 node — exec format error) imagePullPolicy: Always + private registry без working secret
Решение
- Исправить typo в image name (внимательно перечитать)
- Push image в registry, проверить тег: docker push registry/app:v1
- Создать imagePullSecret: kubectl create secret docker-registry regcred --docker-server=... --docker-username=... --docker-password=...; прикрепить к Pod или ServiceAccount
- На Docker Hub использовать аутентифицированный pull (paid plan или mirror)
- Открыть egress в NetworkPolicy для registry, или DNS resolution
- Build multi-arch images: docker buildx build --platform linux/amd64,linux/arm64 -t image --push
- Сменить imagePullPolicy: IfNotPresent если image уже на node
Связанные уроки:
Симптомы
- Pod в Running, container с restartCount растущим и статусом Waiting reason: CrashLoopBackOff. kubelet применяет exponential backoff: 10s, 20s, 40s, 80s, 160s, 300s cap. kubectl describe — Last State: Terminated, Exit Code не 0, или приложение exit 0 сразу.
Причина
Application crash: exception в коде, missing dependency, неправильный entrypoint Missing/wrong config: переменная окружения не задана, ConfigMap mount не такой как ожидался Permission denied: контейнер хочет писать в readOnlyRootFilesystem, или files non-root Liveness probe валит контейнер слишком рано (initialDelay мал) Команда успешно exit'ает (exit 0) — typical если CMD = 'echo hello', и restartPolicy=Always Connection issue к dependency (DB, API) — fail-fast и crash OOM Killed (exit 137) — приложение пожирает память больше limit
Решение
- Исправить ошибку в приложении (см. logs --previous)
- Добавить недостающие env vars, ConfigMap, Secrets
- Увеличить liveness probe initialDelaySeconds, добавить startupProbe для медленных apps
- Если CMD заканчивается слишком рано — изменить на long-running процесс
- Увеличить memory limits если OOM, или fix memory leak
- Использовать kubectl debug или sleep команду для interactive отладки: command: ['sh', '-c', 'sleep 1d']
Связанные уроки:
Симптомы
- Container terminated с reason: OOMKilled, exitCode: 137. kubectl describe — Last State: Terminated. В системных событиях на node: 'Memory cgroup out of memory: Killed process'. Pod может перезапускаться и снова падать (loop).
Причина
Container превысил memory limit (приложение жадно использует RAM) Memory leak в приложении Загрузка большого dataset в память (in-memory cache, ML model) JVM heap не настроен под container limit (старая Java < 10 без -XX:+UseContainerSupport) Sidecar тоже потребляет память, считаемую в Pod limit Node MemoryPressure — kubelet evict Pod (другой scenario — exit code 137 но reason 'Evicted')
Решение
- Увеличить resources.limits.memory у container
- Установить requests.memory ближе к limits — Guaranteed QoS защищает от eviction
- Profile приложение и устранить leak (heap dump для JVM, pprof для Go)
- JVM: -XX:MaxRAMPercentage=75.0 или -Xmx; Node.js: --max-old-space-size
- Если sidecar — посмотреть его memory отдельно, ограничить его
- Увеличить replicas Deployment чтобы распределить нагрузку
Связанные уроки:
Симптомы
- Pod в статусе Pending долгое время, kubectl describe pod — Events: 'FailedScheduling' с сообщением '0/3 nodes are available: ...'. kubectl get pod -o wide показывает NODE: <none>. Scheduler не может найти подходящий node.
Причина
Недостаточно ресурсов: CPU/memory requests превышают allocatable на всех nodes nodeSelector / nodeAffinity не matches ни один node Taint на node без соответствующего toleration PVC не bound (зависит от PV/StorageClass) Anti-affinity конфликт: requiredDuringScheduling не может быть satisfied Все nodes имеют unschedulable=true (cordoned) PodTopologySpreadConstraints не могут быть satisfied
Решение
- Уменьшить resources.requests, или добавить node в кластер (autoscaler)
- Исправить nodeSelector label matching, проверить kubectl get nodes -l <label>
- Добавить toleration: spec.tolerations: [{ key: ..., operator: Equal, value: ..., effect: NoSchedule }]
- Создать PV вручную или fix StorageClass / provisioner
- Ослабить anti-affinity (preferredDuringScheduling вместо required) или добавить node
- kubectl uncordon <node>
Связанные уроки:
Симптомы
- kubectl delete pod возвращает 'deleted', но Pod продолжает висеть в статусе Terminating минуты или часы. kubectl get pod показывает Terminating и deletionTimestamp в past. Новые Pods через ReplicaSet могут не создаваться (для StatefulSet — обязательно ждёт).
Причина
Finalizers на Pod (типа kubernetes.io/pv-protection или custom) не удалены контроллером Volume не unmount: NFS unresponsive, CSI driver hung, multi-attach issue preStop hook hung / sleep слишком долгий + большой terminationGracePeriodSeconds Node, на котором был Pod, NotReady (kubelet не отвечает) Контейнер игнорирует SIGTERM, ждёт SIGKILL после grace period API server / etcd проблемы — delete не applied
Решение
- Force delete: kubectl delete pod <pod> --grace-period=0 --force (только если уверен)
- Удалить finalizers вручную: kubectl patch pod <pod> -p '{"metadata":{"finalizers":null}}'
- Если node NotReady — устранить проблему kubelet/network на node, потом Pod удалится сам
- Уменьшить terminationGracePeriodSeconds или fix preStop hook
- Application должен обрабатывать SIGTERM и graceful shutdown
- Перезапустить kubelet на node если все остальные методы не помогли
Связанные уроки:
Симптомы
- Pod в статусе Init:0/2, Init:CrashLoopBackOff, или Init:Error. Main containers не стартуют. kubectl get pod — STATUS: Init:CrashLoopBackOff, READY: 0/n.
Причина
Init container скрипт fail (exit code не 0) Init container ждёт dependency (DB, API) которая не доступна Permission issue — init нужен root для chown volume но pod runAsNonRoot Wrong image / wrong command в init Timeout: init требует больше времени чем activeDeadlineSeconds Pod ConfigMap / Secret для init не существует
Решение
- Fix init script (exit 0 при успехе)
- Проверить что dependency dostupna: nslookup, telnet из init container
- Init может бежать с разным securityContext чем main containers
- Использовать busybox / alpine для wait-for-it pattern
- Увеличить timeoutы у dependency check loops
Связанные уроки:
Симптомы
- Pod в статусе Pending или Running, container в Waiting с reason: CreateContainerConfigError. kubectl describe — Events: 'Error: configmap "xxx" not found' / 'Error: secret "xxx" not found' / 'Error: couldn't find key xxx in ConfigMap'.
Причина
ConfigMap или Secret referenced в env/volumeMount не существует в namespace Опечатка в name ConfigMap/Secret Ключ в ConfigMap/Secret не существует (configMapKeyRef.key) Cross-namespace reference — ConfigMap не в том namespace что Pod Secret имеет неверный type для использования (например tls без tls.crt/tls.key)
Решение
- Создать недостающий ConfigMap / Secret: kubectl create configmap ...
- Исправить опечатку в spec
- Создать ConfigMap в namespace Pod (нельзя ссылаться на другой NS)
- Использовать optional: true в configMapKeyRef если ключ может отсутствовать
- Проверить совпадение type secret (Opaque, kubernetes.io/tls etc)
Связанные уроки:
Симптомы
- Pod в Pending. Events: '0/3 nodes are available: 3 Insufficient cpu' или 'Insufficient memory'. Scheduler видит nodes, но allocatable < requests Pod.
Причина
Pod requests слишком большие — нет node с таким свободным запасом Кластер близок к full — старые Pods не освободили ресурсы Allocatable меньше capacity из-за system-reserved / kube-reserved / eviction-hard reserve ResourceQuota namespace ограничивает summa requests
Решение
- Уменьшить resources.requests Pod (если over-provisioned)
- Добавить nodes в кластер (cluster autoscaler scales up при Pending Pods)
- Evict / drain noisy neighbors, scale-down неважные workloads
- Увеличить ResourceQuota если ограничение namespace-level
- Использовать priorityClassName + preemption для critical workloads
Связанные уроки:
Симптомы
- Pod в Pending. Events: '0/3 nodes are available: 3 node(s) didn't match Pod's node affinity/selector'. Pod указал nodeSelector или affinity, но ни один node не имеет matching labels.
Причина
Label на node не задан или другой ключ/значение Опечатка в значении (disktype=sssd вместо ssd) Все nodes с нужным label cordoned / drained requiredDuringSchedulingIgnoredDuringExecution affinity, который не satisfied
Решение
- Добавить label на node: kubectl label node worker-1 disktype=ssd
- Исправить опечатку в Pod spec
- Использовать preferredDuringScheduling вместо required для мягкого предпочтения
- Расширить selector (matchExpressions с In вместо single value)
Связанные уроки:
Симптомы
- Pod в Pending. Events: '0/3 nodes are available: 1 node(s) had taint {key: value}, that the pod didn't tolerate'. Control plane nodes часто имеют node-role.kubernetes.io/control-plane:NoSchedule.
Причина
Control-plane taint — Pod пытается schedule на master без toleration GPU/специализированные nodes имеют taint типа dedicated=gpu:NoSchedule kubectl taint вручную добавил taint NotReady taint (node.kubernetes.io/not-ready:NoSchedule) automatically added kubelet'ом Memory/disk pressure taints автоматически applied
Решение
- Добавить toleration в Pod spec: tolerations: [{ key: dedicated, operator: Equal, value: gpu, effect: NoSchedule }]
- Использовать operator: Exists без value — match любое значение ключа
- Удалить taint если он не нужен: kubectl taint node worker-1 dedicated-
- Для system workloads tolerate все: tolerations: [{ operator: Exists }]
Связанные уроки:
Симптомы
- Из одного Pod curl http://service-name отдаёт connection refused, timeout, или DNS resolution fails. kubectl get svc показывает ClusterIP, но запросы не доходят до backend Pods.
Причина
Service selector не совпадает с labels Pods — EndpointSlices пустые Backend Pods не Ready (readiness probe fail) — не в EndpointSlices Pods слушают на другом порту чем targetPort NetworkPolicy блокирует ingress на backend Pods Service в другом namespace — нужно FQDN <svc>.<ns>.svc.cluster.local kube-proxy crashed на node клиента или сервера Pods используют hostNetwork и Service selector не совпадает
Решение
- Исправить selector Service — должен совпадать с labels Pods
- Дождаться Ready Pods, fix readiness probe если она fail unjustly
- Согласовать targetPort с containerPort приложения
- Добавить NetworkPolicy ingress rule разрешающий трафик от клиента
- Использовать FQDN для cross-namespace: backend.api.svc.cluster.local
- Перезапустить kube-proxy: kubectl rollout restart ds/kube-proxy -n kube-system
Связанные уроки:
Симптомы
- nslookup из Pod возвращает 'server can't find ...: NXDOMAIN' или timeout. Запросы по DNS-имени Service fail, но по ClusterIP работают. Application logs показывают 'no such host', 'getaddrinfo ENOTFOUND'.
Причина
CoreDNS pods crashed или не Ready Service kube-dns не доступен с node Pod (NetworkPolicy блокирует egress на 10.96.0.10:53) /etc/resolv.conf в Pod неверный (dnsPolicy: None без dnsConfig) Search domain не включает нужный namespace — short name только в same NS ndots: 5 default — много extra queries (foo.com.<ns>.svc.cluster.local etc), внешние имена тормозят Upstream DNS broken — внешние имена не резолвятся CoreDNS не имеет permissions watch endpoints (RBAC issue)
Решение
- Перезапустить CoreDNS: kubectl rollout restart deployment/coredns -n kube-system
- Добавить NetworkPolicy egress для UDP 53 на kube-system
- Использовать FQDN для cross-namespace (api.prod.svc.cluster.local)
- Снизить ndots в Pod: dnsConfig: { options: [{ name: ndots, value: '2' }] }
- Проверить upstream в CoreDNS Corefile (forward . /etc/resolv.conf)
- NodeLocal DNSCache для уменьшения load и улучшения latency
Связанные уроки:
Симптомы
- Запрос на Ingress endpoint возвращает 404 'default backend - 404' (NGINX), 502 Bad Gateway, или 503 Service Unavailable. Service напрямую работает корректно (curl ClusterIP — OK).
Причина
404: rule не matches Host или path — typo, неверный pathType 404: ingressClassName не указан, или нет default IngressClass 404: Service name в backend не существует или в другом namespace 502: backend Pod не отвечает или crash'ит 502: targetPort неверный, controller не может connect 503: backend Service не имеет endpoints (Pods not Ready) TLS issue: certificate невалидный, hostname mismatch
Решение
- Проверить host + path в правилах Ingress, pathType: Prefix vs Exact
- Указать ingressClassName в Ingress, или назначить default IngressClass
- Исправить service.name и service.port в backend
- Fix backend app — посмотри logs Pod и probes
- Согласовать backend service port и Pod targetPort
- Проверить readiness probe — Pods должны быть Ready
- Обновить TLS Secret (cert-manager или вручную)
Связанные уроки:
Симптомы
- После применения NetworkPolicy одни Pods перестали общаться с другими, DNS перестал работать, или внешние API недоступны. Раньше всё работало.
Причина
Default deny включён (policyTypes: Ingress, Egress) но не все нужные правила добавлены Forgot to allow DNS egress (UDP 53 to kube-system) — все DNS-имена ломаются namespaceSelector не matches kube-system (по default нет label kubernetes.io/metadata.name) podSelector pyramid: Policy выбирает не те Pods (пустой selector = ВСЕ Pods в NS) CNI не поддерживает NetworkPolicy (Flannel default — нет; нужен Calico, Cilium, Weave) Egress policy блокирует API server endpoint (для in-cluster API calls) Pod selector в from/to пустой = ANY Pod, но затронутый namespaceSelector ограничивает
Решение
- Добавить explicit allow для DNS: egress to namespaceSelector matching kube-system, ports UDP/TCP 53
- Залейбить namespaces: kubectl label ns kube-system kubernetes.io/metadata.name=kube-system (с v1.22 это automatic)
- Указать узкий podSelector — НЕ пустой если не хотим affect everyone
- Сменить CNI на поддерживающий NetworkPolicy
- Добавить allow egress на API server: ports tcp 443 to ipBlock с cluster CIDR
- Использовать observability (Cilium Hubble) для tracing drops
Связанные уроки:
Симптомы
- Ingress в namespace ingress-nginx указывает на Service в другом namespace и возвращает 503 / Service not found. Или LoadBalancer Service ссылается на Pods, которые в другом namespace.
Причина
Ingress backend.service.name — Service должен быть в ТОМ ЖЕ namespace что Ingress LoadBalancer Service selector matches только Pods в его namespace Gateway API HTTPRoute в другом namespace без ReferenceGrant ExternalName Service не настроен правильно
Решение
- Создать Ingress в namespace где backend Service
- Использовать ExternalName Service-proxy: в Ingress namespace create Service type=ExternalName pointing to real-svc.other-ns.svc.cluster.local
- Gateway API: создать ReferenceGrant в backend namespace разрешающий cross-NS reference
- Перенести Service в namespace Ingress (если возможно)
Связанные уроки:
Симптомы
- kubectl get pvc показывает STATUS: Pending. Описание показывает 'no persistent volumes available for this claim and no storage class is set' или 'waiting for first consumer to be created before binding'.
Причина
Нет default StorageClass и в PVC не указан storageClassName StorageClass указан, но provisioner не установлен (CSI driver не работает) Static PV не существует с подходящими параметрами (size, accessModes, storageClassName) volumeBindingMode: WaitForFirstConsumer — нормальное состояние пока Pod не создан PVC размер больше чем любой available PV AccessMode PVC не поддерживается provisioner (RWX на EBS — невозможно)
Решение
- Указать storageClassName в PVC, или назначить default SC
- Установить / fix CSI driver (logs csi-controller, csi-node)
- Создать static PV с подходящими полями
- Если WaitForFirstConsumer — создать Pod, использующий PVC
- Уменьшить request.storage в PVC
- Сменить accessMode на supported (RWO для EBS, RWX для EFS/NFS)
Связанные уроки:
Симптомы
- Pod в Pending. Events: 'Multi-Attach error for volume "pvc-xxx" Volume is already used by pod(s) other-pod' или 'FailedAttachVolume'. Возникает при rolling update RWO PVC, или Pod move между nodes.
Причина
PVC с accessMode RWO монтируется в Pod на node A, новый Pod schedule на node B Старый Pod не удалился полностью (stuck Terminating) Block storage (EBS, GCE PD) attached к одному VM exclusively Несколько replicas Deployment пытаются маунтить one RWO PVC Node failure: kubelet on old node не отвечает, controller не может detach
Решение
- Удалить старый Pod (force delete если stuck): kubectl delete pod <old> --grace-period=0 --force
- Перенести PVC на StatefulSet — у каждого Pod свой PVC через volumeClaimTemplates
- Использовать RWX storage (NFS, EFS, CephFS) если действительно нужен shared
- Recreate strategy в Deployment вместо RollingUpdate для RWO PVC
- Удалить stale VolumeAttachment вручную: kubectl delete volumeattachment <name>
- Drain старый node если он не Ready
Связанные уроки:
Симптомы
- PVC stuck Pending. Events: 'storageclass.storage.k8s.io "standard" not found'. Кластер не имеет default StorageClass или указанный SC удалён.
Причина
Default StorageClass отсутствует и PVC не задаёт storageClassName StorageClass был удалён (а PVC ссылается) Опечатка в storageClassName в PVC Bare-metal кластер без storage provisioner
Решение
- Создать StorageClass: kubectl apply -f sc.yaml с provisioner вашего CSI
- Назначить default: kubectl annotate sc <name> storageclass.kubernetes.io/is-default-class=true
- Установить CSI driver для облака (ebs-csi, gce-pd-csi) или local-path-provisioner для bare-metal
- Исправить опечатку в PVC
Связанные уроки:
Симптомы
- Pod evicted, kubectl describe — 'Evicted' / 'The node was low on resource: ephemeral-storage'. Container уходил в OOM-like при больших файлах в /tmp.
Причина
emptyDir растёт до bigger size, заполняя node disk Node ephemeral-storage approached eviction threshold (nodefs.available < 10% by default) Нет sizeLimit на emptyDir — нет ограничения Log files в контейнере накапливаются (writable layer) medium: Memory emptyDir не подсчитан в memory limit правильно
Решение
- Добавить sizeLimit к emptyDir: emptyDir: { sizeLimit: 1Gi }
- Установить ephemeral-storage requests/limits на container
- Перевести на PVC если данные persistent
- Очистить старые logs / temp files в приложении
- Increase node disk size, или add nodes
- Использовать medium: Memory только для маленьких volumes (учитывать memory limit)
Связанные уроки:
Симптомы
- После kubectl apply / edit ConfigMap, новое значение не появилось в контейнере. Приложение использует старую конфигурацию. Файл в volume — тоже старый.
Причина
ConfigMap mounted через subPath — обновления НЕ подхватываются автоматически ConfigMap используется через env vars — обновления НЕ применяются (нужен restart Pod) kubelet syncFrequency задержка (~1 минута для volume updates) Приложение читает файл один раз при старте и кэширует ConfigMap имеет immutable: true — обновления невозможны Pod использовал старую revision RS, deploy не triggered
Решение
- Убрать subPath, mount directory целиком — обновления автоматические
- Триггерить rollout deployment при ConfigMap update: kubectl rollout restart deployment/<name>
- Использовать tool типа Reloader / ConfigMap-Reload для auto-restart Pods
- Подождать ~1 минуту kubelet sync (для non-subPath mounts)
- Приложение должно перечитывать конфиг (SIGHUP, file watch)
- Для immutable — создать новый ConfigMap с другим именем и обновить Deployment
Связанные уроки:
Симптомы
- Pod стартует, но приложение fail с 'credentials not found'. Файл с secret отсутствует или пустой. env var из Secret не передан.
Причина
Опечатка в name Secret или key Secret в другом namespace чем Pod — cross-namespace не работает напрямую Secret type не соответствует (tls без tls.crt/tls.key полей) ServiceAccount не существует или нет permissions read Secret defaultMode на projected secret слишком restrictive (file unreadable) imagePullSecret не type kubernetes.io/dockerconfigjson
Решение
- Создать Secret в namespace Pod
- Исправить name / key spec
- Использовать правильный type: kubernetes.io/tls для TLS, Opaque для general
- Установить defaultMode: 0400 для секретов (читаемо owner)
- Использовать external-secrets для синхронизации из Vault/AWS Secrets Manager
- Проверить RBAC permissions ServiceAccount
Связанные уроки:
Симптомы
- Pod не может pull image из private registry. Events: 'Failed to pull image ... unauthorized' / '403 Forbidden' даже несмотря на наличие imagePullSecret. ImagePullBackOff несмотря на secret.
Причина
Secret в другом namespace чем Pod Secret type не kubernetes.io/dockerconfigjson Wrong registry URL в secret (https://, http://, без protocol — important) Token истёк (для cloud registries auth tokens живут часы) Учётка не имеет permissions на конкретный repo imagePullSecret не указан в Pod spec / ServiceAccount Server мнения отличаются: registry.example.com vs registry.example.com:443
Решение
- Recreate secret: kubectl create secret docker-registry regcred --docker-server=<url> --docker-username=<u> --docker-password=<p>
- Создать в правильном namespace или duplicate в нужный
- Прикрепить к ServiceAccount чтобы auto-apply всем Pods: kubectl patch sa default -p '{"imagePullSecrets":[{"name":"regcred"}]}'
- Для AWS ECR — использовать IAM роль через IRSA вместо static secret
- Refresh token периодически (CronJob с aws ecr get-login-password)
- Сверить registry URL точно (включая порт, scheme)
Связанные уроки:
Симптомы
- kubectl или приложение получает 'Error from server (Forbidden)': User/ServiceAccount 'xxx' cannot 'verb' resource 'yyy' in API group 'zzz'. API call не проходит authorization.
Причина
Нет Role/ClusterRole с нужным verb на resource Нет RoleBinding/ClusterRoleBinding к subject Subject типа ServiceAccount но binding к User (не matches) RoleBinding в неверном namespace ClusterRole используется через RoleBinding но resource cluster-scoped (нужен ClusterRoleBinding) resourceNames ограничивает только конкретные объекты API group неверный: '' (core) vs 'apps' vs 'batch'
Решение
- Создать Role: rules с правильным apiGroups, resources, verbs
- Bind через RoleBinding (namespaced) или ClusterRoleBinding (cluster-wide)
- Использовать встроенные ClusterRoles: view, edit, admin, cluster-admin
- Aggregate permissions через aggregationRule
- Использовать impersonation для testing: kubectl --as=...
- Для cluster-scoped resources — ClusterRoleBinding обязателен
Связанные уроки:
Симптомы
- kubectl apply возвращает 'violates PodSecurity "restricted:latest": ...' или warning. Pod не создаётся / с warning. Описывает конкретное нарушение: runAsNonRoot, allowPrivilegeEscalation, capabilities, seccompProfile.
Причина
Namespace помечен PSS labels (pod-security.kubernetes.io/enforce: restricted) Pod нарушает: runAsRoot, hostPath volume, privileged container, добавляет capabilities Образ запускается как root (USER 0 в Dockerfile) Нет seccompProfile.type: RuntimeDefault при PSS Restricted allowPrivilegeEscalation: true (default — нужно явно false)
Решение
- Привести Pod к PSS Restricted: runAsNonRoot, runAsUser, capabilities drop ALL, seccompProfile RuntimeDefault, allowPrivilegeEscalation false, readOnlyRootFilesystem if possible
- Перебилдить image с non-root USER (USER 10001)
- Сменить level namespace на более слабый (baseline вместо restricted) — НЕ для prod
- Использовать exemption (только для special workloads, kube-system)
- Перенести privileged workload в отдельный namespace c baseline
Связанные уроки:
Симптомы
- Приложение, использующее SA token для in-cluster API calls, получает 'Unauthorized' или 401. Старые client libraries не handle token rotation. CronJob, запускающийся раз в неделю, может видеть expired token.
Причина
TokenRequest bound tokens (v1.22+) auto-rotated каждый час, library должна re-read /var/run/secrets/kubernetes.io/serviceaccount/token Legacy long-lived token Secret deprecated в v1.24+, не создаётся automatically Изменилась audience у token (если used kubectl create token --audience) automountServiceAccountToken: false — token не mounted вообще SA удалён — token invalid
Решение
- Обновить client library до использующей file watch или re-reading token (client-go это делает auto)
- Использовать TokenRequestProjection в Pod spec с expirationSeconds
- Для legacy approach — manually create Secret типа kubernetes.io/service-account-token (но deprecated)
- Restart Pod чтобы получить свежий token
- Установить automountServiceAccountToken: true
Связанные уроки:
Симптомы
- Pod рестартится каждые ~30-60 секунд. kubectl describe — Events: 'Liveness probe failed: HTTP probe failed with statuscode: ...' или connection refused. restartCount растёт.
Причина
Probe настроен на неверный path / port initialDelaySeconds слишком мал — приложение ещё не запустилось App slow startup без startupProbe — liveness валит до того как app ready App overload — health endpoint медленный, timeoutSeconds слишком мал DNS/dependency check в health endpoint — fail при transient issues Liveness слишком aggressive (failureThreshold: 1) — flapping GC pause / IO stalls делают app temporarily unresponsive
Решение
- Согласовать probe path/port с приложением
- Добавить startupProbe с failureThreshold * periodSeconds покрывающим startup time
- Увеличить initialDelaySeconds или периодичность
- Увеличить timeoutSeconds (default 1s часто мало)
- Liveness endpoint должен быть LIGHTWEIGHT — никаких DB checks (это readiness)
- Failure threshold: 3 (default) или больше для tolerance
- Если в коде long GC — fix или увеличить probe timeouts
Связанные уроки:
Симптомы
- Pod в CreateContainerConfigError, или Pod стартует но fail: env var пустой, файл отсутствует. Events: 'couldn't find key XYZ in ConfigMap'.
Причина
Опечатка в configMapKeyRef.key ConfigMap создан без этого ключа (data: пустой или другой) ConfigMap обновили — старый ключ удалён envFrom без prefix — переменные ENV с дефисом в ключе ConfigMap игнорируются Case-sensitivity в имени ключа
Решение
- Добавить недостающий key в ConfigMap: kubectl edit cm <name>
- Использовать optional: true в configMapKeyRef если key опционален
- Для invalid ENV-name keys использовать volume mount вместо env
- Зафиксировать соглашение naming (только [A-Z][A-Z0-9_]*)
Связанные уроки:
Симптомы
- kubectl rollout status deployment висит, потом 'error: deployment exceeded its progress deadline'. kubectl get deploy показывает READY: 2/5, не движется. Старый ReplicaSet остался scaled, новый — частично готов.
Причина
Новые Pods не становятся Ready: readiness probe fails, или crash ImagePullBackOff на новых Pods Quota / limit достигнут — новые Pods не schedule maxSurge=0 + maxUnavailable=0 — невозможно сделать ни одного шага progressDeadlineSeconds слишком короткий для медленного rollout PDB запрещает evict старых Pods (minAvailable > replicas - 1)
Решение
- Fix root cause в новых Pods: image, probes, config
- kubectl rollout undo deployment/<name> — откат на предыдущий RS
- Увеличить progressDeadlineSeconds в Deployment
- Скорректировать maxSurge/maxUnavailable хотя бы одно ненулевое
- Скорректировать PDB чтобы можно было evict
- kubectl rollout pause, fix manually, kubectl rollout resume