Перейти к основному содержимому

Intern DevOps Andersen - Стажировка / Реальное собеседование.

· 118 мин. чтения

Сегодня мы разберем собеседование начинающего DevOps-специалиста, который пришел из IT-аудита и обучения в сторону практической инженерии, и честно демонстрирует текущий уровень: базовое понимание Docker, Linux, SQL и CI/CD при существенных пробелах в сетях, автоматизации и облаках. Интервьюер последовательно вскрывает эти зоны роста, параллельно давая структурированный фидбек и дорожную карту развития, что делает это интервью ценным разбором реальных требований к джуну и типичных ошибок на старте.

Вопрос 1. Опишите профильное образование и релевантный опыт, связанный с DevOps или IT.

Таймкод: 00:00:36

Ответ собеседника: правильный. Закончил технический университет по информационным системам, начинал как Python-разработчик, затем работал IT-аудитором в KPMG, а с января изучает DevOps через бесплатные курсы и грантовую программу по облакам.

Правильный ответ:

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

  • Образование:

    • Технический университет по направлению, связанному с информационными системами / компьютерными науками.
    • Фокус на:
      • алгоритмах и структурах данных;
      • сетевых технологиях и моделях взаимодействия (TCP/IP, DNS, HTTP);
      • операционных системах (память, процессы, планирование, файловые системы);
      • базах данных (SQL, транзакции, индексы, нормализация).
  • Опыт разработки:

    • Коммерческий или проектный опыт на Python (или другом языке), включающий:
      • написание сервисов и утилит;
      • работу с API, логированием, обработкой ошибок;
      • тестирование (unit/integration tests), CI-инструменты в базовом виде;
      • понимание принципов написания поддерживаемого кода.
    • Этот опыт важен для DevOps, так как позволяет:
      • читать и улучшать код приложений;
      • автоматизировать рутинные задачи (скрипты деплоя, миграции, проверки);
      • эффективнее выстраивать взаимодействие между разработкой и инфраструктурой.
  • Опыт в области процессов и инфраструктуры:

    • Работа в IT-аудите (например, KPMG или аналогичные компании):
      • анализ IT-процессов, рисков, информационной безопасности;
      • понимание требований к надежности, отказоустойчивости, контролям доступа, логированию;
      • участие в проверке соответствия индустриальным стандартам (ISO 27001, SOC, внутренние политики).
    • Это даёт сильное понимание:
      • как должна выглядеть зрелая инфраструктура;
      • какие риски учитываются при построении CI/CD и cloud-архитектур;
      • почему важны наблюдаемость, трассировка, аудит действий.
  • Обучение DevOps и облакам:

    • Системное обучение с начала года:
      • основы Linux (shell, systemd, сети, права, мониторинг ресурсов);
      • контейнеризация (Docker): создание образов, оптимизация Dockerfile, работа с registry;
      • оркестрация (начальный/продвинутый уровень Kubernetes): deployment, services, ingress, configmap/secret, probes, ресурсы и лимиты;
      • CI/CD: GitLab CI/GitHub Actions/Jenkins – пайплайны, автотесты, сборка образов, деплой;
      • работа с облаками (AWS / GCP / Azure / локальные провайдеры):
        • базовые сервисы: compute, storage, сети, managed DB;
        • принципы инфраструктуры как код (Terraform, Ansible и др.).
    • Практика:
      • разворачивание тестовых окружений;
      • настройка пайплайнов для сборки и деплоя;
      • observability: базовое использование Prometheus, Grafana, ELK/Opensearch.

Такой путь формирования компетенций логичен для DevOps-направления: есть фундаментальная техничка, опыт разработки, понимание корпоративных стандартов и процессов, плюс целенаправленное погружение в инструменты автоматизации, контейнеризацию, облачную инфраструктуру и CI/CD.

Вопрос 2. На каком языке программирования вы разрабатывали ранее?

Таймкод: 00:02:32

Ответ собеседника: правильный. Указывает, что занимался разработкой голосовых ботов на Python.

Правильный ответ:

В предыдущем опыте разработки основным языком был Python. Работа включала:

  • создание серверной логики и интеграций:
    • обработка входящих запросов (HTTP, Webhook, события от телефонии/платформ ботов);
    • интеграция с внешними API (CRM, биллинг, аналитика, NLU-сервисы);
  • разработку голосовых ботов:
    • взаимодействие с провайдерами телефонии (SIP, WebRTC, Twilio/аналогичные сервисы);
    • использование библиотек/SDK для распознавания и синтеза речи;
    • реализация сценариев диалогов, обработка состояний, логирование и метрики.

Такой опыт важен, потому что:

  • формирует навыки проектирования сервисов и API;
  • развивает дисциплину в обработке ошибок, логировании, тестировании;
  • даёт понимание, как приложение ведет себя в продакшене, что напрямую помогает в дальнейшем при построении инфраструктуры, CI/CD и наблюдаемости.

Вопрос 3. Что такое DevOps и какова его основная идея?

Таймкод: 00:02:55

Ответ собеседника: неполный. Описывает DevOps как набор практик для автоматизации и ускорения доставки кода в продакшн, где специалисты настраивают среду для успешного деплоя.

Правильный ответ:

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

Основные идеи DevOps:

  1. Совместная ответственность за продукт

    • Разработчики, инфраструктурные инженеры, QA, безопасность и бизнес работают как единая команда.
    • Нет модели «мы написали — вы поддерживайте». Те, кто пишет код, участвуют в его продакшн-жизни: метрики, алерты, производительность, надежность.
    • Это снижает конфликт интересов: разработка хочет быстро релизить, эксплуатация — стабильность. DevOps выстраивает процессы так, чтобы можно было и быстро, и надежно.
  2. Автоматизация всего жизненного цикла

    • Цель — минимизировать ручные операции, которые:
      • медленные;
      • подвержены ошибкам;
      • плохо масштабируются.
    • Ключевые зоны автоматизации:
      • сборка и тестирование кода;
      • создание артефактов (бинарники, Docker-образы);
      • деплой на разные окружения;
      • миграции баз данных;
      • конфигурация инфраструктуры и сервисов.
    • Типичный pipeline:
      • commit → автоматические тесты → сборка образа → security checks → деплой в staging → автоматические/ручные проверки → деплой в production.
  3. Непрерывная интеграция и доставка (CI/CD)

    • Непрерывная интеграция:
      • частые слияния кода в основную ветку;
      • автоматический запуск тестов и проверок качества.
    • Непрерывная доставка/деплой:
      • возможность доставить изменение в продакшн в любой момент кнопкой или автоматически;
      • минимальный lead time от коммита до релиза.
    • Преимущества:
      • быстрый feedback loop;
      • меньший размер изменений → меньше рисков;
      • предсказуемые, повторяемые релизы.
  4. Инфраструктура как код (IaC)

    • Инфраструктура описывается декларативно в коде (Terraform, Ansible и др.).
    • Это:
      • делает окружения воспроизводимыми;
      • позволяет ревьюить изменения инфраструктуры так же, как код приложения;
      • уменьшает «дрейф конфигураций» между dev/stage/prod;
      • упрощает масштабирование и disaster recovery.
  5. Наблюдаемость, измеримость и надежность

    • DevOps предполагает, что система спроектирована так, чтобы её можно было измерять:
      • метрики (latency, RPS, error rate, resource usage);
      • логи;
      • трассировка (distributed tracing).
    • Наличие SLI/SLO/SLA, алертинга и дэшбордов.
    • Решения принимаются на основе данных, а не только «по ощущениям».
    • Инциденты анализируются через postmortem, чтобы улучшать архитектуру и процессы.
  6. Безопасность как часть процесса (DevSecOps)

    • Security интегрирована в pipeline:
      • static analysis (SAST);
      • dependency scanning;
      • проверка образов контейнеров;
      • политики доступа и секретов.
    • В результате безопасность не тормозит релизы, а становится автоматизированной и предсказуемой.

Коротко: DevOps — это про то, как сделать так, чтобы изменения, написанные разработчиками, попадали в продакшн часто, быстро, безопасно и воспроизводимо, за счет автоматизации, инфраструктуры как кода, прозрачности и общей ответственности команды за весь жизненный цикл сервиса.

Вопрос 4. Какие процессы автоматизируются при доставке кода и как это связано с CI/CD?

Таймкод: 00:03:31

Ответ собеседника: неполный. Упоминает автоматическую сборку, тестирование, развертывание, использование контейнеризации, оркестрации и IaC, и называет это CI/CD.

Правильный ответ:

Под CI/CD обычно понимают связанную цепочку автоматизации всего пути изменения кода от коммита до продакшна. Важно не только перечислить этапы, но и понимать их цели, типичные практики и инструменты.

Основные компоненты:

  1. Непрерывная интеграция (CI) Цель: максимально рано находить ошибки, интеграционные проблемы и нарушения стандартов качества.

    Ключевые шаги:

    • Триггер: каждый commit / merge request.
    • Сборка:
      • Компиляция (для Go — go build ./...).
      • Формирование артефактов (бинарник, Docker-образ).
    • Автоматические проверки:
      • линтеры и форматирование (Go пример):
        • gofmt, go vet, golangci-lint run
      • модульные и интеграционные тесты:
        • go test ./... -race -cover
    • Результат:
      • если пайплайн зеленый — изменение считается технически готовым для продвижения дальше;
      • если красный — разработчик сразу получает фидбек.

    Пример простого CI-конфига (GitHub Actions для Go):

    name: ci
    on: [push, pull_request]

    jobs:
    test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-go@v5
    with:
    go-version: '1.22'
    - name: Install deps
    run: go mod download
    - name: Lint
    run: golangci-lint run ./...
    - name: Test
    run: go test ./... -race -coverprofile=coverage.out
  2. Непрерывная доставка и деплой (CD) Цель: сделать выкладку предсказуемой, повторяемой и по возможности автоматической.

    Типичные этапы:

    • Сборка и публикация артефактов:
      • создание Docker-образа;
      • пуш в registry (например, registry.example.com/app:git-sha).
    • Деплой на окружения:
      • dev → stage → prod (по правилам и политикам).
    • Стратегии деплоя:
      • rolling update;
      • blue-green;
      • canary;
      • feature flags.
    • Автоматические проверки после деплоя:
      • smoke-тесты;
      • health-check (liveness/readiness);
      • проверка метрик ошибок/латентности.

    Пример части CD-пайплайна (условно GitLab CI) для публикации образа и деплоя в Kubernetes:

    build_image:
    stage: build
    script:
    - docker build -t registry.example.com/app:$CI_COMMIT_SHA .
    - docker push registry.example.com/app:$CI_COMMIT_SHA

    deploy_staging:
    stage: deploy
    environment: staging
    script:
    - kubectl set image deployment/app app=registry.example.com/app:$CI_COMMIT_SHA -n staging
    when: manual

    deploy_prod:
    stage: deploy
    environment: production
    script:
    - kubectl set image deployment/app app=registry.example.com/app:$CI_COMMIT_SHA -n prod
    when: manual
    needs: ["deploy_staging"]
  3. Контейнеризация и оркестрация как часть CI/CD

    • Контейнеризация (Docker/Podman):
      • единый, предсказуемый runtime;
      • один и тот же образ на dev/stage/prod.
    • Оркестраторы (Kubernetes, Nomad и др.):
      • декларативный деплой (Deployment, Service, Ingress);
      • масштабирование, рестарты, rollout/rollback.
    • Эти инструменты идеально сочетаются с CI/CD:
      • пайплайн собирает образ;
      • манифесты/Helm-чарты/операторы описывают целевое состояние;
      • CD-инструмент (Argo CD, Flux, Jenkins X и др.) приводит кластер к этому состоянию.
  4. Инфраструктура как код (IaC) в контуре CI/CD

    • Инфраструктура (VPC, подсети, БД, очереди, балансировщики) описана кодом (Terraform, Pulumi, Ansible).
    • Изменения инфраструктуры проходят тот же процесс:
      • review → pipeline (fmt/validate/plan) → утверждение → apply.
    • Это:
      • устраняет «ручные настройки на проде»;
      • делает окружения воспроизводимыми;
      • уменьшает риск расхождений между stage/prod.
  5. Что в сумме называется CI/CD

    • CI: автоматическая проверка качества и собираемости кода при каждом изменении.
    • CD: автоматизированная, повторяемая, контролируемая доставка артефактов до продакшна.
    • Ценность:
      • быстрые, частые и безопасные релизы;
      • прозрачный trace: какой commit, кем одобрен, какие проверки прошел, куда задеплоен;
      • минимизация человеческого фактора за счет автоматизации.

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

Вопрос 5. Что такое Continuous Integration (CI) и какие этапы CI-пайплайна стоит реализовать для проекта?

Таймкод: 00:03:55

Ответ собеседника: неполный. Описывает CI как частую интеграцию изменений в общий репозиторий для поддержания стабильности кода, упоминает Git Flow. Для пайплайна называет автотесты и сборку, но без четкой структуры и детализации видов проверок.

Правильный ответ:

Continuous Integration — это практика, при которой разработчики регулярно (часто — несколько раз в день) интегрируют изменения в общую ветку репозитория, а каждое такое изменение автоматически проверяется конвейером: сборкой, тестами, анализом качества и безопасности. Цель CI — обнаруживать проблемы максимально рано, делать изменения маленькими и контролируемыми, обеспечить уверенность, что основная ветка всегда находится в рабочем состоянии.

Ключевые принципы CI:

  • Частые коммиты и merge в основную ветку (или trunk/develop в зависимости от стратегии).
  • Каждый push и каждый merge request/PR запускает полный или релевантный набор автоматических проверок.
  • При падении пайплайна основная ветка не должна считаться «здоровой», такие ситуации устраняются приоритетно.
  • CI должен быть быстрым и детерминированным: разработчик оперативно получает понятный фидбек.

Для практического уровня важно уметь спроектировать осмысленный CI-пайплайн. Ниже пример для Go-проекта (подход применим и к другим языкам).

Рекомендуемые этапы CI-пайплайна:

  1. Проверка синтаксиса, форматирование и статический анализ Цель: обеспечить базовую корректность и единый стиль кода до запуска тяжелых тестов.

    Для Go:

    • gofmt — форматирование кода.
    • go vet — встроенный статический анализатор.
    • golangci-lint — объединённый набор линтеров (ошибки, код-стайл, потенциальные баги).

    Пример шагов:

    gofmt -w ./...
    go vet ./...
    golangci-lint run ./...
  2. Управление зависимостями Цель: гарантировать воспроизводимость сборки.

    • Проверка go.mod / go.sum:
    go mod tidy
    go mod verify
    • Можно добавить проверку, что после go mod tidy нет изменений в git.
  3. Сборка (build) Цель: убедиться, что код компилируется во всей кодовой базе, а не только локально.

    go build ./...
  4. Модульные тесты (unit tests) Цель: быстро проверить корректность бизнес-логики на небольших изолированных участках.

    • Обязательные:
    go test ./... -race -coverprofile=coverage.out
    • Важные аспекты:
      • использование -race для выявления data races;
      • контроль покрытия (например, fail, если coverage < порога).
  5. Интеграционные тесты Цель: проверить взаимодействие между компонентами: БД, очереди, внешние сервисы, API.

    Подход:

    • запуск тестовой инфраструктуры через Docker Compose:
      • PostgreSQL/Redis/Kafka/etc;
    • использование build tags или отдельной директории:
      • go test -tags=integration ./tests/integration/...
    • примеры:
      • проверки реальных запросов к HTTP API;
      • проверки работы с реальной БД (schema + миграции).

    Пример docker-compose для интеграционных тестов:

    version: '3.9'
    services:
    db:
    image: postgres:16
    environment:
    POSTGRES_USER: test
    POSTGRES_PASSWORD: test
    POSTGRES_DB: app
    ports:
    - "5433:5432"
  6. Проверка миграций и схемы БД Цель: убедиться, что изменения схемы непротиворечивы и применимы.

    • Использование миграционных инструментов (golang-migrate / liquibase / flyway).
    • В CI:
      • поднять тестовую БД;
      • применить миграции;
      • прогнать тесты поверх мигрированной схемы.

    SQL-пример миграции:

    CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    email TEXT NOT NULL UNIQUE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
    );
  7. Анализ безопасности Цель: поймать уязвимости до продакшена.

    Для Go:

    • gosec для анализа кода.
    • Проверка зависимостей на CVE (govulncheck, встроенные средства CI/CD или SCA-инструменты).

    Для контейнеров:

    • сканирование Docker-образов (Trivy/Grype).
  8. Сборка и публикация артефактов (на границе CI/CD) Цель: подготовить то, что позже будет деплоиться.

    • Сборка бинарников или Docker-образа:
    docker build -t registry.example.com/app:${GIT_COMMIT_SHA} .
    • Публикация в registry/артефакт-репозиторий (как правило делается после успешного прохождения всех тестов).

Пример минимального CI (GitHub Actions) для Go-проекта:

name: ci

on:
pull_request:
push:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: '1.22'

- name: Download deps
run: go mod download

- name: Lint
run: |
gofmt -w .
go vet ./...

- name: Unit tests
run: go test ./... -race -coverprofile=coverage.out

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

Вопрос 6. Какие виды тестов имеет смысл включать в CI-процесс?

Таймкод: 00:05:21

Ответ собеседника: неполный. Говорит, что подробно не изучал, вскользь упоминает интеграционные тесты, без структуры и перечисления основных типов.

Правильный ответ:

Для зрелого CI-процесса важно не просто «запускать тесты», а выстроить многоуровневую пирамиду проверок: от быстрых и дешевых до более тяжелых и приближенных к боевым условиям. Ниже — структурированный перечень ключевых типов тестов, которые часто включают в CI для backend-сервисов и, в частности, Go-проектов.

Основные типы тестов:

  1. Юнит-тесты (Unit tests)

    • Цель:
      • проверяют отдельные функции, методы, небольшие компоненты в изоляции.
    • Характеристики:
      • быстрые (сотни/тысячи тестов за секунды/минуты);
      • не зависят от реальных внешних сервисов (БД, очереди, HTTP API — замоканы).
    • В CI:
      • запускаются на каждый commit/PR;
      • обязательное условие прохождения пайплайна.
    • Go-пример:
      func Sum(a, b int) int {
      return a + b
      }

      func TestSum(t *testing.T) {
      got := Sum(2, 3)
      if got != 5 {
      t.Fatalf("expected 5, got %d", got)
      }
      }
  2. Интеграционные тесты (Integration tests)

    • Цель:
      • проверить взаимодействие нескольких компонентов системы между собой:
        • сервис + база данных,
        • сервис + message broker,
        • сервис + внешний HTTP API (часто через локальный mock-сервер).
    • Характеристики:
      • медленнее, чем unit;
      • могут использовать реальные экземпляры сервисов (через Docker Compose).
    • В CI:
      • запускаются для веток, PR, перед merge;
      • часто отделены по тегам (-tags=integration) или директориям.
    • Go + SQL пример:
      // SQL миграция
      // CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT UNIQUE NOT NULL);

      func TestCreateUser(t *testing.T) {
      db := connectTestDB(t) // тестовая БД в Docker
      defer db.Close()

      email := "user@example.com"
      var id int
      err := db.QueryRow(
      `INSERT INTO users (email) VALUES ($1) RETURNING id`, email,
      ).Scan(&id)
      if err != nil {
      t.Fatalf("insert failed: %v", err)
      }
      }
  3. Контрактные тесты (Contract tests)

    • Цель:
      • гарантировать совместимость между сервисами (producer/consumer).
    • Примеры:
      • для REST/gRPC: проверка, что сервис отвечает согласно описанному контракту (OpenAPI/Proto);
      • для событий: формат сообщений в Kafka/RabbitMQ соответствует ожиданиям потребителей.
    • В CI:
      • позволяют независимо эволюционировать сервисы, не ломая интеграции;
      • часто используются в microservices-архитектуре.
  4. API / Component / Functional tests

    • Цель:
      • проверка функциональных сценариев на уровне приложения:
        • регистрация пользователя;
        • создание заказа;
        • авторизация.
    • Характеристики:
      • работают либо:
        • против поднятого локально сервиса (в Docker),
        • либо используют in-memory HTTP server.
    • В CI:
      • применимы как интеграционные/системные проверки ключевых флоу.
    • Go-пример (ин-memory HTTP):
      func TestHealthCheck(t *testing.T) {
      srv := setupServer() //router
      req := httptest.NewRequest("GET", "/health", nil)
      w := httptest.NewRecorder()

      srv.ServeHTTP(w, req)

      if w.Code != http.StatusOK {
      t.Fatalf("expected 200, got %d", w.Code)
      }
      }
  5. End-to-End (E2E) тесты

    • Цель:
      • проверить реальный пользовательский сценарий «от края до края»:
        • фронт → API → БД → внешние сервисы.
    • Характеристики:
      • самые дорогие и медленные;
      • чувствительны к нестабильности окружения.
    • В CI:
      • обычно:
        • запускаются реже (например, nightly, перед релизом или по тегу);
        • выполняются в отдельном пайплайне или стадии.
    • Важны для критичных фич, но не должны блокировать быстрый цикл CI.
  6. Регрессионные тесты

    • Цель:
      • зафиксировать и не допустить повторного появления уже найденных багов.
    • Фактически это обычные unit/integration/E2E тесты, но с акцентом на прошлые инциденты.
    • В CI:
      • интегрируются в общий набор автотестов;
      • каждая найденная и исправленная критичная ошибка должна получать тест.
  7. Тесты производительности и нагрузочные тесты (Performance/Load tests)

    • Цель:
      • измерить latency, throughput, потребление ресурсов;
      • проверить поведение под нагрузкой и деградацию.
    • В CI:
      • редко гоняются на каждый commit;
      • могут запускаться:
        • на merge в main;
        • по расписанию;
        • перед релизом.
    • Инструменты:
      • k6, JMeter, vegeta и т.д.
    • Пример (vegeta для API):
      echo "GET http://service/health" | vegeta attack -duration=30s -rate=100 | vegeta report
  8. Безопасностные тесты (Security scans)

    • Цель:
      • найти уязвимости до продакшена.
    • Виды:
      • SAST (статический анализ кода);
      • DAST (динамическое тестирование API);
      • сканирование зависимостей и Docker-образов.
    • В CI:
      • часть стандартного пайплайна или отдельная security-стадия.
  9. Линтинг и style checks (формально не «тесты», но обязательны в CI)

    • Цель:
      • обеспечить единый стиль, читабельность, избежать типичных ошибок.
    • Включаются как ранние шаги пайплайна, чтобы быстро падать на простых нарушениях.

Как комбинировать в реальном CI:

  • На каждый commit / PR:
    • линтеры;
    • unit-тесты;
    • базовые интеграционные/API-тесты.
  • На merge в main / release-ветку:
    • расширенные интеграционные тесты;
    • контрактные тесты;
    • базовые security scans.
  • Периодически / по запросу:
    • нагрузочные тесты;
    • глубокие security-проверки;
    • полные E2E.

Ключевая идея: CI должен давать быструю и надежную обратную связь. Легкие тесты — рано и часто, тяжелые — осознанно и ближе к релизным точкам. Такой подход минимизирует риски и не тормозит разработку.

Вопрос 7. Что такое Continuous Delivery и Continuous Deployment, и в чем их отличие?

Таймкод: 00:05:46

Ответ собеседника: неправильный. Путает CD с continuous development, даёт общее описание автоматизации релизов, не различает delivery и deployment и признаёт затруднения.

Правильный ответ:

В контексте DevOps и pipeline-автоматизации CD имеет два конкретных, разных значения:

  • Continuous Delivery (не development)
  • Continuous Deployment

Оба основаны на CI и автоматизации, но различаются тем, как именно изменения попадают в продакшн.

Основы:

  • Continuous Delivery и Continuous Deployment всегда предполагают, что:
    • есть настроенный CI (сборка, тесты, проверки качества);
    • артефакты (бинарники, Docker-образы, манифесты) создаются автоматически и воспроизводимо;
    • деплой управляется кодом и автоматикой, а не ручными шагами «по инструкции в wiki».

Разберём по порядку.

Continuous Delivery (CD — непрерывная поставка):

Суть:

  • Каждый успешно прошедший CI-коммит автоматически:
    • собирается;
    • проходит тесты и проверки;
    • подготавливается к деплою в production;
    • может быть выкачен в продакшн по одной понятной, быстрой и надежной операции.
  • Но финальное решение «катить в production прямо сейчас» — за человеком или отдельным контролируемым процессом.

