Рейтинг
0.00

H3LLO CLOUD

0 читателей, 12 топиков

Даже не влезайте в Kubernetes без этого




Главный прикол с k8s: поднять базовый кластер займёт всего 15 минут. А вот чтобы он реально заработал, ответить на все вопросы перед установкой, всё спланировать — на это нужны дни, реально дни мозгового штурма и планирования. Ну или потом придётся разбирать и делать ещё раз. Несколько раз.

Кубер унижает человеческое достоинство разными способами и на разных этапах. Это часть опыта от пользования продуктом. Так задумано.

И вот про эти самые вопросы мы сейчас и поговорим, потому что там целое волшебное поле грабель.

Начнём с простых вещей, например, выбора дистрибутива, выбора способа хранения данных (и динамического выделения места), а также того, куда складывать пароли к критичным ресурсам. На этих трёх выборах ломается примерно 50 % админов.

Поехали в ад!



Архитектура

Kubernetes — это кластер. То есть много серверов, которые работают вместе. Эти серверы называются нодами, или узлами. Есть ноды управляющие (Control Plane). Раньше их звали Master, но сейчас это слово немодное, так что Control Plane. А есть ноды рабочие (Worker Nodes) — вот на них-то и крутятся ваши приложения в контейнерах (точнее, в подах).

Рядом с Control Plane живёт etcd. Это распределённая база данных (ключ-значение), где Кубер хранит всю инфу о себе и о том, что в нём запущено: какие поды, какие сервисы, какие конфиги — вот это всё. Она тоже обычно распределена по нескольким управляющим нодам для надёжности. Для полной надёжности её ставят отдельно.

Работает это так: вы пишете файл с конфигом, который описывает условия и состояния, например: «Хочу такой-то деплой из стольких-то подов, чтобы он при таких условиях вёл себя так, а при таких — вот так». Эти правила хранятся в etcd, а Control Plane следит за их исполнением. Если есть разница между тем, как надо, и тем, как есть, — всё приводится в нужное состояние. То есть что-то грохается или добавляется. Автоматически.

Казалось бы, всё просто, но тут огромная кроличья нора.

Дело — в условиях и работе приложений. Чтобы прописать нормальные условия для работы кластера, придётся вытащить наружу кучу метрик изнутри ваших приложений. Потому что более-менее доступные условия «из коробки» — это, например, «Если нагрузка на процессор больше 80 % 10 секунд подряд», а не «Если я записываю в базу данных быстрее чем за 0,1 секунды, и очередь — меньше 1 000 событий».

То есть вам надо будет продумать внешнюю сигнализацию своим приложениям, чтобы отдельные контейнеры могли сообщать внешнему миру о своём состоянии. Например, когда они перегружены, недогружены или что-то ещё.

Вторая особенность — вам надо обеспечить правильное поведение приложения при добавлении-убавлении контейнеров. Это касается транзакций (надо корректно закрываться после обработки транзакции, а не терять её), записи на диск и так далее.

С диском — тоже отдельный сюрприз: мышление придётся изменить. Но давайте по порядку.

Если у вас планируется реально большой, нагруженный кластер, то Control Plane и особенно etcd лучше выносить на отдельные, выделенные ноды. Не надо на них вешать рабочую нагрузку, пусть они занимаются только управлением. У себя на деве или стейдже мы можем и совмещать, чтобы экономить ресурсы, но на проде лучше делить. В наших собственных продуктах на dev-части мы совмещаем узлы, а на прод-части создаём два отдельных контура.

Почему это проблема?



Потому что это унижает человеческое достоинство. Просто знайте: то, что у вас поначалу не всё получается, — это нормально. Это часть процесса. Так задумано. Вот как сталкивались с этим наши участники команды разработки:
  • Начинаешь с Kubernetes — вроде всё понятно по верхам, курс прошёл, потыкал. Гугл и AI помогают. Но потом лезешь глубже: драйверы для хранилищ, autoprovisioning, сети… и это просто стена! Понять базово — это одно, а настроить под себя — совсем другое. Нужны или огромный опыт, или куча времени, чтобы просто сидеть и ковыряться. Настроить выход наружу? Тоже целая история с кучей нюансов: часто просто копируешь решения, не до конца понимая.
  • Написать код — быстро. А вот развернуть его в Кубере первый раз — это дни мучений! Сертификаты, связь между сервисами, настройка Argo CD с ключами — всё совершенно неинтуитивно. Если рядом нет опытного DevOps, который проведёт за ручку, то это просто взрыв мозга и боль: не понимаешь, за что страдаешь! Но когда наконец всё настроишь, думаешь: «Боже, как же хорошо! Теперь только так и буду работать! Только, пожалуйста, пусть кто-нибудь другой развернёт сам Кубер, я этого больше не хочу!»
  • Пытаешься соединить внутренние сервисы Кубера с внешним миром — и тут начинаются танцы с бубном. Куча технологий, и все — на разной стадии готовности.
  • Самое сложное в начале — просто понять, как вообще работать с Kubernetes. У тебя не запускается простой сайт в Docker-контейнере, а DevOps объясняет проблему терминами Кубера, и ты даже не понимаешь, как это отладить, куда смотреть. Потом тебе дают конфиг для kubectl и говорят: «Разбирайся сам». Ощущение, будто тебя выкинули с лодки посреди озера: плыви!

Что ставить
Это классика жанра для новичка. Открываешь доку Кубера, а там бац — десяток способов установки! И хрен поймёшь, какой тебе нужен.

Дальше можно гуглить «Kubernetes Hard Way» и «Kubernetes Simple Way»: там статьи разной степени упоротости. Поскольку на дворе — XXI век, можно попросить помочь и LLM, но это будет step-by-step-воспроизведение одной из этих двух инструкций.

Третий путь — пойти к облачному провайдеру и там найти managed-service для k8s, обычно выглядящий для пользователя как волшебная кнопка «Создать кластер Kubernetes». Жмакнули, подождали минут 10–15, и вам вываливается файлик с конфигом. А дальше наступает ступор. Смотрите на этот файлик и думаете: «Ну зашибись, а дальше-то что?»

Мой личный совет: прежде чем лезть в облака или мучиться со своими серверами, поставьте себе на ноут Minikube. Это такая песочница, маленький Кубер на одной машине. Погоняйте его, потыкайте палочкой, разберитесь с базовыми понятиями: что такое Pod, Service, Deployment — вот это всё. Освойте kubectl — это главная команда для общения с кластером. Вот когда вы через это пройдёте, тогда и Managed Service в облаке станет в разы понятнее, и вы будете знать, что делать с этим загадочным admin.conf.

Итак, вариантов три с половиной:
  • Managed Service в облаке (типа нашего H3LLO CLOUD) — это когда вы приходите к провайдеру, говорите: «Хочу Кубер», нажимаете пару кнопок (ну или не пару: всё зависит от упоротости провайдера), получая готовый кластер и kubeconfig. Плюсы: не надо париться с установкой и обновлением управляющих нод (Control Plane, etcd): всё это — головная боль провайдера. Масштабирование нод — часто «из коробки»: настроил правило, и если нагрузка растёт, то провайдер сам добавит виртуалок в ваш кластер. Автоматическое выделение persistent volume любого размера в любом количестве — тоже на провайдере, как и LoadBalancer для внешнего доступа. Минусы: зависимость от провайдера, его ограничений (про них ещё скажу) и его ценовой политики.
  • На своих серверах (Bare Metal) или своих виртуалках. Полный контроль над железом, над софтом, над настройками. Делайте что хотите. Но на вас падает вся сложность установки, настройки, обновлений, бэкапов. Масштабировать кластер новыми серверами? Только ручками: пошёл, купил сервер, поставил в стойку, настроил, добавил в кластер. Никакой магии.
  • Руками в облаке на виртуалках: можно, но зачем? Если очень хочется поковыряться в кишках, но своего железа нет. Или если Managed-сервис чем-то критично не устраивает (например, версией Кубера или доступными опциями). По сути, это то же самое, что и Bare Metal, только серверы арендованные. Вся сложность — ваша, но хотя бы серверы покупать не надо.

