DevOps для Go — где на самом деле живёт ваш сервис
Скомпилировать статический бинарник — это половина истории. В production этот бинарник не запускается напрямую: он упакован в контейнер, контейнер запущен как pod, pod размещён Kubernetes на узле, а ресурсы узла нарезаны между десятками таких pod через cgroups. Каждый слой меняет то, что видит и чувствует ваш процесс — и каждый слой даёт собственный набор ловушек.
Главное заблуждение, которое разбирает эта тема: «контейнер — это лёгкая виртуальная машина». Нет. Контейнер — это обычный процесс на ядре хоста, которому ядро дало изолированное представление через namespaces и обрезало ресурсы через cgroups. Из этого следует всё остальное: почему контейнер стартует за миллисекунды, почему ps внутри pod видит только свои процессы, почему процесс может быть убит OOM killer, когда у хоста ещё гигабайты свободны, и почему главный процесс контейнера — это PID 1 со всеми его особыми обязанностями. Когда сервис тормозит, отличить упор в CPU от упора в I/O нужно по профилю и метрикам, а не по интуиции. Тема разбирает этот слой эксплуатации сверху вниз — от контейнера до узкого места.
Карта темы
- Контейнеры — почему контейнер это процесс на ядре хоста, а не виртуальная машина с гостевой ОС.
- Linux namespaces — как ядро даёт группе процессов изолированное представление PID, сети, файловой системы и hostname.
- Стратегии масштабирования — горизонтальное добавляет экземпляры за балансировщиком, вертикальное даёт одному экземпляру больше ресурсов.
- Основы Kubernetes — оркестратор контейнеров, pod как минимальная единица, Deployment, ReplicaSet и Service.
- Cgroups — как control groups ограничивают и учитывают CPU, память и I/O группы процессов, и при чём тут OOM killer.
- Процессная модель в Kubernetes — почему главный процесс контейнера это PID 1 и какие обязанности это на него накладывает.
- Профилирование Go — как снять CPU, heap, goroutine, block и mutex профили через
pprofи разобрать ихgo tool pprof. - Анализ узких мест — как по загрузке CPU и профилю отличить CPU-bound сервис от I/O-bound.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
| Считать, что контейнер запускает собственное ядро | Неверная модель стоимости — не объяснить, почему контейнер легче VM |
| Думать, что контейнер это только формат упаковки без изоляции | Упустить роль namespaces и cgroups, которые и создают контейнер |
| Путать namespaces с cgroups | namespaces изолируют видимость, cgroups ограничивают ресурсы — это разные механизмы |
| Думать, что один namespace покрывает все ресурсы сразу | Каждый namespace изолирует один класс ресурсов ядра |
| Считать, что превышение лимита памяти просто логируется | Ядро запускает OOM killer внутри cgroup и завершает процесс |
| Думать, что pod — это всегда ровно один контейнер | pod может содержать несколько контейнеров с общим network namespace |
| Путать Deployment с pod | Deployment управляет ReplicaSet и плавающими обновлениями |
| Менять местами горизонтальное и вертикальное масштабирование | Горизонтальное добавляет экземпляры, вертикальное — ресурсы одному |
| Считать, что главный процесс контейнера — обычный PID | Это PID 1, и он обязан реапить зомби и обрабатывать SIGTERM |
| Думать, что профилирование требует особого флага сборки | Достаточно импорта net/http/pprof или вызова runtime/pprof |
| Судить о природе нагрузки только по задержке | Без загрузки CPU и профиля не отличить CPU-bound от I/O-bound |
Значение для собеседований
DevOps-вопросы на Go-интервью проверяют не знание команд kubectl, а понимание, на каком слое инфраструктуры живёт ваш процесс и какие из этого следствия. Интервьюер хочет видеть рабочую ментальную модель: контейнер → namespaces + cgroups → pod → Kubernetes.
Что обычно проверяют:
- Чем контейнер отличается от виртуальной машины — общее ядро хоста против полной гостевой ОС.
- Что изолируют Linux namespaces и что ограничивают cgroups — это два разных механизма.
- Что происходит при превышении лимита памяти cgroup — OOM killer внутри группы.
- Что такое pod, Deployment, ReplicaSet и Service в Kubernetes.
- Чем горизонтальное масштабирование отличается от вертикального и что каждое требует.
- Почему главный процесс контейнера — PID 1 и какие обязанности это накладывает.
- Как профилировать Go-сервис в production через
pprofи как отличить CPU-bound от I/O-bound.
Типичный неверный ответ: «контейнер — это виртуальная машина, просто полегче». Это запускает разбор того, что контейнер не имеет собственного ядра и гипервизора, а является обычным процессом, которому ядро хоста дало изолированное представление через namespaces и обрезало ресурсы через cgroups.