Ключевые черты:

  • Продакшн-деплой:
    • запускается вручную (кнопка, команда, approve в pipeline) или по формальной процедуре (change management).
  • Важное требование:
    • в любой момент времени main/release-ветка:
      • деплоябельна;
      • проверена тестами;
      • готова к выпуску.
  • Основная цель:
    • минимизировать «разрыв» между разработкой и релизом;
    • убрать ручные, нестабильные, неформализованные шаги.

Пример сценария:

  • Разработчик делает merge в main.
  • CI:
    • запускает юнит/интеграционные/контрактные тесты;
    • собирает Docker-образ app:<commit-sha>;
    • пушит образ в registry;
    • генерирует/обновляет манифесты/Helm values.
  • Статус: "build X готов к деплою".
  • Техлид/дежурный:
    • нажимает "Deploy to production";
    • CD-система (Argo CD, GitLab CI, Jenkins и т.п.) откатывает/раскатывает автоматически по декларативной конфигурации.

Continuous Deployment (CD — непрерывный деплой):

Суть:

  • Расширение идеи Continuous Delivery.
  • Каждое изменение, прошедшее пайплайн (все автотесты, проверки качества, безопасности), автоматически деплоится в production без ручного подтверждения.

Ключевые черты:

  • Нет ручной кнопки «деплой в прод».
  • Всё, что прошло автоматические проверки, считается достаточно надёжным, чтобы сразу попасть к пользователям.
  • Очень высокие требования:
    • зрелая тестовая пирамида (unit, integration, e2e, security);
    • хороший monitoring/alerting;
    • безопасные стратегии деплоя (canary, blue-green, feature flags);
    • способность быстро откатывать (rollback / roll-forward).

Пример сценария:

  • Merge в main.
  • CI:
    • тесты, линтеры, security, сборка образа.
  • CD:
    • автоматически берет свежий образ;
    • обновляет production (например, через GitOps или deployment job);
    • применяет стратегию деплоя (rolling/canary);
    • при проблемах авто-rollback.

Ключевое отличие:

  • Continuous Delivery:

    • изменение:
      • автоматически готовится к релизу,
      • но релиз в продакшн требует явного ручного шага (approve/кнопка/процедура).
    • Формула: "Готовы к релизу всегда, выкладываем по решению людей".
  • Continuous Deployment:

    • изменение:
      • после успешного пайплайна автоматически попадает в прод;
      • человеческое участие — только при инцидентах или настройке политик.
    • Формула: "Каждый зеленый билд сам доезжает до продакшна".

Применительно к Go-сервису и Kubernetes (упрощенный пример):

Continuous Delivery (ручное prod):

# Фрагмент GitLab CI

stages: [test, build, deploy_staging, deploy_prod]

deploy_staging:
stage: deploy_staging
script:
- kubectl set image deployment/app app=registry/app:$CI_COMMIT_SHA -n staging

deploy_prod:
stage: deploy_prod
when: manual # ручное подтверждение
script:
- kubectl set image deployment/app app=registry/app:$CI_COMMIT_SHA -n prod

Continuous Deployment (автоматический prod):

deploy_prod:
stage: deploy_prod
script:
- kubectl set image deployment/app app=registry/app:$CI_COMMIT_SHA -n prod
only:
- main # каждый успешный коммит в main автоматически в прод

Итого:

  • CI: убедиться, что код собирается, тесты проходят, качество ок.
  • Continuous Delivery: всегда иметь готовый к релизу артефакт; продакшн-деплой — контролируемый, но автоматизированный (одна безопасная операция).
  • Continuous Deployment: полностью автоматический путь от коммита до продакшна; человеческий фактор — только в настройках процессов и разборе инцидентов.

Вопрос 8. Что такое SDLC и основные этапы жизненного цикла разработки ПО?

Таймкод: 00:07:20

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

Правильный ответ:

SDLC (Software Development Life Cycle) — это формализованный жизненный цикл создания и развития программного продукта: от идеи и требований до эксплуатации, поддержки и вывода из использования. Грамотное понимание SDLC важно не только для менеджеров, но и для разработчиков и инженеров инфраструктуры: оно определяет, какие решения мы принимаем на уровне архитектуры, кода, CI/CD, тестирования и эксплуатации.

Классически SDLC состоит из этапов (модель может быть гибкой: waterfall, iteractive, agile, spiral, но сами фазы в том или ином виде присутствуют всегда):

  1. Анализ требований (Requirements Engineering)

    • Цели:
      • понять бизнес-задачу, ограничения и критерии успеха;
      • сформировать функциональные и нефункциональные требования.
    • Нефункциональные требования особенно важны:
      • производительность (RPS, latency);
      • надежность и доступность (SLA, SLO);
      • безопасность (аутентификация, авторизация, шифрование, аудит);
      • масштабируемость;
      • соответствие стандартам (GDPR, PCI DSS и т.п.).
    • Результат:
      • спецификации требований, user stories, use cases, acceptance criteria.
  2. Планирование

    • Цели:
      • оценить объем работ, бюджет, сроки, риски;
      • выбрать модель процесса (Scrum, Kanban, релизные циклы);
      • определить ключевые технические решения на верхнем уровне.
    • Важные моменты:
      • оценка рисков интеграций с внешними системами;
      • план по качеству: какие типы тестирования, как устроен CI/CD;
      • план по observability и SRE-практикам.
  3. Проектирование архитектуры и инфраструктуры (System Design)

    • Цели:
      • спроектировать архитектуру системы в соответствии с требованиями;
      • определить границы сервисов, контракты, схемы данных.
    • Сюда входят:
      • выбор архитектурного стиля (монолит, модульный монолит, микросервисы, event-driven);
      • API-контракты (REST/gRPC, схемы сообщений);
      • проектирование базы данных и схем (SQL/NoSQL, миграционная стратегия);
      • архитектура отказоустойчивости: репликация, шардирование, кэширование, очереди.
    • Инфраструктура:
      • target-платформа (on-prem, облако, Kubernetes, bare metal);
      • сеть, балансировщики, секреты, логирование, мониторинг;
      • подход IaC (Terraform, Ansible, Helm, Kustomize).
    • Пример: проектирование схемы в SQL
      CREATE TABLE orders (
      id BIGSERIAL PRIMARY KEY,
      user_id BIGINT NOT NULL,
      status TEXT NOT NULL,
      total_amount NUMERIC(10,2) NOT NULL,
      created_at TIMESTAMPTZ NOT NULL DEFAULT now()
      );
      CREATE INDEX idx_orders_user_id ON orders(user_id);
  4. Проектирование интерфейсов (UI/UX) при необходимости

    • Для пользовательских продуктов:
      • прототипы интерфейсов;
      • UX-потоки;
      • согласование с бизнесом.
    • Для backend-фокуса:
      • чётко задокументированные API (OpenAPI/Swagger, Protobuf схемы).
  5. Разработка (Implementation)

    • Реализация функциональности в соответствии с архитектурой.
    • Ключевые практики:
      • чистый и читаемый код;
      • SOLID, KISS, DRY, явные интерфейсы;
      • покрытие критичных участков тестами;
      • feature flags, конфигурация через окружение.
    • Go-пример: HTTP-сервис, готовый к продакшену (минимальный скелет)
      package main

      import (
      "log"
      "net/http"
      "os"
      "time"
      )

      func main() {
      mux := http.NewServeMux()
      mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
      w.WriteHeader(http.StatusOK)
      _, _ = w.Write([]byte("ok"))
      })

      srv := &http.Server{
      Addr: ":" + getEnv("PORT", "8080"),
      Handler: mux,
      ReadTimeout: 5 * time.Second,
      WriteTimeout: 10 * time.Second,
      }

      log.Println("starting server on", srv.Addr)
      if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
      log.Fatalf("server error: %v", err)
      }
      }

      func getEnv(key, def string) string {
      if v := os.Getenv(key); v != "" {
      return v
      }
      return def
      }
  6. Тестирование и валидация качества

    • Цель:
      • убедиться, что продукт соответствует требованиям и не ломается при типичных сценариях.
    • Уровни:
      • unit-тесты;
      • интеграционные и контрактные тесты;
      • end-to-end и регрессионные;
      • security-тесты, performance-тесты.
    • Тесная интеграция с CI:
      • каждый merge запускает набор проверок;
      • без успешного CI код не попадает дальше по конвейеру.
  7. Деплой и релиз (Deployment & Release)

    • Цель:
      • безопасно и воспроизводимо доставить приложение в production и другие окружения.
    • Практики:
      • CI/CD pipelines;
      • контейнеризация (Docker), оркестрация (Kubernetes);
      • стратегии:
        • rolling, blue-green, canary;
        • миграции БД без простоя.
    • GitOps / IaC:
      • декларативные манифесты;
      • traceability изменений инфраструктуры.
  8. Эксплуатация, мониторинг и сопровождение (Operations & Maintenance)

    • Сюда входят:
      • мониторинг (Prometheus, Grafana), логи, метрики, трассировка;
      • алертинг по SLO (латентность, error rate, saturation);
      • управление инцидентами, postmortem-практики;
      • плановые обновления зависимостей, security-патчи;
      • улучшения по результатам реальной эксплуатации.
    • Пример ключевых метрик для Go-сервиса:
      • p95 latency по endpoint;
      • количество 5xx;
      • количество активных goroutines, GC-паузы;
      • использование CPU/RAM.
  9. Эволюция и вывод из эксплуатации

    • На зрелом уровне SDLC учитывает:
      • как мы безопасно меняем архитектуру, не ломая клиентов;
      • backward compatibility для API и схем БД;
      • процесс миграции данных;
      • корректный decommission старых сервисов/версий.

Важно:

  • SDLC — это не только последовательность этапов «сверху вниз», а непрерывный цикл:
    • фидбек из продакшена (метрики, инциденты, отзывы) возвращается в этапы анализа, проектирования и разработки;
    • DevOps-практики, CI/CD, тестирование и мониторинг встроены во все этапы, а не добавляются «в конце».
  • Сильный инженер учитывает требования эксплуатации, наблюдаемости, безопасности и масштабирования уже на стадии архитектуры и кода, а не после релиза.

Вопрос 9. С какими системами автоматизации SDLC и построения CI/CD-пайплайнов вы знакомы?

Таймкод: 00:09:41

Ответ собеседника: неполный. Сообщает, что практического опыта с такими системами нет, путает их с Docker и другими DevOps-инструментами, позже упоминает Jenkins как пример, но подтверждает, что им и аналогами не пользовался.

Правильный ответ:

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

Ниже краткий обзор ключевых систем и то, как они обычно применяются (на примерах, близких к Go-проектам):

Основные CI/CD-системы:

  1. Jenkins

    • Один из самых известных и гибких инструментов.
    • Особенности:
      • своя конфигурация через Jenkinsfile (Declarative или Scripted Pipeline);
      • огромная экосистема плагинов;
      • подходит для сложных корпоративных ландшафтов.
    • Минусы:
      • требует администрирования;
      • легко превратить в «зоопарк» плагинов и нестабильных джоб.
    • Пример Jenkinsfile для Go:
      pipeline {
      agent any

      stages {
      stage('Checkout') {
      steps { checkout scm }
      }
      stage('Build') {
      steps { sh 'go build ./...' }
      }
      stage('Test') {
      steps { sh 'go test ./... -race -coverprofile=coverage.out' }
      }
      stage('Build Docker') {
      steps {
      sh '''
      docker build -t registry.example.com/app:${GIT_COMMIT} .
      docker push registry.example.com/app:${GIT_COMMIT}
      '''
      }
      }
      }
      }
  2. GitLab CI/CD

    • Плотно интегрирован с GitLab-репозиториями.
    • Хранит конфигурацию в .gitlab-ci.yml прямо в репозитории.
    • Поддерживает:
      • стадии (stages),
      • окружения (environments),
      • ручные джобы, approvals,
      • удобную реализацию Continuous Delivery/Deployment.
    • Пример для Go:
      stages: [lint, test, build, deploy]

      lint:
      stage: lint
      image: golang:1.22
      script:
      - go vet ./...

      test:
      stage: test
      image: golang:1.22
      script:
      - go test ./... -race -coverprofile=coverage.out

      build:
      stage: build
      image: docker:24
      services:
      - docker:24-dind
      script:
      - docker build -t registry.example.com/app:$CI_COMMIT_SHA .
      - docker push registry.example.com/app:$CI_COMMIT_SHA
  3. GitHub Actions

    • Интегрирован с GitHub.
    • Конфигурация через YAML workflows в .github/workflows.
    • Удобен для open-source и небольших команд, но отлично масштабируется.
    • Пример для Go:
      name: ci

      on:
      push:
      branches: [ main ]
      pull_request:

      jobs:
      build-test:
      runs-on: ubuntu-latest
      steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
      with:
      go-version: '1.22'
      - run: go test ./... -race -coverprofile=coverage.out
  4. TeamCity

    • Часто используется в крупных компаниях.
    • Поддерживает декларативные и UI-настройки билд-конфигураций.
    • Сильные стороны:
      • гибкий билд-оркестратор;
      • хорошая интеграция с monorepo, сложными пайплайнами.
  5. CircleCI, Travis CI и аналогичные облачные CI

    • Фокус на:
      • простоте;
      • быстром старте;
      • интеграции с GitHub/Bitbucket.
    • YAML-конфиги в репозитории, концепции схожи: jobs, steps, workflows.
  6. GitOps-инструменты для CD (Argo CD, Flux)

    • Отдельный класс, важный в современном стеке:
    • Используются в связке с CI:
      • CI собирает артефакты (Docker-образы, Helm charts, манифесты);
      • GitOps-система следит за Git-репозиторием с декларативным описанием окружения;
      • любые изменения в Git → автоматический sync в Kubernetes.
    • Это делает деплой:
      • декларативным;
      • воспроизводимым;
      • прозрачно версионируемым.

Как правильно позиционировать знания:

  • Базовый, но адекватный ответ:
    • назвать конкретные CI/CD-системы;
    • кратко описать, как с ними работали или как бы использовали:
      • настройка пайплайнов из шагов: checkout → build → tests → security → package → deploy;
      • использование Docker для сборки артефактов;
      • интеграция с Kubernetes/облаком.
  • Если практики мало, важно показать понимание концепций:
    • триггеры (push, merge request, tag, schedule);
    • stages/jobs;
    • артефакты и кеш;
    • environment/promotions (dev → stage → prod);
    • ручной vs автоматический деплой.

Пример концептуального ответа (как он должен звучать на интервью):

  • Знаком с Jenkins, GitLab CI и GitHub Actions:
    • понимаю принципы описания пайплайна в репозитории (Jenkinsfile / .gitlab-ci.yml / workflows);
    • умею собрать цепочку: линтеры, unit-тесты, интеграционные тесты, сборка Docker-образа, деплой в Kubernetes;
    • понимаю, как через эти инструменты реализуется Continuous Integration и Continuous Delivery;
    • знаю базовые подходы к секретам, кешированию, параллельным job, approval’ам и стратегиям выкладки.

Такой ответ показывает различие между системами CI/CD и вспомогательными инструментами (Docker, Terraform, Kubernetes) и демонстрирует понимание того, как всё это связывается в единый автоматизированный конвейер SDLC.

Вопрос 10. С какими системами автоматизации CI/CD-пайплайнов вы знакомы и есть ли опыт написания собственных пайплайнов?

Таймкод: 00:10:51

Ответ собеседника: неполный. Уточняет про Jenkins и похожие инструменты, говорит, что знает о них теоретически, но пайплайны не писал и практического опыта нет.

Правильный ответ:

Отвечая на этот вопрос, важно:

  • четко различать инструменты CI/CD и сопутствующий стек (Docker, Kubernetes, Terraform и т.д.);
  • перечислить конкретные системы;
  • показать понимание, как в них описываются и организуются пайплайны;
  • привести примеры собственных конфигураций.

Краткий, содержательный ответ может выглядеть так.

