linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • Симулятор
  • База знаний
  • Собеседование
Index
Categories
All entries
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
home/linux/kb/Контейнеры (бонус)/kubelet-internals

kb/containers ── Контейнеры (бонус) ── advanced

kubelet - архитектура агента ноды Kubernetes

kubelet - демон на каждой ноде. Получает PodSpec через API, запускает контейнеры через CRI, монтирует volumes через CSI, следит за health. При pressure делает eviction. Image GC и cgroup-tree - тоже его.

view as markdownaka: kubelet, k8s-kubelet, node-agent, cri, container-manager

Что такое kubelet

kubelet, единственный k8s-компонент, который работает с контейнерами на ноде. Всё, что вы видите в kubectl get pod результат того, что kubelet:

  1. Подписался на PodSpec'ы для своей ноды через kube-apiserver
  2. Сравнивает desired (от API) и actual (что реально запущено)
  3. Делает изменения через CRI/CSI/CNI
  4. Репортит status обратно в API

Не путать с container runtime (containerd), kubelet это уровень выше, он командует runtime'ом через CRI gRPC.

kube-apiserver
      ▲ ▼ list/watch + status
kubelet (на каждой ноде)
      │
      ├── CRI gRPC → containerd → [[runc-and-runsc|runc]] → контейнер
      ├── CSI gRPC → csi-driver → mount/attach volume
      └── CNI exec → calico/cilium → сеть pod'а

Главные подсистемы

Pod sync loop

Сердце kubelet'а, syncLoop в pkg/kubelet/kubelet.go. Цикл every-N-seconds (default 10s) или event-driven:

  1. Получить desired set of pods из источников: API server, --pod-manifest-path (static pods для control plane), HTTP-URL.
  2. Сравнить с running pods.
  3. Для каждого pod, который должен быть, но не запущен, syncPod.
  4. Для каждого, который запущен но не должен, killPod.

Источников три:

  • api: основной, watch на kube-apiserver
  • file: static pods из /etc/kubernetes/manifests/ (apiserver, etcd, controller-manager сами стартуют так на control-plane)
  • http: внешний URL с PodList

CRI, Container Runtime Interface

Разговор с container runtime через gRPC. Сокет, unix:///var/run/containerd/containerd.sock (или CRI-O аналог). Два сервиса:

  • RuntimeService, pods/containers lifecycle (RunPodSandbox, CreateContainer, StartContainer, StopPodSandbox)
  • ImageService, pull/list/remove image

Pod в CRI, это PodSandbox (контейнер с pause-image, держит netns + ipc/uts namespace) плюс N containers внутри него, все шарят netns с pause.

Pause container нужен, чтобы:

  • PID 1 в pod-namespace всегда был жив (для reap zombie)
  • Перезапуск application-контейнера не убивал pod-network

Container Manager

Управляет cgroup-tree на ноде:

/sys/fs/cgroup/
├── system.slice/                    # systemd
├── kubepods.slice/                 

▸kubelet root

│   ├── kubepods-burstable.slice/    # QoS class
│   │   └── kubepods-burstable-pod<UID>.slice/
│   │       └── cri-containerd-<containerID>.scope/
│   ├── kubepods-besteffort.slice/
│   └── kubepods-pod<UID>.slice/     # Guaranteed

Иерархия, по QoS-классу pod'а:

  • Guaranteed: requests == limits на всех контейнерах. Высший приоритет.
  • Burstable: requests < limits или часть контейнеров без обоих
  • BestEffort: ни requests, ни limits

При [[oom-killer|OOM]] на ноде первыми падают BestEffort, потом Burstable (по oom_score), Guaranteed, последние.

Полезно: --reserved-cpus, --cpu-manager-policy=static выделять физические CPU под Guaranteed-pod'ы.

Volume Manager

Реактирует на pod'ы с volumes: вызывает CSI-drivers (Attach → Mount → unmount → Detach). Для CSI это всё gRPC к socket'у CSI-Node-plugin'а на ноде.