Последний способ — запустить отдельный контейнер в контейнерном хостинге типа нашего L1VESTACK. Тут вообще от платформы и серверов полностью абстрагируемся. Но это скорее для быстрого решения текущей задачи, а не для того, чтобы правильно настроить Кубер.

Как ставить
  • Kubeadm — официальная утилита от создателей Кубера. Но это, как я говорю, «путь самурая». Все команды — ручками, все конфиги — ручками, бутстрэп каждой ноды — ручками. Если вы не мазохист и ставите Кубер первый раз — не советую категорически: намучаетесь и проклянёте всё на свете.
  • Kubespray — это наш выбор для своих инсталляций на железе или виртуалках. Это, по сути, набор готовых Ansible-сценариев. Он автоматизирует 90 % рутины по развёртыванию кластера на куче нод. Есть неплохие статьи, как его подготовить и запустить. Очень рекомендую начинать именно с него, если вы решили ставить сами и уже выросли из Minikube. Очевидный плюс — в дальнейшем удачный конфиг можно многократно воспроизвести.
  • Экзотика — есть и другие штуки, часто привязанные к специфичным ОС. Например, для Talos OS есть свои утилиты — Talos CTL (ручками) и talm (типа Helm, но для Талоса). Интересные вещи, но это уже для тех, кто в теме.

На какую ОС ставить ноды
  • Есть обычные Linux — старая добрая Ubuntu, CentOS, Debian, и всё это прекрасно работает. Привычно, понятно, куча доков в сети.
  • Специализированные ОС — есть дистрибутивы, заточенные специально под контейнеры и Кубер. Это Fedora CoreOS / Flatcar Container Linux: часто у них read-only — корневая файловая система для безопасности, атомарные обновления. Но есть нюансы: например, FCOS вместо привычного cloud-init использует свой формат конфигов — Ignition. Надо разбираться, как его готовить (там есть ещё утилита Butane). Talos — ещё более хардкорный вариант. Там вообще нет привычной ОС с shell! Всё управляется через API. Максимальная безопасность, минимализм. Но требует полного переосмысления подхода к управлению хостами.

Если нет особых требований к безопасности или специфике, то начинайте с обычной Ubuntu LTS — не ошибётесь.

Container Runtime
  • Docker (точнее, dockershim) — до недавнего времени был стандартом де-факто. Сейчас его официально выпилили из Кубера, но многие дистрибутивы и инсталляторы по-прежнему его поддерживают через прослойку. Если у вас нет веских причин для другого — можно начать с него: он самый привычный.
  • Containerd — а вот это стандарт де-факто сейчас, поддерживаемый Кубером напрямую через интерфейс Container Runtime Interface. Кстати, Docker сам под капотом использует containerd, так что переход на него обычно безболезненный. Большинство новых инсталляций использует именно его.
  • CRI-O — ещё реализация CRI, разрабатываемая Red Hat. Тоже хороший вариант.
  • Есть и совсем специфичные вещи типа Kata Containers (запускает контейнеры в легковесных виртуалках для изоляции) или Kubevirt (позволяет запускать полноценные ВМ внутри Кубера). Но это уже для особых случаев.

Рекомендация: начинайте с containerd. Это сейчас мейнстрим.

Хранение
Вот это критически важный вопрос, если у вас не только stateless-вебморды, но и базы данных, очереди, кэши — всё, что должно переживать перезапуск и падение подов.

Сразу забудьте про использование локальных дисков на нодах для хранения важных данных в продакшене! Нода умерла — данные пропали.

То есть данные должны быть размазаны по кластеру или храниться в выделенном отдельном хранилище.

Кубер для работы с хранилищами использует стандартный интерфейс Container Storage Interface. Это позволяет подключать к нему самые разные системы хранения данных — как локальные, так и сетевые.

Базовое решение Ceph — очень популярное, мощное и надёжное распределённое хранилище. Наш опыт показывает, что это отличный выбор. Его можно развернуть отдельно, а можно прямо внутри Кубера с помощью специального оператора Rook. Rook сам поднимает все компоненты Ceph (мониторы, OSD на дисках нод) в виде подов и делает его доступным для Кубера через CSI. Мы у себя часто используем именно связку Ceph + Rook.

Всё это работает так. У вас есть приложение, работающее в контейнере внутри Kubernetes K8s. Этому приложению, например, базе данных, нужно место для хранения данных — скажем, пять гигабайт. Создаётся PVC (Persistent Volume Claim). Это как заявка на диск. Кубер понимает запрос и обращается к системе хранения данных (например, Ceph) через autoprovisioning с командой: «Создай диск на пять гигабайт». В Ceph создаётся блочный диск (RBD) и выделяется Куберу в виде его сущности PV (Persistent Volume). И уже этот PV подключается к поду.

Ceph — это распределённая система хранения, которая обычно устанавливается на те же серверы (узлы), где работают и сами контейнеры (сейчас мы — про Bare Metal). Она объединяет диски этих серверов в единое большое хранилище.

Хотя компоненты Ceph (например, процессы, управляющие отдельными дисками — OSD) могут запускаться в своих управляющих контейнерах (часто — с помощью инструмента Rook), сама система хранения тесно связана с физическими дисками и серверами. Когда контейнер падает, диск в Ceph остаётся. Когда падает целый сервер, данные сохраняются. Система не хранит всех данных на одном диске или узле: при записи копии данных «размазываются» по разным дискам на разных серверах. Мы сами выбираем, какой уровень репликации установить. Ceph автоматически обнаружит потерю OSD и начнёт перераспределять данные по оставшимся рабочим дискам, чтобы восстановить нужный уровень избыточности.

Если контейнер поднимется на другой машине после перезапуска, то новый экземпляр контейнера просто подключится к тому же самому постоянному тому (Persistent Volume), который был создан для него в Ceph. Данные на диске никуда не делись. Управление самим кластером Ceph — тоже отказоустойчиво. Есть специальные процессы-мониторы и манагер с репликами в режиме standby. Если сервер с главным манагером выходит из строя, то управление автоматически переходит к другому манагеру на другом сервере.

Конечно, система не неуязвима. Если выйдет из строя слишком много серверов или дисков одновременно, так, что оставшегося места не хватит для хранения всех данных или их реплик, или если не останется достаточно узлов для поддержания кворума управления кластером, то Ceph может перестать работать или перейти в состояние «Только для чтения». Там лучше разбираться руками.

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

Ещё варианты:
  • LinStor — ещё одно распределённое хранилище, и тоже с CSI-драйвером. Говорят, может быть производительнее Ceph в некоторых сценариях. Тоже достойный вариант.
  • Если вы в облаке, то у провайдера есть свои CSI-драйверы для их блочных хранилищ (Amazon EBS, Google Persistent Disk, Azure Disk, etc.). Обычно это самый простой вариант в облаке. Причём их можно смешивать для разных условий и для разных типов данных.
  • Более экзотичные GlusterFS, локальные диски через Local Persistent Volumes (только для специфичных задач!) и куча других вариантов.

Вы можете определить разные «классы» хранения (StorageClass), например, fast-ssd, slow-hdd, cloud-premium. А потом в манифесте для вашего приложения (в PersistentVolumeClaim) вы просто указываете имя нужного класса. Кубер через CSI-драйвер сам создаст диск нужного типа в соответствующем бэкенде (Ceph, облако, etc.). Это позволяет абстрагироваться от конкретной реализации хранилища.



Сеть
Если диски ещё более-менее понятны, то тут спотыкаются даже опытные админы. Сеть в Кубере — это, пожалуй, одна из самых сложных тем. Вам точно понадобятся хотя бы базовые знания сетей (IP-адресация, маршрутизация, файрволы).