Я знаком и работал со следующими системами CI/CD:

  1. GitLab CI/CD

    • Использовал для:
      • полноценной реализации CI: линт, тесты, сборка бинарников и Docker-образов;
      • CD: деплой в dev/stage/prod окружения.
    • Писал .gitlab-ci.yml вручную:
      • разносил pipeline на стадии (stages): lint, test, build, deploy;
      • использовал кеширование зависимостей (go mod download), артефакты и условия для разных окружений;
      • настраивал ручные джобы (manual) для Continuous Delivery на прод.
    • Пример пайплайна для Go-сервиса в Kubernetes:
    stages: [lint, test, build, deploy_staging, deploy_prod]

    lint:
    stage: lint
    image: golang:1.22
    script:
    - go vet ./...
    - golangci-lint run ./...

    test:
    stage: test
    image: golang:1.22
    script:
    - go test ./... -race -coverprofile=coverage.out

    build:
    stage: build
    image: docker:24
    services:
    - docker:24-dind
    script:
    - docker build -t registry.example.com/app:$CI_COMMIT_SHA .
    - docker push registry.example.com/app:$CI_COMMIT_SHA
    artifacts:
    expire_in: 1 week
    paths:
    - coverage.out

    deploy_staging:
    stage: deploy_staging
    image: bitnami/kubectl:latest
    script:
    - kubectl set image deployment/app app=registry.example.com/app:$CI_COMMIT_SHA -n staging
    environment:
    name: staging
    only:
    - main

    deploy_prod:
    stage: deploy_prod
    image: bitnami/kubectl:latest
    script:
    - kubectl set image deployment/app app=registry.example.com/app:$CI_COMMIT_SHA -n prod
    environment:
    name: production
    when: manual # Continuous Delivery с ручным подтверждением
    only:
    - main
  2. GitHub Actions

    • Использовал для open-source и внутренних сервисов.
    • Писал workflows в .github/workflows/*.yml:
      • триггеры: push, pull_request, релизные теги;
      • джобы для запуска тестов, сборки образов, публикации в registry.
    • Пример CI-пайплайна для Go:
    name: ci

    on:
    push:
    branches: [ main ]
    pull_request:

    jobs:
    build-test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-go@v5
    with:
    go-version: '1.22'
    - name: Test
    run: go test ./... -race -coverprofile=coverage.out
  3. Jenkins

    • Знаком с концепциями:
      • Jenkinsfile (declarative pipeline),
      • stages/steps,
      • агенты, master/agent архитектура,
      • интеграция с Docker, Kubernetes, Git.
    • Типичные сценарии, которые умею спроектировать:
      • pipeline: checkout → линт → тесты → build → push Docker image → deploy;
      • разделение на параллельные стадии;
      • использование credentials и secrets.
    • Пример декларативного Jenkinsfile для Go:
    pipeline {
    agent any

    stages {
    stage('Checkout') {
    steps { checkout scm }
    }
    stage('Test') {
    steps {
    sh 'go test ./... -race -coverprofile=coverage.out'
    }
    }
    stage('Build Docker') {
    steps {
    sh '''
    docker build -t registry.example.com/app:${GIT_COMMIT} .
    docker push registry.example.com/app:${GIT_COMMIT}
    '''
    }
    }
    }
    }
  4. GitOps-инструменты (Argo CD / Flux) — при работе с Kubernetes

    • Понимаю подход:
      • CI собирает и пушит образ,
      • обновляет Git-репозиторий с манифестами или Helm values,
      • Argo CD/Flux отслеживает изменения и синхронизирует кластер.
    • Такое разделение:
      • CI отвечает за создание артефакта,
      • CD/Argo CD отвечает за доставку артефакта в окружение декларативно.

Важно правильно сформулировать:

  • Если есть реальный опыт:
    • прямо указать, какие пайплайны настраивали, для каких проектов, какие стадии.
  • Если опыта мало:
    • не останавливаться на «не пользовался», а показать, что:
      • понимаете модель: jobs, stages, triggers, artifacts, environments;
      • можете прочитать и написать базовый конфиг;
      • видите, как связать CI/CD с Docker, Kubernetes, IaC, тестами и стратегиями деплоя.

Такой ответ демонстрирует не только знание названий инструментов, но и умение выстроить рабочий end-to-end пайплайн для реального сервиса.

Вопрос 11. Есть ли у вас опыт работы с Linux и с какими дистрибутивами вы знакомы?

Таймкод: 00:11:06

Ответ собеседника: правильный. Сообщает, что в рамках обучения работал с Ubuntu и CentOS.

Правильный ответ:

Опыт работы с Linux критичен для эффективной разработки и эксплуатации серверных приложений, особенно в контексте контейнеризации, CI/CD и высоконагруженных сервисов.

Качественный ответ должен включать:

  • упоминание конкретных дистрибутивов;
  • базовые и продвинутые навыки работы в Linux-среде;
  • понимание того, как это связано с разработкой и эксплуатацией сервисов.

Примерно так:

  • Используемые дистрибутивы:

    • Ubuntu (часто как основа для серверов, dev-окружений и Docker-образов).
    • CentOS / Rocky Linux / AlmaLinux (ранее широко использовались в продакшене, близки к RHEL).
    • При необходимости: Debian, Amazon Linux, etc.
  • Практические навыки:

    • Работа в терминале:
      • базовые команды: ls, cd, cp, mv, rm, find, grep, sed, awk, less, tail, cat;
      • работа с правами: chmod, chown, umask, понимание sudo.
    • Управление пакетами:
      • Ubuntu/Debian: apt update, apt install, apt upgrade;
      • CentOS/RHEL: yum / dnf для установки и обновлений.
    • Работа с сервисами:
      • systemd: systemctl start/stop/status/enable, анализ unit-файлов;
      • настройка и просмотр логов: journalctl -u <service>.
    • Сетевые утилиты:
      • проверка доступности и DNS: ping, curl, dig, nslookup;
      • проверка портов и соединений: ss -tulpen, netstat (если доступен), telnet/nc.
    • Работа с процессами и ресурсами:
      • ps aux, top, htop;
      • анализ использования памяти, CPU, диска: free -m, df -h, iostat, vmstat.
    • Логи и диагностика:
      • просмотр логов приложений в /var/log;
      • фильтрация и поиск по логам через grep, awk, sed.
  • Связь с backend/Go-разработкой и DevOps:

    • Сборка и запуск Go-сервисов в Linux-среде:
      GOOS=linux GOARCH=amd64 go build -o app ./cmd/app
      ./app
    • Подготовка минимальных Docker-образов на основе Linux-дистрибутивов:
      FROM golang:1.22-alpine AS builder
      WORKDIR /app
      COPY . .
      RUN go build -o app ./cmd/app

      FROM alpine:3.20
      RUN adduser -D appuser
      USER appuser
      COPY --from=builder /app/app /app/app
      ENTRYPOINT ["/app/app"]
    • Понимание различий Debian-based и RHEL-based семейств:
      • пути, пакеты, политика обновлений, SELinux (на CentOS/RHEL), что влияет на деплой и отладку.

Сильный ответ не ограничивается перечислением дистрибутивов, а показывает уверенное владение CLI, понимание системных сервисов, логов и сетевых инструментов, а также умение использовать Linux как целевую среду для продакшен-приложений и CI/CD-пайплайнов.

Вопрос 12. К каким семействам относятся указанные дистрибутивы Linux и какие пакетные менеджеры в них используются?

Таймкод: 00:11:21

Ответ собеседника: правильный. Указывает, что Ubuntu основан на Debian и использует apt, CentOS относится к RHEL-семейству и использует yum (и dnf как обновлённую версию).

Правильный ответ:

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

  • Ubuntu:

    • Относится к семейству Debian-based.
    • Основной пакетный менеджер:
      • apt (Advanced Package Tool), используется поверх dpkg.
    • Примеры:
      • установка пакета:
        sudo apt update
        sudo apt install nginx
  • Debian:

    • Базовое дистрибутивное семейство, на котором основана Ubuntu.
    • Также использует apt + dpkg.
  • CentOS (и современные аналоги: Rocky Linux, AlmaLinux):

    • Относятся к семейству RHEL-based (Red Hat Enterprise Linux).
    • Исторически использовали:
      • yum (Yellowdog Updater, Modified).
    • В современных версиях RHEL-семейства:
      • dnf (Dandified YUM) как замена yum, с улучшенным разрешением зависимостей и производительностью.
    • Пример:
      sudo yum install nginx
      # или
      sudo dnf install nginx

Понимание семейств важно потому, что:

  • отличается структура путей, политика обновлений, наличие и оформление пакетов;
  • при сборке Docker-образов и деплое Go-сервисов нужно выбирать базовый образ и команды установки исходя из семейства:
    • Debian/Ubuntu:
      FROM debian:12
      RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
    • RHEL/CentOS:
      FROM rockylinux:9
      RUN dnf install -y ca-certificates && dnf clean all

Корректное сопоставление семейства и пакетного менеджера — базовый, но обязательный навык для работы с Linux-инфраструктурой и CI/CD-окружениями.

Вопрос 13. Какая система инициализации используется в указанных Linux-дистрибутивах?

Таймкод: 00:11:54

Ответ собеседника: правильный. После подсказки называет systemd и связывает её с управлением сервисами через systemctl.

Правильный ответ:

В современных версиях Ubuntu и CentOS (а также большинства производных Debian/RHEL) используется система инициализации systemd.

Ключевые моменты, которые важно знать:

  • systemd — это:

    • система инициализации (PID 1), которая запускается первой при старте системы;
    • фреймворк для управления службами, логами, зависимостями, таймерами, монтированием и многим другим.
  • Управление сервисами через systemctl:

    • запустить сервис:
      sudo systemctl start myservice
    • остановить:
      sudo systemctl stop myservice
    • перезапустить:
      sudo systemctl restart myservice
    • включить автозапуск:
      sudo systemctl enable myservice
    • посмотреть статус:
      systemctl status myservice
  • Логи:

    • systemd интегрирован с journald, логи можно смотреть:
      journalctl -u myservice
  • Практический аспект для backend-разработки и DevOps:

    • Умение описать корректный unit-файл для сервиса (например, Go-приложения):
      [Unit]
      Description=My Go Service
      After=network.target

      [Service]
      ExecStart=/usr/local/bin/my-service
      Restart=always
      User=appuser
      Group=appuser
      Environment=ENV=prod
      WorkingDirectory=/var/lib/my-service

      [Install]
      WantedBy=multi-user.target
    • Понимание зависимостей (After/Requires), рестарт-политик и управления окружением.

Знание systemd и systemctl — базовый и необходимый навык для работы с сервисами в современных Linux-средах, в том числе при отладке, деплое и эксплуатации Go-приложений.

Вопрос 14. Есть ли опыт работы с unit-файлами systemd и созданием собственных сервисов?

Таймкод: 00:12:39

Ответ собеседника: правильный. Сообщает, что собственные unit-файлы не писал, ограничивался управлением уже существующими сервисами: запуск, остановка, проверка статуса.

Правильный ответ:

Для работы с backend-сервисами и инфраструктурой важно не только уметь управлять существующими сервисами через systemctl, но и уметь оформлять свои приложения (в т.ч. на Go) как полноценные systemd-сервисы.

Ключевые моменты:

  1. Основы unit-файлов systemd
  • Unit-файлы описывают:
    • что запускать;
    • в каком окружении;
    • с какими зависимостями;
    • как рестартовать;
    • при каких условиях считать сервис «живым».
  • Основные типы unit:
    • service — фоновые службы (то, что нас интересует для приложений);
    • timer, socket, mount и др. (расширенные сценарии).
  1. Типичная структура unit-файла сервиса

Минимальный пример для Go-приложения:

[Unit]
Description=My Go HTTP Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/my-service
User=appuser
Group=appuser
Restart=on-failure
RestartSec=5
Environment=ENV=prod
WorkingDirectory=/var/lib/my-service

[Install]
WantedBy=multi-user.target

Разбор ключевых полей:

  • Description — человекочитаемое описание.
  • After=network.target — запускать после поднятия сети.
  • ExecStart — команда запуска приложения.
  • User/Group — под каким пользователем запускать (никогда не root, если нет необходимости).
  • Restart — политика рестарта:
    • no, on-failure, always, on-abort.
  • RestartSec — задержка перед повторным запуском.
  • Environment — переменные окружения (или EnvironmentFile для вынесения в отдельный файл).
  • WorkingDirectory — рабочая директория (часто важна для логов/конфигов).
  • WantedBy=multi-user.target — позволяет включить автозапуск через systemctl enable.
  1. Базовый рабочий цикл создания сервиса
  • Создаем unit-файл:
    sudo nano /etc/systemd/system/my-service.service
  • Перечитываем конфигурацию:
    sudo systemctl daemon-reload
  • Включаем автозапуск:
    sudo systemctl enable my-service
  • Запускаем и проверяем:
    sudo systemctl start my-service
    sudo systemctl status my-service
    journalctl -u my-service -f
  1. Практические best practices:
  • Логи:
    • не писать лог-файлы вручную средствами приложения, а логировать в stdout/stderr:
      • systemd и journald их соберут;
      • просмотр через journalctl -u my-service.
  • Безопасность:
    • отдельный системный пользователь (User=appuser);
    • минимум прав на файлы и директории;
    • можно дополнительно использовать:
      • NoNewPrivileges=yes
      • ProtectSystem=full
      • ProtectHome=true
      • ReadOnlyPaths= / ReadWritePaths=.
  • Надежность:
    • Restart=on-failure + разумный RestartSec;
    • health-check endpoint в приложении, чтобы внешние системы/балансировщики могли проверять статус.
  1. Пример Go-сервиса, корректного для systemd
package main

import (
"log"
"net/http"
"os"
"time"
)

func main() {
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("ok"))
})

addr := ":" + getenv("PORT", "8080")
srv := &http.Server{
Addr: addr,
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}

log.Println("starting server on", addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("server error: %v", err)
}
}

func getenv(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}

Такое приложение:

  • не daemonize-ится самостоятельно;
  • пишет логи в stdout/stderr;
  • читает конфигурацию из окружения (что удобно в unit-файлах и контейнерах);
  • идеально подходит для управления через systemd.

Итог:

Уверенное владение unit-файлами — это умение:

  • превратить любое приложение (особенно свой Go-сервис) в управляемый системный сервис;
  • контролировать его запуск, перезапуск, логи и безопасность;
  • интегрировать его в общую инфраструктуру, в том числе как часть CI/CD-процесса выкладки на bare metal или VM.

Вопрос 15. Что такое POSIX-стандарт в Unix-системах?

Таймкод: 00:13:14

Ответ собеседника: неправильный. Указывает, что слышал про POSIX, но не знает, что это.

Правильный ответ:

POSIX (Portable Operating System Interface) — это семейство стандартизированных спецификаций, разработанных IEEE, которые определяют единый интерфейс взаимодействия приложений с операционной системой в Unix-подобных системах. Цель POSIX — обеспечить переносимость программного обеспечения между разными системами (Linux, *BSD, macOS, коммерческие Unix и др.) без значительных изменений кода.

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

Основные аспекты POSIX:

  1. Системные вызовы и API POSIX определяет стандартный набор системных вызовов и библиотечных функций, включая:

    • работа с файлами и директориями:
      • open, close, read, write, lseek, stat, unlink, mkdir, rmdir;
    • процессы:
      • fork, exec*, wait, exit;
    • управление правами и пользователями:
      • chmod, chown, umask, UID/GID-модель;
    • сигналы:
      • signal, kill, sigaction;
    • межпроцессное взаимодействие:
      • каналы (pipe), FIFO, сокеты, shared memory, семафоры;
    • потоки (POSIX Threads — pthreads).

    Это задаёт общий контракт: приложение ожидает, что эти вызовы ведут себя одинаково на любой POSIX-системе.

  2. Стандартные утилиты и shell POSIX определяет:

    • базовый набор команд командной строки:
      • sh (POSIX shell), ls, cp, mv, rm, grep, find, sed, awk, xargs, cat, echo, test и др.;
    • их минимальное поведение и опции.

    Это важно для написания переносимых shell-скриптов: скрипт, соответствующий POSIX sh, будет работать на разных Unix-системах без завязки на особенности Bash/Zsh и т.п.

  3. Модель прав и процессов POSIX задает:

    • концепцию пользователей и групп;
    • модель прав rwx для user/group/other;
    • базовые гарантии поведения процессов, сигналов, управления временем и т.д.
  4. POSIX и Linux/BSD/macOS

    • Linux:
      • не является формально сертифицированным POSIX, но практически очень близок и реализует большую часть POSIX API; большинство приложений POSIX-переносимы на Linux.
    • *BSD, macOS:
      • исторически сильнее ориентированы на POSIX-соответствие.
    • Различия всё ещё возможны (расширения, нестандартные фичи), поэтому для максимальной переносимости важно держаться POSIX, а не специфичных возможностей конкретного дистрибутива.
  5. Практический смысл для разработки и DevOps

    Почему это важно:

    • Переносимость:
      • если вы пишете shell-скрипты деплоя, миграций, CI/CD шагов — опора на POSIX-команды и POSIX shell уменьшает риск, что скрипты «сломаются» на другом дистрибутиве.
    • Предсказуемость поведения:
      • многие инструменты (Docker base images, минимальные образы) содержат POSIX-совместимый набор утилит; знание стандарта помогает не зависеть от "удобных, но нестандартных" фич.
    • Разработка на Go/C:
      • Go рантайм и стандартная библиотека под Unix в значительной степени опираются на модель, совместимую с POSIX:
        • файловые дескрипторы, сигналы, сокеты, права, процессы.
      • Понимание POSIX помогает лучше понимать, как ваши приложения работают «под капотом» в Linux/Unix-среде.
  6. Простой пример: переносимый shell-скрипт деплоя

Непереносимый (Bash-специфичный) код:

#!/usr/bin/env bash
my_array=(a b c)
for x in "${my_array[@]}"; do
echo "$x"
done

Более POSIX-совместимый подход:

#!/bin/sh
for x in a b c; do
echo "$x"
done

Такой скрипт с #!/bin/sh и без bash-специфичных конструкций с высокой вероятностью будет работать на любых POSIX-системах.

Итого:

  • POSIX — это стандарт, описывающий единый программный интерфейс и поведение Unix-подобных систем.
  • Он критичен для:
    • переносимости кода и скриптов;
    • предсказуемости поведения приложений;
    • корректной работы инструментов автоматизации в гетерогенных окружениях.

Вопрос 16. Как изменить права доступа к файлу, чтобы добавить возможность записи или выполнения?

Таймкод: 00:13:36

Ответ собеседника: правильный. Упоминает использование команды chmod для изменения прав для владельца, группы и остальных.

Правильный ответ:

В Unix/POSIX-системах права доступа определяют, кто может читать, записывать или выполнять файл/каталог. Основные сущности:

  • категории:
    • u — владелец (user),
    • g — группа (group),
    • o — остальные (others),
    • a — все (all: u+g+o).
  • типы прав:
    • r — чтение (read),
    • w — запись (write),
    • x — выполнение (execute / проход для каталогов).

Для изменения прав используется команда chmod. Есть два способа записи: символьный и числовой.

  1. Символьная форма (рекомендуется для наглядности):
  • Добавить право:
    • + — добавить;
    • - — убрать;
    • = — установить ровно указанные права (остальные убрать).

Примеры:

  • Добавить право выполнения для владельца:
    chmod u+x script.sh
  • Добавить право записи для группы:
    chmod g+w file.txt
  • Добавить право выполнения для всех:
    chmod a+x script.sh
  • Убрать право записи для остальных:
    chmod o-w file.txt
  1. Числовая форма (удобна для явного задания полного набора):

Права кодируются суммой:

  • r = 4, w = 2, x = 1.

Три цифры: u g o.

Примеры:

  • chmod 755 script.sh
    • 7 (4+2+1) — владелец: rwx;
    • 5 (4+1) — группа: r-x;
    • 5 (4+1) — остальные: r-x.
  • chmod 644 file.txt
    • владелец: rw-;
    • группа: r--;
    • остальные: r--.
  • chmod 664 config.ini
    • владелец и группа: rw-;
    • остальные: r-- (или 664 → o: r--? нет, это 4; пример корректен).
  1. Практические рекомендации:
  • Для исполняемых скриптов:
    chmod 755 script.sh
  • Для бинарников (например, Go-приложения):
    chmod 755 /usr/local/bin/my-service
  • Для конфигов:
    • только владелец может писать:
    chmod 640 config.yaml

Понимание chmod и прав доступа важно для:

  • безопасного запуска сервисов;
  • ограничения доступа к конфигурациям и секретам;
  • корректной работы деплоя и CI/CD (скрипты должны быть исполняемыми, конфиги — не открытыми всем).

Вопрос 17. Как с помощью chmod задать права: владельцу — чтение и запись, группе — только чтение, остальным — без прав?

Таймкод: 00:14:06

Ответ собеседника: правильный. Предлагает задать права в символьном виде через u, g и o, не давая прав для остальных; логика соответствует требуемой схеме.

Правильный ответ:

Нужно установить права:

  • владелец (u): чтение + запись → rw-
  • группа (g): только чтение → r--
  • остальные (o): нет прав → ---

Это можно сделать:

  1. В числовой форме:
chmod 640 filename

Расшифровка:

  • 6 = 4 (r) + 2 (w) → rw-
  • 4 = r--
  • 0 = ---
  1. В символьной форме:
chmod u=rw,g=r,o= filename

Или эквивалентно (сначала убрать все, затем выставить нужное):

chmod u=rw,g=r,o= filename

Такие права часто используются для конфигурационных файлов и секретов (ключи, пароли), чтобы:

  • владелец (часто сервисный пользователь) мог читать и при необходимости обновлять;
  • группа имела read-only-доступ (например, для связанных сервисов);
  • остальные пользователи системы не имели доступа вовсе.

Вопрос 18. Как интерпретируются права 777 и что означает цифра 7 в этом контексте?

Таймкод: 00:14:49

Ответ собеседника: правильный. Объясняет, что 7 означает наличие прав чтения, записи и выполнения, ссылаясь на двоичное представление.

Правильный ответ:

Права доступа в Unix-системах часто задаются в числовой (окталной) форме, где каждая цифра описывает набор прав для:

  • первой цифры — владельца (user),
  • второй — группы (group),
  • третьей — остальных (others).

Каждая цифра — сумма флагов:

  • 4 — право чтения (r),
  • 2 — право записи (w),
  • 1 — право выполнения (x).

Цифра 7:

  • 7 = 4 + 2 + 1 → rwx
  • то есть:
    • чтение (r),
    • запись (w),
    • выполнение (x).

Права 777:

  • первая 7 (u): владелец — rwx;
  • вторая 7 (g): группа — rwx;
  • третья 7 (o): остальные — rwx.

То есть файл или каталог полностью открыт для всех:

  • любой пользователь может:
    • читать,
    • изменять,
    • выполнять (или заходить в каталог).

Практические замечания:

  • chmod 777 почти всегда плохая практика для продакшн-среды:
    • нарушает безопасность,
    • увеличивает риск изменения или удаления файлов любым пользователем.
  • Корректнее:
    • минимизировать права (principle of least privilege),
    • использовать 755 для публичных исполняемых файлов/директорий,
    • 644/640/600 для конфигов и секретов.

Вопрос 19. Как изменить владельца файла на другого пользователя?

Таймкод: 00:15:18

Ответ собеседника: неправильный. Упоминает usermod (используется для изменения параметров пользователя), а не chown, который предназначен для смены владельца файла.

Правильный ответ:

Для изменения владельца файла или директории в Unix/Linux используется команда chown (change owner), а не usermod.

Базовый синтаксис:

chown [новый_владелец] файл
chown [новый_владелец]:[новая_группа] файл

Примеры:

  1. Изменить только владельца:

    sudo chown alice somefile.txt
    • Теперь владельцем somefile.txt является пользователь alice.
    • Группа при этом останется прежней.
  2. Изменить владельца и группу:

    sudo chown alice:developers somefile.txt
    • Владелец: alice
    • Группа: developers
  3. Рекурсивная смена владельца для директории и всех вложенных файлов:

    sudo chown -R alice:developers /var/www/app

Практические моменты и best practices:

  • Команду chown обычно может выполнять только root или пользователь с достаточными привилегиями.
  • Использование рекурсивного -R требует аккуратности:
    • неправильное применение к системным каталогам может нарушить работу сервисов.
  • В контексте деплоя и Go/DevOps:
    • часто создают отдельного системного пользователя для сервиса:
      sudo useradd -r -s /usr/sbin/nologin appuser
      sudo mkdir -p /var/lib/my-service
      sudo chown -R appuser:appuser /var/lib/my-service
    • и запускают сервис (через systemd или другой менеджер) от имени этого пользователя, чтобы ограничить права и повысить безопасность.

Команда usermod используется для изменения атрибутов пользователя (группа, shell, домашний каталог и т.д.), но не для изменения владельца файлов.

Вопрос 20. Что такое cron в Linux?

Таймкод: 00:16:01

Ответ собеседника: неправильный. Говорит, что забыл и не использовал cron на практике.

Правильный ответ:

cron — это системный сервис (демон) в Unix/Linux, который предназначен для планирования и регулярного выполнения задач по расписанию. Он позволяет автоматически запускать команды, скрипты или программы в заданное время: каждую минуту, час, день, по будням, раз в месяц и т.д.

Ключевые моменты:

  • crond (cron daemon) постоянно работает в фоне и проверяет таблицы заданий (crontab), чтобы понять, что и когда нужно запустить.
  • Настройки заданий хранятся:
    • в пользовательских crontab-файлах (crontab -e);
    • или в системных директориях (/etc/crontab, /etc/cron.d/*, /etc/cron.daily, /etc/cron.hourly и т.п.).

Структура записи в crontab:

Каждая строка имеет формат:

* * * * * команда
│ │ │ │ │
│ │ │ │ └─ день недели (0-7, где 0 и 7 — воскресенье)
│ │ │ └─── месяц (1-12)
│ │ └───── день месяца (1-31)
│ └─────── час (0-23)
└───────── минута (0-59)

Примеры:

  1. Запуск скрипта каждый день в 03:00:

    0 3 * * * /usr/local/bin/backup.sh
  2. Запуск Go-сервиса/утилиты каждые 5 минут:

    */5 * * * * /usr/local/bin/update-metrics >> /var/log/update-metrics.log 2>&1
  3. Запуск задачи по будням в 09:30:

    30 9 * * 1-5 /usr/local/bin/report.sh

Проверка и редактирование crontab:

  • Открыть crontab текущего пользователя:
    crontab -e
  • Посмотреть установленные задания:
    crontab -l

Практическая роль для разработки и эксплуатации:

  • Автоматизация рутинных задач:
    • сбор и ротация логов;
    • резервное копирование;
    • периодические очистки (cleanup) временных данных;
    • регулярный запуск batch- или maintenance-скриптов.
  • В продакшене:
    • вместо «магических» ручных запусков гарантирует повторяемость и предсказуемость.
  • В связке с Go:
    • можно:
      • либо использовать cron для запуска вашей Go-утилиты по расписанию;
      • либо реализовывать планировщик внутри Go-сервиса (но системный cron часто проще и надежнее для простых задач).

Важно помнить:

  • cron запускает команды в минимальном окружении:
    • переменные окружения могут отличаться от интерактивной сессии;
    • лучше указывать полный путь к бинарникам и скриптам (/usr/bin/go, /usr/local/bin/app).
  • Логи нужно явно перенаправлять:
    */10 * * * * /usr/local/bin/app-task >> /var/log/app-task.log 2>&1

Таким образом, cron — базовый инструмент автоматизации в Unix-системах, знание которого критично для администрирования, DevOps и поддержки бэкенд-сервисов.

Вопрос 21. Знакомы ли вы с моделью OSI и можете ли перечислить уровни?

Таймкод: 00:16:26

Ответ собеседника: неполный. Знает, что в модели 7 уровней, но затрудняется с последовательностью и содержанием.

Правильный ответ:

Модель OSI (Open Systems Interconnection) — это концептуальная семиуровневая модель, описывающая, как данные передаются по сети от приложения на одной машине до приложения на другой. Она задает абстракции и роли каждого уровня, помогает структурировать сетевые протоколы и упрощает понимание взаимодействия систем.

Сама модель теоретическая, но её понимание критично для работы с сетями, отладки, настройки сервисов, балансировщиков, а также для разработки сетевых и веб-приложений (в т.ч. на Go).

Семь уровней модели OSI (снизу вверх):

  1. Физический уровень (Physical)

    • Отвечает за:
      • передачу битов по физической среде: электрические сигналы, оптика, радио;
      • разъемы, кабели, частоты, напряжения.
    • Примеры:
      • кабели (UTP, оптоволокно),
      • Ethernet на уровне физики,
      • repeaters, hubs.
    • Для разработчика важен косвенно: качество канала, скорость, дуплекс, физические сбои.
  2. Канальный уровень (Data Link)

    • Отвечает за:
      • доставку кадров (frames) между соседними устройствами в одной локальной сети;
      • адресацию на уровне MAC-адресов;
      • обнаружение и иногда коррекцию ошибок.
    • Примеры:
      • Ethernet (L2),
      • Wi-Fi (802.11),
      • PPP, VLAN (802.1Q),
      • протокол ARP формально между L2/L3.
    • Важен при:
      • диагностике проблем в LAN,
      • работе с MAC, VLAN, L2-сегментацией.
  3. Сетевой уровень (Network)

    • Отвечает за:
      • логическую адресацию и маршрутизацию пакетов между сетями.
    • Ключевой протокол:
      • IP (IPv4, IPv6).
    • Также:
      • ICMP (ping, диагностика),
      • маршрутизаторы.
    • Для backend/DevOps:
      • понимание IP-адресов, подсетей (CIDR), маршрутов, NAT;
      • влияние на доступность сервисов между сегментами сети, Kubernetes node-поды и т.п.
  4. Транспортный уровень (Transport)

    • Отвечает за:
      • доставку данных «конец-в-конец» между приложениями;
      • управление сессиями передачи, порядком, надёжностью.
    • Основные протоколы:
      • TCP:
        • надёжный, с установлением соединения (3-way handshake),
        • контроль последовательности, повторные передачи, контроль перегрузки.
      • UDP:
        • без установления соединения,
        • без гарантии доставки, но проще и быстрее, подходит для DNS, стриминга, RTP, некоторых RPC.
    • В Go:
      • net.Dial("tcp", ...), net.Listen("tcp", ...) — TCP;
      • net.ListenPacket("udp", ...) — UDP.
    • Критично для:
      • понимания портов, connection pooling,
      • таймаутов, ретраев, backoff, нагрузочных характеристик.
  5. Сеансовый уровень (Session)

    • В классической модели:
      • управление сессиями, диалогами, установлением/поддержанием/завершением сеансов;
      • контроль контекста между взаимодействующими приложениями.
    • На практике:
      • часто «размазан» между транспортом и прикладными протоколами;
      • примеры: TLS-сессии, сессии приложений, RPC-сессии.
  6. Представительский уровень (Presentation)

    • Отвечает за:
      • формат представления данных,
      • кодирование/декодирование, сериализацию,
      • шифрование/дешифрование.
    • Примеры:
      • JSON, XML, Protobuf, Avro;
      • кодировки (UTF-8, ASCII);
      • TLS-шифрование (частично сюда относят).
    • Для Go-разработки:
      • маршалинг/анмаршалинг JSON/Proto,
      • работа с кодировками,
      • обработка бинарных протоколов.
  7. Прикладной уровень (Application)

    • Ближе всего к коду приложения.
    • Отвечает за:
      • высокоуровневые протоколы, с которыми работают приложения.
    • Примеры:
      • HTTP/HTTPS,
      • gRPC,
      • SMTP, IMAP,
      • DNS (как прикладной протокол поверх UDP/TCP),
      • FTP, SSH, WebSocket и т.д.
    • В Go:
      • net/http, gRPC-библиотеки, любые клиентские/серверные реализации.

Практическая связка с реальным миром (важно уметь проговорить на интервью):

  • В реальной практике чаще опираются на стек TCP/IP, а OSI используют как модель для объяснения:
    • где возникает проблема (L2 — MAC/VLAN, L3 — маршрутизация, L4 — TCP-порты, L7 — HTTP-протокол/баги в приложении).
  • Пример:
    • HTTP-запрос к Go-сервису:
      • L7: HTTP (методы, заголовки, JSON, gRPC);
      • L4: TCP-соединение (порт 80/443);
      • L3: IP-пакеты между клиентом и сервером;
      • L2: Ethernet/Wi-Fi кадры в локальной сети;
      • L1: физический сигнал по кабелю или радио.

Хороший лаконичный ответ:

  • «Да, это семиуровневая модель. Снизу вверх: физический, канальный, сетевой, транспортный, сеансовый, представительский, прикладной. Она используется как логическая схема, чтобы разделять ответственность: от битов на проводе до HTTP/gRPC на уровне приложения. В работе помогаeт диагностировать, на каком уровне возникла проблема и как на неё влияют наши решения в коде и инфраструктуре.»

Вопрос 22. Что происходит при переходе в браузере по адресу сайта (например, https://google.com) с точки зрения сетевых протоколов и модели OSI?

Таймкод: 00:16:50

Ответ собеседника: неправильный. Не может последовательно описать процесс; частично упоминает DNS после подсказок, путается и не раскрывает капсулирование, работу TCP/HTTP, TLS и маршрутизацию.

Правильный ответ:

Разберем по шагам, что происходит, когда вы вводите в браузере https://google.com и нажимаете Enter. Это типичный вопрос, который проверяет глубокое понимание сети, OSI-модели и практической работы веб-приложений.

Опишем в логике «сверху вниз» (от уровня приложения к физике), с привязкой к OSI-уровням и реальным протоколам.

  1. Обработка URL и определение протокола (уровень 7 — прикладной)
  • Браузер парсит URL:
    • схема: https
    • хост: google.com
    • порт по умолчанию для HTTPS: 443
    • путь: / (если не указан).
  • Понимает, что нужно:
    • установить защищенное соединение HTTPS (HTTP поверх TLS поверх TCP);
    • обратиться к хосту google.com по порту 443.
  1. DNS-разрешение доменного имени (уровни 7 → 3/4)
  • Если IP-адрес для google.com не закеширован (в браузере, системе, у DNS-резолвера), запускается DNS-запрос.
  • Шаги:
    • Клиент проверяет:
      • /etc/hosts;
      • локальный DNS-кеш;
      • кеш резолвера.
    • Если нет:
      • формируется DNS-запрос (обычно UDP, порт 53; при некоторых сценариях — TCP или DoT/DoH).
      • запрос отправляется на настроенный DNS-сервер (обычно у провайдера или публичный: 8.8.8.8, 1.1.1.1 и т.п.).
    • DNS-сервер возвращает один или несколько IP-адресов для google.com.
  • OSI:
    • Application: DNS-протокол.
    • Transport: UDP (или TCP).
    • Network: IP (адрес DNS-сервера и клиента).
    • Data Link / Physical: кадры в локальной сети и дальше по маршруту.
  1. Установление TCP-соединения (уровень 4 — транспортный, уровни ниже для доставки)

Теперь, когда есть IP-адрес (например, 142.250.x.x):

  • Клиент инициирует TCP-соединение к:
    • IP-адресу сервера,
    • порту 443.
  • Трехстороннее рукопожатие TCP (3-way handshake):
    • Клиент → Сервер: SYN
    • Сервер → Клиент: SYN-ACK
    • Клиент → Сервер: ACK
  • После этого устанавливается надёжный двусторонний канал (байтовый поток) между клиентом и сервером.

OSI:

  • Уровень 4: TCP — управляет установлением соединения, порядком, контролем перегрузки, ретраями.
  • Уровень 3: IP — маршрутизация пакетов от клиента к серверу через цепочку маршрутизаторов.
  • Уровень 2: Ethernet/Wi-Fi кадры между соседними узлами.
  • Уровень 1: физический сигнал по кабелю/радио.
  1. TLS-рукопожатие для HTTPS (между уровнями 6–7, поверх TCP)

Поскольку используется https, поверх установленного TCP запускается TLS.

Типичный упрощенный сценарий TLS 1.2/1.3:

  • Клиент → Сервер: ClientHello
    • поддерживаемые версии TLS, наборы шифров, расширения (включая SNI — имя хоста).
  • Сервер → Клиент:
    • ServerHello (выбор параметров),
    • сертификат сервера,
    • (опционально) цепочка сертификатов,
    • параметры ключевого обмена.
  • Клиент:
    • проверяет сертификат:
      • цепочка доверия (CA),
      • срок действия,
      • что CN/SAN соответствует google.com.
  • Стороны:
    • договариваются о сессионных ключах (Diffie-Hellman/ECDHE),
    • переходят на шифрованный канал.
  • После завершения рукопожатия:
    • любые данные по этому TCP-соединению шифруются.

OSI:

  • Представительский (6): шифрование/дешифрование.
  • Прикладной (7): протоколы работают поверх защищенного канала.
  • Фактически в реальности стек TCP/IP, но в терминах OSI это зона 5–7.
  1. HTTP-запрос (уровень 7 — прикладной)

После успешного TLS-handshake браузер отправляет HTTP-запрос (обычно HTTP/2 или HTTP/3; HTTP/3 уже поверх QUIC/UDP — отдельный кейс).

Пример HTTP-запроса (для HTTP/1.1):

GET / HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
  • Запрос идет:
    • поверх TLS (зашифрован),
    • поверх TCP,
    • к IP сервера.

Сервер:

  • принимает запрос;
  • проксируется/балансируется (L7/L4 балансировщики, reverse proxy);
  • обрабатывается веб-сервером/приложением;
  • формирует HTTP-ответ:
    • статус-код (200, 301, 404, 500...),
    • заголовки,
    • тело (HTML, JSON, и т.д.).

Браузер:

  • получает HTTP-ответ,
  • парсит HTML,
  • делает дополнительные запросы за CSS, JS, изображениями, шрифтами (каждый — отдельный HTTP-запрос, часто по уже установленным соединениям).
  1. Маршрутизация и капсулирование (уровни 3–1)

Важно понимать, как данные «едут по сети»:

  • На стороне клиента:
    • HTTP-данные → TLS-шифрованные блоки → TCP-сегменты → IP-пакеты → Ethernet/Wi-Fi кадры → физический сигнал.
  • На маршрутизаторах:
    • работают, в основном, на уровне 3 (IP):
      • пересылают пакеты в сторону целевой сети,
      • не «видят» HTTP/HTTPS содержимого.
  • На стороне сервера:
    • все в обратном порядке: физика → L2 → L3 → L4 (TCP) → TLS → HTTP → приложение.

Каждый уровень добавляет свой заголовок (это и есть инкапсуляция):

  • L7: HTTP-заголовки/данные.
  • L4: TCP-заголовок.
  • L3: IP-заголовок.
  • L2: Ethernet-заголовок.
  • L1: конкретное кодирование сигнала.
  1. Особенности для HTTPS/современного веба
  • Возможны редиректы:
    • с http:// на https://.
  • HTTP/2:
    • мультиплексирование нескольких запросов по одному TCP-соединению.
  • HTTP/3:
    • работает поверх QUIC (UDP), логика схожа, но транспорт другой.
  • CDN, Anycast:
    • DNS может отдать ближайший к пользователю IP (Google Frontend/Edge).
  1. Как это связано с практикой backend/Go-разработки

Важно уметь применять это понимание:

  • Диагностика:
    • «не открывается сайт» → проверяем DNS (dig/nslookup), IP/маршруты (ping/traceroute), порты (curl, nc), TLS-сертификаты (openssl, curl -v).
  • Настройка сервисов:
    • понимание портов (L4), виртуальных хостов/Host header (L7), reverse-proxy (Nginx/Envoy/Traefik).
  • В Go:
    • стандартная библиотека берет на себя много деталей:
      resp, err := http.Get("https://google.com")
      • под капотом: DNS, TCP, TLS, HTTP — всё в соответствии с тем, что описано выше,
      • но инженер должен понимать, что именно может «сломаться» и как это дебажить.

Краткая формулировка, подходящая для собеседования:

  • «Браузер парсит URL, через DNS получает IP-адрес google.com. Затем устанавливает TCP-соединение с сервером на порт 443, поверх него выполняет TLS-рукопожатие для HTTPS. После установления защищенного канала отправляет HTTP-запрос (GET /). Ответ проходит обратный путь. На каждом шаге данные инкапсулируются: HTTP → TLS → TCP → IP → Ethernet → физический уровень, проходят через цепочку маршрутизаторов, а на стороне сервера раскладываются в обратном порядке. Это классический пример работы прикладного протокола поверх транспортного и сетевого в терминах OSI-модели.»

Вопрос 23. Что делает DNS при обращении к доменному имени и для чего он нужен?

Таймкод: 00:18:19

Ответ собеседника: правильный. Объясняет, что DNS сопоставляет доменное имя с IP-адресом.

Правильный ответ:

DNS (Domain Name System) — это распределенная иерархическая система, которая преобразует человекочитаемые доменные имена (например, google.com, api.example.com) в IP-адреса (например, 142.250.x.x или 2a00:1450:...) и обратно (обратное разрешение). Он нужен для того, чтобы пользователи и приложения могли обращаться к сервисам по удобным именам, не оперируя напрямую IP-адресами.

Ключевые функции DNS:

  1. Преобразование имени в IP-адрес (Forward lookup)

    • Основная задача:
      • по доменному имени найти один или несколько IP-адресов.
    • Пример:
      • запрос: api.example.com → ответ: 203.0.113.10.
    • Без DNS:
      • приходилось бы вбивать IP вручную и обновлять их при каждом изменении инфраструктуры.
    • Это критично для:
      • user experience;
      • гибкости и эволюции инфраструктуры.
  2. Обратное разрешение (Reverse lookup)

    • По IP-адресу получить доменное имя (через PTR-записи).
    • Используется:
      • в диагностике,
      • антиспаме,
      • логировании и аналитике.
  3. Абстракция и гибкость инфраструктуры

    • DNS позволяет:
      • менять IP-сервера без изменения доменного имени;
      • балансировать нагрузку с помощью нескольких A/AAAA-записей (simple round-robin);
      • использовать CNAME-записи для делегирования имен.
    • Примеры:
      • миграция сервера:
        • меняем IP в DNS-записи → пользователи продолжают ходить по тому же домену.
      • балансировка:
        • service.example.com → несколько IP,
        • клиенты распределяются по ним.
  4. Типы DNS-записей (важно знать на собеседовании):

    • A — имя → IPv4-адрес.
    • AAAA — имя → IPv6-адрес.
    • CNAME — алиас одного доменного имени на другое.
    • MX — почтовые сервера домена.
    • TXT — произвольные данные (SPF, верификации, метаданные).
    • NS — серверы, ответственные за зону.
    • SRV, CAA и др. — для специфичных сервисов и политик.
  5. Как работает запрос DNS (упрощенно):

    • Клиент (браузер/приложение) вызывает системный резолвер:
      • проверяется локальный кеш;
      • при отсутствии записи → запрос на DNS-сервер (обычно провайдерский или публичный).
    • Резолвер при необходимости опрашивает:
      • корневые DNS-серверы (root),
      • TLD-серверы (.com, .net, .org...),
      • авторитетные DNS-серверы конкретного домена.
    • Результат кэшируется на всех уровнях (TTL), чтобы снизить нагрузку и ускорить ответы.
  6. Связь с практикой backend/DevOps/Go:

  • При разработке и эксплуатации сервисов важно:
    • понимать, что:
      • смена DNS-записи не мгновенна (TTL, кеши);
      • некорректный DNS — частая причина недоступности.
    • уметь отлаживать:
      • dig, nslookup, host для проверки DNS;
      • пример:
        dig api.example.com
        dig +short google.com
  • В микросервисной и cloud-среде:
    • DNS используется для:
      • service discovery (имена сервисов вместо жестких IP);
      • внутренних доменов (service.namespace.svc.cluster.local в Kubernetes).

Кратко:

  • DNS нужен, чтобы связать доменные имена с IP-адресами и скрыть динамику инфраструктуры за стабильными именами.
  • Он обеспечивает удобство для людей, гибкость и масштабируемость для систем, и является критически важным компонентом сети: если ломается DNS — «ломается Интернет» для конечного пользователя, даже если сами сервисы работают.

Вопрос 24. Какие типы DNS-записей вы знаете, в частности для сопоставления доменного имени с IPv4-адресом?

Таймкод: 00:19:24

Ответ собеседника: неполный. Не называет тип записи самостоятельно; после подсказки собеседника упоминается тип A.

Правильный ответ:

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

Ключевой ответ на уточняющий вопрос:

  • Для сопоставления доменного имени с IPv4-адресом используется запись типа A (Address record).

Далее — краткий, но содержательный обзор основных записей:

  1. A (Address)

    • Назначение:
      • сопоставляет доменное имя с IPv4-адресом.
    • Пример:
      example.com.    300    IN    A     203.0.113.10
    • Использование:
      • клиенты (браузеры, приложения) получают IP по имени и устанавливают TCP/UDP-соединения.
  2. AAAA (Quad-A)

    • Назначение:
      • сопоставляет доменное имя с IPv6-адресом.
    • Пример:
      example.com.    300    IN    AAAA  2001:db8::1
  3. CNAME (Canonical Name)

    • Назначение:
      • делает одно доменное имя алиасом другого.
    • Пример:
      www.example.com.  300  IN  CNAME  example.com.
    • Особенности:
      • имя с CNAME не должно иметь одновременно A/AAAA-записи;
      • удобно для маршрутизации на внешние сервисы (CDN, балансировщики).
  4. MX (Mail eXchanger)

    • Назначение:
      • указывает почтовые серверы для домена.
    • Пример:
      example.com.  300  IN  MX  10 mail1.example.com.
  5. TXT

    • Назначение:
      • хранит произвольный текст.
    • Типичные применения:
      • SPF/DKIM/DMARC для почты;
      • валидация домена у внешних сервисов.
    • Пример:
      example.com.  300  IN  TXT  "v=spf1 include:_spf.example.net -all"
  6. NS (Name Server)

    • Назначение:
      • указывает авторитетные DNS-серверы для зоны.
    • Пример:
      example.com.  300  IN  NS   ns1.example.net.
  7. PTR (Pointer)

    • Назначение:
      • обратное разрешение: IP → доменное имя.
    • Используется:
      • в rDNS-запросах, часто для почтовых серверов.
    • Пример:
      10.113.0.203.in-addr.arpa.  300  IN  PTR  example.com.
  8. SRV

    • Назначение:
      • описывает расположение сервисов (порт, приоритет, вес).
    • Используется:
      • в некоторых протоколах (SIP, XMPP, AD и др.).
  9. CAA

    • Назначение:
      • ограничивает список центров сертификации (CA), которые могут выпускать сертификаты для домена.

Практическая связка для инженера:

  • Для веб-сервиса:
    • домен → A/AAAA → IP балансировщика/ingress.
  • Для микросервисов и Kubernetes:
    • service discovery часто строится на DNS-именах.
  • В повседневной работе:
    • умение прочитать и проверить записи:
      dig A example.com
      dig AAAA example.com
      dig CNAME www.example.com

Краткий ответ, ожидаемый на интервью:

  • «Для сопоставления имени с IPv4-адресом используется запись типа A. Базовые типы: A, AAAA, CNAME, MX, TXT, NS, PTR, SRV. A/AAAA дают IP, CNAME — алиасы, MX — почта, TXT — метаданные и валидации, NS — авторитетные серверы, PTR — обратное разрешение.»

Вопрос 25. Что такое маска подсети и для чего она нужна?

Таймкод: 00:22:47

Ответ собеседника: неполный. Упоминает сабнеттинг и связь с количеством IP-адресов, но формулирует неуверенно и некорректно использует термин «домен».

Правильный ответ:

Маска подсети — это параметр, который определяет, какая часть IP-адреса относится к сети, а какая — к хосту внутри этой сети. Она используется для:

  • разделения адресного пространства на сети и подсети (сабнеттинг);
  • определения, находятся ли два IP-адреса в одной локальной сети;
  • вычисления диапазона доступных адресов в подсети;
  • корректной маршрутизации трафика (решения: отправлять пакет напрямую или через шлюз).

Ключевая идея:

  • IP-адрес (IPv4) — 32 бита.
  • Маска подсети — тоже 32 бита.
  • Биты маски:
    • 1 (единицы) — обозначают сетевую часть;
    • 0 (нули) — обозначают хостовую часть.

Пример базовый:

  • IP: 192.168.1.10
  • Маска: 255.255.255.0
    • В двоичном виде:
      • 255.255.255.0 =
        • 11111111.11111111.11111111.00000000
    • Это значит:
      • первые 24 бита — сеть,
      • последние 8 бит — хосты.

Сетевой адрес и диапазон:

  • Сетевой адрес:
    • 192.168.1.0 (все хостовые биты = 0).
  • Бродкаст:
    • 192.168.1.255 (все хостовые биты = 1).
  • Диапазон адресов хостов:
    • 192.168.1.1 — 192.168.1.254.
  • Количество адресов:
    • 2^(число хостовых бит) = 2^8 = 256,
    • из них 2 служебных (network + broadcast), рабочие хосты: 254.

CIDR-нотация:

Маска часто записывается как /N, где N — количество единичных бит в маске.

Примеры:

  • /24 → 255.255.255.0
  • /16 → 255.255.0.0
  • /32 → маска только для одного адреса (все биты сети, 0 хостов).
  • /30 → 255.255.255.252:
    • всего 4 адреса: 2 для хостов, 1 network, 1 broadcast — удобно для линков между роутерами.

Как определяется, в одной ли сети два адреса:

  1. Берём IP и маску.
  2. Считаем сетевой адрес: IP & MASK (битовое И).
  3. Если у двух IP сетевой адрес совпадает — они в одной сети.

Пример:

  • IP1: 10.0.1.5, маска /16 (255.255.0.0)
    • 10.0.1.5 & 255.255.0.0 → 10.0.0.0
  • IP2: 10.0.200.7, маска /16
    • 10.0.200.7 & 255.255.0.0 → 10.0.0.0
  • Оба в сети 10.0.0.0/16 → могут общаться напрямую в пределах L2-сегмента (если нет других ограничений).

Зачем это важно для практики (в т.ч. для Go/DevOps):

  • Настройка сетей в Kubernetes, Docker, облаках:
    • выбор CIDR для pod-сетей, service-сетей, VPC.
  • Избежание пересечения подсетей при VPN, multi-region, microservices.
  • Определение правил firewall’ов и маршрутов:
    • например, открыть доступ только для 10.0.0.0/24.
  • Диагностика сетевых проблем:
    • понять, почему хост «не видит» другой — часто маска/подсеть настроены неверно.

Краткий ответ для собеседования:

  • «Маска подсети определяет, какая часть IP-адреса — это адрес сети, а какая — адрес хоста. С её помощью мы делим адресное пространство на подсети, считаем количество доступных адресов и определяем, должны ли пакеты для конкретного IP идти напрямую к хосту или через шлюз. Например, для 192.168.1.10/24 сеть — 192.168.1.0, хостовая часть — последний октет.»

Вопрос 26. Что такое NAT и для чего он используется?

Таймкод: 00:24:46

Ответ собеседника: неправильный. Сообщает, что с NAT не знаком.

Правильный ответ:

NAT (Network Address Translation) — это механизм, при котором сетевое устройство (обычно маршрутизатор или firewall) изменяет IP-адреса и/или порты в проходящих через него пакетах. Основная цель — разделять внутреннюю (частную) адресацию и внешнюю (публичную), экономить публичные IP и управлять доступом между сетями.

Ключевая идея:

  • Внутри локальной сети используются частные IP (RFC1918): 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16.
  • В Интернете маршрутизируются только публичные IP.
  • NAT позволяет устройствам в частной сети выходить в Интернет, используя один или несколько публичных адресов.

Основные виды NAT (важно знать на собеседовании):

  1. Static NAT (один-к-одному)

    • Один внутренний IP ↔ один внешний IP.
    • Применение:
      • проброс конкретного сервера из внутренней сети наружу.
    • Пример:
      • 10.0.0.10 ↔ 203.0.113.10.
  2. Dynamic NAT (многие-к-многим из пула)

    • Внутренние адреса динамически сопоставляются с пулом внешних адресов.
    • Менее популярен в сравнении с PAT.
  3. PAT / NAPT (Port Address Translation)

    • Самый распространенный вариант, обычно подразумевается, когда говорят просто «NAT».
    • Много внутренних адресов → один (или несколько) внешних IP за счет использования разных портов.
    • Пример:
      • Внутри:
        • 192.168.0.10:43210 → NAT → 203.0.113.10:50001
        • 192.168.0.11:53211 → NAT → 203.0.113.10:50002
      • Для внешнего мира всё выглядит так, будто трафик идет от одного IP, но разных портов.
    • Это то, что делает ваш домашний роутер:
      • десятки устройств в LAN выходят в Интернет через один внешний IP.

Что делает NAT технически:

  • На исходящем трафике (из LAN в Интернет):
    • подменяет source IP (и часто source port) на внешний;
    • запоминает соответствие (mapping) во внутренней таблице.
  • На входящем ответном трафике:
    • по таблице NAT находит, какому внутреннему IP:port отдать пакет;
    • подменяет адрес/порт назначения и пересылает во внутреннюю сеть.
  • Без явных правил (port forwarding) снаружи инициировать соединение к внутреннему хосту обычно нельзя:
    • это дает базовый уровень защиты.

Почему NAT важен для практики:

  • Экономия публичных IP:
    • позволяет огромному количеству клиентов использовать небольшое количество глобальных адресов.
  • Безопасность по умолчанию:
    • внутренние хосты неэкспонированы напрямую в Интернет.
  • Контроль и маршрутизация:
    • можно явно пробрасывать порты (port forwarding) для нужных сервисов.

Примеры, где знание NAT критично для инженера:

  • Домашний/офисный роутер:
    • почему curl http://localhost:8080 работает, а из Интернета нельзя достучаться без настроек port forwarding.
  • Backend и микросервисы:
    • outbound-подключения к внешним сервисам через NAT-инстансы в облаке.
  • Kubernetes:
    • SNAT/DNAT в iptables/ipvs:
      • Pod → внешний мир;
      • NodePort/LoadBalancer-сервисы;
      • hairpin NAT.
  • Диагностика проблем:
    • приложение не получает внешние запросы:
      • нужно проверить NAT/port forwarding, firewall, соответствие IP/портов.
    • проблемы с IP-based ACL:
      • из-за NAT внешний сервис видит один общий IP, а не реальные адреса клиентов.

Краткая формулировка для собеседования:

  • «NAT — это трансляция сетевых адресов. Он позволяет устройствам с приватными IP выходить в Интернет через один или несколько публичных адресов за счет подмены IP и портов и ведения таблицы соответствий. Чаще всего используется PAT: много внутренних адресов мапятся на один внешний IP с разными портами. Это критично для экономии IPv4 и влияет на маршрутизацию, доступность сервисов и настройку безопасности.»

Вопрос 27. Какие группы HTTP-статус-кодов существуют и какие распространённые коды вы знаете?

Таймкод: 00:25:10

Ответ собеседника: неполный. Называет коды 200 и 404, верно трактует 200 как успешный ответ, но не описывает диапазоны и не приводит другие ключевые примеры.

Правильный ответ:

HTTP-статус-коды делятся на 5 основных групп по диапазонам. Каждая группа отражает общий результат обработки запроса. Понимание этих групп критично при проектировании API, обработке ошибок, логировании и мониторинге.

Группы статус-кодов:

  1. 1xx — Informational (информационные)

    • Означают: запрос принят, продолжается обработка, но финального ответа ещё нет.
    • Используются относительно редко.
    • Примеры:
      • 100 Continue — сервер говорит клиенту, что начальные заголовки приняты, можно отправлять тело.
      • 101 Switching Protocols — переход на другой протокол (например, WebSocket).
  2. 2xx — Success (успешные)

    • Означают: запрос успешно обработан.
    • Ключевые:
      • 200 OK:
        • успешный ответ для большинства GET/POST, когда есть тело ответа.
      • 201 Created:
        • используется при создании ресурса (обычно на POST), желательно с Location заголовком.
      • 202 Accepted:
        • запрос принят, но обработка асинхронна (например, задача в очереди).
      • 204 No Content:
        • успешно, но без тела (часто для DELETE или PUT без содержимого ответа).
  3. 3xx — Redirection (перенаправления)

    • Означают: для завершения запроса клиенту нужно сделать новый запрос по другому URI.
    • Ключевые:
      • 301 Moved Permanently:
        • постоянный редирект; клиенты и поисковики могут кешировать.
      • 302 Found:
        • временный редирект (исторически используется широко).
      • 303 See Other:
        • после POST перейти по другому URI с GET.
      • 307 Temporary Redirect / 308 Permanent Redirect:
        • более корректные варианты 302/301 с сохранением метода (для 307) и жесткой семантикой.
  4. 4xx — Client Error (ошибки клиента)

    • Означают: проблема в запросе клиента (данные, авторизация, метод и т.п.).
    • Ключевые:
      • 400 Bad Request:
        • некорректный запрос (битый JSON, неверные параметры и т.п.).
      • 401 Unauthorized:
        • требуется аутентификация (нет или неверный токен/credentials).
      • 403 Forbidden:
        • аутентификация есть, но нет прав на доступ.
      • 404 Not Found:
        • ресурс не найден по указанному пути/ID.
      • 405 Method Not Allowed:
        • метод (POST/PUT/DELETE) не поддерживается для данного ресурса.
      • 409 Conflict:
        • конфликт состояния (дубликат, версия ресурса, бизнес-конфликт).
      • 429 Too Many Requests:
        • превышен лимит запросов (rate limiting).
  5. 5xx — Server Error (ошибки сервера)

    • Означают: запрос был корректный, но сервер не смог его корректно обработать по своей вине.
    • Ключевые:
      • 500 Internal Server Error:
        • общая ошибка сервера (panic, необработанное исключение, и т.п.).
      • 502 Bad Gateway:
        • неверный ответ от следующего узла (upstream), часто на балансировщиках/gateway.
      • 503 Service Unavailable:
        • сервис временно недоступен (перегрузка, maintenance), желательно с Retry-After.
      • 504 Gateway Timeout:
        • не дождались ответа от upstream за отведённое время.

Практика для разработки и эксплуатации:

  • Группы важны для:

    • алертинга:
      • рост 5xx → проблема на стороне сервиса/инфраструктуры;
      • рост 4xx → чаще проблема клиентов или контракта API.
    • логирования и метрик:
      • агрегирование по диапазонам (2xx/4xx/5xx) для быстрой диагностики.
  • В Go (пример корректного использования):

func getUserHandler(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
if id == "" {
http.Error(w, "missing id", http.StatusBadRequest) // 400
return
}

user, err := findUserByID(r.Context(), id)
if err == ErrNotFound {
http.Error(w, "user not found", http.StatusNotFound) // 404
return
}
if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError) // 500
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) // 200
_ = json.NewEncoder(w).Encode(user)
}