Особенности:

  • Mount/Unmount idempotent, kubelet ретраит при ошибке
  • Attach/Detach делает не kubelet, а external-attacher sidecar при attachRequired: true в CSI driver'е (но решение принимает A/D Controller в kube-controller-manager)
  • Mount-флаги (noatime, nodev) задаются в [[kubernetes-storage|StorageClass]]

Probes

Health checking подов:

  • livenessProbe, pod жив? Если N раз провалил → kill + restart
  • readinessProbe, pod готов принимать трафик? Если нет → убрать из Endpoints (но не убивать)
  • startupProbe, заменяет liveness на старте, для медленно стартующих приложений (избегаем kill во время загрузки)

Типы probe'ов: httpGet, tcpSocket, exec (запустить команду внутри контейнера, exit-code 0, ok), grpc (с k8s 1.27+).

Probe-логика, целиком в kubelet, без сети к API.

Image GC и Disk eviction

kubelet не оставляет неиспользуемые images вечно, диск заполнится. Алгоритм:

Периодически (--image-gc-high-threshold, default 85%) проверяет использование /var/lib/containerd (или CRI-эквивалент). Если

threshold, удаляет неиспользуемые images до --image-gc-low-threshold (default 80%).

Сортировка: LRU по времени последнего использования. Image, на который ссылается running container, не удаляется.

Для логов: kubelet ротирует stdout/stderr через CRI ( --container-log-max-size, default 10Mi, --container-log-max-files 5).

Node-pressure eviction

Когда ресурсы ноды на исходе, kubelet проактивно убивает pods. Соблюдается приоритет:

СигналDefault thresholdЧто эвиктится
memory.available< 100MiBestEffort → Burstable → Guaranteed
nodefs.available< 10%то же по QoS
nodefs.inodesFree< 5%то же
imagefs.available< 15%image GC сначала, потом eviction
pid.available< 10%то же

Soft eviction (--eviction-soft) с --eviction-soft-grace-period pod получает время на graceful shutdown. Hard eviction, сразу SIGKILL.

Это отдельно от [[oom-killer|OOM-killer]] kernel'а: kernel убивает за секунды, kubelet проактивно эвиктит за минуты.

kubelet config

ConfigMap-based конфиг (вместо CLI-флагов):

yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd                  # должен совпадать с containerd
containerRuntimeEndpoint: unix:///var/run/containerd/containerd.sock
clusterDNS: [10.96.0.10]
clusterDomain: cluster.local
podCIDR: 10.244.1.0/24                  # из node-spec
evictionHard:
  memory.available: "200Mi"
  nodefs.available: "10%"
systemReserved:
  cpu: "500m"
  memory: "1Gi"
kubeReserved:
  cpu: "500m"
  memory: "1Gi"
maxPods: 110
serializeImagePulls: false              # параллельно pull

cgroupDriver, частая боль. systemd (default современных) vs cgroupfs. Должен совпадать с containerd-config'ом, иначе kubelet видит pods «не свои» и творится бардак.

Static pods

Pods, которые kubelet запускает независимо от API server'а, читая YAML из /etc/kubernetes/manifests/. Используется для control-plane (kube-apiserver, etcd, controller-manager, scheduler, каждый из них static-pod на control plane node'е).

В API появляются зеркальные pods (mirror pod), но kubelet не слушает их edits, источник истины файл.

Полезно: для self-bootstrapping control plane (как kubeadm), для standalone-агентов.

CSI / CNI / Device Plugin

kubelet вызывает три внешних:

  • CSI через gRPC (mounted в well-known path /var/lib/kubelet/plugins/<driver>/csi.sock)
  • CNI через exec бинарника (/opt/cni/bin/<plugin>)
  • Device Plugin через gRPC (для GPU, FPGA, RDMA, DaemonSet регистрируется в kubelet, kubelet пробрасывает в pod)