Kubernetes создаёт свою собственную виртуальную сеть поверх вашей физической (или облачной) сети. Каждый под (контейнер) получает свой IP-адрес в этой виртуальной сети. И поды, запущенные на разных физических нодах, должны иметь возможность общаться друг с другом по этим виртуальным IP. Чтобы эта магия работала, нужно выбрать и установить CNI-плагин (Container Network Interface). Это такой софт, который и отвечает за настройку сети для подов. Наши фавориты:
  • Cilium — очень мощный и быстрый плагин. Он использует новомодную eBPF прямо в ядре Linux, что даёт ему производительность и кучу фич (сетевые политики, шифрование, балансировка). У Cilium есть ещё крутой инструмент Hubble — это UI и CLI для визуализации сетевых потоков и зависимостей между сервисами. Супервещь для отладки, мы его любим.
  • Calico — тоже очень популярный и зрелый плагин. Часто идёт по умолчанию во многих инсталляторах (например, в Kubespray). Умеет работать как с оверлейными сетями (VXLAN, IPIP), так и без оверлея, используя BGP-маршрутизацию, если ваша сеть это поддерживает. Тоже отличный выбор.
  • Flannel (простой, для начала неплох).
  • Weave Net, Antrea и куча других, в том числе специфичных для облаков (AWS VPC CNI, Azure CNI, GKE Netd).

В зависимости от выбранного CNI и вашей инфраструктуры вам может потребоваться разбираться с BGP, VXLAN, IPIP-туннелями, настройкой маршрутов и файрволов. Сеть — это то место, где можно застрять надолго. Как сказал один наш сетевик, когда разбирался с какой-то проблемой: «По идее должно быть так, но хрен его знает, иди копай». Эта фраза будет преследовать вас примерно везде по мере реализации.

«Я запустил Nginx в поде, как мне его открыть в браузере?»

В Кубере есть специальная штука — Service. Это абстракция, которая даёт постоянный IP-адрес и DNS-имя для группы подов (например, для всех реплик вашего веб-сервера) и умеет балансировать трафик между ними.

Типы сервисов:
  • ClusterIP: сервис получает внутренний IP, доступный только изнутри кластера. Для внутренних бэкендов — самое то.
  • NodePort: сервис открывает порт на каждой ноде кластера. Вы можете обратиться на IP_любой_ноды:NodePort и попасть на сервис. Неудобно, небезопасно, порты надо выбирать из определённого диапазона. Для продакшена — фу.
  • LoadBalancer: вот это уже интереснее. Если вы в облаке, то при создании сервиса такого типа облачный провайдер автоматически создаёт вам внешний облачный балансировщик (ELB, Google LB, etc.), назначает ему публичный IP и направляет трафик на ваш сервис (точнее, на его NodePort на нодах). Очень удобно! А что делать, если у вас свой кластер на железе (Bare Metal)? Облачного балансировщика нет. Тут на помощь приходит MetalLB. Это такой специальный контроллер, который эмулирует облачный LoadBalancer. Вы выделяете ему пул IP-адресов из вашей локальной или публичной сети (если вы крутой и имеете свою публичную сеть), и когда создаёте сервис типа LoadBalancer, MetalLB берёт свободный IP из пула и «анонсирует» его в вашей сети (обычно через ARP или BGP), чтобы трафик на этот IP приходил на ноды вашего кластера. Обязательная штука для Bare Metal!
  • Есть еще ExternalName, который использует DNS с кастомными параметрами, но даже в официальной доке предупреждают, что для некоторых протоколов типа http(s) могут быть проблемы. Мы его не используем.

Ingress — стандарт для публикации веб-приложений (HTTP/HTTPS) в продакшене. Ingress — это не тип сервиса, это отдельный ресурс в Кубере. Он работает как умный реверс-прокси или «входные ворота» для внешнего трафика. Он позволяет рулить трафиком на разные сервисы внутри кластера на основе хоста (например, api.mysite.com -> сервис API, blog.mysite.com -> сервис блога) или пути (mysite.com/app1 -> сервис App1, mysite.com/app2 -> сервис App2). Ingress также обычно отвечает за TLS-терминацию (SSL-сертификаты).

Чтобы Ingress заработал, вам нужно установить Ingress-контроллер. Это, по сути, и есть тот самый реверс-прокси, который читает Ingress-ресурсы и настраивает себя соответствующим образом. Самый популярный — Nginx Ingress Controller: знакомый многим Nginx, но специально заточенный под Кубер. Мощный, гибкий. Но! Будьте осторожны: недавно в нём находили неприятные уязвимости, позволяющие читать секреты из кластера. Это к важности своевременных апдейтов. Из других хороши HAProxy Ingress, Traefik (очень популярен, умеет сам получать Let's Encrypt-сертификаты), Contour и другие.

Настроить Ingress, сертификаты, DNS — это тоже задача не на пять минут. А если обратиться к официальной документации, то она рекомендует идти не в Ingress, а в Gateway API. Тут может показаться, что надо начать с него, и даже есть классные GUI для Gateway API, и хорошо бы ими воспользоваться! Но погодите )

Gateway API — это относительно новая спецификация в Кубере, которая со временем должна заменить или дополнить Ingress. Она более гибкая, позволяет разделять ответственность по маршрутам для разработчиков. Выглядит перспективно. Но я бы пока советовал не лезть в экзотику типа Kong Gateway. Да, у него есть красивый UI, что подкупает в мире консольного Кубера. Но он ломает саму идеологию Кубера с его декларативными манифестами в etcd и использует другую логику представлений. Лучше освоить стандартный Ingress с Nginx или Traefik, а к Gateway API присматриваться постепенно по мере накопления опыта. Есть реализации на базе того же Nginx, Envoy (Istio, Contour), HAProxy. Если разбираться с Gateway API, то лучше начать с них.

Кусок информационной безопасности
Пароли от баз данных, ключи к внешним API, всякие токены — ни в коем случае нельзя хардкодить в коде приложения (если кто-то ещё так делает и не был распят) или пихать прямо в YAML-манифесты! Этим вы создаёте огромную дыру в безопасности.

Для этого в Кубере есть специальные объекты:
  • Secrets — для хранения чувствительных данных (пароли, токены, TLS-сертификаты). Они хранятся в etcd в base64 (что не шифрование!), но Кубер предоставляет механизмы для их шифрования «at rest» и контроля доступа к ним через RBAC.
  • ConfigMaps — для хранения нечувствительной информации конфигурации (URL’ы, параметры, конфиг-файлы целиком).
  • Продвинутый уровень: можно интегрировать Кубер с внешними системами управления секретами, например, HashiCorp Vault, с помощью специальных инструментов (Vault Agent Injector, External Secrets Operator). Это даёт централизованное управление, ротацию секретов и т.д.

Вы можете «подсунуть» данные из Secrets и ConfigMaps вашему приложению как переменные окружения для контейнера или как файлы, смонтированные в определённую директорию внутри контейнера.

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

Но идеальный сценарий — это когда, например, кластер Cassandra создаётся автоматически через оператор типа k8ssandra, и приложение берёт креды для доступа из секрета, созданного оператором при запуске кластера. Ни в БД, ни в приложение ручками их никто не прокидывает.