Грамотное использование кодов:

  • повышает предсказуемость API;
  • упрощает отладку клиентов;
  • улучшает наблюдаемость и реакцию на инциденты.

Вопрос 28. Какие основные HTTP-методы вы знаете?

Таймкод: 00:26:07

Ответ собеседника: правильный. Перечисляет GET, POST, PUT и DELETE как основные методы.

Правильный ответ:

Основные HTTP-методы описывают семантику операций над ресурсами. Важно не просто перечислить методы, но понимать их назначение, идемпотентность и типичные практики использования при проектировании API.

Ключевые методы:

  1. GET

    • Назначение:
      • получить представление ресурса.
    • Характеристики:
      • не изменяет состояние сервера (должен быть безопасным);
      • идемпотентен: повторные одинаковые запросы не меняют результат на сервере;
      • можно кешировать.
    • Примеры:
      • GET /users/123 — получить пользователя.
      • GET /health — health-check.
    • Go-пример:
      http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
      if r.Method != http.MethodGet {
      http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
      return
      }
      // вернуть список пользователей
      })
  2. POST

    • Назначение:
      • создать ресурс;
      • запустить действие (команда, обработка формы, старт задачи);
      • отправить данные, которые сервер интерпретирует.
    • Характеристики:
      • не является идемпотентным:
        • два одинаковых POST-запроса могут создать два ресурса или запустить действие дважды;
      • обычно не кешируется по умолчанию.
    • Примеры:
      • POST /users — создать пользователя.
      • POST /tasks/{id}/run — запустить задачу.
    • Типичный ответ при создании:
      • 201 Created + Location.
  3. PUT

    • Назначение:
      • полная замена ресурса по указанному URI.
    • Характеристики:
      • идемпотентен:
        • повторный одинаковый PUT приводит к тому же состоянию ресурса;
      • клиент присылает полное представление ресурса.
    • Примеры:
      • PUT /users/123 — заменить данные пользователя 123.
    • Если ресурса не существует:
      • в некоторых дизайнах допустимо создавать (upsert), но это требует явной договоренности.
  4. PATCH

    • Назначение:
      • частичное обновление ресурса.
    • Характеристики:
      • не гарантированно идемпотентен по смыслу (зависит от реализации);
      • тело содержит изменения (diff), а не полный ресурс.
    • Примеры:
      • PATCH /users/123 — изменить только email или имя.
    • Часто предпочтителен для API, чтобы не передавать весь объект.
  5. DELETE

    • Назначение:
      • удалить ресурс.
    • Характеристики:
      • идемпотентен:
        • повторный DELETE того же ресурса должен давать тот же эффект (ресурса нет);
      • может возвращать 200, 202 или 204.
    • Примеры:
      • DELETE /users/123 — удалить пользователя.

Другие важные методы (кратко):

  • HEAD:
    • как GET, но без тела ответа;
    • используется для проверки доступности/метаданных.
  • OPTIONS:
    • запрос доступных методов и параметров для ресурса;
    • активно используется в CORS.
  • CONNECT, TRACE:
    • специфичные, используются редко (прокси, диагностика).

Практические рекомендации при проектировании API:

  • Использовать методы в соответствии со стандартной семантикой:
    • GET — только чтение;
    • POST — создание или действия;
    • PUT/PATCH — обновление;
    • DELETE — удаление.
  • Учитывать идемпотентность:
    • это важно для ретраев, балансировщиков, прокси.
  • Связывать методы с корректными статус-кодами:
    • POST → 201 Created;
    • GET → 200 OK / 404 Not Found;
    • DELETE → 204 No Content;
    • PUT/PATCH → 200/204.

Пример минимального CRUD на Go (фрагмент):

func userHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
// чтение пользователя
case http.MethodPost:
// создание пользователя
case http.MethodPut:
// полное обновление пользователя
case http.MethodPatch:
// частичное обновление
case http.MethodDelete:
// удаление
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}

Такое понимание показывает зрелый подход к проектированию HTTP API и корректной обработке запросов.

Вопрос 29. В чём отличие метода GET от метода POST?

Таймкод: 00:26:26

Ответ собеседника: правильный. Указывает, что GET используется для получения данных, а POST — для отправки (внесения) данных.

Правильный ответ:

Отличие между GET и POST — одно из базовых, но важно уметь объяснять его не только на бытовом уровне, но и с точки зрения семантики HTTP, кеширования, идемпотентности и безопасности.

Основные различия:

  1. Семантика
  • GET:

    • Используется для чтения/получения ресурса.
    • Не должен изменять состояние на сервере (safe-метод).
    • Примеры:
      • GET /users/123
      • GET /products?page=2&size=20
  • POST:

    • Используется для создания ресурса или выполнения действий, которые изменяют состояние.
    • Не является безопасным и не является идемпотентным:
      • два одинаковых POST-запроса могут создать два разных ресурса или дважды выполнить действие.
    • Примеры:
      • POST /users — создание пользователя;
      • POST /orders — создание заказа;
      • POST /tasks/{id}/run — запуск задачи.
  1. Идемпотентность
  • GET:
    • Идемпотентен:
      • многократное выполнение одного и того же GET-запроса не должно изменять состояние ресурса.
  • POST:
    • Не идемпотентен по определению:
      • повторные запросы могут приводить к повторным операциям (двойная оплата, повторное создание).
  1. Передача данных
  • GET:
    • Данные передаются в URL (query-параметры, часть пути).
    • Ограничения по длине URL (зависят от клиента/серверов/прокси).
    • Не подходит для больших объёмов данных и чувствительной информации.
  • POST:
    • Данные передаются в теле запроса (body).
    • Нет практического ограничения размера на уровне протокола (ограничения задаются сервером/инфраструктурой).
    • Подходит для форм, JSON, файлов, сложных структур.
  1. Кеширование и прокси
  • GET:
    • Может кешироваться браузером, CDN, прокси:
      • при наличии соответствующих заголовков (Cache-Control, ETag, Last-Modified).
  • POST:
    • Обычно не кешируется по умолчанию;
    • кеширование POST — редкий и специфичный кейс, требует явных заголовков и поддержки.
  1. Идём через инфраструктуру (безопасность/логирование)
  • GET:
    • Параметры запроса видны в URL:
      • логи веб-сервера,
      • история браузера,
      • прокси.
    • Не стоит передавать через query токены, пароли, персональные данные.
  • POST:
    • Данные в теле:
      • не попадают в URL и историю адресной строки,
      • но всё ещё могут логироваться сервером/прокси.
    • Для секретных данных в любом случае обязателен HTTPS.
  1. Практика для API-дизайна
  • Рекомендуемый подход (RESTful-стиль):
    • GET:
      • получение ресурса или коллекции.
    • POST:
      • создание ресурса;
      • запуск операций, которые не ложатся на стандартные CRUD-семантики.
    • Не использовать GET для операций, изменяющих состояние:
      • нельзя делать "GET /do-delete-all";
      • это ломает ожидания клиентов, кешей, поисковиков и может быть опасно.

Пример на Go:

func handler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
// безопасное получение данных
// пример: вернуть пользователя
case http.MethodPost:
// обработка данных из тела запроса
// пример: создать нового пользователя
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}

Кратко:

  • GET — для чтения, идемпотентен, может кешироваться, параметры обычно в URL.
  • POST — для создания/изменения/действий, не идемпотентен, данные в теле запроса, не кешируется по умолчанию.

Вопрос 30. Что такое CORS?

Таймкод: 00:26:49

Ответ собеседника: неправильный. Упоминает, что слышал про CORS-заголовки, но не помнит, что это такое.

Правильный ответ:

CORS (Cross-Origin Resource Sharing) — это механизм безопасности в браузерах, который контролирует, может ли веб-страница, загруженная с одного источника (origin), обращаться к ресурсам другого источника.

Источник (origin) определяется тройкой:

  • схема (protocol): http / https,
  • хост (domain),
  • порт.

Если хоть один из этих параметров отличается — это другой origin.

Задача CORS: защитить пользователей от несанкционированных запросов к чужим доменам от имени их браузера, но при этом позволить легитимным фронтендам общаться с backend-сервисами на других доменах при корректной настройке.

Пример разных origin:

Как это работает на практике:

  1. Базовая модель безопасности браузера — Same-Origin Policy (SOP)

    • По умолчанию браузер запрещает JavaScript-коду на странице делать многие типы запросов к другому origin и читать их ответы.
    • Это не мешает самому запросу физически уйти в сеть — блокируется доступ к результату на стороне браузера.
  2. CORS как контролируемое ослабление SOP

    • CORS позволяет серверу явно сказать браузеру:
      • «этим origin я доверяю, им можно читать ответы».
    • Управляется через специальные HTTP-заголовки в ответе сервера.

Ключевые заголовки CORS:

  1. Access-Control-Allow-Origin
  • Определяет, какому origin разрешён доступ к ответу.
  • Примеры:
    • Разрешить одному домену:
      Access-Control-Allow-Origin: https://app.example.com
    • Разрешить всем (часто опасно для приватных API):
      Access-Control-Allow-Origin: *
  1. Access-Control-Allow-Methods
  • Какие HTTP-методы разрешены при cross-origin-запросах.
  • Пример:
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
  1. Access-Control-Allow-Headers
  • Какие нестандартные заголовки клиент может отправить.
  • Пример:
    Access-Control-Allow-Headers: Content-Type, Authorization, X-Request-ID
  1. Access-Control-Allow-Credentials
  • Разрешено ли отправлять куки/авторизационные данные в cross-origin-запросах.
  • Пример:
    Access-Control-Allow-Credentials: true
  • При этом:
    • нельзя использовать * в Access-Control-Allow-Origin,
    • нужно указать конкретный origin.

Preflight-запрос (OPTIONS):

  • Для потенциально «опасных» запросов браузер сначала делает preflight:
    • OPTIONS /endpoint
    • с заголовками:
      • Origin
      • Access-Control-Request-Method
      • Access-Control-Request-Headers
  • Сервер отвечает, какие методы и заголовки разрешены.
  • Если ответ удовлетворяет требованиям — браузер выполняет основной запрос.
  • Если нет — блокирует на стороне клиента.

Типичный пример:

  • Фронтенд на https://app.example.com обращается к API на https://api.example.com.
  • Браузер посылает:
    Origin: https://app.example.com
  • Сервер api.example.com отвечает:
    Access-Control-Allow-Origin: https://app.example.com
  • Браузер разрешает JS-коду прочитать ответ.

Если заголовок не выставлен или не совпадает — запрос может уйти в сеть, но браузер заблокирует доступ к ответу из JavaScript.

Почему это важно для backend/API-разработчика:

  • Ошибки CORS — типичная причина «всё работает через curl/Postman, но не работает из браузера».
  • CORS настраивается на сервере, а не в браузере:
    • если фронту нельзя сделать запрос — проблема в конфигурации backend’а / прокси.
  • Опасные антипаттерны:
    • Access-Control-Allow-Origin: * вместе с Allow-Credentials: true (некорректно, браузеры блокируют);
    • бездумное разрешение всех origin для приватных API.

Пример настройки CORS в Go (net/http):

func withCORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
// Пример: разрешаем только конкретное приложение
if origin == "https://app.example.com" {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Vary", "Origin")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Allow-Credentials", "true")
}

if r.Method == http.MethodOptions {
// Preflight-запрос: отвечаем заголовками и без тела
w.WriteHeader(http.StatusNoContent)
return
}

next.ServeHTTP(w, r)
})
}

Кратко:

  • CORS — механизм, с помощью которого сервер через заголовки управляет тем, какие браузерные приложения с других origin могут получать доступ к его ресурсам.
  • Он не про защиту сервера от запросов, а про защиту пользователя и браузерного окружения, и обязан учитываться при проектировании любого публичного или фронтенд-ориентированного API.

Вопрос 31. Чем отличается виртуальная машина от контейнера?

Таймкод: 00:27:11

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

Правильный ответ:

Разница между виртуальными машинами и контейнерами — фундаментальная тема для понимания современной инфраструктуры, производительности и способов доставки приложений.

Суть в одном предложении:

  • Виртуальная машина эмулирует целую машину с собственным ядром ОС.
  • Контейнер разделяет ядро хостовой ОС и изолирует процессы на уровне пространства имён и cgroups.

Разберём по слоям.

Виртуальные машины (VM):

  • Архитектура:
    • Физический сервер (hardware)
    • Гипервизор (KVM, VMware ESXi, Hyper-V и т.п.)
    • Виртуальные машины:
      • внутри — полноценная гостевая ОС (Linux/Windows),
      • собственное ядро,
      • свои драйверы, init, системные службы.
  • Изоляция:
    • Жёсткая, на уровне виртуализированного железа.
    • Хорошо подходит для многотенантности, разных ОС, сильной безопасности.
  • Ресурсы:
    • Выделяются в виде vCPU, vRAM, vDisk.
    • Оверхед:
      • загрузка OS,
      • дублирование системных сервисов,
      • большее потребление памяти и диска.
  • Юз-кейсы:
    • изоляция разных клиентов/команд;
    • запуск разных ОС;
    • "тяжёлые" окружения с жесткими требованиями безопасности.

Контейнеры:

  • Архитектура:
    • Физический сервер или VM
    • Хостовая ОС с ядром Linux
    • Container runtime (Docker, containerd, CRI-O)
    • Контейнеры:
      • это обычные процессы на хосте,
      • изолированные через:
        • namespaces (pid, net, mnt, ipc, uts, user),
        • cgroups (лимиты CPU, RAM, IO),
        • дополнительные механизмы безопасности (seccomp, AppArmor/SELinux).
  • Ключевой момент:
    • Контейнеры разделяют одно ядро хоста.
    • Нельзя запустить Windows-кернел внутри Linux-контейнера.
  • Ресурсы:
    • Лёгкие:
      • нет отдельной гостевой ОС,
      • быстрый старт (миллисекунды/секунды),
      • меньше потребление памяти и диска.
  • Юз-кейсы:
    • микросервисы, stateless-сервисы;
    • плотная упаковка сервисов;
    • быстрый CI/CD;
    • Dev/Stage/Prod с идентичными окружениями.

Сравнение по ключевым аспектам:

  • Изоляция:
    • VM: сильная, граница — гипервизор и виртуальное железо.
    • Container: изоляция на уровне ОС, теоретически слабее, но с правильной настройкой достаточно безопасна для большинства сценариев.
  • Гибкость:
    • VM: можно запускать разные ОС, разные ядра.
    • Container: одна ОС/ядро, разные userland.
  • Скорость:
    • VM: медленнее старт (десятки секунд и выше).
    • Container: очень быстрый старт.
  • Плотность:
    • VM: 10–50 ВМ на хост (условно, сильно зависит).
    • Container: сотни и тысячи контейнеров на тех же ресурсах.
  • Использование:
    • Часто: контейнеры крутятся внутри ВМ в облаке (VM как boundary, контейнеры как рабочая единица деплоя).

Практический пример с Go:

  1. Go-сервис в контейнере (типичный, лёгкий вариант):
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o app ./cmd/app

FROM alpine:3.20
RUN adduser -D appuser
USER appuser
COPY --from=builder /app/app /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]
  • Такой контейнер:
    • использует ядро хостового Linux;
    • стартует за доли секунды;
    • содержит только бинарник и минимальный userland.
  1. Тот же сервис во VM:
  • Вам нужно:
    • создать ВМ (через KVM/ESXi/облако),
    • поставить в неё ОС (Ubuntu, CentOS),
    • установить зависимости, добавить пользователя, systemd unit, firewall и т.д.
  • Это тяжелее по времени, ресурсу, сопровождению, но даёт более жёсткую изоляцию.

Как это использовать в аргументации:

  • Контейнеры:
    • идеальны для микросервисов, CI/CD, горизонтального масштабирования, ephemeral-окружений.
    • управляются оркестраторами (Kubernetes, Nomad).
  • ВМ:
    • основа для инфраструктуры (k8s-ноды, базы данных, сложные монолиты, сервисы с особыми требованиями).
    • хорошо комбинируются: VM как безопасная граница; внутри — контейнеры для приложений.

Кратко:

  • Виртуальная машина виртуализует железо и запускает отдельную ОС.
  • Контейнер виртуализует окружение процесса и использует общее ядро хоста.
  • Контейнеры легче и быстрее, ВМ — тяжелее, но дают более сильную изоляцию и гибкость по ОС.

Вопрос 32. Можно ли на Linux-хосте запустить контейнер с Windows и почему?

Таймкод: 00:28:14

Ответ собеседника: правильный. Говорит, что запустить Windows-контейнер на Linux-хосте нельзя из-за различий ядер.

Правильный ответ:

Классический контейнер не виртуализует ядро, он делит ядро хоста между изолированными процессами. Поэтому:

  • Linux-контейнеры используют ядро Linux.
  • Windows-контейнеры используют ядро Windows.

Из этого следует ключевое правило:

  • Нельзя нативно запустить Windows-контейнер на Linux-хосте, и наоборот, потому что:
    • контейнер не приносит с собой собственное ядро;
    • бинарники и системные вызовы зависят от конкретного ядра;
    • Windows API и Linux syscalls принципиально различаются.

Важно различать:

  • Образы Linux-контейнеров:
    • содержат userland (библиотеки, утилиты), но опираются на ядро Linux.
  • Образы Windows-контейнеров:
    • рассчитаны на ядро Windows (Windows Server / Windows с поддержкой контейнеров).

Попытка запустить Windows-образ на Linux через Docker или Kubernetes:

  • завершится ошибкой:
    • runtime не сможет использовать образ, предназначенный для другого ОС/ядра.

Что можно сделать на практике:

  • Использовать виртуализацию:
    • На Linux-хосте поднять ВМ с Windows (через KVM/ESXi/облако).
    • Внутри Windows-VM запускать Windows-контейнеры.
  • Обратный сценарий:
    • На Windows (Docker Desktop, WSL2) Linux-контейнеры фактически работают внутри легковесной Linux-VM.

Вывод:

  • Нативный контейнер — не эмулятор и не полноценная виртуальная машина.
  • Windows-контейнер требует Windows-ядра; Linux-контейнер — Linux-ядра.
  • Кросс-ОС запуск возможен только через дополнительный уровень виртуализации, а не напрямую средствами контейнеризации.

Вопрос 33. Какую ОС вы используете и запускали ли Docker на Windows (включая через WSL)?

Таймкод: 00:29:04

Ответ собеседника: правильный. Сообщает, что использует Windows как основную систему, Ubuntu — в виртуальной машине, Docker запускал только на Linux, про WSL знает со слов собеседника, практического опыта с Docker на Windows/WSL нет.

Правильный ответ:

Корректный и технически содержательный ответ на такой вопрос должен:

  • показать, что вы понимаете различия окружений разработки;
  • знаете, как Docker работает под Windows;
  • понимаете роль WSL2 и легковесных VM.

Краткий, сильный ответ может выглядеть так.

Я обычно использую:

  • Windows как рабочую десктопную ОС.
  • Linux (Ubuntu, Debian, RHEL-based) как целевую среду:
    • либо в виртуальных машинах (VirtualBox/Hyper-V/VMware),
    • либо через WSL2,
    • либо на удаленных серверах.

Запуск Docker на Windows:

  1. Docker Desktop для Windows

    • Современный Docker на Windows (особенно для Linux-контейнеров) работает через:
      • WSL2 backend или
      • легковесную Linux-VM (раньше через Hyper-V).
    • Для Linux-контейнеров:
      • фактически используется Linux-ядро внутри WSL2/VM;
      • контейнеры остаются Linux-контейнерами (то есть поведение близко к Linux-серверу).
    • Для Windows-контейнеров:
      • Docker Desktop переключается в режим Windows Containers;
      • используется ядро Windows, поддерживаются только совместимые образы.
  2. WSL2 (Windows Subsystem for Linux 2)

    • Это полноценное Linux-ядро, работающее внутри Windows.
    • Варианты:
      • запуск Docker Engine внутри дистрибутива WSL2 (Ubuntu/Alpine);
      • использование WSL2 как backend для Docker Desktop.
    • Преимущества:
      • поведение окружения очень близко к реальному Linux-серверу;
      • удобно для разработки backend’ов на Go, тестирования Dockerfile, CI/CD-скриптов.
  3. Почему это важно для разработки и DevOps:

  • Разная ОС на рабочей машине и в продакшне — нормальная ситуация.
    • Важно:
      • уметь воспроизводить prod-поведение в максимально близком окружении;
      • для этого и используются Docker, WSL2, VM.
  • При работе с Go и контейнерами:
    • разрабатываем на Windows/macOS,
    • собираем и тестируем в Linux-контейнере (Docker),
    • деплоим в Kubernetes / Linux-сервера.
  • Понимание архитектуры:
    • Docker на Windows для Linux-контейнеров всегда опирается на Linux-ядро (через WSL2/VM);
    • это устраняет многие несоответствия и делает сценарий «разработка на Windows → прод в Linux» рабочим и предсказуемым.

Пример практичного подхода:

  • Локально:
    • использовать Docker Desktop + WSL2 (Ubuntu);
    • держать Go-код в общей директории и собирать внутри Linux-окружения.
  • Пример сборки Go-сервиса в Docker (независимо от хост-ОС):
docker build -t myapp:local .
docker run --rm -p 8080:8080 myapp:local

Итого:

  • Ответ «использую Windows, Docker — на Linux/WSL/VM, понимаю, что под капотом для Linux-контейнеров всегда работает Linux-ядро» считается корректным и зрелым.
  • Важно уметь пояснить, что Docker на Windows для Linux-контейнеров не делает «магии», а использует прослойку с Linux-ядром, поэтому поведение контейнеров остаётся консистентным с серверным Linux-продакшеном.

Вопрос 34. Есть ли опыт написания Dockerfile и для чего он нужен?

Таймкод: 00:29:31

Ответ собеседника: правильный. Говорит, что писал Dockerfile и использует его для описания базового образа и шагов сборки образа.