Когда что-то пошло не так

  • Node not ready сразу после старта, kubelet не достучался до CRI socket'а, или CNI не настроен (/etc/cni/net.d/ пустой). journalctl -u kubelet.
  • failed to get system container stats: failed to get cgroup stats cgroup driver mismatch (systemd vs cgroupfs) между kubelet и containerd.
  • Pod Evicted без видимой причины, kubectl describe node → Conditions: (MemoryPressure/DiskPressure/PIDPressure). Чаще всего, disk full из-за кэша images. crictl rmi --prune.
  • exec format error в pause container, multi-arch image не подходит ноде. kubectl describe pod → image arch.
  • Pod stuck in Terminating, finalizer держит, или CRI не отвечает на StopPodSandbox. crictl ps → есть ли container, crictl logs <id>.
  • kubelet OOM на самом себе, слишком много pod'ов и больших PodSpec'ов в RAM. Tune --max-pods, добавить --kube-api-qps/burst.
  • CSI mount висит, csi-node-plugin pod упал. kubectl get pods -n kube-system | grep csi.
  • kubelet log shows syncPod errored, частое и общее. Полезно --v=4 для verbose, но логи раздуются.

Полезные диагностические артефакты

  • journalctl -u kubelet -f, основные логи
  • crictl ps (вместо docker ps для CRI runtime'ов)
  • crictl logs <id>, логи контейнера
  • crictl images, что pulled на ноде
  • cat /var/lib/kubelet/config.yaml, текущий конфиг
  • curl -k https://localhost:10250/metrics, Prometheus метрики
  • curl -k https://localhost:10250/healthz

§ команды

bash
systemctl status kubelet

Статус демона - живой/мёртвый, последние ошибки в журнале

bash
journalctl -u kubelet -f --since '5 min ago'

Стрим логов kubelet - первое место смотреть при проблемах с подами на ноде

bash
crictl ps -a

Все контейнеры через CRI - alternative для docker ps когда runtime containerd/CRI-O

bash
crictl logs <container-id>

stdout/stderr контейнера прямо через CRI без k8s API

bash
crictl rmi --prune

Удалить unused images - быстрый способ освободить диск перед eviction

bash
kubectl describe node <node-name>

Conditions, аллокации, события - первое место смотреть когда нода unhealthy

bash
ls /etc/kubernetes/manifests/

Static pods - control-plane на kubeadm-кластерах

§ см. также

  • kubernetes-pod-lifecycleKubernetes pod lifecycle - от Pending до TerminatedPod проходит фазы Pending → Running → Succeeded/Failed/Unknown. Init-containers выполняются последовательно до основных. Probes: startup → readiness/liveness. SIGTERM + grace period при удалении.
  • runc-and-runscrunc, runsc, kata - container runtimesrunc - стандартный OCI-runtime, namespaces+cgroups+seccomp. runsc/gVisor - userspace-ядро для дополнительной изоляции. kata - облегчённая VM на контейнер. Performance ↔ isolation trade-off.
  • cgroups-v2-deepcgroups v2 - unified hierarchy, PSI, eBPF controlcgroups v2 - один tree вместо отдельных hierarchies на controller. Чистая семантика, новые fields (memory.high, io.cost). PSI показывает resource pressure. eBPF может управлять resources. Default в RHEL 9, Ubuntu 22+.
  • ebpf-basicseBPF - программируемый kerneleBPF - запуск sandboxed-программ в kernel без kernel-modules. Прицепляются к hooks (kprobe, uprobe, tracepoint, perf, socket, XDP). Verifier гарантирует завершение и safety. JIT компилирует в native. bpftool/libbpf/BCC - userspace tooling.
  • cni-pluginsCNI plugins - сеть Kubernetes (calico, cilium, flannel)CNI - спека плагина: дай pod IP и сеть. Реализации: flannel (VXLAN L2-overlay), calico (BGP routing), cilium (eBPF в kernel). Каждая даёт NetworkPolicy для firewall'а между подами. IPAM - часть CNI, выделяет адреса.
  • prometheus-basicsPrometheus: scrape, TSDB, PromQL и production-pitfallsPrometheus - сервер мониторинга: сам опрашивает приложения по HTTP, собирает числовые метрики, хранит во встроенной БД ~15 дней. По ним строят графики в Grafana и алерты через Alertmanager. Стандарт de-facto в Kubernetes.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки