Kubernetes и k3s

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

k3s — дистрибутив Kubernetes, упакованный в один основной binary с необходимыми компонентами. Он удобен для небольших кластеров, лабораторных окружений и edge-сценариев, но сохраняет основные объекты и API Kubernetes.

Архитектура k3s

Узлы делятся на два типа:

Server

k3s server запускает control plane и datastore:

  • Kubernetes API server;
  • scheduler;
  • controller manager;
  • cloud controller manager;
  • datastore;
  • supervisor k3s.

Server node также запускает agent-компоненты и по умолчанию может размещать workload.

Agent

k3s agent подключает worker node к server:

  • kubelet;
  • container runtime;
  • CNI;
  • kube-proxy или его выбранную замену;
  • локальный load balancer для соединения с server nodes.

Для одного server по умолчанию подходит встроенная SQLite. Для HA используются нечётное число server nodes со встроенным etcd — обычно не меньше трёх — либо поддерживаемое внешнее хранилище.

Базовые объекты

Pod

Минимальная единица размещения. Контейнеры одного pod разделяют сеть и могут разделять volumes. Pod обычно не создают вручную: им управляет Deployment, StatefulSet, DaemonSet или Job.

Deployment

Описывает stateless workload и стратегию обновления. Deployment управляет ReplicaSet, а ReplicaSet — pods.

Deployment
└── ReplicaSet
    ├── Pod
    ├── Pod
    └── Pod

Service

Даёт стабильную виртуальную точку доступа к группе pods. Выбирает их по labels. Если selector не совпал, Service существует, но endpoints будут пустыми.

ConfigMap и Secret

Передают конфигурацию. Secret — не «зашифрованный файл по умолчанию», а отдельный Kubernetes-объект с контролем доступа; значение в manifest обычно лишь base64-encoded. Защита зависит от RBAC, шифрования at rest и способа доставки.

Ingress

Описывает HTTP-маршрутизацию извне. Сам ресурс работает только при наличии ingress controller.

Доступ к кластеру

Kubeconfig содержит:

  • clusters — адрес API и CA;
  • users — credentials или способ их получить;
  • contexts — пару cluster/user и namespace;
  • current context.
kubectl config get-contexts
kubectl config current-context
kubectl config use-context <context>

Перед любой изменяющей командой проверяю context и namespace.

Admin kubeconfig k3s

Файл /etc/rancher/k3s/k3s.yaml даёт права system:admin и неограниченный доступ к кластеру. Его нельзя коммитить, пересылать как обычный текст или использовать там, где достаточно отдельной service account с RBAC.

Встроенные команды:

sudo k3s kubectl get nodes
sudo k3s crictl ps
sudo k3s ctr containers list
sudo journalctl -u k3s

k3s kubectl по умолчанию умеет читать admin kubeconfig k3s. k3s crictl помогает смотреть container runtime через CRI, когда Kubernetes API показывает недостаточно.

Как читаю состояние pod

kubectl get pod <pod> -n <namespace> -o wide
kubectl describe pod <pod> -n <namespace>
kubectl logs <pod> -n <namespace> -c <container>
kubectl logs <pod> -n <namespace> -c <container> --previous

Поле STATUS — краткое представление. Причину ищу в:

  • status.containerStatuses;
  • state и lastState;
  • reason и exit code;
  • conditions;
  • events;
  • логах текущего или предыдущего контейнера.

Частые состояния

СостояниеЧто проверяю
Pendingscheduler events, requests, affinity, taints, PVC
ImagePullBackOffимя/tag, registry, credentials, platform
CrashLoopBackOffexit code, current/previous logs, command, config
OOMKilledmemory limit, heap, native memory, нагрузку
Running, но не Readyreadiness probe и зависимости
Terminatingfinalizers, preStop, grace period, volume detach

Probes

  • startup probe даёт медленно запускающемуся приложению время подняться;
  • readiness probe отвечает, можно ли направлять трафик;
  • liveness probe отвечает, нужно ли перезапустить контейнер.

Плохая liveness probe может превратить временную медленную зависимость в бесконечные рестарты. Readiness не лечит приложение — она исключает pod из endpoints.

Service не видит pod

Проверяю цепочку:

kubectl get service <service> -n <namespace> -o yaml
kubectl get endpoints <service> -n <namespace>
kubectl get pods -n <namespace> --show-labels
kubectl get endpointslice -n <namespace>

Ключевые вопросы:

  1. selector совпадает с labels?
  2. pod Ready?
  3. targetPort соответствует реальному порту контейнера?
  4. запрос идёт в правильный namespace?
  5. NetworkPolicy разрешает трафик?

Rollout

kubectl rollout status deployment/<name> -n <namespace>
kubectl rollout history deployment/<name> -n <namespace>
kubectl get rs -n <namespace>

Во время обновления смотрю не только на число pods, но и на их revision, image digest и readiness. Один и тот же tag может скрывать разные образы, поэтому для воспроизводимости лучше immutable tag или digest.

Ресурсы Java-приложения

В контейнере сопоставляю:

  • Kubernetes memory limit;
  • максимальный heap;
  • metaspace;
  • direct buffers;
  • thread stacks;
  • память native-библиотек и JVM.

-Xmx не равен всей памяти процесса. Если heap занимает почти весь limit, контейнер может получить OOM kill без Java OutOfMemoryError.

CPU limit влияет на throttling. Медленный ответ не всегда означает нехватку потоков приложения: процесс мог получать меньше CPU, чем ожидается.

Порядок безопасной диагностики

kubectl config current-context
kubectl get nodes
kubectl get pods -A
kubectl describe pod <pod> -n <namespace>
kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp
kubectl logs <pod> -n <namespace> --previous
kubectl get deployment <name> -n <namespace> -o yaml

Сначала использую read-only команды. delete pod, rollout restart, scale и edit меняют состояние и могут уничтожить следы причины.

Вопросы для собеседования

Чем Deployment отличается от StatefulSet?

Deployment подходит для взаимозаменяемых replicas. StatefulSet даёт pod стабильную идентичность, порядок создания/удаления и шаблоны отдельных persistent volumes.

Почему приложение доступно через port-forward, но не через Service?

Port-forward обращается к выбранному pod напрямую. У Service могут быть неверные selectors, пустые endpoints, неправильный targetPort или сетевые ограничения.

Что произойдёт при падении pod?

Контейнер внутри pod может перезапустить kubelet. Если pod исчез, controller создаст замену в соответствии с желаемым числом replicas. Новый pod не обязан получить прежнее имя, IP или node.

В чём разница request и limit?

Request участвует в scheduling и задаёт гарантируемую основу. Limit ограничивает потребление: CPU обычно throttled, превышение memory limit может завершить контейнер.

Почему kubectl logs пуст после рестарта?

Команда читает текущий контейнер. Для непосредственно предыдущего экземпляра нужен --previous; более старые логи должны находиться в централизованном хранилище.

Связанные заметки

Актуальные источники