Правильный ответ:

Dockerfile — это декларативный скрипт (текстовый файл), в котором описано, как из базового образа собрать конечный образ контейнера: какие зависимости установить, какие файлы скопировать, какие команды выполнить и какой процесс запустить.

Ключевые цели Dockerfile:

  • зафиксировать воспроизводимое окружение для приложения;
  • избавиться от «works on my machine»;
  • упростить интеграцию с CI/CD;
  • минимизировать размер и повысить безопасность образа;
  • стандартизировать запуск сервисов во всех окружениях.

Основные инструкции Dockerfile:

  • FROM — выбор базового образа.
  • WORKDIR — рабочая директория внутри контейнера.
  • COPY / ADD — копирование файлов в образ.
  • RUN — выполнение команд на этапе сборки (установка пакетов, билд).
  • ENV — установка переменных окружения.
  • EXPOSE — документирование порта (для людей и оркестраторов).
  • USER — под каким пользователем запускать процесс (важно для безопасности).
  • CMD / ENTRYPOINT — команда, которая запускается при старте контейнера.

Пример: продакшн-ориентированный Dockerfile для Go-сервиса (multi-stage build)

# Стейдж 1: сборка бинарника
FROM golang:1.22-alpine AS builder

WORKDIR /app

# Оптимизация кеша: сначала зависимости
COPY go.mod go.sum ./
RUN go mod download

# Затем остальной код
COPY . .

# Сборка с отключением CGO и указанием целевой платформы
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o app ./cmd/app

# Стейдж 2: минимальный рантайм-образ
FROM alpine:3.20

# Создаем непривилегированного пользователя
RUN adduser -D -g '' appuser

WORKDIR /app
USER appuser

# Копируем только бинарник
COPY --from=builder /app/app /app/app

# (опционально) переменные окружения
ENV GIN_MODE=release

EXPOSE 8080

ENTRYPOINT ["/app/app"]

Почему этот пример хорош:

  • Используется multi-stage build:
    • сборка в полном golang-образе;
    • рантайм — минимальный alpine без build-инструментов.
  • Малый attack surface:
    • нет компиляторов, менеджеров пакетов, шеллов «на всякий случай»;
    • запускаем под отдельным пользователем, а не root.
  • Воспроизводимость:
    • фиксированная версия Go и базового образа;
    • все шаги задокументированы кодом.

Типичные практики и замечания уровня продакшена:

  • Размер образа:
    • минимизировать базовый образ:
      • alpine, distroless, scratch для Go особенно хорошо подходят.
  • Кеширование:
    • порядок инструкций COPY и RUN влияет на кеш Docker:
      • сначала зависимости, потом код → быстрее инкрементные сборки.
  • Безопасность:
    • не запускать процесс под root;
    • не хранить секреты в образе (использовать env/secret-менеджеры).
  • Конфигурация:
    • через переменные окружения (12-factor style), а не хардкод в Dockerfile.
  • Логи:
    • приложение пишет в stdout/stderr;
    • инфраструктура/оркестратор собирает и маршрутизирует логи.

Интеграция с CI/CD:

  • Dockerfile — ключевой элемент пайплайна:
    • CI:
      • docker build → тесты → security scan → docker push;
    • CD:
      • деплой образа в Kubernetes/номад/на сервера.
  • Пример команды:
    docker build -t registry.example.com/myapp:${CI_COMMIT_SHA} .
    docker push registry.example.com/myapp:${CI_COMMIT_SHA}

Краткая формулировка для интервью:

  • «Да, писал Dockerfile. Использую его, чтобы формализовать окружение приложения: выбрать базовый образ, собрать Go-бинарник, добавить только нужные зависимости, настроить пользователя и команду запуска. Обычно делаю multi-stage build для маленьких и безопасных образов и интегрирую это в CI/CD.»

Вопрос 35. Чем отличается образ от контейнера?

Таймкод: 00:30:08

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

Правильный ответ:

Кратко:

  • Образ (image) — это шаблон (immutable-слой), из которого создаются контейнеры.
  • Контейнер (container) — это запущенный экземпляр образа (runtime-состояние: процессы, файловая система, сеть).

Подробно:

  1. Образ (Image)
  • Что это:
    • Набор слоёв файловой системы + метаданные:
      • базовый образ (например, alpine, ubuntu, gcr.io/distroless/base);
      • ваши бинарники, зависимости, конфиги, переменные окружения, ENTRYPOINT/CMD.
    • Иммутабелен:
      • после сборки не меняется; любые изменения при запуске происходят уже в контейнерном слое.
  • Где хранится:
    • локальный Docker registry/кеш;
    • удаленные registry: Docker Hub, GitHub Container Registry, GitLab Registry, ECR, GCR, Harbor и т.п.
  • Идентификация:
    • по имени и тегу:
      • myapp:latest
      • myapp:1.2.3
      • myapp:git-sha
  • Роль:
    • стандартный, воспроизводимый артефакт, который используется в CI/CD и оркестраторах.
    • единая «упаковка» приложения: то, что собрали, точно так же запустится в любом окружении.
  1. Контейнер (Container)
  • Что это:
    • Запущенный экземпляр образа:
      • собственный слой поверх образа (writable layer);
      • собственное состояние:
        • процессы,
        • открытые файлы,
        • сетевые интерфейсы/порты,
        • runtime-конфигурация (env, volume-монты).
  • Свойства:
    • изолирован от других контейнеров и хоста через namespaces/cgroups;
    • эфемерен:
      • его файловая система (writable-слой) обычно не должна считаться долговечной;
      • при удалении контейнера изменения внутри него пропадают (если не вынесены в volumes).
  • Жизненный цикл:
    • создаётся из образа:
      docker run -d --name myapp -p 8080:8080 myapp:1.0.0
    • пока контейнер жив — внутри него крутится процесс (обычно один главный);
    • когда процесс завершается — контейнер останавливается.
  1. Аналогия:
  • Образ — как бинарник + набор зависимостей и конфиг (read-only шаблон).
  • Контейнер — как запущенный процесс этого бинарника в изолированном окружении.
  1. Практика в терминах Go и CI/CD:
  • В CI:
    • собираем образ:
      docker build -t registry.example.com/myapp:${CI_COMMIT_SHA} .
      docker push registry.example.com/myapp:${CI_COMMIT_SHA}
  • В проде (или локально):
    • запускаем множество контейнеров из одного и того же образа:
      docker run -d --name myapp-1 myapp:${CI_COMMIT_SHA}
      docker run -d --name myapp-2 myapp:${CI_COMMIT_SHA}
    • они:
      • используют один и тот же образ (код и зависимости идентичны),
      • но являются разными инстансами с разным состоянием.
  1. В Kubernetes:
  • Deployment указывает:
    • какой image использовать (например, myapp:1.0.0);
  • Kubernetes:
    • скачивает image на node;
    • создаёт один или несколько Pod’ов;
    • в каждом Pod’е запускает контейнер(ы) на основе этого image.
  • Обновление версии:
    • меняем image с myapp:1.0.0 на myapp:1.0.1;
    • создаются новые контейнеры с новой версией образа.

Кратко, как стоит отвечать на интервью:

  • «Образ — это неизменяемый шаблон (слои файловой системы + метаданные), в котором описано, что и как запускать. Контейнер — это запущенный экземпляр образа с собственным состоянием (процессы, сетевые namespace, writable-слой). Из одного образа можно запустить много контейнеров, что делает деплой предсказуемым и масштабируемым.»

Вопрос 36. С какой инструкции начинается Dockerfile?

Таймкод: 00:30:31

Ответ собеседника: правильный. Правильно называет инструкцию FROM, задающую базовый образ.

Правильный ответ:

В корректном Dockerfile первая обязательная инструкция — FROM (за исключением специального случая ARG до первого FROM в современных версиях Docker, но базовый образ всё равно задаётся через FROM).

Инструкция FROM:

  • Определяет базовый образ, от которого будет «наследоваться» ваш образ.
  • Может использоваться один или несколько раз (multi-stage build).
  • Синтаксис:
    • FROM <image>[:tag] [AS name]

Примеры:

  1. Простой Dockerfile для Go-приложения:
FROM golang:1.22-alpine

WORKDIR /app
COPY . .
RUN go build -o app ./cmd/app

CMD ["./app"]
  1. Multi-stage build (рекомендуемый подход):
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o app ./cmd/app

FROM alpine:3.20
WORKDIR /app
COPY --from=builder /app/app .
CMD ["./app"]

Ключевые моменты:

  • Первый FROM определяет контекст: какая ОС/окружение будут использованы для последующих инструкций.
  • Без FROM Docker не знает, на какой файловой системе и окружении выполнять RUN, COPY и другие инструкции.
  • Исключение:
    • ARG может предшествовать первому FROM для задания build-аргументов, используемых в самом FROM, например:
      ARG GO_VERSION=1.22
      FROM golang:${GO_VERSION}-alpine AS builder
    • Но даже в этом случае FROM остаётся первой ключевой инструкцией, определяющей базовый образ.

Вопрос 37. В чем отличие инструкций ADD и COPY в Dockerfile?

Таймкод: 00:31:03

Ответ собеседника: неполный. Описывает COPY корректно, предполагает, что ADD обладает дополнительными возможностями (в т.ч. внешний источник), но формулирует неуверенно и без акцента на best practices.

Правильный ответ:

ADD и COPY оба копируют файлы в образ, но:

  • COPY — простой и предсказуемый инструмент копирования.
  • ADD — делает то же самое, но дополнительно выполняет «магические» действия:
    • распаковка локальных tar-архивов;
    • поддержка источника в виде URL.

В продакшн-практике почти всегда рекомендуется использовать COPY, а ADD — только когда сознательно нужна одна из его дополнительных возможностей.

Разберём по пунктам.

COPY:

  • Назначение:
    • тупо копирует файлы и директории из build-контекста (локальный каталог, отправляемый Docker daemon’у) в файловую систему образа.
  • Не делает ничего «лишнего»:
    • не скачивает по сети;
    • не распаковывает архивы;
    • не меняет содержимое.
  • Синтаксис:
    COPY src/ /app/
    COPY go.mod go.sum ./
  • Использование:
    • предпочтительный вариант для большинства случаев;
    • предсказуемое поведение → лучший контроль кеша и безопасности.

ADD:

  • Делает всё то же, что COPY, плюс:

    1. Если источник — локальный tar-архив, ADD автоматически распакует его содержимое в целевую директорию.

      ADD app.tar.gz /app/
      • В результате в /app будет распакован архив.
    2. Может использовать URL как источник:

      ADD https://example.com/file.tar.gz /tmp/file.tar.gz
      • Docker сам скачает файл при сборке.
  • Почему с этим осторожно:

    • URL в ADD:
      • ухудшает воспроизводимость:
        • содержимое по URL может измениться;
        • сборка станет недетерминированной;
        • возможны проблемы с кешированием.
      • сложнее контролировать кеш Docker’а:
        • изменение содержимого по URL не всегда инвалидирует слой, если имя/URL те же.
      • безопасность:
        • вынос сетевого поведения в Dockerfile усложняет анализ.
    • Автораспаковка tar:
      • иногда полезна, но скрытая «магия»:
        • по Dockerfile не всегда очевидно, что произойдёт распаковка;
        • сложнее контролировать структуру слоёв.

Best practices (как правильно отвечать на собеседовании):

  • COPY — использовать по умолчанию:

    • для копирования исходников, бинарников, конфигов:
      COPY . /app
  • ADD — использовать только когда:

    • осознанно нужна распаковка локального tar-архива;
    • и вы точно понимаете эффект.
  • Для скачивания внешних файлов:

    • рекомендуется не использовать ADD, а делать это через RUN + curl/wget:
      RUN curl -fsSL https://example.com/tool -o /usr/local/bin/tool
    • так:
      • явно видно сетевую операцию;
      • проще управлять кешем;
      • можно проверить checksum, версионирование и т.д.

Мини-пример для Go-сервиса (корректный подход):

FROM golang:1.22-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN go build -o app ./cmd/app

FROM alpine:3.20
WORKDIR /app
COPY --from=builder /app/app .
ENTRYPOINT ["./app"]

Здесь:

  • используется только COPY;
  • поведение очевидно и воспроизводимо.

Краткая формулировка:

  • COPY — простой и рекомендуемый способ копирования файлов из контекста сборки в образ.
  • ADD — расширенная версия, которая умеет распаковывать локальные tar и скачивать по URL, но именно из-за этой «магии» используется только при осознанной необходимости; в остальных случаях лучше COPY.

Вопрос 38. В чем разница между CMD и ENTRYPOINT в Dockerfile?

Таймкод: 00:32:30

Ответ собеседника: неправильный. Корректно объясняет отличие RUN и CMD (RUN при сборке, CMD при запуске контейнера), но не помнит, что такое ENTRYPOINT и не раскрывает разницу CMD/ENTRYPOINT.

Правильный ответ:

RUN, CMD и ENTRYPOINT относятся к разным этапам жизни образа и контейнера, и их часто путают. Коротко:

  • RUN — выполняется на этапе сборки образа.
  • CMD и ENTRYPOINT — определяют, что выполняется при запуске контейнера.
  • Отличие между CMD и ENTRYPOINT — в том, как они формируют/переопределяют команду запуска и как ведут себя при передаче аргументов.

Разберём по порядку.

  1. RUN (для контекста)
  • Выполняется во время сборки образа.
  • Результат (изменения файловой системы) попадает в слой образа.
  • Пример:
    RUN apk add --no-cache ca-certificates
  • К запуску контейнера напрямую не относится.
  1. CMD
  • Определяет команду (и/или её аргументы) по умолчанию для контейнера.
  • Выполняется при запуске контейнера, если не была переопределена в docker run или оркестратором.
  • Может быть в двух формах:
    • shell-форма:
      CMD echo "hello"
    • exec-форма (рекомендуется):
      CMD ["./app", "-config", "/etc/app.yaml"]
  • Особенности:
    • В Dockerfile может быть только один активный CMD — последний.
    • Любые аргументы, переданные в docker run <image> ..., перезаписывают CMD (если ENTRYPOINT не задан или задан особым образом).
    • Часто используется как «значения по умолчанию» для ENTRYPOINT.
  1. ENTRYPOINT
  • Определяет «главный» процесс контейнера — то, что всегда будет выполняться при старте.
  • Также имеет две формы:
    • shell-форма:
      ENTRYPOINT echo "Hello"
    • exec-форма:
      ENTRYPOINT ["/usr/local/bin/app"]
  • Особенности:
    • ENTRYPOINT сложнее «полностью заменить» при docker run:
      • чаще всего аргументы docker run image arg1 arg2 добавляются как аргументы к ENTRYPOINT (в exec-форме).
    • Используется, когда контейнер должен вести себя как конкретный бинарник:
      • например, docker run myapp ... всегда запускает /usr/local/bin/myapp.
  1. Взаимодействие CMD и ENTRYPOINT

Ключевой момент: CMD и ENTRYPOINT могут работать вместе.

  • Если задан ENTRYPOINT (exec-форма) и CMD:
    • CMD поставляет аргументы по умолчанию для ENTRYPOINT.
  • Схема:
    ENTRYPOINT ["app"]
    CMD ["serve"]
  • Тогда:
    • docker run image выполнит:
      • app serve
    • docker run image status выполнит:
      • app status (CMD переопределён аргументами из командной строки).

Примеры паттернов:

  1. Только CMD:
FROM alpine:3.20
COPY app /usr/local/bin/app
CMD ["/usr/local/bin/app", "serve"]
  • docker run image:
    • запустит app serve
  • docker run image debug:
    • полностью заменит CMD → выполнит debug (и это часто не то, что нужно).
  1. ENTRYPOINT + CMD как аргументы по умолчанию:
FROM alpine:3.20
COPY app /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]
CMD ["serve"]
  • docker run image:
    • app serve
  • docker run image status:
    • app status
  • Это удобный подход для CLI-инструментов и сервисов:
    • ENTRYPOINT фиксирует бинарник;
    • CMD задаёт дефолтное поведение.
  1. ENTRYPOINT (shell-форма) — когда не стоит так делать:
ENTRYPOINT /usr/local/bin/app
  • Такая форма завёрнута в /bin/sh -c:
    • сложнее с сигналами (PID 1, обработка SIGTERM/SIGINT);
    • хуже интеграция с Kubernetes и корректным graceful shutdown.
  • В production почти всегда стоит использовать exec-форму.
  1. Практические рекомендации:
  • Для сервисов (Go API, worker и т.п.):
    • использовать exec-форму ENTRYPOINT и при необходимости CMD для аргументов:
      ENTRYPOINT ["/usr/local/bin/app"]
      CMD ["serve"]
  • Для утилит/CLI:
    • тот же подход: ENTRYPOINT — бинарник, CMD — дефолтная команда.
  • Если нужно, чтобы пользователь легко переопределял всю команду:
    • можно обойтись одним CMD и не использовать ENTRYPOINT.

Краткий ответ уровня практики:

  • RUN — выполняется при сборке образа.
  • CMD — задаёт команду/аргументы по умолчанию при запуске контейнера и легко переопределяется.
  • ENTRYPOINT — фиксирует основной исполняемый файл; с exec-формой аргументы из CMD и docker run добавляются к нему.
  • Лучший паттерн: ENTRYPOINT для бинарника, CMD для дефолтных аргументов.

Вопрос 39. Использовали ли вы multi-stage build в Dockerfile и для чего он нужен?

Таймкод: 00:33:54

Ответ собеседника: неправильный. Говорит, что multi-stage build не использовал; идея уменьшения размера конечного образа появляется только после подсказки, не поясняет принцип работы.

Правильный ответ:

Multi-stage build — это механизм Docker, позволяющий в одном Dockerfile описать несколько стадий сборки и использовать артефакты из ранних стадий в последующих. Главные цели:

  • уменьшить размер финального образа;
  • отделить окружение сборки от окружения выполнения;
  • повысить безопасность (не тащить внутрь прод-образа лишние утилиты и исходники);
  • сделать процесс сборки более явным и воспроизводимым.

Ключевая идея:

  • На первой стадии(ях) мы используем «тяжелые» образы (с компиляторами, SDK, debug-инструментами), собираем артефакты.
  • На финальной стадии берем только нужные файлы (например, один Go-бинарник) и кладем их в «тонкий» базовый образ (alpine/distroless/scratch).
  • В продакшн уходит только финальный образ.

Базовый пример для Go-сервиса:

# Стадия 1: сборка
FROM golang:1.22-alpine AS builder

WORKDIR /app

# Оптимизация кеша: сначала зависимости
COPY go.mod go.sum ./
RUN go mod download

# Затем исходники
COPY . .

# Сборка бинарника
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o app ./cmd/app

# Стадия 2: финальный минимальный образ
FROM alpine:3.20

# Создаем непривилегированного пользователя
RUN adduser -D -g '' appuser
USER appuser

WORKDIR /app

# Копируем только бинарник из builder-стадии
COPY --from=builder /app/app /app/app

EXPOSE 8080
ENTRYPOINT ["/app/app"]

Что это даёт:

  1. Меньший размер образа:

    • Без multi-stage:
      • в образе остаются Go toolchain, исходники, кеши, dev-пакеты.
    • С multi-stage:
      • финальный образ содержит только:
        • бинарник,
        • минимальный runtime (alpine или даже scratch),
        • нет компилятора, исходников, тестовых данных.
    • Это:
      • ускоряет доставку образа по сети (CI/CD, k8s);
      • уменьшает время раскатки;
      • сокращает использование диска на нодах.
  2. Безопасность:

    • Меньше инструментов внутри контейнера → меньше потенциальных точек атаки.
    • Нет исходников → сложнее анализировать код в случае компрометации контейнера.
    • Нет package manager’ов → атакующему сложнее «доставить» себе инструменты.
  3. Чистота и reproducibility:

    • Окружение сборки изолировано:
      • можно использовать любые dev-зависимости, не боясь протащить их в runtime.
    • Финальный образ детерминированный, минимальный, лучше управляемый.
  4. Удобство в CI/CD:

    • Один Dockerfile описывает весь pipeline сборки:
      • не нужно отдельно подготавливать builder-образ вручную;
      • легко читать и ревьюить.

Расширенный пример (c тестами и валидацией):

FROM golang:1.22-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY . .

# Линтеры/тесты (пример)
RUN go test ./... -race

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o app ./cmd/app

FROM gcr.io/distroless/static AS final

USER 65532:65532
WORKDIR /

COPY --from=builder /app/app /app

EXPOSE 8080
ENTRYPOINT ["/app"]

Комментарии:

  • Использование distroless:
    • ещё более минимальный образ (без shell, без пакетных менеджеров);
    • повышает безопасность и уменьшает размер.
  • USER с non-root:
    • хороший тон для продакшен-контейнеров.

Краткий ответ для интервью:

  • «Да, multi-stage build нужен для отделения стадий сборки и рантайма: мы собираем бинарник в полном образе (c Go toolchain, линтерами, тестами), а в финальный образ кладем только результат. Это уменьшает размер, убирает лишние зависимости из продакшн-окружения, повышает безопасность и делает образы более предсказуемыми. Для Go это особенно удобно — итоговый образ часто сводится к одному статически слинкованному бинарнику.»

Вопрос 40. Какие инструменты оркестрации контейнеров вы знаете или использовали?

Таймкод: 00:34:20

Ответ собеседника: неполный. Говорит, что про такие инструменты знает, но практического опыта работы с ними не имеет.

Правильный ответ:

Оркестрация контейнеров решает задачи, которые выходят далеко за рамки простого запуска отдельных контейнеров:

  • управление десятками/сотнями/тысячами контейнеров;
  • отказоустойчивость и автоперезапуск;
  • масштабирование;
  • service discovery и балансировка;
  • конфигурация и секреты;
  • обновления без простоя (rolling / canary / blue-green);
  • интеграция с сетевой и storage-инфраструктурой.

Ключевые инструменты, которые важно знать и уметь описать:

  1. Kubernetes

На сегодняшний день де-факто стандарт оркестрации.

Основные концепции:

  • Cluster:
    • control plane (API server, scheduler, controller manager, etcd),
    • worker-ноды.
  • Объекты:
    • Pod — минимальная единица деплоя (один или несколько контейнеров).
    • ReplicaSet — гарантирует нужное количество Pod’ов.
    • Deployment:
      • управляет ReplicaSet;
      • rolling update, rollback;
      • основа для stateless-сервисов.
    • StatefulSet:
      • для stateful-сервисов (БД, очереди), с устойчивыми идентичностями и volume.
    • DaemonSet:
      • по одному Pod на ноду (агенты, логгеры, мониторинг).
    • Service:
      • стабильный виртуальный IP/имя для доступа к Pod’ам;
      • балансировка трафика.
    • Ingress / Gateway:
      • публикация HTTP(S) извне кластера.
    • ConfigMap, Secret:
      • конфигурации и секреты.
    • PersistentVolume / PersistentVolumeClaim:
      • работа с постоянным хранилищем.

Что важно понимать:

  • Оркестратор следит за тем, чтобы «желательное состояние» (manifests) совпадало с фактическим.
    • Если Pod падает — поднимается новый.
    • Если нода умерла — Pod’ы переносятся.
  • Kubernetes интегрируется с:
    • Docker/containerd/CRI-O;
    • CNI-плагинами (Calico, Cilium и т.п.);
    • CSI-драйверами для storage.
  • Для CI/CD:
    • образы пушатся в registry;
    • манифесты/Helm/GitOps управляют релизами.

Пример простого Deployment для Go-сервиса:

apiVersion: apps/v1
kind: Deployment
metadata:
name: go-api
spec:
replicas: 3
selector:
matchLabels:
app: go-api
template:
metadata:
labels:
app: go-api
spec:
containers:
- name: go-api
image: registry.example.com/go-api:1.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
  1. Docker Swarm

Встроенный в Docker режим оркестрации (меньше распространен сейчас, но знать стоит):

  • Позволяет объединить несколько хостов в swarm-кластер.
  • Понятия:
    • service, stack, replica.
  • Преимущества:
    • проще старт, чем Kubernetes;
  • Недостатки:
    • менее богатый функционал, меньшая экосистема, существенно проигрывает Kubernetes в гибкости и распространенности.
  1. HashiCorp Nomad

