1. Обзор раздела. Среда выполнения backend

Linux, Docker, Kubernetes

«Единственный способ выучить новый язык — писать на нем программы. А единственный способ понять, где живет ваша программа, — досконально изучить операционную систему».
— Брайан Керниган

Раздел инфраструктуры — это тот самый фундаментальный мост между вашим исходным кодом на Go и реальным кремниевым железом, на котором он исполняется в продакшене. Можно писать безупречный идиоматичный код, виртуозно оптимизировать аллокации памяти и настраивать планировщик, но если вы не понимаете, как ваша программа взаимодействует с операционной системой, вы неизбежно столкнетесь с необъяснимыми аномалиями: от необъяснимых миллисекундных сетевых задержек до внезапных падений процессов по OOM (Out of Memory).

Операционная система Linux — это де-факто стандартная среда выполнения для современного бэкенда. Независимо от того, деплоите ли вы скомпилированный бинарник на выделенный сервер (Bare Metal), упаковываете ли его в легковесный Docker-контейнер или оркестрируете сотнями реплик в кластере Kubernetes, под капотом всегда работает ядро Linux.

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


Go и Linux: Идеальный брак

Если вы переходите в Go из мира Java, C# или Python, вы привыкли к тяжелым рантаймам и виртуальным машинам (JVM, CLR, CPython), которые намеренно возводят массивную стену абстракций между кодом и операционной системой. Язык Go пошел принципиально другим путем. Он не пытается построить виртуальный компьютер поверх ОС. Вместо этого Go напрямую опирается на возможности ядра Linux, используя их с предельной механической эффективностью (Mechanical Sympathy).

Главный козырь Go — это статическая компиляция. Ваш Go-код компилируется в native ELF-исполняемый файл (Executable and Linkable Format), который не требует внешних зависимостей (библиотек glibc, libc++ и т.д.) для своего запуска, если вы собираете его с флагом CGO_ENABLED=0.

CGO и ловушка динамической линковки

Как только вы импортируете пакет, использующий CGO (например, драйверы БД вроде go-sqlite3, или обертки над C-библиотеками), всё меняется.


Среда выполнения: Слои абстракции

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

flowchart TD
    A["Go Application Code"] --> B["Go Runtime"]
    B --> C{"CGO Enabled?"}
    C -->|"Нет - CGO_ENABLED=0"| D["Прямые Syscalls в ядро"]
    C -->|"Да - CGO_ENABLED=1"| E["glibc или musl"]
    E --> D
    D --> F["Ядро Linux - Kernel Space"]
    F --> G["Железо - CPU, RAM, NIC, Disk"]

    H["Docker Engine"] -.-> I["Namespaces - Изоляция"]
    H -.-> J["Cgroups - Лимиты ресурсов"]
    I -.-> A
    J -.-> F

    style A fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#fff
    style B fill:#312e81,stroke:#818cf8,stroke-width:2px,color:#fff
    style F fill:#881337,stroke:#f43f5e,stroke-width:2px,color:#fff
    style G fill:#0f172a,stroke:#475569,stroke-width:1px,color:#fff
    style H fill:#78350f,stroke:#f59e0b,stroke-width:2px,color:#fff
  

Как видно из диаграммы, Docker (и Kubernetes) не добавляют новые слои между вашим кодом и ядром. Они лишь используют механизмы изоляции Linux — Namespaces (чтобы процесс думал, что он один в системе) и Cgroups (чтобы ядро ограничивало потребление CPU и памяти для этого процесса).

Это кардинально отличается от виртуальных машин (VM), где поверх железа поднимается гипервизор, а внутри него — полноценная гостевая ОС со своим ядром. Контейнеры — это просто изолированные процессы на хостовой ОС. Для Go-разработчика это значит, что системный вызов из контейнера напрямую попадает в ядро хоста, без виртуализации железа.


Mechanical Sympathy: Ядро и ваши горутины

Go-планировщик (G-M-P) работает в User Space, управляя горутинами. Но сами потоки ОС (M), на которых исполняются горутины, планирует ядро Linux.

Вам важно понимать, как ядро видит ваш Go-процесс:

  1. Потоки (OS Threads): Рантайм Go создает потоки ОС по мере необходимости (до значения GOMAXPROCS для executing threads, и больше для блокирующихся в syscalls). Для ядра Linux каждый такой поток — это обычный task_struct, который ставится в очередь на выполнение на логических ядрах CPU.
  2. Системные вызовы: Когда горутина делает сетевой запрос, Go-рантайм использует асинхронный интерфейс Linux — epoll (через Net Poller). Это позволяет одному потоку ОС ожидать завершения тысяч сетевых операций, не блокируясь.
  3. Файловый IO: А вот чтение с диска (file IO) в Go по умолчанию блокирующее (syscall read/pread). Рантайму приходится отдавать блокирующемуся потоку M горутину G и создавать/переиспользовать другой поток M, чтобы не остановить работу планировщика (механизм Hand-off).

Что мы будем разбирать в этом разделе

Чтобы писать надежный бэкенд на Go, нужно понимать правила игры, которые задает Linux и инфраструктура. Мы последовательно разберем:

  1. Процессы и сигналы: Как Graceful Shutdown работает на уровне ОС, почему SIGTERM и SIGINT — ваши лучшие друзья, а SIGKILL — враг.
  2. Файловая система: Что такое Inodes, жесткие и символические ссылки, и почему запись в файл в Go требует понимания буферизации на уровне ОС.
  3. Права доступа: Модель безопасности Linux (UID/GID) и почему контейнеры не стоит запускать от root.
  4. Systemd: Как управлять Go-сервисами на "голом" железе и настраивать автозапуск.
  5. Networking: Как работают сокеты, порты, epoll и маршрутизация внутри ядра Linux.

Инфраструктура — это не просто конфиги DevOps-инженеров. Это продолжение вашего кода в продакшене. Понимание её устройства отличает разработчика, пишущего код "который работает на моей машине", от инженера, создающего системы, которые стабильно держат нагрузку в проде.

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