Аутентификация и авторизация
В вашем кластере наверняка будут работать разные люди: DevOps/SRE, разработчики команды А, разработчики команды Б, может быть, QA-инженеры… Кто может деплоить приложения, кто может смотреть секреты, кто может удалять ноды?
  • Аутентификация. Сертификаты X.509, токены (статичные, JWT, через OIDC-провайдера типа Keycloak или Dex), интеграция с LDAP/AD.
  • Авторизация (RBAC — Role-Based Access Control). Вот это — самое главное. RBAC позволяет очень гибко настроить, что (какие действия: get, list, create, update, delete) с какими ресурсами (pods, services, secrets, nodes, etc.) в каких неймспейсах (или во всём кластере) и кто может делать (какой пользователь, группа или сервисный аккаунт). Вы создаёте Роли (Roles) или Кластерные Роли (ClusterRoles), где описываете набор разрешений. Затем вы создаёте Привязки Ролей (RoleBindings) или Кластерные Привязки (ClusterRoleBindings), чтобы связать роль с конкретным пользователем, группой или сервисным аккаунтом.
  • Namespaces: используйте неймспейсы для логической изоляции ресурсов разных команд, проектов или окружений (dev, stage, prod). Это помогает упорядочить кластер и упрощает настройку RBAC (роли можно создавать внутри неймспейса). Хотя для окружений более правильно и эффективно использовать разные кластеры (опять же привет, облака).
  • Золотое правило — минимальные привилегии. Давайте пользователям и сервисным аккаунтам только те права, которые им реально необходимы для выполнения их задач. Не надо всем раздавать cluster-admin: это прямой путь к катастрофе.

Observability
Вот мы и приехали к той самой волшебной части. Работать с кластером Kubernetes без настроенной системы мониторинга и логирования — это посадить админом слепого котёнка.

Метрики:
  • Нужен Metrics Server — это базовый компонент (обычно ставится аддоном), который собирает основные метрики использования ресурсов (CPU, RAM) с нод и подов. Он нужен как минимум для работы kubectl top и базового HPA.
  • Стек Prometheus + Grafana — это де-факто стандарт для сбора, хранения и визуализации метрик в мире Кубера. Prometheus — это база данных временных рядов, которая сама опрашивает метрики с разных источников (ноды, поды, сервисы). Grafana — инструмент для построения красивых дашбордов на основе данных из Prometheus (и не только).
  • Приложения должны сами отдавать метрики в формате, понятном Prometheus (обычно это текстовый формат по HTTP-эндпоинту /metrics). Не только CPU/RAM, но и специфичные для приложения: количество запросов в секунду, время ответа, размер очереди, количество ошибок, бизнес-метрики. Без этого ваш мониторинг будет неполным.

А я предупреждал )

Логи:
  • Контейнеры в Кубере обычно пишут логи просто в stdout и stderr. Эти логи собираются движком контейнеров (containerd/docker) и доступны через kubectl logs. Но смотреть логи каждого пода по отдельности — это ад.
  • Нужен централизованный сбор логов. Необходимо настроить агентов (например, Fluentd, Fluent Bit, Promtail), которые будут бегать на каждой ноде, собирать логи из файлов или напрямую от движка контейнеров и отправлять их в центральное хранилище.
  • Популярные варианты хранилищ логов — Elasticsearch (в составе стека ELK/EFK: Elasticsearch + Logstash/Fluentd + Kibana) или Loki (от создателей Grafana, хорошо интегрируется с Prometheus, обычно используется в стеке LGTM: Loki + Grafana + Promtail).

Это, кстати, значит, что логи должны быть структурированными, чтобы их было легко парсить и искать по ним. Вероятно, тут вам ещё раз придётся вернуться к разработке приложения и навести порядок в том, как оно кладёт логи под себя.

Трассировка. Если у вас микросервисная архитектура, то один запрос пользователя может проходить через десяток разных сервисов. Чтобы понять, где именно возникла задержка или ошибка, нужна распределённая трассировка. Инструменты: Jaeger, Zipkin, Tempo.

Конечно, приложения должны уметь принимать и передавать дальше специальные заголовки (trace ID, span ID) и отправлять информацию о своих операциях (спанах) в коллектор трейсов. Это тоже требует доработки кода.

Масштабирование
Ну что, база есть, доступ настроен, всё видно. Теперь можно и фишками обмазаться.

Устали поднимать руками и настраивать PostgreSQL, Kafka, Redis, Elasticsearch в Кубере? Следить за их состоянием, бэкапами, обновлениями? Для этого придумали операторов. Оператор — это, по сути, ваш кастомный контроллер внутри Кубера, который знает, как управлять определённым типом сложного приложения. Он расширяет API Кубера с помощью Custom Resource Definitions.

Работает это так:
  • Вы устанавливаете CRD для, скажем, PostgreSQL. Теперь Кубер знает о новом типе ресурса, например, PostgresCluster.
  • Вы устанавливаете сам Оператор PostgreSQL (это обычное приложение, работающее в поде).
  • Вы создаёте простой YAML-файл, где пишете — kind: PostgresCluster, name: my-db, spec: { replicas: 3, version: «15», storage: «fast-ssd» }.
  • Применяете этот YAML (kubectl apply-f).
  • Оператор видит, что вы создали новый ресурс PostgresCluster, и начинает действовать: создаёт нужные StatefulSets/Deployments, заказывает диски (PersistentVolumeClaims) нужного StorageClass, настраивает сервисы для доступа, конфигурирует репликацию, может настроить бэкапы и мониторинг — в общем, делает за вас всю грязную работу по развёртыванию и поддержке сложного stateful-приложения.

Существует огромное количество готовых операторов от сообщества и вендоров для баз данных, очередей, мониторинга, CI/CD — да чего угодно! Это очень мощный механизм расширения Кубера. Для совсем уж специфичных внутренних систем можно даже написать свой оператор (но это уже высший пилотаж).

Автомасштабирование — одна из главных фишек Кубера, ради которой его часто и внедряют.
  • Горизонтальное автомасштабирование подов (HPA — Horizontal Pod Autoscaler) — самый частый и полезный вид. Вы создаёте HPA-ресурс и говорите: «Хочу, чтобы количество реплик моего приложения my-app было от двух до десяти, и чтобы оно масштабировалось, если средняя загрузка CPU по подам превысит 70 % (или RAM, или кастомная метрика из Prometheus, например, длина очереди)». Кубернейтс сам будет следить за метриками и автоматически добавлять или удалять поды my-app в указанных пределах.
  • Вертикальное автомасштабирование подов (VPA — Vertical Pod Autoscaler). Менее популярный, но тоже бывает полезен. VPA анализирует реальное потребление ресурсов подами и может автоматически изменять requests и limits по CPU/RAM в манифесте пода. Чаще используется в режиме «рекомендаций», чтобы понять, сколько ресурсов реально нужно приложению. Использовать VPA вместе с HPA нужно осторожно: они могут конфликтовать.
  • Масштабирование кластера (Cluster Autoscaler). Это уже про добавление и удаление целых серверов (нод) в кластер. Работает так: если в кластере появляются поды, которые не могут никуда запланироваться (состояние Pending) из-за нехватки ресурсов (CPU, RAM) на существующих нодах, то Cluster Autoscaler замечает это и автоматически заказывает у облачного провайдера новую ноду (или несколько), ждёт, пока она не поднимется, и добавляет её в кластер. Поды тут же планируются на неё. И наоборот: если Cluster Autoscaler видит, что какие-то ноды долгое время недозагружены и все их поды можно переместить на другие ноды, то он может автоматически удалить эти лишние ноды, чтобы сэкономить деньги. Это главная фишка облачных Managed Kubernetes. Там это обычно работает «из коробки». Если у вас свой кластер — на железе или виртуалках, то заставить Cluster Autoscaler работать можно, но сложнее. Нужно использовать Cluster API. Это такой проект, который позволяет управлять жизненным циклом кластеров Kubernetes (в том числе создавать и удалять ноды) с помощью самого Kubernetes. Cluster API имеет провайдеров для разных инфраструктур (vSphere, OpenStack, AWS, Azure, Bare Metal через Tinkerbell/MAAS). Но это отдельная большая тема.