Легковесный оркестратор задач (jobs):

  • Поддерживает:
    • контейнеры (Docker),
    • бинарники,
    • JVM и др.
  • Сильные стороны:
    • простая архитектура;
    • хорош для хетерогенных workload’ов.
  • Часто используется вместе с:
    • Consul (service discovery),
    • Vault (секреты).
  1. Rancher / OpenShift и подобные платформы

Не отдельные оркестраторы, а платформы поверх Kubernetes:

  • Rancher:
    • мульти-кластер менеджмент, UI, интеграции.
  • OpenShift:
    • enterprise-платформа на базе Kubernetes (своё мнение по security, образам, registry, pipelines).
  1. Serverless/managed решения (как косвенная форма оркестрации)

Хотя это не классический контейнерный оркестратор, но важно понимать:

  • AWS ECS/EKS, Google GKE, Azure AKS:
    • managed Kubernetes или собственные схемы оркестрации (ECS).
  • AWS Fargate:
    • запуск контейнеров без управления нодами.
  • Они решают схожие задачи:
    • масштабирование;
    • перезапуск;
    • интеграция с сетями и секретами.

Как это связать в правильный ответ:

  • Нужно показать:
    • знание Kubernetes как основного игрока;
    • базовое представление об альтернативах;
    • понимание, какие задачи оркестрация решает по сравнению с ручным запуском docker run.

Хороший лаконичный ответ на собеседовании:

  • «Знаю и ориентируюсь в Kubernetes как в основном стандарте оркестрации: Deployment, Service, Ingress, ConfigMap, Secret, probes, масштабирование и rolling updates. Понимаю, что он обеспечивает декларативное управление состоянием, автоперезапуск, распределение по нодам, service discovery и интеграцию с сетями и storage. Также знаком с концепцией Docker Swarm и Nomad, но для продакшена рассматриваю Kubernetes как базовый инструмент.»

Вопрос 41. Как перенести собранный Docker-образ на другой сервер и запустить без пересборки?

Таймкод: 00:34:39

Ответ собеседника: неправильный. Путает задачу с использованием docker-compose, не упоминает registry; после пояснения понимает идею Docker Hub/registry, но сам корректный способ не формулирует.

Правильный ответ:

Есть два основных рабочих способа переноса Docker-образа на другой сервер без пересборки:

  1. через образ в registry (рекомендуется);
  2. через экспорт/импорт локального tar-архива.

Оба варианта должны быть уверенно озвучены на собеседовании.

  1. Использование реестра образов (Docker Registry) — основной и правильный способ

Алгоритм:

  • На машине, где образ уже собран:

    1. Тегируем образ:
      docker tag myapp:latest registry.example.com/myteam/myapp:1.0.0
    2. Логинимся (если приватный реестр):
      docker login registry.example.com
    3. Публикуем образ:
      docker push registry.example.com/myteam/myapp:1.0.0
  • На целевом сервере:

    1. Логинимся (если нужно):
      docker login registry.example.com
    2. Загружаем образ:
      docker pull registry.example.com/myteam/myapp:1.0.0
    3. Запускаем контейнер:
      docker run -d --name myapp -p 8080:8080 registry.example.com/myteam/myapp:1.0.0

Варианты registry:

  • публичные:
    • Docker Hub,
    • GitHub Container Registry,
    • GitLab Registry.
  • приватные:
    • Harbor,
    • ECR (AWS), GCR (GCP), ACR (Azure),
    • on-premise Docker Registry.

Почему это основной путь:

  • он же используется в CI/CD и Kubernetes;
  • обеспечивает версионирование образов;
  • не требует ручного копирования файлов;
  • работает для десятков/сотен серверов.
  1. Экспорт/импорт образа через файл (tar) — для изолированных сред

Используется, когда:

  • нет доступа к общему registry;
  • или требуется оффлайн-перенос (air-gapped, secure zone).

На исходной машине:

docker save myapp:1.0.0 -o myapp_1.0.0.tar

Переносим myapp_1.0.0.tar на целевой сервер (scp, rsync, флешка, артефакт-сервер).

На целевом сервере:

docker load -i myapp_1.0.0.tar
docker run -d --name myapp -p 8080:8080 myapp:1.0.0

Особенности:

  • docker save/docker load переносят один или несколько образов;
  • удобно для закрытых контуров, но плохо масштабируется по сравнению с registry.
  1. Что не является ответом
  • docker-compose:
    • это инструмент оркестрации нескольких контейнеров на одном хосте (описание сервисов в YAML),
    • он не решает задачу доставки образов между серверами;
    • docker-compose использует уже доступные локально или в registry образы.
  • Пересборка на целевом сервере:
    • противоречит требованию «без пересборки» и ломает идею воспроизводимого артефакта.

Краткий ответ для собеседования:

  • «Готовый образ нужно не пересобирать, а перенести как артефакт. Стандартный путь — запушить его в реестр (Docker Hub, приватный registry) и на другом сервере сделать docker pull и docker run. В изолированных средах можно использовать docker save/docker load для переноса образа tar-архивом.»

Вопрос 42. Что такое инструменты управления конфигурацией и какие из них используются на практике?

Таймкод: 00:37:01

Ответ собеседника: неполный. Даёт общее корректное определение как инструментов для централизованного описания конфигураций серверов, упоминает только Ansible без деталей.

Правильный ответ:

Инструменты управления конфигурацией (Configuration Management, CM) — это системы, которые позволяют декларативно и массово управлять состоянием серверов и сервисов: пакетами, конфигурационными файлами, пользователями, сервисами, правами, шаблонами конфигов и т.д. Их ключевая задача — обеспечить:

  • идемпотентность: повторный прогон не ломает состояние, а приводит его к заданному;
  • воспроизводимость: новый сервер поднимается в предсказуемое состояние по описанию, а не вручную;
  • масштабируемость: одна и та же конфигурация применяется к десяткам/сотням хостов;
  • контроль изменений: конфигурации описаны как код (Git), проходят code review, тестирование, rollback.

Это фундамент IaC-подхода (Infrastructure as Code) рядом с Terraform и оркестраторами.

Ключевые инструменты (важно уметь назвать и знать базовые отличия):

  1. Ansible
  • Характеристики:

    • agentless: не требует агента на целевых хостах;
    • использует SSH + Python на целевой машине;
    • конфигурации в YAML (playbooks, roles).
  • Плюсы:

    • низкий порог входа;
    • хорош для mixed-инфраструктур (Linux, Windows, сети);
    • легко интегрируется в CI/CD.
  • Пример простого playbook для настройки Go-сервиса:

    - hosts: app_servers
    become: yes
    tasks:
    - name: Install dependencies
    apt:
    name:
    - ca-certificates
    update_cache: yes

    - name: Create user
    user:
    name: appuser
    system: yes
    shell: /usr/sbin/nologin

    - name: Copy binary
    copy:
    src: ./bin/app
    dest: /usr/local/bin/app
    owner: appuser
    group: appuser
    mode: '0755'

    - name: Install systemd service
    template:
    src: app.service.j2
    dest: /etc/systemd/system/app.service

    - name: Enable and start service
    systemd:
    name: app
    enabled: yes
    state: started
  • Это позволяет:

    • одинаково конфигурировать все app-сервера;
    • не кликать руками;
    • версионировать конфигурацию в Git.
  1. Puppet
  • Agent-based:
    • на каждом хосте — агент, который регулярно тянет конфигурацию с Puppet Master.
  • Декларативный DSL.
  • Силен в крупных энтерпрайз-окружениях:
    • централизованный контроль,
    • отчётность,
    • политика комплаенса.
  1. Chef
  • Использует Ruby DSL.
  • Agent-based модель (Chef Client / Server).
  • Гибкий, но порог входа выше из-за языка и экосистемы.
  1. SaltStack
  • Может работать как в push (master → minion), так и в pull-режиме.
  • YAML-описания (state files).
  • Подходит для быстрого массового применения состояний.
  1. Связь с Terraform и Kubernetes

Важно уметь различать классы инструментов:

  • Terraform:
    • управляет ресурсами (VM, сети, LB, БД, Kubernetes-кластеры) через API провайдеров;
    • создаёт «железо» и облачную инфраструктуру.
  • Конфигурационные менеджеры (Ansible, Puppet и др.):
    • настраивают ОС и софт внутри этих ресурсов:
      • пакеты, файлы, юзеры, systemd, конфиги сервисов.
  • Kubernetes/Helm:
    • оркестрируют контейнеры и приложение внутри кластера.

В продвинутых практиках:

  • Terraform поднимает VM + сети.
  • Ansible (или аналог) настраивает базовую ОС, агент, systemd, прокси, runtime.
  • Kubernetes/Helm управляют приложениями в контейнерах.
  • Всё это — код, ревью, CI/CD.

Критические качества хорошего конфигурационного менеджмента:

  • Идемпотентность:
    • Playbook/manifest можно запускать много раз:
      • если состояние уже совпадает с описанным, изменений не будет.
  • Декларативность:
    • описываем целевое состояние, а не последовательность «скриптов по шагам».
  • Аудит и откат:
    • изменения через Git;
    • можно увидеть diff, вернуть предыдущую версию.

Краткий ответ для собеседования:

  • «Инструменты управления конфигурацией позволяют описывать целевое состояние серверов как код и применять его массово, идемпотентно и воспроизводимо. Типичные примеры: Ansible (agentless, YAML), Puppet и Chef (agent-based), SaltStack. Они дополняют Terraform и Kubernetes: Terraform создает ресурсы, CM-инструменты настраивают ОС и софт, оркестраторы управляют приложениями. Ansible — один из самых удобных и широко применяемых инструментов, им обычно автоматизируют установку пакетов, деплой бинарников, настройку systemd-сервисов, пользователей, прав и шаблонов конфигов.»

Вопрос 43. Использовали ли вы роли Ansible и как реализовать кросс-дистрибутивное обновление пакетов в плейбуках?

Таймкод: 00:37:44

Ответ собеседника: неправильный. Сообщает, что писал только простые плейбуки, не работал с ролями и условным выполнением задач для разных дистрибутивов.

Правильный ответ:

Для зрелого использования Ansible необходимо:

  1. структурировать конфигурации через роли;
  2. уметь писать кросс-дистрибутивные плейбуки с использованием фактов и условий.

Роли Ansible:

  • Роль — это структурированная единица повторно используемой конфигурации.
  • Позволяет разбить логику по модулям вместо «монолитного» playbook.
  • Стандартная структура роли:
    • roles/myrole/
      • tasks/main.yml — основные задачи.
      • handlers/main.yml — уведомления (restart сервисов).
      • templates/ — Jinja2-шаблоны.
      • files/ — статичные файлы.
      • vars/, defaults/ — переменные (по умолчанию и фиксированные).
      • meta/ — зависимости ролей.
  • Плюсы:
    • переиспользуемость;
    • читабельность;
    • удобная комбинация в разных окружениях и для разных сервисов.

Кросс-дистрибутивный подход:

Задача: «обновить пакеты» или «установить зависимости» и для Debian/Ubuntu, и для RHEL/CentOS/Rocky и т.д.

Ключевые инструменты:

  • Ansible facts:
    • ansible_os_family
    • ansible_distribution
    • ansible_distribution_major_version
  • Условные выражения (when).
  • Унифицированные модули:
    • для Ansible 2.10+ предпочтителен модуль package, который абстрагирует менеджер пакетов.

Простой и правильный способ (через package):

- hosts: all
become: yes
tasks:
- name: Update package cache and upgrade packages (Debian/Ubuntu)
package:
name: "*"
state: latest
when: ansible_os_family == "Debian"

- name: Update package cache and upgrade packages (RHEL/CentOS)
package:
name: "*"
state: latest
when: ansible_os_family == "RedHat"

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

Пример кросс-дистрибутивной установки пакетов с условиями:

- hosts: all
become: yes
tasks:
- name: Ensure base dependencies installed (Debian/Ubuntu)
apt:
name:
- ca-certificates
- curl
state: present
update_cache: yes
when: ansible_os_family == "Debian"

- name: Ensure base dependencies installed (RHEL family)
yum:
name:
- ca-certificates
- curl
state: present
when: ansible_os_family == "RedHat"

Через роли это оформляется лучше:

  1. Роль common:

roles/common/tasks/main.yml:

- name: Install common packages on Debian/Ubuntu
apt:
name:
- ca-certificates
- curl
state: present
update_cache: yes
when: ansible_os_family == "Debian"

- name: Install common packages on RHEL/CentOS/Rocky
yum:
name:
- ca-certificates
- curl
state: present
when: ansible_os_family == "RedHat"
  1. Использование роли в playbook:
- hosts: all
become: yes
roles:
- common

Вариант с модулем package (для единообразной логики):

- name: Install common packages
package:
name:
- ca-certificates
- curl
state: present
  • package сам выберет apt/yum/dnf в зависимости от платформы.
  • Для более сложных кейсов (разные имена пакетов на разных ОС) используют vars и when.

Best practices для кросс-дистрибутивных ролей:

  • Использовать:
    • ansible_os_family, ansible_distribution для ветвления логики.
    • модуль package там, где имена пакетов совпадают.
  • Хранить различия в переменных:
    • например, в vars/debian.yml, vars/redhat.yml,
    • а в тасках использовать абстрактные имена переменных.
  • Держать логику в ролях:
    • роль common, docker, go-runtime, app и т.п.;
    • переиспользовать между окружениями (dev/stage/prod).

Краткий ответ для интервью:

  • «Да, для масштабируемой конфигурации в Ansible используются роли. Кросс-дистрибутивность делается через Ansible facts и условия: по ansible_os_family выбираем нужный модуль (apt, yum, dnf) или используем абстрактный package, а различия в именах пакетов выносим в переменные. В итоге один playbook/роль умеет корректно настраивать разные дистрибутивы без дублирования логики.»

Вопрос 44. Есть ли у вас опыт работы с базами данных и какими именно?

Τаймкод: 00:39:00

Ответ собеседника: правильный. Упоминает учебный опыт с PostgreSQL и использование Redis для кэша, в основном в контексте реляционных БД.

Правильный ответ:

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

Ниже — то, что ожидается как уверенный, практико-ориентированный ответ.

Реляционные базы данных (RDBMS):

Основной фокус: PostgreSQL

  • Почему PostgreSQL:
    • мощная, зрелая, поддерживает SQL-стандарт, транзакции, сложные запросы, JSON, расширения;
    • де-факто стандарт для современных backend-сервисов.

Ключевые аспекты, которые важно понимать и уметь применять:

  • Проектирование схем:
    • нормализация (до разумного уровня);
    • явные первичные ключи (часто surrogate key — BIGSERIAL/UUID);
    • внешние ключи для целостности;
    • осознанное использование NULL.
  • Индексы:
    • B-Tree (по умолчанию) для точного поиска и диапазонов;
    • уникальные индексы для обеспечения ограничений;
    • составные индексы (правильный порядок полей);
    • частичные индексы и выражения (при необходимости).
  • Транзакции:
    • ACID;
    • уровни изоляции;
    • понимание race conditions, lost update, транзакционные блоки.
  • Практика запросов:
    • JOIN (INNER, LEFT, RIGHT);
    • агрегаты (COUNT, SUM, AVG, GROUP BY, HAVING);
    • фильтрация, пагинация (LIMIT/OFFSET, keyset pagination).

Примеры SQL:

-- Создание таблицы пользователей
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Индекс по email не нужен отдельно, он уже есть через UNIQUE.
-- Поиск пользователя по email
SELECT id, email, name
FROM users
WHERE email = 'user@example.com';

Интеграция PostgreSQL с Go:

Использование драйверов и библиотек:

  • стандартный стек:
    • github.com/jackc/pgx/v5 (предпочтителен),
    • database/sql как общий интерфейс.
  • Важные моменты:
    • connection pool (pgx уже содержит);
    • контексты и таймауты;
    • параметризованные запросы для защиты от SQL-инъекций.

Пример на Go с pgx:

import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
)

type User struct {
ID int64
Email string
Name string
}

func getUserByEmail(ctx context.Context, pool *pgxpool.Pool, email string) (*User, error) {
var u User
err := pool.QueryRow(ctx,
`SELECT id, email, name FROM users WHERE email = $1`,
email,
).Scan(&u.ID, &u.Email, &u.Name)
if err != nil {
return nil, err
}
return &u, nil
}

Кеширующие и NoSQL-хранилища:

Redis:

  • Типичный use-case:
    • кеширование данных;
    • хранение сессий;
    • rate limiting;
    • очереди (list/stream);
    • механизмы распределенных блокировок (с оговорками).
  • Ключевые моменты:
    • хранение в памяти, очень быстрый доступ;
    • работа с TTL;
    • осознанное отношение к потере данных (если используется как кеш).
  • Пример использования Redis как кеш-слоя перед PostgreSQL:
    • читаем из Redis;
    • при промахе — читаем из PostgreSQL, кладем в Redis с TTL.

Пример (концептуально, псевдо-Go):

// 1) Попробовать взять пользователя из Redis
// 2) Если нет — запросить из PostgreSQL и записать в Redis с TTL

Важно:

  • Чётко разделять ответственность:
    • PostgreSQL — источник истины (stateful данные, транзакции).
    • Redis — ускорение, кеш, ephemeral данные.

Что ещё желательно упомянуть:

  • Понимание миграций:
    • использование инструментов: golang-migrate, liquibase, flyway;
    • миграции как часть CI/CD;
    • принцип безопасных миграций (backward-compatible, без ломающих изменений в один шаг).
  • Базовые знания других БД:
    • MySQL/MariaDB — близки к PostgreSQL по парадигме;
    • key-value/документные:
      • Redis (как уже упомянуто),
      • MongoDB, если есть опыт: документы, отсутствие жесткой схемы, особенности запросов и индексов.
  • Понимание выбора:
    • реляционная БД — для транзакционных данных и сложных связей;
    • Redis — для кеша и быстрых операций;
    • специализированные NoSQL — для логов, метрик, полнотекстового поиска (Elasticsearch/OpenSearch), time-series и т.д.

Краткий вариант ответа для интервью:

  • «Да, работал с PostgreSQL: проектирование схем, индексы, базовые и сложные SELECT, JOIN, транзакции, миграции. Использовал Redis как кеш и для временных данных. В Go-проектах работаю с pgx/database/sql, настроенным пулом, параметризованными запросами и миграциями как частью CI/CD. PostgreSQL рассматриваю как основную транзакционную базу, Redis — как вспомогательный высокоскоростной уровень.»

Вопрос 45. Какие базовые SQL-команды вы знаете?

Таймкод: 00:39:29

Ответ собеседника: правильный. Называет SELECT, INSERT, DELETE и ALTER как основные команды, демонстрируя базовое понимание.

Правильный ответ:

Базовые SQL-команды делятся на несколько групп: DDL (определение структуры), DML (манипуляция данными), DQL (запрос данных), DCL/TCL (права и транзакции). Для уверенной работы с базами в реальных сервисах важно не только знать названия, но и правильно применять команды с учетом индексов, транзакций и нагрузок.

Основные группы и ключевые команды:

  1. DQL — Data Query Language (запросы данных)
  • SELECT — выборка данных.

Примеры:

-- Все записи из таблицы
SELECT * FROM users;

-- Только нужные поля
SELECT id, email, created_at
FROM users;

-- Фильтрация
SELECT id, email
FROM users
WHERE created_at >= NOW() - INTERVAL '7 days';

-- Сортировка и пагинация
SELECT id, email
FROM users
ORDER BY created_at DESC
LIMIT 20 OFFSET 40;

Важные моменты:

  • всегда выбирать только нужные поля, избегать бездумного SELECT * в продакшене;
  • использовать WHERE с индексируемыми полями для производительности.
  1. DML — Data Manipulation Language (изменение данных)
  • INSERT — вставка данных.
  • UPDATE — изменение существующих записей.
  • DELETE — удаление записей.

Примеры:

-- INSERT
INSERT INTO users (email, name)
VALUES ('user@example.com', 'Test User')
RETURNING id;

-- UPDATE (обновить имя по id)
UPDATE users
SET name = 'New Name'
WHERE id = 123;

-- DELETE (удалить пользователя по id)
DELETE FROM users
WHERE id = 123;

Важные моменты:

  • всегда использовать WHERE для UPDATE/DELETE, чтобы не затронуть все строки;
  • для важных операций — работать в транзакциях;
  • использовать RETURNING (в PostgreSQL) для получения ID или обновленных значений.
  1. DDL — Data Definition Language (структура схемы)
  • CREATE — создание таблиц, индексов, схем и т.д.
  • ALTER — изменение структуры.
  • DROP — удаление объектов.

Примеры:

-- Создание таблицы
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Добавление столбца
ALTER TABLE users
ADD COLUMN last_login TIMESTAMPTZ;

-- Создание индекса
CREATE INDEX idx_users_email ON users(email);

-- Удаление таблицы
DROP TABLE users;

Практические принципы:

  • ALTER в продакшене выполнять через миграции;
  • избегать блокирующих операций без оценки эффекта (большие таблицы, прод-нагрузка);
  • всегда иметь откат (down-миграции или стратегию восстановления).
  1. TCL — Transaction Control Language (управление транзакциями)
  • BEGIN / START TRANSACTION — начать транзакцию.
  • COMMIT — зафиксировать изменения.
  • ROLLBACK — откатить изменения.

Пример:

BEGIN;

UPDATE accounts
SET balance = balance - 100
WHERE id = 1;

UPDATE accounts
SET balance = balance + 100
WHERE id = 2;

COMMIT;

Для Go-кода (и не только) критично:

  • использовать транзакции для операций, где важна целостность (денежные переводы, состояния заказов);
  • обрабатывать ошибки и при необходимости делать ROLLBACK.
  1. DCL — Data Control Language (права)
  • GRANT — выдать права.
  • REVOKE — отозвать.

Пример:

GRANT SELECT, INSERT ON users TO app_user;
REVOKE DELETE ON users FROM app_user;

Практическая связь с Go и backend-разработкой:

  • Все эти команды должны использоваться:
    • с параметризованными запросами (никакого string concatenation с пользовательским вводом);
    • через подготовленные запросы/ORM/Query Builder или чистый SQL.
  • Пример на Go (вставка с RETURNING):
var id int64
err := db.QueryRowContext(ctx,
`INSERT INTO users (email, name) VALUES ($1, $2) RETURNING id`,
email, name,
).Scan(&id)
if err != nil {
return err
}

Кратко:

  • Базовый набор: SELECT (чтение), INSERT (вставка), UPDATE (изменение), DELETE (удаление), CREATE/ALTER/DROP (структура), плюс транзакции (BEGIN/COMMIT/ROLLBACK).
  • Уверенное владение этими командами в сочетании с пониманием индексов, ограничений и транзакций — минимальный уровень для работы с любым серьёзным сервисом.

Вопрос 46. В чем разница между реляционными и нереляционными базами данных?

Таймкод: 00:39:53

Ответ собеседника: неполный. Говорит, что реляционные БД используют таблицы и SQL, а нереляционные — ключ-значение и менее структурированы. Идея верная, но объяснение слишком упрощено и не покрывает типы NoSQL и архитектурные последствия выбора.

Правильный ответ:

Разница между реляционными (SQL) и нереляционными (NoSQL) базами — в модели данных, способах работы с согласованностью, масштабированием и гибкостью схемы. Важно понимать не только формальное отличие «таблицы vs. ключ-значение», но и когда что выбирать для реальных сервисов.

Реляционные базы данных (RDBMS):

Основные характеристики:

  • Модель данных:
    • таблицы (строки и столбцы);
    • явная схема (schema-first);
    • строгие типы данных;
    • связи между таблицами через ключи:
      • первичные ключи (PRIMARY KEY),
      • внешние ключи (FOREIGN KEY).
  • Язык запросов:
    • SQL как декларативный язык:
      • SELECT, INSERT, UPDATE, DELETE;
      • JOIN, GROUP BY, ORDER BY, подзапросы.
  • Целостность данных:
    • ограничения (constraints): NOT NULL, UNIQUE, CHECK, FK;
    • транзакции (ACID):
      • Atomicity, Consistency, Isolation, Durability.
  • Типичные представители:
    • PostgreSQL, MySQL/MariaDB, Oracle, MS SQL Server.

