Logo Craft Homelab Docs Контакты Telegram
K3s на одной виртуальной машине: деплой приложений и проверка устойчивости Трендовые github проекты в нашем телеграм канале. Подпишись →
7 августа 2026 г.

Компактный кластер для сервисов с предсказуемым деплоем

K3s позволяет собрать рабочую Kubernetes-среду на одной виртуальной машине и получить привычные механизмы оркестратора: декларативные манифесты, self-healing для подов, rolling update, Service, Ingress и постоянные тома. Такой стенд полезен для небольшого сервиса, пет-проекта и обучения эксплуатационным практикам на реальной нагрузке.

Одноузловая схема имеет чёткую границу: кластер переживает сбой отдельного процесса или пода, но потеря самой виртуальной машины остановит все сервисы. Высокая доступность на уровне инфраструктуры требует нескольких control-plane и worker-узлов, репликации хранилища и независимых зон отказа. Это важно зафиксировать до начала работы, чтобы возможности кластера соответствовали ожиданиям.

Почему для старта подходит K3s

Полная установка Kubernetes требует заметного объёма ресурсов и внимания к отдельным компонентам control plane. K3s упаковывает лёгкую конфигурацию Kubernetes в один дистрибутив и сохраняет совместимость со стандартным API и манифестами. В базовой конфигурации доступны CoreDNS, Traefik в роли ingress-контроллера и Local Path Provisioner для PersistentVolumeClaim.

Для небольшого набора сервисов разумной отправной точкой будет виртуальная машина с 2 vCPU, 4 ГБ оперативной памяти и SSD-диском от 30 ГБ. Память понадобится самому кластеру, PostgreSQL, приложению и временным задачам CI. SSD снижает задержки для persistent volume и ускоряет работу с образами. Цифры остаются ориентиром: перед вводом в эксплуатацию стоит измерить потребление именно своих контейнеров.

У каждого контейнера следует описать requests и limits. Requests участвуют в планировании и резервируют минимум ресурсов; limits не дают одному процессу занять весь CPU или всю память узла. Например:

resources:
  requests:
    memory: "128Mi"
    cpu: "100m"
  limits:
    memory: "512Mi"
    cpu: "500m"

При CPU limit приложение может отвечать медленнее под всплеском трафика, зато узел сохранит ресурс для DNS, ingress-контроллера, базы и остальных сервисов. Лимит памяти требует более внимательного расчёта: превышение обычно приводит к завершению контейнера по OOM.

Состояние PostgreSQL: StatefulSet, PVC и секреты

База данных требует постоянного хранилища и стабильной идентичности экземпляра. Для неё подходит StatefulSet с volumeClaimTemplates: контроллер создаёт PVC, а после пересоздания пода подключает прежний том. Удаление postgres-0 в таком сценарии не означает автоматическую потерю файлов данных — новый под получит связанный persistent volume.

Одна реплика PostgreSQL на одном PVC решает задачу сохранности данных при перезапуске пода. Она не создаёт кластер баз данных и не обеспечивает продолжение записи после сбоя узла. Для репликации и переключения роли понадобятся отдельная архитектура PostgreSQL, несколько узлов и продуманный механизм failover.

Параметры подключения и пароли храните в Kubernetes Secret. Имена ресурсов должны быть однозначными: Secret с учётными данными приложения и Secret типа kubernetes.io/dockerconfigjson для доступа к registry — разные объекты. Изменение типа уже созданного секрета Kubernetes не разрешает, поэтому ошибка конфигурации исправляется удалением или созданием нового объекта с корректным именем.

Для асинхронного Python-приложения строку подключения можно собрать из переменных окружения:

- name: DATABASE_URL
  value: "postgresql+asyncpg://$(DB_USER):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_NAME)"

Префикс postgresql+asyncpg выбирает диалект PostgreSQL и асинхронный драйвер для SQLAlchemy. Передавать секреты в репозиторий или образ контейнера нельзя: используйте Secret, ограничения доступа RBAC и отдельные значения для окружений.

Маршрутизация, TLS и готовность приложений

Внешний трафик удобно завести через Ingress: путь / направляется во frontend-service, а /api — во backend-service. Статический фронтенд раздаёт Nginx, API-запросы идут к сервису бэкенда напрямую через ingress-контроллер. Такая схема исключает лишний прокси-переход внутри фронтенд-пода.

Если FastAPI ожидает маршруты без префикса /api, Traefik может удалить его Middleware StripPrefix. При этом особенно важны namespace и имя провайдера в аннотации Ingress. Для CRD Traefik используется суффикс @kubernetescrd; неверный провайдер оставит маршрут недоступным при исправных подах и Service.

TLS-сертификаты можно выпускать cert-manager через ACME HTTP-01 challenge. Для успешной проверки необходимы корректный DNS, адрес электронной почты в ClusterIssuer и доступ робота центра сертификации к портам 80 и 443. Диагностику удобно вести по цепочке ресурсов:

kubectl get certificate
kubectl describe order
kubectl describe certificaterequest

Готовность сервиса к трафику определяет readinessProbe. Пока endpoint не вернул ожидаемый ответ, pod не входит в endpoints Service. livenessProbe контролирует зависшие процессы и инициирует перезапуск контейнера. Пробы должны проверять лёгкий endpoint без обращения к медленным внешним системам, иначе они сами станут причиной нестабильности.

Деплой из CI/CD внутри кластера

GitLab Runner можно запускать в отдельном namespace и выдать ему ServiceAccount с минимально необходимыми правами. Широкая роль cluster-admin упрощает первые эксперименты, однако для постоянного контура безопаснее выделить Role или ClusterRole только на нужные namespace и типы ресурсов.

Сборка образов Docker-in-Docker часто требует privileged-режима и добавляет сетевые сложности. Kaniko собирает контейнерные образы внутри Kubernetes без Docker daemon, используя контекст репозитория и конфигурацию авторизации registry. После публикации образа задача deploy применяет манифесты обычной командой:

kubectl apply -f k3s/ --recursive

Такой pipeline напрямую обращается к Kubernetes API, а состояние окружения остаётся описанным в Git-репозитории. Версии образов лучше фиксировать тегом коммита или релиза: тег latest усложняет откат и расследование инцидентов.

Что проверять после настройки

Манифесты приобретают ценность после проверки отказов. Для rolling update задайте maxSurge: 1, maxUnavailable: 0 и readiness probe. Во время обновления Kubernetes создаст новую реплику, дождётся её готовности и только затем завершит старую. Параллельный цикл запросов к сервису покажет, были ли ошибки в момент выкладки.

Отдельно удалите один stateless pod: ReplicaSet должен восстановить его, а Service направит трафик на оставшиеся готовые реплики. Затем проверьте PostgreSQL: удаление pod StatefulSet должно привести к повторному подключению PVC и сохранению данных. Нагрузочный тест поможет увидеть, как CPU limits влияют на задержки и не лишают ли соседние сервисы ресурсов.

Эти проверки стоит дополнить резервным копированием базы, мониторингом метрик, логированием и тестом восстановления на отдельном окружении. Кластер становится надёжнее благодаря регулярно подтверждаемым сценариям восстановления, а не только набору YAML-файлов.