k3s-лаборатория на одном VPS: GitOps, Gitea и Grafana

Кейсы
18 сентября 2026 Время чтения 5 мин

k3s — это «маленький Kubernetes»: тот же API и те же манифесты, но ставится одной командой и живёт на одной виртуалке. Мы собрали на нём полноценную лабораторию: git-хостинг с CI, мониторинг, базы данных, объектное хранилище и сам сайт service-lab.online. Всё, что принято называть «инфраструктурой продакшена», умещается в одном репозитории.

Зачем это нужно? Лаборатория — полигон: любое изменение инфраструктуры сначала обкатывается здесь, а не в боевом проекте клиента. При этом это не «игрушка» — кластер обслуживает реальные сервисы с TLS-сертификатами, бэкапами и графиками дашбордов.


Что внутри

Стек лаборатории:

  • k3s server на одном VPS, всё состояние — на локальном SSD;
  • Traefik как ingress-контроллер с редиректом на HTTPS;
  • cert-manager и Let's Encrypt, wildcard-сертификат *.service-lab.online;
  • sealed-secrets — шифрованные секреты прямо в git;
  • Postgres и Valkey как общие базы;
  • MinIO — S3-совместимое хранилище;
  • kube-prometheus-stack (Prometheus, Grafana, Alertmanager), Loki с Promtail для логов, Tempo и OpenTelemetry для трейсов;
  • Gitea с Actions-раннером — git-хостинг и CI;
  • Outline как база знаний.

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


Один репозиторий вместо десяти

Весь кластер описан в монорепозитории с двумя корнями: gitops/ — инфраструктура и общие сервисы, apps/ — собственные приложения:

gitops/
   ├── infrastructure/     # traefik, storage, cert-manager, kubeseal
   ├── shared-services/    # postgres, valkey, minio, logging-stack
   └── applications/       # gitea (+ act-runner), outline-wiki

apps/
   ├── service-lab.online/ # исходники + Dockerfile + k8s/
   └── demo-axum/          # Rust-демо с CI-деплоем

Исходники и манифесты живут рядом: поменял код — CI собрал образ и обновил деплой. Поменял конфиг инфраструктуры — переприменил папку. Это тот же принцип «просто и в одном месте», за который мы любим чистый монолит вместо микросервисов, только на уровне инфраструктуры.


Загрузка с нуля

Кластер поднимается по документированному порядку шагов, где у каждого есть проверка результата:

  1. Traefik — ingress-контроллер, IngressClass по умолчанию;
  2. PVC под базы и хранилища — все в статусе Bound;
  3. cert-manager — сертификаты Let's Encrypt, issuer Ready;
  4. sealed-secrets — контроллер и публичный ключ, закоммиченный в репозиторий;
  5. Postgres и Valkey — общие сервисы данных;
  6. MinIO — S3 API отвечает 200;
  7. стек наблюдаемости — Prometheus, Grafana, Loki, Promtail, OTel-коллектор;
  8. Gitea и Actions-раннер — раннер виден в админке.

Вся процедура записана в README репозитория: от чистой виртуалки до работающего CI — меньше вечера.


Секреты, которые можно коммитить

Все секреты в репозитории — SealedSecrets: они зашифрованы публичным ключом контроллера, и расшифровать их может только кластер:

# пишем обычный Secret во временный файл (в git не попадёт)
cat > /tmp/plain.yaml <<EOF
apiVersion: v1
kind: Secret
stringData:
  TOKEN: example
EOF

# запечатываем в репозиторий, рядом с манифестами
kubeseal --cert gitops/infrastructure/kubeseal/pub-cert.pem \
  --format yaml < /tmp/plain.yaml \
  > gitops/shared-services/my-secret.yaml
rm /tmp/plain.yaml

В git уходит только шифрованый YAML. Отдельно храним резервную копию ключа контроллера — без неё переезд на новый кластер означал бы перепечатывание всех секретов.


От push до продакшена — 2,5 минуты

CI — Gitea Actions, полностью внутри кластера. Пуш в develop или main запускает pipeline: раннер клонирует репозиторий и создаёт обычный Kubernetes Job с kaniko — образом собирает без Docker-демона:

apiVersion: batch/v1
kind: Job
metadata:
  name: kaniko-service-lab-a1b2c3d
spec:
  template:
    spec:
      restartPolicy: Never
      initContainers:
        - name: git-clone
          image: alpine/git:v2.47.2
          args: ["git", "clone", "--depth", "1", "...", "/workspace"]
      containers:
        - name: kaniko
          image: gcr.io/kaniko-project/executor:v1.23.2
          resources:
            requests: {cpu: "2", memory: 2Gi}
            limits: {cpu: "8", memory: 12Gi}
          args:
            - --dockerfile=/workspace/apps/service-lab.online/Dockerfile
            - --destination=gitea.service-lab.online/gitops/service-lab:a1b2c3d

Дальше pipeline применяет манифесты, пинит образ к SHA коммита и проверяет раскатку. От git push до живого сайта проходит около двух с половиной минут — Rust-сборка в кластере получает 8 ядер и 12 ГБ памяти, чтобы не растягивать ожидание.

Никакого внешнего CI: раннер, registry и цели деплоя — один и тот же кластер. Контур замкнут, и за его безопасность отвечает тот самый SealedSecret с токеном раннера.


Наблюдаемость и бэкапы

Логи любого пода — в Grafana через Loki, метрики — в Prometheus, трейсы — в Tempo через OpenTelemetry-коллектор. Для лаборатории это избыточно ровно до того момента, когда что-то ломается ночью — и по графику за десять секунд видно, что именно.

Бэкапы — CronJob'ы по расписанию: дампы Postgres, снапшоты MinIO и копии репозиториев Gitea. Восстановление проверяем так же цинично, как и бэкап — периодическим тестовым подъёмом.


Повседневность

Ежедневная работа с кластером умещается в две команды:

# обновить чарт: поднять версию в kustomization.yaml, затем
kustomize build gitops/shared-services/databases --enable-helm | kubectl apply -f -

# что-то разъехалось с git? переприменить папку — всё идемпотентно

Желаемое состояние всегда в git, кластер приводится к нему повторным применением. drift-страх перед «ручными правками на сервере» исчезает: править руками бессмысленно — всё равно перезапишется.


Итог

Один VPS, один репозиторий, один push до продакшена.

k3s-лаборатория окупается быстро: эксперименты с инфраструктурой перестают быть риском, а новые сервисы находят себе место в готовом окружении — с TLS, секретами, мониторингом и CI «из коробки».

О том, почему собственные сервисы мы пишем на Rust — в статье Rust и Zig в привычных приложениях.

Обсудим ваш проект?

Расскажите о вашей задаче — вернёмся с оценкой сроков и стоимости в течение двух рабочих дней.

Обсудить проект