От Docker к Kubernetes: Быстрый старт

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

1. От контейнера к оркестрации: почему Docker недостаточно и как Kubernetes решает проблему масштабирования

От контейнера к оркестрации: почему Docker недостаточно

Представьте ночь пятницы. Ваш интернет-магазин работает в Docker-контейнере на мощном виртуальном сервере. Внезапно маркетинговая кампания приносит x10 трафика. Контейнер исчерпывает доступную оперативную память, процесс внутри падает с ошибкой OOM (Out of Memory), и приложение перестает отвечать. Пока вы не проснетесь, не подключитесь по SSH к серверу и не выполните команду перезапуска, бизнес будет терять деньги. Эта ситуация — классическая иллюстрация того момента, когда инструмент упаковки кода начинает использоваться не по назначению, выступая в роли системы управления инфраструктурой.

Границы возможностей Docker

Docker совершил революцию, решив проблему «на моей машине всё работает». Он предоставил стандартный формат упаковки приложения вместе со всеми его зависимостями, библиотеками и конфигурациями. Контейнер гарантирует изоляцию: процесс внутри уверен, что он один владеет файловой системой и сетью.

Однако Docker в чистом виде (и даже Docker Compose) — это инструмент для работы с одним физическим или виртуальным хостом. Как только архитектура перерастает один сервер, инженер сталкивается с непреодолимыми архитектурными барьерами.

Проблема распределения (Scheduling). Допустим, у вас есть три сервера и десять микросервисов, каждому из которых нужно по три реплики для отказоустойчивости. Как решить, на какой сервер поместить конкретный контейнер? В мире без оркестратора это делается вручную или с помощью самописных скриптов. Если один сервер выйдет из строя, контейнеры, работавшие на нем, просто исчезнут. Никто автоматически не перенесет их на оставшиеся два живых сервера.

Проблема сетевых конфликтов. На одном Docker-хосте нельзя запустить два контейнера, биндящих один и тот же внешний порт (например, порт 80 для HTTP-трафика). Если вам нужно запустить пять экземпляров веб-сервера на одной машине, вам придется вручную назначать им разные порты (8081, 8082 и так далее), а затем настраивать внешний балансировщик (например, Nginx или HAProxy), который будет знать обо всех этих нестандартных портах. Это делает конфигурацию хрупкой и трудно поддерживаемой.

Проблема эфемерности. Контейнеры по своей природе смертны и эфемерны. При каждом пересоздании контейнер получает новый внутренний IP-адрес. Если микросервис A должен обращаться к микросервису B, а микросервис B только что перезапустился и сменил IP, микросервис A отправит запрос в пустоту. Docker сам по себе не предоставляет встроенного механизма Service Discovery (обнаружения сервисов), который мог бы надежно маршрутизировать трафик в динамически меняющейся среде.

!Сравнение ручного управления Docker-хостами и единого кластера Kubernetes

Парадигма «Питомцы против Скота»

Чтобы понять суть оркестрации, необходимо осознать концептуальный сдвиг в отношении к инфраструктуре, известный как парадигма «Pets vs. Cattle» (Питомцы против Скота).

В традиционной инфраструктуре серверы — это питомцы. У каждого есть имя (например, db-master-01, web-node-alpha). Инженеры знают их особенности, бережно устанавливают на них обновления, лечат, если они начинают сбоить. Если db-master-01 заболел, вся команда бросается его чинить, потому что его потеря критична.

В облачной парадигме серверы и контейнеры — это стадо (cattle). Им не дают имена, им присваивают номера или случайные хэши (web-pod-7b6c5d4-x9q2). Если один экземпляр начинает сбоить, его не пытаются починить по SSH. Его просто «убивают» и автоматически заменяют новым, абсолютно идентичным клоном.

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

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

Главное архитектурное отличие Kubernetes от ручного управления Docker заключается в способе постановки задач.