Кстати, тут надо передать пламенный привет некоторым российским облакам. У них часто есть довольно смешные лимиты на максимальное количество нод в одном Managed-кластере: где-то — 100 (привет, ТаймВеб), где-то — вообще 32 (Яндекс). А Кубер, на минуточку, сам по себе спокойно тянет 5 000 нод в кластере и 150 тысяч подов и даже не поперхнётся (по официальным тестам). У ВК вроде лимит побольше — 500 нод, у Сбера — 249 (хотя в доке пишут, что 500). Всё равно не 5 000. У нас в H3LLO CLOUD мы постарались эту проблему решить и даём возможность строить реально большие кластеры, близкие к максимальным возможностям Кубера.


Думаете, это конец?
Нет, это начало.

Развернули кластер, настроили сеть, сторадж, мониторинг? Поздравляю! Вы прошли… ну, скажем, разминку.
  • Короткий анонс. Приложения должны быть готовыми жить в динамичной эфемерной среде Кубера. Почитайте про 12-factor app — это мастхэв. Приложение должно:
  • Читать конфиги и секреты из окружения или файлов (никаких локальных config.xml!).
  • Писать логи в stdout/stderr (а не в файлы!).
  • Быть максимально stateless (состояние хранить во внешних базах, кэшах, хранилищах).
  • Быстро стартовать.
  • Корректно обрабатывать сигналы SIGTERM для graceful shutdown.
  • Уметь отдавать метрики для мониторинга (health checks, readiness/liveness probes).
  • Быть упаковано в легковесный Docker-образ.

Вам нужны конвейеры, которые будут автоматически:
  • Собирать Docker-образы вашего приложения при коммите в Git.
  • Пушить образы в Docker Registry (типа Harbor, GitLab Registry, Docker Hub).
  • Обновлять YAML-манифесты (Deployments, StatefulSets, Services, Ingresses) с новой версией образа.
  • Деплоить эти манифесты в Kubernetes (например, с помощью kubectl apply, Helm, Argo CD, Flux).
  • Проводить тесты после деплоя.
  • Обеспечивать лёгкий откат на предыдущую версию.

Инструментов для этого — море: Jenkins, GitLab CI, GitHub Actions, Tekton, Argo CD, Flux…

В идеальном розовом мире единорогов ваши разработчики вообще не должны знать слова «Kubernetes». Они просто пишут код, коммитят в Git, а дальше некая «платформа» сама всё собирает, тестирует и выкатывает куда надо. Нажал кнопку — получил фичу в проде. Достичь такого уровня абстракции — это Грааль платформенного инжиниринга. Это требует не только зрелого Кубера со всеми обвесами, но и глубокого понимания ваших приложений, процессов разработки и кучи инструментов поверх Кубера (часто это называют Internal Developer Platform). Это долгий и сложный путь.

А оно вообще того стоит? Если у вас реально сложная, большая, динамичная система, которая должна быть отказоустойчивой, масштабироваться под нагрузкой, часто обновляться без простоя, то однозначно — ДА, стоит. Головной боли на старте будет много, но потом вы получите такие гибкость, скорость и надёжность, которых другими путями добиться очень сложно, если вообще возможно. Вы реально сможете выкатывать фичи быстрее и спать спокойнее (но это неточно).

Если вы прочитали всё это и подумали: «Мама дорогая, да я в этом никогда не разберусь!», но при этом фишки Кубера вам нужны — не отчаивайтесь. Есть решения, которые берут на себя большую часть этой боли:
  • L1VESTACK или другой контейнерный хостинг — тут мы пошли по пути максимального упрощения: полностью спрятали оркестратор под капот. Вы просто даёте нам свой docker-compose.yaml (да-да, прямо его!) или используете наш простой CLI — и ваше приложение магическим образом разворачивается и работает на нашей инфраструктуре. Вообще не надо думать ни про ноды, ни про сети, ни про стораджи, ни про Ингрессы, ни про RBAC. Просто код и деплой. Идеально для тех, кто хочет «просто чтобы работало». Естественно, нет фишек инфраструктуры для продвинутых, потому что вы запустили контейнер, который работает.
  • H3LLO.CLOUD — на нашей платформе есть Managed Kubernetes. Здесь мы не прячем Кубер полностью, но берём на себя самую нудную и сложную часть: бутстреппинг, Control Plane, etcd, обновления базовой системы, обеспечение работы сети и хранилища, предоставляем автомасштабирование нод «из коробки». Вам всё ещё нужно понимать основные концепции Кубера (поды, сервисы, деплойменты) и уметь пользоваться kubectl, но основную инфраструктурную рутину и сложность мы снимаем. Это хороший баланс между контролем и удобством.

Что почитать или посмотреть

Жирнющие бесплтаные лимиты на облачные сервисы на целый год, включая Managed Kubernetes — H3LLO.CLOUD.

Не менее щедрые лимиты на платформу для запуска приложений в контейнере — L1veStack.

beta.h3llo.cloud
h3llo.cloud

Вайб-кодинг: практика, о которой почему-то не говорят




В феврале мир разработки перевернулся с выходом Sonnet 3.7. Потому что вдруг внезапно оказалось, что джуны уже не очень-то и нужны. И нейросетка нормально заменяет мидлов тоже.

Я откидываюсь в кресле, беру наушники и смотрю, как работает LLM. Можно сразу несколько, работающих над разными частями проекта:


Пример проекта с прикручиванием аналитики к инфраструктуре:
  • Сначала в GPT 4.5 провёл продуктовые исследования и сформулировал требования.
  • Попросил превратить это в архитектурный план.
  • Отревьюил, поправил тупые ошибки.
  • Затем этот план (как метапромпт) скормил Sonnet в VS Code через плагин Cline. Попросил сначала создать общую структуру, шаблонные имплементации, документацию, спецификации API (protobuf для gRPC, REST API).
  • Архитектурно сразу заложил микросервисы. Sonnet для каждого сервиса подобрал и обосновал оптимальную базу данных (где-то Postgres, где-то ClickHouse и т.д.).
  • Сгенерировал SDK для взаимодействия, примеры использования. Сразу заложил observability: централизованные логи, метрики Prometheus, трейсинг Jaeger/Tempo, дашборды для Grafana.
  • Потом итерационно генерировал код: сначала тесты (End-to-end, BDD), потом имплементацию под эти тесты.
  • Написал манифесты для Kubernetes и Docker Compose для локального запуска.
  • Сгенерировал даже скрипты для тестов REST API через curl и gRPC через gRPCurl.

И всё.

А теперь практика — что делать с тем, что современные нейросети учились преимущественно на говнокоде и как быть с джунами.
Качество LLM-кодинга сильно зависит от языка программирования
Главная проблема — объём блока (точнее, зависимостей) и нормальная обучающая выборка.

Scala
Попытки написать на Scala работающий микросервис по OpenAPI-спецификации провалились.

Модель генерирует код, он не компилируется. Скармливаешь ошибки компиляции — следующая итерация кода содержит ещё больше ошибок.

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

Попытки транслировать код с другого языка (например, с Go) в Scala тоже могут быть проблематичны, особенно если требуется сохранить функциональный стиль Scala — на понятиях PHP, например, такое просто не опишешь.

Скала очень недетерминирована, её стиль отличается от мейнстримовых других языков достаточно сильно, а кодовая база не очень большая. В итоге LLM для сложных проектов на практике пока непригодны.