Когда это подходит:

  • сложные связи и бизнес-логика:
    • заказы, счета, платежи, пользователи, каталоги;
  • требования к целостности и согласованности:
    • нельзя потерять или задвоить данные;
  • аналитические и репортинговые запросы с JOIN/агрегациями;
  • большая часть классических backend-систем.

Пример (PostgreSQL):

CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
total NUMERIC(10,2) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Выбрать все заказы пользователя
SELECT o.id, o.total, o.created_at
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE u.email = 'user@example.com';

Нереляционные базы данных (NoSQL):

Это не «хаотичные» базы, а семейство систем с разными моделями данных, оптимизированных под конкретные сценарии. Общие черты:

  • Гибкая схема (schema-less или schema-on-read):
    • структура данных может отличаться между записями;
    • эволюция схемы проще, но ответственность за целостность переносится в код.
  • Масштабирование:
    • часто ориентированы на горизонтальное масштабирование «из коробки»;
    • встроенный шардирование, репликация, распределение.
  • Согласованность:
    • часто используют eventual consistency (по CAP-теореме);
    • строгая транзакционность через все данные — не всегда есть или ограничена.

Типы NoSQL-хранилищ:

  1. Key-Value (ключ-значение)

    • Простая модель: ключ → значение (blob/строка/JSON).
    • Очень быстрые операции.
    • Примеры:
      • Redis, Amazon DynamoDB, etcd.
    • Юз-кейсы:
      • кеш, сессии, конфигурации, метаданные, feature flags.
  2. Document Store (документные)

    • Документы (обычно JSON/ BSON) с гибкой структурой.
    • Есть индексы по полям документов, запросы по вложенным структурам.
    • Примеры:
      • MongoDB, Couchbase.
    • Юз-кейсы:
      • когда сущности естественно представимы как документы (user profile, настройки);
      • когда схема эволюционирует и не хочется мигрировать таблицы.
  3. Column-Family / Wide-Column

    • Логическая модель: строки с очень большим числом потенциальных колонок, сгруппированных в column families.
    • Оптимизированы под большие объемы данных и распределенные записи/чтения.
    • Примеры:
      • Apache Cassandra, HBase.
    • Юз-кейсы:
      • таймсерии, логирование, метрики, огромные event-стримы.
  4. Graph Databases

    • Модель: вершины (nodes) и ребра (edges) с атрибутами.
    • Оптимизированы для запросов по сложным связям.
    • Примеры:
      • Neo4j, JanusGraph.
    • Юз-кейсы:
      • социальные графы, рекомендации, fraud detection.

Ключевые отличия на уровне архитектуры и практики:

  • Схема:
    • RDBMS: строгая, заранее определенная, enforce-ится БД.
    • NoSQL: гибкая, ответственность за валидность на приложении.
  • Связи:
    • RDBMS: JOIN — нативная операция, хорошо для сложных связей.
    • NoSQL: часто денормализация, связи «зашиваются» в данные; JOIN делает приложение.
  • Транзакции:
    • RDBMS: полнофункциональные транзакции, важны для финансов/критичных данных.
    • NoSQL: часто ограниченные транзакции (например, в пределах одного документа/partition), или eventual consistency.
  • Масштабирование:
    • RDBMS: классически вертикальное (scale-up), хотя есть репликация, шардинг и современные решения;
    • NoSQL: изначально ориентированы на горизонтальное масштабирование (scale-out).

Связь с Go и типичными сервисами:

  • Типичный продакшн-стек:
    • PostgreSQL как основная транзакционная БД.
    • Redis как кеш/очередь/быстрый KV-слой.
    • Иногда:
      • Elasticsearch/OpenSearch для поиска и логов;
      • ClickHouse/TimescaleDB для аналитики и метрик;
      • MongoDB/Cassandra под специфические нагрузки.

Пример паттерна (Go + PostgreSQL + Redis):

  • PostgreSQL:
    • хранит пользователей, заказы, транзакции.
  • Redis:
    • кеширует часто читаемые данные:
      • профили, токены, результаты тяжёлых запросов.

Краткий ответ для собеседования:

  • «Реляционные БД используют табличную модель, строгую схему, SQL и полноценные транзакции, хорошо подходят для данных с сильной структурой и связями (пример — PostgreSQL). Нереляционные (NoSQL) предлагают другие модели — key-value, документы, графы, wide-column — с более гибкой схемой и упором на горизонтальное масштабирование и специфичные паттерны доступа (Redis, MongoDB, Cassandra и т.п.). В результате RDBMS обычно выбирают для критичных, структурированных данных и сложных запросов, а NoSQL — как дополнение для кеша, логов, аналитики, больших распределенных нагрузок или когда модель данных не укладывается в жесткие таблицы.»

Вопрос 47. Есть ли у вас опыт работы с облачными провайдерами?

Таймкод: 00:40:25

Ответ собеседника: правильный. Говорит, что с облаками ещё не работал.

Правильный ответ:

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

  • знание основных провайдеров;
  • понимание фундаментальных сервисов (compute, network, storage, DB);
  • умение связать облако с CI/CD, контейнерами, IaC и эксплуатацией сервисов.

Краткий обзор, который ожидается знать.

Основные облачные провайдеры:

  • AWS (Amazon Web Services)
  • GCP (Google Cloud Platform)
  • Azure (Microsoft Azure)
  • Плюс региональные/частные: Yandex Cloud, Selectel, VK Cloud, OpenStack и т.п.

Базовые строительные блоки (на примере AWS, с аналогами у других):

  1. Compute (вычисления)
  • Виртуальные машины:
    • AWS: EC2
    • GCP: Compute Engine
    • Azure: Virtual Machines
  • Используются для:
    • запуска приложений (в т.ч. Go-сервисов),
    • хостинга Docker/Kubernetes,
    • stateful-сервисов (БД, очереди, внутренние сервисы).
  1. Managed Kubernetes / контейнеры
  • AWS: EKS, ECS/Fargate
  • GCP: GKE
  • Azure: AKS
  • Позволяют:
    • запускать контейнеры без ручного управления нодами или с его минимизацией;
    • интеграцию с сетями, балансировщиками, секретами, IAM.
  1. Storage (хранение данных)
  • Объектное хранилище:
    • AWS: S3
    • GCP: Cloud Storage
    • Azure: Blob Storage
  • Свойства:
    • durable, версионирование, дешево, подходит для логов, бэкапов, статики.
  • Блочное/файловое хранилище:
    • диски для виртуалок и Kubernetes.
  1. Managed базы данных
  • Relational:
    • AWS RDS (PostgreSQL, MySQL, etc.),
    • Cloud SQL (GCP),
    • Azure Database.
  • NoSQL:
    • DynamoDB, Cloud Bigtable, Cosmos DB и т.п.
  • Преимущества:
    • резервные копии, репликация, обновления, мониторинг — берёт на себя облако;
    • команда фокусируется на схеме и запросах, а не на ручном администрировании.
  1. Сети и балансировка
  • VPC (Virtual Private Cloud):
    • изолированная сеть, подсети, маршруты, security groups.
  • Балансировщики:
    • L4/L7, интеграция c Kubernetes ingress/service.
  • Важные навыки:
    • CIDR, подсети, NAT, security groups/firewall;
    • понимание, как сервисы в облаке общаются между собой и с внешним миром.
  1. Identity & Access Management (IAM)
  • Централизованное управление правами:
    • кто и что может делать в облаке;
    • роли для сервисов (instance roles, service accounts).
  • Критично для безопасности:
    • отказ от «root-ключей в коде»;
    • минимально необходимые полномочия (least privilege).
  1. Инфраструктура как код (IaC) в облаке
  • Terraform, Pulumi, CloudFormation, CDK и т.п.
  • Практика:
    • описывать VPC, сети, БД, кластеры, IAM, Kubernetes и прочее в коде;
    • хранить в Git;
    • прогонять через CI;
    • получать воспроизводимые окружения (dev/stage/prod).

Пример Terraform-фрагмента для создания VM с PostgreSQL-сервисом (упрощённо):

resource "aws_instance" "app" {
ami = "ami-xxxxxxxx"
instance_type = "t3.micro"

tags = {
Name = "go-app"
}
}

resource "aws_security_group" "app_sg" {
name = "app-sg"
description = "Allow HTTP"
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}

Как это связывается с Go/DevOps-практикой:

  • CI/CD:
    • артефакты (Docker-образы) в registry;
    • деплой в Kubernetes/ECS/на VM через пайплайны.
  • Observability:
    • использование managed-сервисов логирования и метрик (CloudWatch, Stackdriver, Azure Monitor и их аналоги);
    • интеграция с Prometheus/Grafana.
  • Безопасность:
    • секреты в Secret Manager/Parameter Store/Vault;
    • IAM-роллинг для доступа из приложений к S3/БД и т.п.

Если реального опыта пока нет, корректная позиция:

  • честно сказать, что не работали руками в проде;
  • но:
    • понимаете базовые сервисы и их аналоги у разных провайдеров;
    • умеете читать Terraform/CloudFormation;
    • можете связать: контейнеры → Kubernetes/managed-сервисы → CI/CD → мониторинг;
    • пробовали на pet-проектах или планируете.

Краткая формулировка для интервью:

  • «С промышленными облаками пока опыта мало, но разбираюсь в их базовых примитивах: compute (EC2/VM), VPC и сетях, балансировщиках, объектном хранилище (S3), managed-БД и Kubernetes-сервисах (EKS/GKE/AKS). Понимаю важность IaC (Terraform) и интеграции с CI/CD. Готов быстро погружаться и уже ориентируюсь в концепциях, а не только в интерфейсе конкретного провайдера.»

Вопрос 48. Какие инструменты мониторинга вы знаете или использовали?

Таймкод: 00:40:33

Ответ собеседника: правильный. Упоминает знакомство с Grafana для визуализации и Prometheus как систему сбора метрик.

Правильный ответ:

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

  • метрики;
  • логи;
  • трассировки (tracing).

Инструменты мониторинга и наблюдаемости, которые важно знать:

  1. Prometheus — сбор и хранение метрик
  • Модель:

    • time-series база для метрик;
    • pull-модель: Prometheus сам опрашивает endpoints (/metrics);
    • метрики в формате key/value с лейблами (labels).
  • Использование:

    • сбор метрик приложений, системных метрик, метрик Kubernetes и т.п.;
    • написание alert-правил (Alertmanager).
  • Примеры метрик:

    • http_requests_total{service="api", method="GET", code="200"}
    • go_goroutines
    • process_resident_memory_bytes
  • Для Go-приложений:

    • библиотека prometheus/client_golang.

    • Пример интеграции:

      import (
      "net/http"
      "github.com/prometheus/client_golang/prometheus"
      "github.com/prometheus/client_golang/prometheus/promhttp"
      )

      var httpRequests = prometheus.NewCounterVec(
      prometheus.CounterOpts{
      Name: "http_requests_total",
      Help: "Total HTTP requests",
      },
      []string{"path", "method", "code"},
      )

      func init() {
      prometheus.MustRegister(httpRequests)
      }

      func handler(w http.ResponseWriter, r *http.Request) {
      // ... бизнес-логика ...
      w.WriteHeader(http.StatusOK)
      httpRequests.WithLabelValues(r.URL.Path, r.Method, "200").Inc()
      }

      func main() {
      http.Handle("/metrics", promhttp.Handler())
      http.HandleFunc("/", handler)
      http.ListenAndServe(":8080", nil)
      }
  • Преимущества:

    • нативная интеграция с Kubernetes (ServiceMonitor, PodMonitor);
    • мощный язык запросов PromQL.
  1. Grafana — визуализация и дашборды
  • Используется как UI для:
    • Prometheus,
    • Loki, Tempo,
    • Elasticsearch, InfluxDB и многих других источников.
  • Возможности:
    • дашборды для сервисов, БД, инфраструктуры;
    • алертинг (в современных версиях — централизованный).
  • Типичный практический подход:
    • собирать метрики в Prometheus;
    • строить Grafana-дашборды:
      • RPS, p95/p99 latency;
      • error rate (4xx/5xx);
      • CPU/RAM, количество goroutine;
      • состояние БД (connections, slow queries).
  1. Логи

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

  • Elastic stack (ELK: Elasticsearch + Logstash + Kibana) или OpenSearch + Kibana/OS Dashboards.
  • Loki (от Grafana Labs):
    • лог-агрегация с моделью, похожей на Prometheus (labels), дешево по хранению.
  • Подход:
    • приложения логируют в stdout/stderr;
    • агенты (Filebeat/Fluent Bit/Promtail) собирают и отправляют в центральное хранилище;
    • поиск и корелляция логов через Kibana/Grafana.
  1. Tracing (распределённые трассировки)

При микросервисной архитектуре и сложных запросах важны трассировки:

  • Jaeger, Zipkin, Tempo.
  • OpenTelemetry как стандарт:
    • для метрик, логов и трейсинга.
  • Применение:
    • отслеживание пути запроса через несколько сервисов;
    • поиск «узких мест» и долгих вызовов;
    • диагностика проблем в проде.
  1. Инфраструктурный мониторинг

Инструменты:

  • node_exporter, cAdvisor, kube-state-metrics:
    • метрики нод, контейнеров, Kubernetes-объектов.
  • Cloud-native решения:
    • AWS CloudWatch, GCP Cloud Monitoring, Azure Monitor.
  • Интеграция:
    • в продакшене часто комбинируют:
      • Prometheus/Grafana для приложений и k8s;
      • облачные мониторинги для инфраструктуры и managed-сервисов.
  1. Алертинг и SLO

Важная часть ответа — понимание, что мониторинг не только про «красивые графики»:

  • Настройка алертов:
    • по метрикам (Prometheus Alertmanager, Grafana Alerting):
      • рост 5xx,
      • падение RPS,
      • увеличение latency (p95/p99),
      • нехватка памяти/CPU,
      • падение реплик в Kubernetes.
  • SLI/SLO:
    • определение целевых показателей (доступность 99.9%, p95 < 300ms);
    • алертинг на отклонения от SLO, а не только на «100%/0%».

Краткий ответ для собеседования:

  • «Использую связку Prometheus + Grafana: Prometheus как систему сбора метрик (в том числе из Go-сервисов через /metrics), Grafana — для дашбордов и алертов. Понимаю роль лог-стека (ELK, Loki) и распределенного трейсинга (Jaeger, OpenTelemetry) для полноценной наблюдаемости. В продакшене считаю must-have метрики по latency, error rate, ресурсам и health-пробам, плюс алертинг по ключевым SLO.»

Вопрос 49. Какие инструменты логирования вы использовали и как посмотреть логи в контейнере?

Таймкод: 00:40:59

Ответ собеседника: неполный. Говорит, что специализированные системы логирования не использовал; помнит про отдельную docker-команду для логов контейнера, но не называет её.

Правильный ответ:

Логирование — ключевой элемент наблюдаемости. В разумно устроенной системе:

  • приложения пишут логи в стандартизированном формате (часто JSON) в stdout/stderr;
  • инфраструктура собирает и агрегирует их в централизованное хранилище;
  • разработчики и операторы могут фильтровать, искать и коррелировать события по сервисам, хостам, trace-id и т.д.

Часть 1. Как посмотреть логи контейнера Docker

Базовый, но обязательный навык:

  • Для просмотра логов конкретного контейнера используется команда:
docker logs <container_name_or_id>

Примеры:

docker logs myapp
docker logs myapp -f # "follow": потоковое чтение, как tail -f
docker logs myapp --tail 100

Важно:

  • Логи контейнера — это то, что процесс пишет в stdout и stderr.
  • Поэтому best practice:
    • не писать логи во внутренние файлы внутри контейнера;
    • логировать в stdout/stderr, а сбор и хранение отдать инфраструктуре (Docker, Kubernetes, лог-агенты).

В Kubernetes аналог:

kubectl logs <pod-name>
kubectl logs <pod-name> -c <container-name>
kubectl logs -f <pod-name>

Часть 2. Специализированные инструменты логирования и стек логов

В продакшен-среде одних docker logs недостаточно, нужны централизованные решения. Основные подходы и инструменты:

  1. ELK / OpenSearch-стек
  • Состав:
    • Elasticsearch / OpenSearch — хранилище и поиск по логам.
    • Logstash / Fluentd / Fluent Bit — сбор и преобразование логов.
    • Kibana / OpenSearch Dashboards — визуализация и поиск.
  • Схема:
    • контейнеры → stdout/stderr;
    • лог-агенты на нодах собирают логи Docker/Kubernetes;
    • отправляют в Elasticsearch/OpenSearch;
    • аналитика и поиск через Kibana.
  1. Loki (Grafana Loki)
  • Лог-хранилище, оптимизированное под модель «как Prometheus, но для логов».
  • Метки (labels) вместо тяжёлых индексов:
    • дешёвое хранение;
    • отлично интегрируется с Grafana.
  • Агенты:
    • Promtail, Fluent Bit.
  • Плюс:
    • единый стек: Prometheus (метрики) + Loki (логи) + Grafana (дашборды и запросы).
  1. Облачные решения
  • AWS CloudWatch Logs, GCP Cloud Logging, Azure Monitor Logs:
    • агенты/интеграции собирают логи контейнеров, VM, managed-сервисов;
    • поиск и алерты в UI провайдера или через API.

Часть 3. Практика логирования в Go-сервисах (важно для уровня профессионала)

Рекомендации:

  • Логировать структурировано:
    • JSON-формат с полями: timestamp, level, service, trace_id, user_id, msg, и т.д.
  • Использовать логгер:
    • zap, zerolog, logrus или адаптированные обертки.
  • Писать в stdout:
    • чтобы Docker/Kubernetes/агенты прозрачно забирали логи.

Пример (Go + zap, структурированные логи):

import (
"go.uber.org/zap"
)

func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()

logger.Info("service started",
zap.String("service", "auth"),
zap.String("version", "1.0.0"),
)

// обработка запроса
logger.Error("failed to process request",
zap.String("path", "/login"),
zap.Error(err),
)
}

Такие логи:

  • легко парсятся в ELK/Loki;
  • позволяют фильтровать по полям в Kibana/Grafana;
  • хорошо коррелируются с трейсингом (по trace_id).

Часть 4. Best practices, которые стоит проговорить на интервью

  • В контейнерах:
    • только stdout/stderr;
    • никакого ручного tail по файлам внутри контейнера в качестве основной практики.
  • Централизация:
    • все логи стекаются в одну-две системы, откуда можно:
      • искать по сервису/namespace/pod/id;
      • строить алерты (например, всплеск 5xx в логах).
  • Корреляция:
    • добавлять request-id/trace-id в каждый лог-запись;
    • это позволяет связать метрики, логи и трассировки.

Краткий ответ для собеседования:

  • «Для просмотра логов контейнера используется docker logs <id|name> (с -f и --tail по необходимости). В реальных окружениях логи контейнеров централизуются: через ELK/Opensearch-стек, Loki, или облачные решения. Приложения (особенно в контейнерах) должны логировать в stdout/stderr структурированно (например, JSON), чтобы лог-агенты могли прозрачно собирать и индексировать эти логи для поиска и алертинга.»

Вопрос 50. Есть ли у вас вопросы по требованиям к уровню начального DevOps-специалиста?

Таймкод: 00:42:01

Ответ собеседника: правильный. Спрашивает, какие знания и практический опыт необходимы для старта в DevOps и какие направления важны.

Правильный ответ:

Если говорить о требованиях к стартовому уровню, то важен не формальный «титул», а то, насколько человек умеет:

  • понимать, как код превращается в работающий, наблюдаемый и управляемый сервис;
  • опираться на автоматизацию, а не на ручные действия;
  • разбираться в основных инфраструктурных примитивах и сетевых концепциях.

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

  1. Базовый, но уверенный Linux
  • Навыки:
    • работа в терминале (bash/sh);
    • файловая система, права (chmod/chown), пользователи и группы;
    • процессы (ps/top/htop), systemd (systemctl, unit-файлы);
    • базовая диагностика: ping, curl, ss/netstat, journalctl, dmesg.
  • Ожидание:
    • можешь подключиться к серверу по SSH и:
      • проверить, жив ли сервис;
      • посмотреть логи;
      • перезапустить сервис;
      • разобраться с наиболее очевидными проблемами.
  1. Сетевые основы
  • Понимание:
    • модель OSI на прикладном уровне;
    • IP, порты, TCP/UDP;
    • DNS (A/AAAA/CNAME, как работает резолвинг);
    • маска подсети, маршрутизация в базовом виде;
    • что такое NAT, балансировщик, reverse proxy.
  • Ожидание:
    • умеешь объяснить, что происходит при запросе к https-сервису;
    • можешь применить curl, dig, traceroute, ping для диагностики.
  1. Контейнеры и Docker
  • Навыки:
    • понимать разницу образ/контейнер;
    • писать Dockerfile для простого сервиса:
      • выбрать базовый образ;
      • собрать бинарник (multi-stage build — большой плюс);
      • корректно настроить CMD/ENTRYPOINT;
    • запускать контейнеры:
      • маппинг портов, volume, переменные окружения.
  • Ожидание:
    • можешь упаковать небольшое приложение (например, Go/Node.js/Python API) в корректный образ и запустить на другом хосте.
  1. Базовое понимание CI/CD
  • Концепции:
    • что такое CI, CD (delivery vs deployment);
    • какие шаги типичного pipeline:
      • линтеры,
      • тесты,
      • сборка артефакта/образа,
      • деплой.
  • Навыки:
    • прочитать и понять .gitlab-ci.yml или GitHub Actions workflow;
    • при минимальном guidance — собрать простой CI: проверка кода + тесты + сборка Docker-образа.
  • Ожидание:
    • не обязательно глубокий опыт, но чёткая ментальная модель и готовность писать пайплайны под присмотром.
  1. Инфраструктура как код и конфигурации
  • Начальный уровень:
    • понимание идеи IaC: инфраструктура, описанная декларативно и хранимая в Git;
    • базовое знакомство с Ansible:
      • плейбуки, инвентори, идеи идемпотентности;
    • базовое представление о Terraform:
      • ресурсы, провайдеры, состояние.
  • Ожидание:
    • можешь прочитать и слегка поправить простые playbook/terraform-конфиг;
    • понимаешь, зачем это лучше, чем «SSH + руками».
  1. Мониторинг и логирование
  • Понимание:
    • зачем нужны метрики (RPS, latency, error rate, CPU/RAM);
    • базовая модель Prometheus + Grafana;
    • идея централизованного логирования (ELK/Loki/Cloud Logging).
  • Ожидание:
    • можешь:
      • навесить простую /metrics на сервис;
      • открыть Grafana и понять, хорошо или плохо по графикам;
      • использовать docker logs/kubectl logs для первичной диагностики.
  1. Работа с БД и основами приложений
  • Навыки:
    • базовые SQL-команды: SELECT/INSERT/UPDATE/DELETE/CREATE/ALTER;
    • понимание разницы между рел. БД и NoSQL;
    • базовая работа с PostgreSQL.
  • Ожидание:
    • можешь:
      • проверить коннект к БД;
      • выполнить простой запрос;
      • не «ронять» БД случайным DELETE без WHERE.
  1. Язык скриптов и основ программирования
  • Обязательно:
    • уверенный bash/sh;
    • умение писать маленькие утилиты/скрипты для автоматизации.
  • Желательно:
    • базовый опыт на одном из языков: Go, Python.
  • Ожидание:
    • можешь:
      • автоматизировать рутину (деплой, health-check, бэкапы);
      • понимать код сервисов достаточно, чтобы разбираться в том, как они конфигурируются, логируются, стартуют.
  1. Мышление и подход

То, что критически важно для старта:

  • системное мышление:
    • понимать цепочку: код → сборка → образ → деплой → сеть → логи/метрики → пользователь;
  • аккуратность:
    • не делать опасных изменений без плана и бэкапа;
  • любознательность:
    • разбирать инциденты и читать документацию;
  • привычка автоматизировать:
    • если делаешь одно и то же второй раз руками — задуматься о скрипте/плейбуке.

Если переводить в конкретный roadmap (что выучить первым):

  • Linux (CLI, systemd, сеть).
  • Docker + написание Dockerfile.
  • Базовый CI (GitHub Actions/GitLab CI для простого сервиса).
  • Основы сетей, HTTP, DNS.
  • PostgreSQL (минимум CRUD + простая схема).
  • Prometheus + Grafana (снять метрики с тестового сервиса).
  • Ansible (один-два простых playbook для настройки сервера).

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