Docker CLI работает в императивной модели. Вы отдаете конкретные команды, описывающие шаги для достижения цели:

  • Скачай образ.
  • Создай сеть.
  • Запусти контейнер с такими-то портами.
  • Если он упал — перезапусти.
  • Если на третьем шаге произойдет ошибка, скрипт прервется. Императивный подход требует от инженера описывать процесс изменения состояния.

    Kubernetes построен на декларативной модели. Вы не говорите кластеру, как делать работу. Вы описываете желаемое конечное состояние (Desired State) в виде текстового файла (обычно YAML). Вы заявляете: «Я хочу, чтобы в любой момент времени работало ровно 3 экземпляра контейнера Nginx версии 1.21, и они должны суммарно потреблять не более 2 Гигабайт памяти».

    Вы отдаете этот манифест в API Kubernetes, и с этого момента кластер берет ответственность на себя. Как именно он скачает образы, на какие узлы их поместит и в каком порядке запустит — скрыто от пользователя.

    Сердце Kubernetes: Цикл согласования

    Механизм, который обеспечивает работу декларативной модели, называется циклом согласования (Reconciliation Loop, или Control Loop). Это бесконечный процесс, который работает по простой логике непрерывного сравнения:

    В кластере постоянно работают контроллеры, которые наблюдают за текущим физическим состоянием системы (). Если вы запросили 3 реплики приложения (), а один из серверов сгорел и унес с собой один контейнер, становится равно 2.

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

    !Работа цикла согласования при падении контейнера

    Этот цикл работает непрерывно. Вам не нужно писать скрипты проверок (health checks), которые будут будить вас ночью. Kubernetes сам является огромным асинхронным циклом while(true), который неустанно приводит реальность в соответствие с вашими YAML-манифестами.

    Ключевые задачи, которые решает оркестратор

    Переход от одиночных контейнеров к Kubernetes решает класс проблем, специфичных для распределенных систем.

    Автоматическая упаковка (Bin Packing)

    Представьте игру в тетрис, где фигуры — это ваши контейнеры с их требованиями к процессору и памяти, а игровое поле — это пулы доступных серверов (Worker Nodes). Kubernetes выступает в роли идеального игрока. Вы указываете, что сервису A нужно 500 МБ памяти, а сервису B — 2 ГБ. Планировщик (Scheduler) Kubernetes анализирует свободные ресурсы на всех серверах кластера и размещает контейнеры максимально плотно и эффективно, не допуская при этом «переподписки» (состояния, когда контейнерам не хватает физической памяти и они начинают убиваться ядром ОС). Это кардинально снижает затраты на облачную инфраструктуру, так как серверы не простаивают пустыми.

    Самовосстановление (Self-healing)

    Kubernetes не просто запускает процессы, он следит за их жизнеспособностью на уровне логики приложения. Если процесс внутри контейнера завис (Deadlock), но сам контейнер формально жив, Docker этого не заметит. В Kubernetes можно настроить Liveness Probes — специальные сетевые или командные проверки. Если приложение перестает отвечать на HTTP-запросы по пути /health в течение 10 секунд, Kubernetes безжалостно убьет этот контейнер и создаст новый.

    Управление конфигурацией и секретами

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

    Изменение инженерного мышления

    Переход к Kubernetes требует отказа от привязки к конкретному железу. Кластер абстрагирует от вас понятие «сервер». Вы больше не думаете категориями «я положу базу данных на сервер с IP 192.168.1.15».

    Вы начинаете воспринимать весь кластер как единый гигантский суперкомпьютер. Вы отправляете свои приложения в этот суперкомпьютер через API, а операционная система этого компьютера (Kubernetes) сама решает вопросы распределения памяти, сетевой маршрутизации и отказоустойчивости. Docker в этой схеме превращается лишь в низкоуровневый формат упаковки и среду исполнения (Container Runtime), которая послушно выполняет команды оркестратора на конкретных вычислительных узлах.

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

    2. Развертывание локальной лаборатории: установка Minikube и первый запуск декларативного манифеста

    Развертывание локальной лаборатории: установка Minikube и первый запуск декларативного манифеста

    Обучение работе с Kubernetes часто ассоциируется с необходимостью арендовать парк серверов в облаке и оплачивать внушительные счета за инфраструктуру. В реальности для освоения 80% концепций оркестрации, подготовки к сертификации CKA (Certified Kubernetes Administrator) и отладки сложных манифестов достаточно ресурсов обычного ноутбука. Инструменты локальной разработки позволяют сжать распределенную систему до размеров одного процесса, сохраняя при этом полноценный API и логику работы реального кластера.

    Локальный Kubernetes: как вместить кластер в ноутбук

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

    Для решения этой проблемы были созданы локальные дистрибутивы. Самые популярные из них — Kind (Kubernetes IN Docker) и Minikube.

    Kind запускает компоненты кластера как Docker-контейнеры. Он невероятно быстр и отлично подходит для CI/CD пайплайнов. Однако для обучения и локальных экспериментов стандартом де-факто остается Minikube.

    Minikube упаковывает все компоненты Kubernetes — и Control Plane, и Worker Node — в единую виртуальную машину или один Docker-контейнер. Внутри этой изолированной среды запускаются системные процессы оркестратора (API-сервер, планировщик) и среда выполнения контейнеров (Container Runtime).

    !Архитектура Minikube внутри хост-системы

    Такой подход дает два преимущества. Во-первых, ваша основная операционная система остается чистой: все сетевые правила, хранилища и процессы кластера изолированы внутри среды Minikube. Во-вторых, Minikube обладает встроенной системой аддонов — расширений, которые позволяют одной командой включать метрики, панели управления или локальные балансировщики нагрузки.

    Инструменты управления: kubectl и драйверы

    Для взаимодействия с любым кластером Kubernetes, будь то локальный Minikube или гигантский кластер в AWS, используется один и тот же инструмент — утилита командной строки kubectl.

    kubectl — это универсальный пульт управления. Он не выполняет работу по запуску контейнеров, а лишь транслирует ваши команды (или манифесты) в REST API-вызовы к серверу Kubernetes.

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

    Флаг --driver=docker указывает Minikube не создавать тяжеловесную виртуальную машину (VirtualBox или KVM), а использовать Docker для создания изолированного пространства. Minikube скачает базовый образ, запустит контейнер, который будет играть роль узла кластера, и сконфигурирует внутри него все необходимые сертификаты и ключи.

    После успешного старта утилита kubectl автоматически настраивается на работу с новым кластером. Проверить статус узла можно командой:

    Вывод покажет один узел с именем minikube в статусе Ready. Роль этого узла будет обозначена как control-plane. Кластер готов к приему нагрузок.

    Трансформация мышления: от docker run к манифесту

    При работе с чистым Docker запуск веб-сервера выглядит как императивная команда:

    Вы приказываете демону Docker: «скачай образ, создай контейнер, назови его так-то, пробрось порт и запусти в фоне». Если сервер перезагрузится, контейнер (без специальных флагов) не поднимется. Если вы захотите изменить версию образа, вам придется остановить старый контейнер, удалить его и запустить новый с другой командой.

    Kubernetes работает иначе. Вы не отдаете приказы, а описываете желаемое состояние системы в виде YAML-файла — декларативного манифеста.

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

    Рассмотрим структуру манифеста для запуска Nginx. Создадим файл nginx-pod.yaml:

    Любой манифест Kubernetes строится на четырех обязательных фундаментальных блоках:

  • apiVersion — указывает версию схемы API, которую нужно использовать для создания объекта. Разные объекты развиваются с разной скоростью. Базовые вещи, вроде Pod, давно стабилизировались в версии v1. Более сложные сущности могут иметь версии apps/v1 или networking.k8s.io/v1.
  • kind — тип создаваемого ресурса. В нашем случае это Pod. Регистр имеет значение, первая буква всегда заглавная.
  • metadata — данные, помогающие идентифицировать объект. Обязательно поле name (уникальное имя в рамках пространства имен). Поле labels (метки) критически важно для связывания объектов между собой, это система тегов формата ключ-значение.
  • spec (Specification) — самое важное поле. Здесь описывается то самое желаемое состояние объекта. Для Pod в spec обязательно указывается массив containers. Мы определяем имя контейнера внутри Пода, образ, который нужно скачать, и порт, который контейнер слушает изнутри.
  • Этот файл — не скрипт выполнения. Это контракт между вами и кластером.

    Запуск и инспекция: магия согласования в действии

    Чтобы передать манифест в кластер, используется команда применения конфигурации:

    Команда apply — это воплощение декларативного подхода. В отличие от команды kubectl create, которая просто пытается создать объект и выдает ошибку, если он уже существует, apply действует умнее. Она вычисляет разницу между тем, что уже есть в кластере, и тем, что написано в файле, и вносит только необходимые изменения.

    Как только вы нажимаете Enter, запускается сложная цепочка событий. kubectl валидирует синтаксис YAML, конвертирует его в JSON и отправляет POST-запрос к API-серверу Kubernetes. API-сервер сохраняет эту конфигурацию в базу данных кластера (etcd). С этого момента желаемое состояние зафиксировано.

    Далее в дело вступает планировщик (Scheduler). Он видит, что появился новый Pod, который нигде не запущен, и назначает его на доступный узел (в нашем случае — на единственный узел minikube). Локальный агент на узле (Kubelet) замечает это назначение, дает команду Container Runtime скачать образ Nginx и запустить контейнер.

    !Путь манифеста от команды apply до запуска контейнера

    Посмотреть список запущенных объектов можно командой:

    В первые секунды статус может быть ContainerCreating — это означает, что узел прямо сейчас скачивает образ из Docker Hub. Как только скачивание завершится и процесс стартует, статус изменится на Running.

    Если что-то пошло не так (например, вы опечатались в имени образа: nginx:1.21-typo), статус перейдет в ImagePullBackOff или ErrImagePull. Чтобы понять причину ошибки, недостаточно просто посмотреть на статус. Необходим инструмент глубокой инспекции:

    Эта команда выдает подробную карточку объекта. В ней указаны IP-адрес Пода, узел, на котором он запущен, ограничения по ресурсам и, самое главное, секция Events (События) в самом низу. В Events хронологически записывается все, что происходило с объектом: от момента успешного назначения на узел планировщиком до ошибок скачивания образа. Это первый и главный инструмент отладки в Kubernetes.

    Проброс трафика: как достучаться до приложения

    Контейнер запущен, статус Running, но если вы откроете браузер и введете localhost:80, то получите ошибку соединения.

    В Docker при запуске мы использовали флаг -p 8080:80, который жестко связывал порт хост-машины с портом контейнера. В манифесте Пода мы указали containerPort: 80, но это лишь информационное поле. Оно сообщает кластеру, какой порт слушает приложение, но не публикует его наружу.

    Kubernetes присваивает каждому Поду собственный внутренний IP-адрес, который доступен только внутри кластера. С вашего ноутбука этот IP-адрес невидим. В промышленной эксплуатации для маршрутизации внешнего трафика к Подам используются объекты Service и Ingress. Однако для быстрой локальной отладки конкретного Пода существует специальный механизм туннелирования:

    Эта команда блокирует терминал и открывает защищенный туннель между портом 8080 на вашем ноутбуке и портом 80 внутри Пода web-server-pod. Трафик проксируется через API-сервер Kubernetes прямо в контейнер.

    Теперь при переходе на http://localhost:8080 откроется стартовая страница Nginx.

    Важно понимать статус инструмента port-forward. Это исключительно диагностическая утилита. Она обрывает соединение при сетевых сбоях, нагружает API-сервер и не умеет балансировать трафик между несколькими репликами приложения. Она нужна разработчику, чтобы быстро проверить работу кода в кластере, не настраивая сложную сетевую маршрутизацию.

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

    3. Анатомия Pod как базовой единицы исполнения: запуск, инспекция и жизненный цикл приложения в кластере

    В Docker контейнер — абсолютная единица масштабирования. Но если вы попытаетесь запустить голый контейнер через API Kubernetes, система откажется это делать. Вместо этого она обернет его в Pod. Архитекторы Kubernetes пошли на усложнение и ввели дополнительную абстракцию не ради бюрократии, а для решения фундаментальной проблемы изоляции Linux-контейнеров: как заставить несколько тесно связанных процессов делить общие ресурсы, не сливая их в один монолитный образ.

    Невидимое ядро: Pause-контейнер и общие пространства имен

    Когда Kubelet получает команду на создание Pod, он не запускает контейнер с вашим приложением первым. Самым первым стартует крошечный, скрытый от глаз процесс — pause-контейнер (инфраструктурный контейнер).

    Его единственная задача — захватить и удерживать сетевое пространство имен (Network Namespace) и пространство IPC (Inter-Process Communication). pause-контейнер получает IP-адрес от сетевого плагина кластера, открывает порты и уходит в спящий режим, потребляя доли мегабайта памяти.

    !Внутренняя архитектура Pod

    После этого Kubelet запускает пользовательские контейнеры и «подселяет» их в пространства имен, которые удерживает pause. Это дает два мощных архитектурных преимущества:

  • Общий IP и localhost. Все контейнеры внутри одного Pod имеют один и тот же IP-адрес. Если в Pod работают веб-сервер на порту 8080 и кэш на порту 6379, веб-сервер может обращаться к кэшу просто по адресу 127.0.0.1:6379. Никакой сложной маршрутизации внутри Pod не требуется.
  • Бесшовный перезапуск. Если контейнер с вашим приложением упадет из-за ошибки (например, OOMKilled), Kubelet перезапустит только его. При этом IP-адрес Pod не изменится, потому что сеть держит pause-контейнер, который не падал.
  • Многоконтейнерные паттерны: зачем объединять процессы

    Философия микросервисов диктует правило «один процесс — один контейнер». Pod позволяет соблюдать это правило, одновременно группируя процессы, которые физически не могут существовать друг без друга.

    Паттерн Sidecar (Прицеп)

    Основной контейнер выполняет бизнес-логику, а вспомогательный (sidecar) расширяет его функциональность, не вмешиваясь в код. Классический пример: сбор логов. Основной контейнер — Nginx, который пишет логи доступа в обычный текстовый файл на общем томе (Volume) внутри Pod. Второй контейнер — Fluentd или Promtail. Он читает этот файл, парсит строки и отправляет их в централизованное хранилище (Elasticsearch или Loki). Nginx ничего не знает об отправке логов, Fluentd ничего не знает о маршрутизации HTTP-трафика. Они масштабируются и обновляются независимо, но работают как единое целое.

    Init-контейнеры

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

    Они запускаются строго последовательно, один за другим, до старта основных контейнеров. Если Init-контейнер падает, Pod не перейдет в состояние готовности — Kubelet будет перезапускать упавший Init-контейнер до тех пор, пока он не завершится с кодом (успех).

    !Последовательность запуска контейнеров в Pod

    Жизненный цикл и конечный автомат Pod

    Pod — это эфемерная сущность. Он рождается, выполняет работу и умирает. Он никогда не «переезжает» на другой узел: если узел выходит из строя, Pod погибает навсегда, а контроллер более высокого уровня создает новый Pod с новым ID на здоровом узле.

    В процессе жизни Pod проходит через строгий набор фаз (Phases):

  • Pending (Ожидание). API-сервер принял манифест, но Pod еще не запущен. Обычно это время уходит на работу планировщика (поиск подходящего узла) или скачивание тяжелого Docker-образа по сети (событие Pulling image).
  • Running (Выполнение). Pod привязан к узлу, все контейнеры созданы, и как минимум один из них работает, стартует или перезапускается.
  • Succeeded (Успешное завершение). Все контейнеры завершили работу с нулевым кодом возврата (характерно для пакетных задач — CronJob).
  • Failed (Сбой). Все контейнеры завершили работу, и как минимум один завершился с ошибкой (ненулевой код возврата).
  • Unknown (Неизвестно). API-сервер потерял связь с Kubelet на узле, где находится Pod.
  • Состояние CrashLoopBackOff

    Это не фаза, а статус контейнера, который часто вызывает панику у новичков. Если приложение внутри контейнера падает сразу после старта (например, из-за неверного пароля к БД или синтаксической ошибки в конфиге), Kubelet попытается его перезапустить.

    Если приложение снова падает, Kubelet включает алгоритм экспоненциальной задержки. Задержка между перезапусками вычисляется по формуле, где интервал удваивается после каждой неудачи: секунд, вплоть до максимального предела. В Kubernetes максимальный порог ожидания составляет секунд (5 минут). Это защищает узел от перегрузки процессора постоянными попытками поднять заведомо нерабочий процесс. Пока таймер тикает, Pod висит в статусе CrashLoopBackOff.

    Инспекция здоровья: Probes

    В мире Docker контейнер считается живым, если его корневой процесс () работает. В сложных системах этого недостаточно. Java-приложение может поймать взаимную блокировку потоков (deadlock): процесс работает, но на HTTP-запросы не отвечает. С точки зрения Docker — всё отлично. С точки зрения пользователя — сервис лежит.

    Kubernetes решает это через систему проб (Probes) — регулярных проверок состояния, которые Kubelet выполняет прямо изнутри узла.

    Liveness Probe (Проба живучести)

    Отвечает на вопрос: «Нужно ли убить и перезапустить этот контейнер?». Если Liveness-проба проваливается несколько раз подряд (параметр failureThreshold), Kubelet безжалостно убивает контейнер (отправляет SIGTERM, затем SIGKILL) и запускает его заново. Применяется для выхода из состояний deadlock или утечек памяти, когда приложение само восстановиться не может.

    Readiness Probe (Проба готовности)

    Отвечает на вопрос: «Готово ли приложение принимать пользовательский HTTP/TCP трафик?». Тяжелые фреймворки (например, Spring Boot) могут запускаться десятки секунд. Процесс уже висит в памяти, но кэши еще не прогреты, а соединения с БД не установлены. Если в этот момент отправить на Pod трафик, пользователи получат ошибки. Если Readiness-проба проваливается, контейнер не перезапускается. Kubernetes просто вычеркивает IP-адрес этого Pod из списков маршрутизации. Трафик перестает на него поступать до тех пор, пока проба снова не станет успешной.

    | Характеристика | Liveness Probe | Readiness Probe | | :--- | :--- | :--- | | Цель | Обнаружение зависаний и фатальных сбоев | Контроль потока входящего трафика | | Действие при провале | Перезапуск контейнера (Restart) | Изоляция от сети (Удаление из Endpoints) | | Типичный сценарий | Deadlock, бесконечный цикл в коде | Прогрев кэша, временная потеря связи с БД |

    Обе пробы настраиваются в манифесте через указание механизма проверки (HTTP GET-запрос по определенному пути, TCP-сокет или выполнение shell-команды внутри контейнера), а также таймингов: initialDelaySeconds (сколько ждать после старта перед первой проверкой) и periodSeconds (как часто повторять).

    Практика инспекции: заглядываем внутрь

    Когда декларативное описание расходится с реальностью (Pod не переходит в Running), в дело вступает императивная отладка. Ранее мы затрагивали инспекцию через события узла, но для анализа поведения самого приложения нужны другие инструменты.

    Чтение логов Команда kubectl logs <pod-name> выводит стандартный вывод (stdout и stderr) процесса. Если в Pod несколько контейнеров, Kubernetes потребует явно указать, чьи логи вы хотите увидеть. Для этого используется флаг -c (container): kubectl logs <pod-name> -c <container-name>

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

    Интерактивный доступ Иногда логов недостаточно: нужно проверить сетевую связность изнутри Pod, посмотреть содержимое конфигурационного файла на диске или выполнить дамп памяти. Команда kubectl exec позволяет запустить новый процесс внутри уже работающего пространства имен контейнера. Конструкция kubectl exec -it <pod-name> -- /bin/sh открывает интерактивную оболочку внутри Pod (при условии, что бинарный файл sh или bash присутствует в исходном Docker-образе). Вы оказываетесь внутри среды, видите те же переменные окружения и ту же файловую систему, что и ваше приложение.

    Понимание анатомии Pod — это переход от восприятия Kubernetes как «черного ящика» к осознанному управлению ресурсами. Однако Pod смертен по своей природе. Запуск одиночного Pod напрямую используется только для отладки. В реальных высоконагруженных системах нам нужны механизмы, которые будут следить за тем, чтобы нужное количество Pod всегда оставалось в строю, несмотря на падения узлов или всплески трафика.