Golang
Совершенно другая история. Современные модели генерят на Go очень хороший, часто компилирующийся из коробки код. Скорее всего, работает комбинация факторов:
  • Go — очень квадратно-гнездовой язык. Задачу чаще всего можно решить одним, максимум двумя способами. Нет такого разброса стилей и подходов, как в Scala или даже JS. Это упрощает обучение для LLM: меньше вариативность «правильных» ответов на один и тот же запрос. Эта прямолинейность отличается от C, который хотя и прост на первый взгляд, как автомат Калашникова, но позволяет легко «провалиться» на низкий уровень, иногда незаметно. Go тоже позволяет уйти в сторону ассемблера, но при решении стандартных задач он очень явно описывает путь.
  • Строгая стандартизация. Встроенный форматер (gofmt), линтеры (golint), общепринятые конвенции — всё это делает кодовую базу на Go очень однородной. Это лучше, чем в Python, где тоже есть линтеры, но вариативности в написании больше. В Go сама идея языка была в том, чтобы код легко читался всей командой.
  • Качественная и большая обучающая выборка. Огромное количество успешного Open Source написано на Go (Kubernetes и вся его экосистема, Docker, Prometheus, Terraform и т.д.). Вероятно, этот код использовался для обучения моделей с высоким весом, возможно, даже с усилением (как если бы Википедию добавили в базу 10 раз).
  • Меньше «говнокода». Возможно, Go — язык более молодой, его чаще выбирают уже более опытные разработчики для серьёзных проектов (это сейчас слабое допущение). В отличие от Python или JS, куда порог входа ниже и больше новичков, генерирующих код не самого высокого качества, который тоже попадает в обучающие выборки. Плюс сам язык Go меньше прощает ошибок и не провоцирует написание «кривого» кода так, как это делает, например, JS. Сам язык как бы «не предусматривает» говнокода в той же мере.
  • Почему мы сами выбрали Go — бинарники маленькие, памяти ест мало, работает быстро. Хотя и PHP последних версий тоже компилируется, но это отдельный спор. До сих пор, кстати, не утих другой холивар — можно ли считать PHP языком программирования или нет. Я придерживаюсь мнения, что можно. Но пишем мы преимущественно на Go.

Rust
Тоже хайповый современный язык. LLM генерят код, который проходит проверку синтаксиса в IDE. Но при попытке сборки (которая в Rust сложнее, чем в Go) вылезает куча ошибок. И вот с исправлением этих ошибок сборки LLM справляются хуже, чем с написанием изначального кода или исправлением ошибок в Go. Вероятно, дело опять же в сложности языка и, возможно, пока ещё менее репрезентативной выборке кода с решёнными проблемами сборки.
Экспериментировали мы не очень много.

Java
Самая интересная ситуация, показывающая принципы LLM-кодинга.
Проблема — в структуре типичного Java-проекта: там везде глубокая вложенность пакетов, сложные цепочки наследования. Чтобы понять, что происходит в одной функции, LLM нужно проанализировать много связанного кода.
Это требует огромного окна контекста: меньше 64к токенов — почти бесполезно для серьёзного Java-кодинга. К счастью, модели с большими окнами появились.
Ещё нужны агенты для исследования кодовой базы. Инструменты вроде Cursor, Cline позволяют LLM «ходить» по проекту. Но и тут проблема: системный промпт агента (который сам по себе может быть большим, на тысячи токенов), прочитанные файлы классов и интерфейсов, дерево проекта — всё это быстро забивает даже большое окно контекста, особенно учитывая, что в Java много мелких лексем (точки, скобки), каждая из которых — токен.
То есть сама структура проектов на Java такая, что нужно не только смотреть тот блок, где сейчас идёт редактирование, но и держать «в голове» вообще всё то, что происходит в зависимостях. Это, кстати, причина тех самых мемов про состояние потока программиста. Вот у LLM то же самое.

Идеальное решение (которого пока нет в массовом использовании) — агенты, способные хранить состояние проекта в долговременной памяти, а не только в текущем окне контекста. Тогда LLM сможет «помнить» всю структуру и историю изменений. Сейчас же каждый новый агент начинает почти с нуля.
Сравните с Go: там часто достаточно контекста одного файла + импортов, чтобы понять, что происходит. Добавьте нашему вымышленному программисту не только отвлекающие звонки, но и болезнь Альцгеймера, чтобы он забывал задачу каждые 15 минут, — и вы получите примерное представление о LLM-кодинге на Java.

JavaScript
Динамическая типизация, язык многое прощает, разные стили, потенциально огромное количество некачественного кода в обучающей выборке. LLM сложнее строить логические цепочки, одна и та же лексема может иметь разный смысл.
TypeScript показывает заметно лучшие результаты благодаря статической типизации.

Регулярные выражения
В целом LLM пишут их неплохо. Но из-за того, что регулярки состоят из очень маленьких лексем (часто одиночных символов), а модели часто работают с ненулевой «температурой» (параметр, отвечающий за креативность/случайность ответа), они могут быстро начать «галлюцинировать» и вставлять не те символы, ломая логику выражения. С математикой тоже бывают проблемы, хотя современные модели умеют подключать внешних «экспертов» или писать код для вычислений, это не всегда работает идеально.

Про Python рекомендую вот эту публикацию.
github.blog/news-insights/octoverse/octoverse-2024/



Что получается
Для Go-проектов и ряда других LLM уже применимы на уровне мидлов, которые хорошо кодят, но которым надо давать точное техническое задание.

От разработчика нужны глубокие архитектурные знания и умение делать качественное ревью, иначе никак.

Если это есть и он готов выступать тимлидом LLM-агентов, то эффективность растёт в разы.

Разработчик общается с LLM так же, как раньше с джунами — ставит задачу, проверяет, корректирует.

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

Код-ревью становится важнее: генерируемый код нужно внимательно читать.

Понимание архитектуры и умение читать чужой код становятся критически важными.

Меняется структура команд: потребность в джунах, которые в основном занимаются рутиной, снижается. Мидлы должны быстро расти до уровня Middle+, способных работать с архитектурой. Сильно растёт ценность Senior и лидов, способных грамотно ставить задачи и контролировать результат.

Результат: проект, который силами небольшой команды делался бы минимум месяц до начального рабочего состояния, у нас был собран за три дня. Ещё примерно столько же ушло на ревью (в том числе по ИБ), ручное тестирование крайних случаев и подготовку к продакшену. Затраты на токены — около 150 $.

Уверенность в коде? После моего ревью — высокая. Если сравнивать сгенерированный код до ревью с кодом джуна до ревью — уверенности в LLM-коде у меня больше. Он сразу пишет с учётом observability, тестов, лучших практик (если правильно попросить), даже с интеграцией токенов для защиты вызовов. Джуна этому ещё учить и учить.

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

Правильное техническое задание становится самой дорогой и важной частью.

Пайплайн «задача -> реализация» у нас уже радикально упростился. При обсуждении фич мы меньше зависим от опыта конкретного разработчика в конкретной технологии. Нужно интегрировать Cassandra? Не проблема, даже если у человека нет 5 лет опыта с ней. LLM выдаст «курс молодого бойца», поможет спланировать интеграцию, сгенерирует код. Разработчик контролирует процесс, вносит правки с учётом специфики нашего Go-кода. Стек стал гибче. Мы можем выбирать лучшие технологии под задачу, не ограничиваясь текущими знаниями команды. LLM помогает быстро вкатиться и интегрировать. Разработчик при этом получает реальный опыт с новыми инструментами.

Практическая скорость выросла, по нашим оценкам, примерно вдвое.

За один спринт (две недели) команда делает то, что раньше заняло бы месяц, и это при работе над новым проектом с нуля. Создать новый микросервис по образу и подобию существующих? Даёшь LLM пример, и он генерирует всю обвязку. Остаётся наполнить бизнес-логикой.

Время разработчиков перераспределилось: меньше совещаний (экономия оценивается примерно в 8 часов за две недели на человека), так как синхронизироваться по архитектуре нужно меньше.

И давайте ещё раз: меньше совещаний. Это важно. Синки только по необходимости.

Освободившееся время уходит на ревью, продумывание сложной логики и тот самый «вайб-кодинг» — параллельную работу, где на одном экране код генерит LLM, на другом — правишь или пишешь сам, потом меняешься ролями для ревью. Работа становится более «дирижёрской». Даже DevOps-задачи частично можно переложить на LLM (генерация Dockerfile, K8s-манифестов).

Но есть и проблема контроля. Иногда тяжело ограничить место изменений. Просишь LLM поправить что-то в одном конкретном месте, пишешь «только здесь, только это», но нет гарантии, что он не затронет что-то ещё в другом файле или модуле.

Интересно, что даже размер микросервиса теперь может определяться не только бизнес-контекстом, но и размером контекста LLM — микросервисы стали де-факто стандартом, потому что с ними LLM справляются легче.

Как мы теперь нанимаем
Мы требуем уметь хорошо кодить руками на входе.

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

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

Этап джуна придётся проскакивать на тренировках, а не на реальных проектах.

Сам промпт-инжиниринг из области заученных лайфхаков превратился в умение сформулировать задачу. Модели достаточно развились, чтобы понимать свободные формулировки. Осталось только внести в них смысл — ради смысла и нужен профессионал. Нужно декомпозировать задачу, учесть все нюансы: версии библиотек и фреймворков (сказать: «Next.js», не уточнив App Router vs Pages Router — получить не то), протоколы взаимодействия (не указать gRPC — получить HTTP по умолчанию), конкретные требования к архитектуре, базам данных, безопасности.

Промптинг становится итерационным. Мы часто сначала генерируем верхнеуровневую архитектуру (например, в Markdown), правим её руками. Потом генерируем схемы взаимодействия между микросервисами (Protobuf, GraphQL, OpenAPI, SQL DDL) — это становится критически важным шагом, и только потом используем эту схему как часть промпта для генерации кода самих сервисов. Потом — документацию. Потом генерируем тесты (интеграционные тут особенно важны). Потом — структуру файлов и сигнатуры функций с комментариями. И только потом — сам код.

У новых людей меняется отношение к коду. Он становится расходным материалом, а не домашним питомцем — если что-то пошло не так, часто проще откатиться, доработать промпт и сгенерировать заново (иногда и 10 раз), чем пытаться исправить сложный баг в машинном коде.

Ценность навыка написания кода снижается в пользу навыка чтения чужого кода. Однако и тут есть нюанс: после оперативного запуска нового кода ценность смещается в сторону знания кодовой базы. И именно этот фактор делает разработчиков незаменимыми (вспоминаем про bus factor, которого боятся все инвесторы).

Тим О'Райли недавно написал отличную статью о том, как меняется кодинг. Его мысль перекликается с нашими наблюдениями: LLM — это следующий уровень абстракции после фреймворков. Если раньше многие разработчики использовали фреймворк, не до конца понимая, как он работает «под капотом», то скоро появится целый класс специалистов, которые будут «просить LLM сделать», не зная деталей реализации. Программирование как навык и как бизнес ждут серьёзные трансформации в ближайшие 5–10 лет. Да, похожие вещи говорили с появлением BASIC и других высокоуровневых языков, и это нормально — каждая такая революция меняла ландшафт.

Как реагируют программисты? Похоже, сообщество разделилось на несколько групп, как отмечалось в Wired. Первая группа верит, что разработчики будут не нужны. Вторая считает LLM продвинутыми стажёрами, верит в рутину, но не верит в понимание. И реалисты — видят усилитель возможностей и личной эффективности. Мы скорее относим себя к реалистам. LLM не убьёт профессию, но сильно её изменит. Мир разработки переживает переломный момент, и важно адаптироваться. Работы не становится меньше, она становится другой.

beta.h3llo.cloud
h3llo.cloud

Тестируйте наше облако целый год бесплатно




Вам станут доступны бесплатно на год:
  • 2 виртуальных машины с 2 vCPU и 4 ГБ RAM
  • 40 ГБ сетевого диска
  • объектное хранилище на 50 GB
  • белый статический IPv4
Запускаем последнее в России коммерческое облако. Успейте войти в число бета-тестеров и на целый год мы предоставим не только большой объем бесплатных ресурсов, но и скидку до 90% на все сервисы.



beta.h3llo.cloud

Мы протестировали разные облака на скорость PostgreSQL




Мы тут запускаем последнее в России коммерческое облако, и один из побочных эффектов этого процесса — настраиваем тесты всех конкурентов, чтобы понимать объективную картину. Прошлый тест был про переподписку ядер процессора, и он вызывал нехилое такое пригорание пятой точки у суперпереподписанного Таймвеба (хотя по цене за производительность они оказались вполне в порядке).

Так вот, нашу подсеть уважаемый конкурент забанил, чтобы было неповадно их тестировать. А затем, похоже, подкрутил тесты для наших машин так, что они показали скорость света.

Облака в тесте:
  • Selectel.
  • Cloud.ru.
  • Timeweb.
  • VK.
  • Yandex.
  • Rostelecom.
  • H3LLO.CLOUD.

Коротко о результатах


Radar chart по трём показателям: производительность, стоимость к производительности и задержка инвертированная. Больше площадь — лучше
  • Timeweb показал одну из самых низких производительностей, но при этом снова хорошую цену за единицу вычислений.
  • VK Cloud и Яндекс оказались аутсайдерами: и производительность не впечатляет, и стоит дорого. У Яндекса есть ограничитель на максимальную производительность.
  • Потом вы просили добавить нас в тесты, чтобы потом можно было предъявить, если что, и мы добавили. Нам надо было установить цену для своих тарифов, мы взяли её как медианное значение между Cloud.ru и Selectel.

Нельзя просто взять и протестировать
Мы не ожидали, что это обернётся почти масштабным научным исследованием, а не простым «запустил и померил». Делов-то было на 20 минут, как казалось вначале. Ну, поставим бенчмарк, запустим скрипт, получим циферки, забабахаем красивые графики. А в итоге погружаешься в какую-то научную диссертацию, где каждое число может быть оспорено, а каждое методологическое решение требует обоснования.

Потому что вы нас немного заклевали за подход «херак-херак и в продакшен» на первом тесте. Хотя он вполне решал свою задачу — оценить производительность связки vCPU-RAM на машинах с одинаковыми характеристиками у разных провайдеров. Или, проще говоря, выяснить, кто же больше охренел )

В общем, методология. Сразу решили не изобретать велосипед и взяли встроенный в PostgreSQL тест pgbench. Он создаёт три таблицы разного размера, которые отличаются друг от друга в 10 и в 100 000 раз по числу записей. Выбрали фактор масштабирования 200, и это дало нам таблицы размером 200, 2000 и 20 миллиона записей. Достаточно, по нашему мнению, чтобы проверить, как облака справляются с нагрузкой.

Главная проблема методологии: если вы запускаете тесты с локального компьютера, то результаты будут зависеть от вашего интернет-соединения. Человек в другом городе с другим провайдером получит совершенно иные цифры. Все тесты запускались с виртуальных машин, которые находились в той же подсети и зоне доступности, что и тестируемые базы данных. Так мы исключили влияние внешних сетей.
  • База данных Postgres 16.
  • 4vCPU + 16 Гб RAM + 40 Гб SSD (где был выбор между дефолтным флейвором БД и настраиваемым сайзингом, мы выбирали дефолтный флейвор).
  • Где был выбор между Ice Lake vs Cascade Lake мы выбирали Ice Lake.
  • Без пулинга, репликации и бекапов.
  • Без тонкой настройки дополнительных параметров конфигурации Postgres.
  • Для бенчмаркинга выбрали стандартный pgbench, который входит в состав Postgres.
  • Для минимизации времени отклика pgbench запускался из виртуальных машин находящихся в той же приватной подсети, что и Managed Postgres, без SSL.
  • Проводили 4 итерации: 1 разогревочная + 3 фактических.
  • Бенчмаркинг проводился в рабочее время.

4 стандартизированных нагрузки pgbench:
  • default, «Стандартная нагрузка TPC-B»
  • simple_update, скрипт обновляет баланс в таблице `pgbench_accounts`
  • select_only, скрипт выполняет выборку данных из таблиц `pgbench_accounts`, `pgbench_branches` и `pgbench_tellers`
  • complex_write, скрипт обновляет балансы в таблицах `pgbench_accounts`, `pgbench_tellers` и `pgbench_branches`, моделируя сложную транзакцию

Отказались от использования 2 нагрузок, поскольку по результатам предварительных тестов бенчмаркинга они не добавляли информативности к результатам от уже существующих стандартных тестов, а только удлиняли бенчмаркинг:
  • join_heavy: скрипт выполняет соединение таблицы `pgbench_accounts` самой с собой несколько раз, что позволяет оценить производительность при выполнении joins
  • insert_with_indexes: скрипт вставляет новую запись в таблицу `pgbench_history` и обновляет баланс в таблице `pgbench_accounts`, что позволяет оценить производительность при выполнении операций вставки и обновления с использованием индексов

Измеряли 2 метрики:
  • Количество транзакций в секунду (TPS): Сколько операций база данных может выполнить за одну секунду.
  • Время отклика запросов: Сколько времени требуется базе данных для выполнения запроса.

Коэффициент стоимости считался в руб/час/TPS.

Параметры бенчмаркинга:
  • scale factor = 200 (это значит что в базе будут 3 таблицы, по 20 млн записей, 2 тыс. записей и 200 записей соответственно).
  • Одновременные клиенты базы данных client_counts = [16, 32, 64].
  • Рабочие потоки thread_counts = [32, 64, 128].
  • run_time одной итерации = 30 секунд.

Всего для каждого провайдера было проведено 108 тестов: 3 различных client_counts * 3 различных thread_counts * 4 типа нагрузки * 3 итерации.

Где только можно, отключали репликацию и пулинг подключений. С пулингом интересный момент: он реально помогает базе данных не захлебнуться при нагрузке, но мы его специально отключали, чтобы увидеть «чистую» производительность самой СУБД без костылей.

В Timeweb такой возможности вообще не было. Так что мы до сих пор не знаем, тестировали мы их с пулингом или без.

Сберклауд и Яндекс просто не дают подключиться к базе извне их экосистемы. То есть, если у вас приложение не в их облаке, вы к своей базе данных подключиться не сможете. Занавес.

Обе этих компании демонстрировали потрясающий user experience — он был совершенно не приспособлен для обычного человека, который достаточно издалека знает, что такое сеть. По-хорошему нужно в этих сценариях с этими провайдерами приглашать нормального сетевика, который вам всё настроит. Если вы не профессиональный сетевой инженер, настроить подключение — это квест уровня «найди все тайные комнаты в Хогвартсе». Причём на старых аккаунтах всё работает совсем не так, как на новых.

На новых всё запускалось предельно плохо, непонятно, с какими-то невнятными ошибками уровня «обратитесь в поддержку, мы не можем что-то создать». Решение такое: старые аккаунты мы не удалили, но убрали из них старые организации и перевоссоздали новые организации в рамках старого аккаунта. И там всё было совершенно иначе. Вообще другой UX.

Таймвеб нас забанил тогда, когда мы начинали тестировать с локальной машины. Несколько раз мы запускали и перезапускали скрипт. Надо сказать, что мы его запускали синхронно с другими провайдерами. И в определённый момент мы увидели странное поведение от Таймвеба. Сначала посыпались ошибки, а потом скрипт сам перестал работать, потом перестал работать сайт Таймвеба с нашего публичного IP )

Мы не делали ничего необычного — просто запускали стандартный pgbench, который создаёт нагрузку не больше, чем обычное приложение. Примерно так интернет-магазин обращается к своей базе данных. На следующий день Timeweb нас разблокировал, и мы попытались запустить тесты уже с виртуальной машины внутри их сети. И тут произошло что-то странное: они сделали что-то со своим кластером managed db, что он вдруг резко стал показывать производительность гораздо выше, чем всё, что у нас было в тестировании — даже выше, чем наш собственный локально размещённый сервер GEN11 на Xeon 6430.

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

Ростелеком — наш гомерический смех продолжается! Их мы вообще исключили из теста. Потому что снова бюрократия. Мы не смогли запустить облако. Потому что база данных до сих пор на согласовании у их менеджера.

Казалось бы, 21-й век. Но нет, Ростелеком продолжает жить в эпохе «ждите звонка менеджера». Я готов предположить, что они так сделали для того, чтобы избежать потока людей с непонятной репутацией.

Результаты
Зависимость скорости транзакции к задержке


Количество транзакций в зависимости от соединений


Количество транзакций в секунду по типам нагрузки


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


Отношение стоимости к производительности


Распределение количества транзакций в секунду


Среднее количество транзакций по провайдерам


Средние задержки по провайдерам


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

ВК Cloud оказался примерно на 30–40% ниже лидеров. Это, кстати, полностью коррелирует с нашими предыдущими тестами виртуальных машин на Geekbench — похоже, у ВК просто железо послабее.

С Яндексом получилась совершенно абсурдная ситуация. Как только мы пытались подключить 64 клиента — тесты валились с ошибками. Мы просто не могли провести полноценное тестирование, отрезав весь верхний диапазон производительности. Как это интерпретировать? Да никак. Как будто вы пришли тестировать машину на автодроме, а вам говорят: «Извините, больше 60 км/ч ехать нельзя, у нас такие правила».

У большинства хостингов кривая выходит на плато или даже падает при увеличении числа соединений. А у нас для сравнения — практически линейный рост — то есть чем больше клиентов подключается к базе, тем выше общая производительность. Мы не ожидали такого эффекта. Железо действительно решает!

В результате по цене — Timeweb, несмотря на низкую абсолютную производительность, выглядит довольно неплохо. Они компенсируют технические ограничения привлекательной ценой. Как я уже говорил, VK Cloud и Яндекс — производительность не впечатляет, стоит дорого.

Среднее количество транзакций. Мы уверенно обходим всех. По цене получилось 9 тысяч рублей за кластер в месяц. Возможно, имеет смысл перетестить его )


Дисклеймер

Важный момент: все тесты мы проводили только в рабочие дни и только в дневное время.

Никаких ночных или выходных измерений.

Ограниченное количество конфигураций тестов: 3 варианта thread counts и 3 варианта client counts, только 1 вариант scale factor. Фиксированная конфигурация — тестировалась только одна конфигурация CPU/RAM/SSD (4vCPU + 16 Гб RAM + 40 Гб SSD).

Есть влияние факторов, не зависящих от работы сервиса Managed Postgres, таких как сетевая задержка, переподписка, колебания нагрузки в рабочее время у разных провайдеров. Мы минимизировали сетевые задержки за счёт проведения теста на виртуальных машинах в той же зоне доступности и подсети, что и тестируемый сервис Managed Postgres. Тесты выполнялись параллельно, где это было возможно, чтобы минимизировать влияние времени суток на нагрузку на инфраструктуру облачного провайдера; там, где было невозможно обеспечить параллельность, мы выполняли тест в другие дни в примерно одинаковое время суток.

Длительность исследования (несколько дней) и продолжительность итерации (30 секунд) могут быть недостаточны для выявления снижения производительности в долгосрочной перспективе. Мы включили 3 итерации для статистической значимости плюс 1 предварительная прогревочная итерация.

Не принимаются во внимание кастомные модификации в конфигурациях Postgres, которые могут иметь место у различных провайдеров.

Итого: тест — не измерительный инструмент, а ориентир! Результаты стоит рассматривать как примерный подход. Если вы выбираете хостинг для своего проекта, лучше провести собственное тестирование с учётом специфики вашей нагрузки.

Все скрипты и материалы нашего исследования мы выложили на GitHub, чтобы любой желающий мог воспроизвести результаты или провести свои тесты по той же методологии.

h3llo.cloud
auth.h3llo.cloud/register