Intern DevOps Andersen - Стажировка / Реальное собеседование.
Сегодня мы разберем собеседование начинающего 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, так как позволяет:
- читать и улучшать код приложений;
- автоматизировать рутинные задачи (скрипты деплоя, миграции, проверки);
- эффективнее выстраивать взаимодействие между разработкой и инфраструктурой.
- Коммерческий или проектный опыт на Python (или другом языке), включающий:
-
Опыт в области процессов и инфраструктуры:
- Работа в IT-аудите (например, KPMG или аналогичные компании):
- анализ IT-процессов, рисков, информационной безопасности;
- понимание требований к надежности, отказоустойчивости, контролям доступа, логированию;
- участие в проверке соответствия индустриальным стандартам (ISO 27001, SOC, внутренние политики).
- Это даёт сильное понимание:
- как должна выглядеть зрелая инфраструктура;
- какие риски учитываются при построении CI/CD и cloud-архитектур;
- почему важны наблюдаемость, трассировка, аудит действий.
- Работа в IT-аудите (например, KPMG или аналогичные компании):
-
Обучение 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:
-
Совместная ответственность за продукт
- Разработчики, инфраструктурные инженеры, QA, безопасность и бизнес работают как единая команда.
- Нет модели «мы написали — вы поддерживайте». Те, кто пишет код, участвуют в его продакшн-жизни: метрики, алерты, производительность, надежность.
- Это снижает конфликт интересов: разработка хочет быстро релизить, эксплуатация — стабильность. DevOps выстраивает процессы так, чтобы можно было и быстро, и надежно.
-
Автоматизация всего жизненного цикла
- Цель — минимизировать ручные операции, которые:
- медленные;
- подвержены ошибкам;
- плохо масштабируются.
- Ключевые зоны автоматизации:
- сборка и тестирование кода;
- создание артефактов (бинарники, Docker-образы);
- деплой на разные окружения;
- миграции баз данных;
- конфигурация инфраструктуры и сервисов.
- Типичный pipeline:
- commit → автоматические тесты → сборка образа → security checks → деплой в staging → автоматические/ручные проверки → деплой в production.
- Цель — минимизировать ручные операции, которые:
-
Непрерывная интеграция и доставка (CI/CD)
- Непрерывная интеграция:
- частые слияния кода в основную ветку;
- автоматический запуск тестов и проверок качества.
- Непрерывная доставка/деплой:
- возможность доставить изменение в продакшн в любой момент кнопкой или автоматически;
- минимальный lead time от коммита до релиза.
- Преимущества:
- быстрый feedback loop;
- меньший размер изменений → меньше рисков;
- предсказуемые, повторяемые релизы.
- Непрерывная интеграция:
-
Инфраструктура как код (IaC)
- Инфраструктура описывается декларативно в коде (Terraform, Ansible и др.).
- Это:
- делает окружения воспроизводимыми;
- позволяет ревьюить изменения инфраструктуры так же, как код приложения;
- уменьшает «дрейф конфигураций» между dev/stage/prod;
- упрощает масштабирование и disaster recovery.
-
Наблюдаемость, измеримость и надежность
- DevOps предполагает, что система спроектирована так, чтобы её можно было измерять:
- метрики (latency, RPS, error rate, resource usage);
- логи;
- трассировка (distributed tracing).
- Наличие SLI/SLO/SLA, алертинга и дэшбордов.
- Решения принимаются на основе данных, а не только «по ощущениям».
- Инциденты анализируются через postmortem, чтобы улучшать архитектуру и процессы.
- DevOps предполагает, что система спроектирована так, чтобы её можно было измерять:
-
Безопасность как часть процесса (DevSecOps)
- Security интегрирована в pipeline:
- static analysis (SAST);
- dependency scanning;
- проверка образов контейнеров;
- политики доступа и секретов.
- В результате безопасность не тормозит релизы, а становится автоматизированной и предсказуемой.
- Security интегрирована в pipeline:
Коротко: DevOps — это про то, как сделать так, чтобы изменения, написанные разработчиками, попадали в продакшн часто, быстро, безопасно и воспроизводимо, за счет автоматизации, инфраструктуры как кода, прозрачности и общей ответственности команды за весь жизненный цикл сервиса.
Вопрос 4. Какие процессы автоматизируются при доставке кода и как это связано с CI/CD?
Таймкод: 00:03:31
Ответ собеседника: неполный. Упоминает автоматическую сборку, тестирование, развертывание, использование контейнеризации, оркестрации и IaC, и называет это CI/CD.
Правильный ответ:
Под CI/CD обычно понимают связанную цепочку автоматизации всего пути изменения кода от коммита до продакшна. Важно не только перечислить этапы, но и понимать их цели, типичные практики и инструменты.
Основные компоненты:
-
Непрерывная интеграция (CI) Цель: максимально рано находить ошибки, интеграционные проблемы и нарушения стандартов качества.
Ключевые шаги:
- Триггер: каждый commit / merge request.
- Сборка:
- Компиляция (для Go —
go build ./...). - Формирование артефактов (бинарник, Docker-образ).
- Компиляция (для Go —
- Автоматические проверки:
- линтеры и форматирование (Go пример):
gofmt,go vet,golangci-lint run
- модульные и интеграционные тесты:
go test ./... -race -cover
- линтеры и форматирование (Go пример):
- Результат:
- если пайплайн зеленый — изменение считается технически готовым для продвижения дальше;
- если красный — разработчик сразу получает фидбек.
Пример простого 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 -
Непрерывная доставка и деплой (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"] - Сборка и публикация артефактов:
-
Контейнеризация и оркестрация как часть CI/CD
- Контейнеризация (Docker/Podman):
- единый, предсказуемый runtime;
- один и тот же образ на dev/stage/prod.
- Оркестраторы (Kubernetes, Nomad и др.):
- декларативный деплой (Deployment, Service, Ingress);
- масштабирование, рестарты, rollout/rollback.
- Эти инструменты идеально сочетаются с CI/CD:
- пайплайн собирает образ;
- манифесты/Helm-чарты/операторы описывают целевое состояние;
- CD-инструмент (Argo CD, Flux, Jenkins X и др.) приводит кластер к этому состоянию.
- Контейнеризация (Docker/Podman):
-
Инфраструктура как код (IaC) в контуре CI/CD
- Инфраструктура (VPC, подсети, БД, очереди, балансировщики) описана кодом (Terraform, Pulumi, Ansible).
- Изменения инфраструктуры проходят тот же процесс:
- review → pipeline (fmt/validate/plan) → утверждение → apply.
- Это:
- устраняет «ручные настройки на проде»;
- делает окружения воспроизводимыми;
- уменьшает риск расхождений между stage/prod.
-
Что в сумме называется 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-пайплайна:
-
Проверка синтаксиса, форматирование и статический анализ Цель: обеспечить базовую корректность и единый стиль кода до запуска тяжелых тестов.
Для Go:
gofmt— форматирование кода.go vet— встроенный статический анализатор.golangci-lint— объединённый набор линтеров (ошибки, код-стайл, потенциальные баги).
Пример шагов:
gofmt -w ./...
go vet ./...
golangci-lint run ./... -
Управление зависимостями Цель: гарантировать воспроизводимость сборки.
- Проверка
go.mod/go.sum:
go mod tidy
go mod verify- Можно добавить проверку, что после
go mod tidyнет изменений в git.
- Проверка
-
Сборка (build) Цель: убедиться, что код компилируется во всей кодовой базе, а не только локально.
go build ./... -
Модульные тесты (unit tests) Цель: быстро проверить корректность бизнес-логики на небольших изолированных участках.
- Обязательные:
go test ./... -race -coverprofile=coverage.out- Важные аспекты:
- использование
-raceдля выявления data races; - контроль покрытия (например, fail, если coverage < порога).
- использование
-
Интеграционные тесты Цель: проверить взаимодействие между компонентами: БД, очереди, внешние сервисы, 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" - запуск тестовой инфраструктуры через Docker Compose:
-
Проверка миграций и схемы БД Цель: убедиться, что изменения схемы непротиворечивы и применимы.
- Использование миграционных инструментов (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()
); -
Анализ безопасности Цель: поймать уязвимости до продакшена.
Для Go:
gosecдля анализа кода.- Проверка зависимостей на CVE (govulncheck, встроенные средства CI/CD или SCA-инструменты).
Для контейнеров:
- сканирование Docker-образов (Trivy/Grype).
-
Сборка и публикация артефактов (на границе 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-проектов.
Основные типы тестов:
-
Юнит-тесты (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)
}
}
- Цель:
-
Интеграционные тесты (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)
}
}
- Цель:
-
Контрактные тесты (Contract tests)
- Цель:
- гарантировать совместимость между сервисами (producer/consumer).
- Примеры:
- для REST/gRPC: проверка, что сервис отвечает согласно описанному контракту (OpenAPI/Proto);
- для событий: формат сообщений в Kafka/RabbitMQ соответствует ожиданиям потребителей.
- В CI:
- позволяют независимо эволюционировать сервисы, не ломая интеграции;
- часто используются в microservices-архитектуре.
- Цель:
-
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)
}
}
- Цель:
-
End-to-End (E2E) тесты
- Цель:
- проверить реальный пользовательский сценарий «от края до края»:
- фронт → API → БД → внешние сервисы.
- проверить реальный пользовательский сценарий «от края до края»:
- Характеристики:
- самые дорогие и медленные;
- чувствительны к нестабильности окружения.
- В CI:
- обычно:
- запускаются реже (например, nightly, перед релизом или по тегу);
- выполняются в отдельном пайплайне или стадии.
- обычно:
- Важны для критичных фич, но не должны блокировать быстрый цикл CI.
- Цель:
-
Регрессионные тесты
- Цель:
- зафиксировать и не допустить повторного появления уже найденных багов.
- Фактически это обычные unit/integration/E2E тесты, но с акцентом на прошлые инциденты.
- В CI:
- интегрируются в общий набор автотестов;
- каждая найденная и исправленная критичная ошибка должна получать тест.
- Цель:
-
Тесты производительности и нагрузочные тесты (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
- Цель:
-
Безопасностные тесты (Security scans)
- Цель:
- найти уязвимости до продакшена.
- Виды:
- SAST (статический анализ кода);
- DAST (динамическое тестирование API);
- сканирование зависимостей и Docker-образов.
- В CI:
- часть стандартного пайплайна или отдельная security-стадия.
- Цель:
-
Линтинг и 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-ветка:
- деплоябельна;
- проверена тестами;
- готова к выпуску.
- в любой момент времени 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, но сами фазы в том или ином виде присутствуют всегда):
-
Анализ требований (Requirements Engineering)
- Цели:
- понять бизнес-задачу, ограничения и критерии успеха;
- сформировать функциональные и нефункциональные требования.
- Нефункциональные требования особенно важны:
- производительность (RPS, latency);
- надежность и доступность (SLA, SLO);
- безопасность (аутентификация, авторизация, шифрование, аудит);
- масштабируемость;
- соответствие стандартам (GDPR, PCI DSS и т.п.).
- Результат:
- спецификации требований, user stories, use cases, acceptance criteria.
- Цели:
-
Планирование
- Цели:
- оценить объем работ, бюджет, сроки, риски;
- выбрать модель процесса (Scrum, Kanban, релизные циклы);
- определить ключевые технические решения на верхнем уровне.
- Важные моменты:
- оценка рисков интеграций с внешними системами;
- план по качеству: какие типы тестирования, как устроен CI/CD;
- план по observability и SRE-практикам.
- Цели:
-
Проектирование архитектуры и инфраструктуры (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);
- Цели:
-
Проектирование интерфейсов (UI/UX) при необходимости
- Для пользовательских продуктов:
- прототипы интерфейсов;
- UX-потоки;
- согласование с бизнесом.
- Для backend-фокуса:
- чётко задокументированные API (OpenAPI/Swagger, Protobuf схемы).
- Для пользовательских продуктов:
-
Разработка (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
}
-
Тестирование и валидация качества
- Цель:
- убедиться, что продукт соответствует требованиям и не ломается при типичных сценариях.
- Уровни:
- unit-тесты;
- интеграционные и контрактные тесты;
- end-to-end и регрессионные;
- security-тесты, performance-тесты.
- Тесная интеграция с CI:
- каждый merge запускает набор проверок;
- без успешного CI код не попадает дальше по конвейеру.
- Цель:
-
Деплой и релиз (Deployment & Release)
- Цель:
- безопасно и воспроизводимо доставить приложение в production и другие окружения.
- Практики:
- CI/CD pipelines;
- контейнеризация (Docker), оркестрация (Kubernetes);
- стратегии:
- rolling, blue-green, canary;
- миграции БД без простоя.
- GitOps / IaC:
- декларативные манифесты;
- traceability изменений инфраструктуры.
- Цель:
-
Эксплуатация, мониторинг и сопровождение (Operations & Maintenance)
- Сюда входят:
- мониторинг (Prometheus, Grafana), логи, метрики, трассировка;
- алертинг по SLO (латентность, error rate, saturation);
- управление инцидентами, postmortem-практики;
- плановые обновления зависимостей, security-патчи;
- улучшения по результатам реальной эксплуатации.
- Пример ключевых метрик для Go-сервиса:
- p95 latency по endpoint;
- количество 5xx;
- количество активных goroutines, GC-паузы;
- использование CPU/RAM.
- Сюда входят:
-
Эволюция и вывод из эксплуатации
- На зрелом уровне SDLC учитывает:
- как мы безопасно меняем архитектуру, не ломая клиентов;
- backward compatibility для API и схем БД;
- процесс миграции данных;
- корректный decommission старых сервисов/версий.
- На зрелом уровне SDLC учитывает:
Важно:
- 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-системы:
-
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}
'''
}
}
}
}
-
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
-
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
-
TeamCity
- Часто используется в крупных компаниях.
- Поддерживает декларативные и UI-настройки билд-конфигураций.
- Сильные стороны:
- гибкий билд-оркестратор;
- хорошая интеграция с monorepo, сложными пайплайнами.
-
CircleCI, Travis CI и аналогичные облачные CI
- Фокус на:
- простоте;
- быстром старте;
- интеграции с GitHub/Bitbucket.
- YAML-конфиги в репозитории, концепции схожи: jobs, steps, workflows.
- Фокус на:
-
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:
-
GitLab CI/CD
- Использовал для:
- полноценной реализации CI: линт, тесты, сборка бинарников и Docker-образов;
- CD: деплой в dev/stage/prod окружения.
- Писал
.gitlab-ci.ymlвручную:- разносил pipeline на стадии (stages):
lint,test,build,deploy; - использовал кеширование зависимостей (
go mod download), артефакты и условия для разных окружений; - настраивал ручные джобы (manual) для Continuous Delivery на прод.
- разносил pipeline на стадии (stages):
- Пример пайплайна для 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 - Использовал для:
-
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 -
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}
'''
}
}
}
} - Знаком с концепциями:
-
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для установки и обновлений.
- Ubuntu/Debian:
- Работа с сервисами:
systemd:systemctl start/stop/status/enable, анализ unit-файлов;- настройка и просмотр логов:
journalctl -u <service>.
- Сетевые утилиты:
- проверка доступности и DNS:
ping,curl,dig,nslookup; - проверка портов и соединений:
ss -tulpen,netstat(если доступен),telnet/nc.
- проверка доступности и DNS:
- Работа с процессами и ресурсами:
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), что влияет на деплой и отладку.
- Сборка и запуск Go-сервисов в Linux-среде:
Сильный ответ не ограничивается перечислением дистрибутивов, а показывает уверенное владение 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
- Debian/Ubuntu:
Корректное сопоставление семейства и пакетного менеджера — базовый, но обязательный навык для работы с 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), рестарт-политик и управления окружением.
- Умение описать корректный unit-файл для сервиса (например, Go-приложения):
Знание systemd и systemctl — базовый и необходимый навык для работы с сервисами в современных Linux-средах, в том числе при отладке, деплое и эксплуатации Go-приложений.
Вопрос 14. Есть ли опыт работы с unit-файлами systemd и созданием собственных сервисов?
Таймкод: 00:12:39
Ответ собеседника: правильный. Сообщает, что собственные unit-файлы не писал, ограничивался управлением уже существующими сервисами: запуск, остановка, проверка статуса.
Правильный ответ:
Для работы с backend-сервисами и инфраструктурой важно не только уметь управлять существующими сервисами через systemctl, но и уметь оформлять свои приложения (в т.ч. на Go) как полноценные systemd-сервисы.
Ключевые моменты:
- Основы unit-файлов systemd
- Unit-файлы описывают:
- что запускать;
- в каком окружении;
- с какими зависимостями;
- как рестартовать;
- при каких условиях считать сервис «живым».
- Основные типы unit:
service— фоновые службы (то, что нас интересует для приложений);timer,socket,mountи др. (расширенные сценарии).
- Типичная структура 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.
- Базовый рабочий цикл создания сервиса
- Создаем 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
- Практические best practices:
- Логи:
- не писать лог-файлы вручную средствами приложения, а логировать в stdout/stderr:
- systemd и journald их соберут;
- просмотр через
journalctl -u my-service.
- не писать лог-файлы вручную средствами приложения, а логировать в stdout/stderr:
- Безопасность:
- отдельный системный пользователь (
User=appuser); - минимум прав на файлы и директории;
- можно дополнительно использовать:
NoNewPrivileges=yesProtectSystem=fullProtectHome=trueReadOnlyPaths=/ReadWritePaths=.
- отдельный системный пользователь (
- Надежность:
Restart=on-failure+ разумныйRestartSec;- health-check endpoint в приложении, чтобы внешние системы/балансировщики могли проверять статус.
- Пример 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:
-
Системные вызовы и 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-системе.
- работа с файлами и директориями:
-
Стандартные утилиты и shell POSIX определяет:
- базовый набор команд командной строки:
sh(POSIX shell),ls,cp,mv,rm,grep,find,sed,awk,xargs,cat,echo,testи др.;
- их минимальное поведение и опции.
Это важно для написания переносимых shell-скриптов: скрипт, соответствующий POSIX sh, будет работать на разных Unix-системах без завязки на особенности Bash/Zsh и т.п.
- базовый набор команд командной строки:
-
Модель прав и процессов POSIX задает:
- концепцию пользователей и групп;
- модель прав rwx для user/group/other;
- базовые гарантии поведения процессов, сигналов, управления временем и т.д.
-
POSIX и Linux/BSD/macOS
- Linux:
- не является формально сертифицированным POSIX, но практически очень близок и реализует большую часть POSIX API; большинство приложений POSIX-переносимы на Linux.
- *BSD, macOS:
- исторически сильнее ориентированы на POSIX-соответствие.
- Различия всё ещё возможны (расширения, нестандартные фичи), поэтому для максимальной переносимости важно держаться POSIX, а не специфичных возможностей конкретного дистрибутива.
- Linux:
-
Практический смысл для разработки и DevOps
Почему это важно:
- Переносимость:
- если вы пишете shell-скрипты деплоя, миграций, CI/CD шагов — опора на POSIX-команды и POSIX shell уменьшает риск, что скрипты «сломаются» на другом дистрибутиве.
- Предсказуемость поведения:
- многие инструменты (Docker base images, минимальные образы) содержат POSIX-совместимый набор утилит; знание стандарта помогает не зависеть от "удобных, но нестандартных" фич.
- Разработка на Go/C:
- Go рантайм и стандартная библиотека под Unix в значительной степени опираются на модель, совместимую с POSIX:
- файловые дескрипторы, сигналы, сокеты, права, процессы.
- Понимание POSIX помогает лучше понимать, как ваши приложения работают «под капотом» в Linux/Unix-среде.
- Go рантайм и стандартная библиотека под Unix в значительной степени опираются на модель, совместимую с POSIX:
- Переносимость:
-
Простой пример: переносимый 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. Есть два способа записи: символьный и числовой.
- Символьная форма (рекомендуется для наглядности):
- Добавить право:
+— добавить;-— убрать;=— установить ровно указанные права (остальные убрать).
Примеры:
- Добавить право выполнения для владельца:
chmod u+x script.sh - Добавить право записи для группы:
chmod g+w file.txt - Добавить право выполнения для всех:
chmod a+x script.sh - Убрать право записи для остальных:
chmod o-w file.txt
- Числовая форма (удобна для явного задания полного набора):
Права кодируются суммой:
r = 4,w = 2,x = 1.
Три цифры: u g o.
Примеры:
chmod 755 script.sh7(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; пример корректен).
- Практические рекомендации:
- Для исполняемых скриптов:
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): нет прав → ---
Это можно сделать:
- В числовой форме:
chmod 640 filename
Расшифровка:
- 6 = 4 (r) + 2 (w) → rw-
- 4 = r--
- 0 = ---
- В символьной форме:
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 [новый_владелец]:[новая_группа] файл
Примеры:
-
Изменить только владельца:
sudo chown alice somefile.txt- Теперь владельцем
somefile.txtявляется пользовательalice. - Группа при этом останется прежней.
- Теперь владельцем
-
Изменить владельца и группу:
sudo chown alice:developers somefile.txt- Владелец:
alice - Группа:
developers
- Владелец:
-
Рекурсивная смена владельца для директории и всех вложенных файлов:
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-файлах (
Структура записи в crontab:
Каждая строка имеет формат:
* * * * * команда
│ │ │ │ │
│ │ │ │ └─ день недели (0-7, где 0 и 7 — воскресенье)
│ │ │ └─── месяц (1-12)
│ │ └───── день месяца (1-31)
│ └─────── час (0-23)
└───────── минута (0-59)
Примеры:
-
Запуск скрипта каждый день в 03:00:
0 3 * * * /usr/local/bin/backup.sh -
Запуск Go-сервиса/утилиты каждые 5 минут:
*/5 * * * * /usr/local/bin/update-metrics >> /var/log/update-metrics.log 2>&1 -
Запуск задачи по будням в 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 (снизу вверх):
-
Физический уровень (Physical)
- Отвечает за:
- передачу битов по физической среде: электрические сигналы, оптика, радио;
- разъемы, кабели, частоты, напряжения.
- Примеры:
- кабели (UTP, оптоволокно),
- Ethernet на уровне физики,
- repeaters, hubs.
- Для разработчика важен косвенно: качество канала, скорость, дуплекс, физические сбои.
- Отвечает за:
-
Канальный уровень (Data Link)
- Отвечает за:
- доставку кадров (frames) между соседними устройствами в одной локальной сети;
- адресацию на уровне MAC-адресов;
- обнаружение и иногда коррекцию ошибок.
- Примеры:
- Ethernet (L2),
- Wi-Fi (802.11),
- PPP, VLAN (802.1Q),
- протокол ARP формально между L2/L3.
- Важен при:
- диагностике проблем в LAN,
- работе с MAC, VLAN, L2-сегментацией.
- Отвечает за:
-
Сетевой уровень (Network)
- Отвечает за:
- логическую адресацию и маршрутизацию пакетов между сетями.
- Ключевой протокол:
- IP (IPv4, IPv6).
- Также:
- ICMP (ping, диагностика),
- маршрутизаторы.
- Для backend/DevOps:
- понимание IP-адресов, подсетей (CIDR), маршрутов, NAT;
- влияние на доступность сервисов между сегментами сети, Kubernetes node-поды и т.п.
- Отвечает за:
-
Транспортный уровень (Transport)
- Отвечает за:
- доставку данных «конец-в-конец» между приложениями;
- управление сессиями передачи, порядком, надёжностью.
- Основные протоколы:
- TCP:
- надёжный, с установлением соединения (3-way handshake),
- контроль последовательности, повторные передачи, контроль перегрузки.
- UDP:
- без установления соединения,
- без гарантии доставки, но проще и быстрее, подходит для DNS, стриминга, RTP, некоторых RPC.
- TCP:
- В Go:
net.Dial("tcp", ...),net.Listen("tcp", ...)— TCP;net.ListenPacket("udp", ...)— UDP.
- Критично для:
- понимания портов, connection pooling,
- таймаутов, ретраев, backoff, нагрузочных характеристик.
- Отвечает за:
-
Сеансовый уровень (Session)
- В классической модели:
- управление сессиями, диалогами, установлением/поддержанием/завершением сеансов;
- контроль контекста между взаимодействующими приложениями.
- На практике:
- часто «размазан» между транспортом и прикладными протоколами;
- примеры: TLS-сессии, сессии приложений, RPC-сессии.
- В классической модели:
-
Представительский уровень (Presentation)
- Отвечает за:
- формат представления данных,
- кодирование/декодирование, сериализацию,
- шифрование/дешифрование.
- Примеры:
- JSON, XML, Protobuf, Avro;
- кодировки (UTF-8, ASCII);
- TLS-шифрование (частично сюда относят).
- Для Go-разработки:
- маршалинг/анмаршалинг JSON/Proto,
- работа с кодировками,
- обработка бинарных протоколов.
- Отвечает за:
-
Прикладной уровень (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-запрос к Go-сервису:
Хороший лаконичный ответ:
- «Да, это семиуровневая модель. Снизу вверх: физический, канальный, сетевой, транспортный, сеансовый, представительский, прикладной. Она используется как логическая схема, чтобы разделять ответственность: от битов на проводе до HTTP/gRPC на уровне приложения. В работе помогаeт диагностировать, на каком уровне возникла проблема и как на неё влияют наши решения в коде и инфраструктуре.»
Вопрос 22. Что происходит при переходе в браузере по адресу сайта (например, https://google.com) с точки зрения сетевых протоколов и модели OSI?
Таймкод: 00:16:50
Ответ собеседника: неправильный. Не может последовательно описать процесс; частично упоминает DNS после подсказок, путается и не раскрывает капсулирование, работу TCP/HTTP, TLS и маршрутизацию.
Правильный ответ:
Разберем по шагам, что происходит, когда вы вводите в браузере https://google.com и нажимаете Enter. Это типичный вопрос, который проверяет глубокое понимание сети, OSI-модели и практической работы веб-приложений.
Опишем в логике «сверху вниз» (от уровня приложения к физике), с привязкой к OSI-уровням и реальным протоколам.
- Обработка URL и определение протокола (уровень 7 — прикладной)
- Браузер парсит URL:
- схема:
https - хост:
google.com - порт по умолчанию для HTTPS: 443
- путь:
/(если не указан).
- схема:
- Понимает, что нужно:
- установить защищенное соединение HTTPS (HTTP поверх TLS поверх TCP);
- обратиться к хосту
google.comпо порту 443.
- 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: кадры в локальной сети и дальше по маршруту.
- Установление 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: физический сигнал по кабелю/радио.
- 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.
- 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-запрос, часто по уже установленным соединениям).
- Маршрутизация и капсулирование (уровни 3–1)
Важно понимать, как данные «едут по сети»:
- На стороне клиента:
- HTTP-данные → TLS-шифрованные блоки → TCP-сегменты → IP-пакеты → Ethernet/Wi-Fi кадры → физический сигнал.
- На маршрутизаторах:
- работают, в основном, на уровне 3 (IP):
- пересылают пакеты в сторону целевой сети,
- не «видят» HTTP/HTTPS содержимого.
- работают, в основном, на уровне 3 (IP):
- На стороне сервера:
- все в обратном порядке: физика → L2 → L3 → L4 (TCP) → TLS → HTTP → приложение.
Каждый уровень добавляет свой заголовок (это и есть инкапсуляция):
- L7: HTTP-заголовки/данные.
- L4: TCP-заголовок.
- L3: IP-заголовок.
- L2: Ethernet-заголовок.
- L1: конкретное кодирование сигнала.
- Особенности для HTTPS/современного веба
- Возможны редиректы:
- с
http://наhttps://.
- с
- HTTP/2:
- мультиплексирование нескольких запросов по одному TCP-соединению.
- HTTP/3:
- работает поверх QUIC (UDP), логика схожа, но транспорт другой.
- CDN, Anycast:
- DNS может отдать ближайший к пользователю IP (Google Frontend/Edge).
- Как это связано с практикой 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:
-
Преобразование имени в IP-адрес (Forward lookup)
- Основная задача:
- по доменному имени найти один или несколько IP-адресов.
- Пример:
- запрос:
api.example.com→ ответ:203.0.113.10.
- запрос:
- Без DNS:
- приходилось бы вбивать IP вручную и обновлять их при каждом изменении инфраструктуры.
- Это критично для:
- user experience;
- гибкости и эволюции инфраструктуры.
- Основная задача:
-
Обратное разрешение (Reverse lookup)
- По IP-адресу получить доменное имя (через PTR-записи).
- Используется:
- в диагностике,
- антиспаме,
- логировании и аналитике.
-
Абстракция и гибкость инфраструктуры
- DNS позволяет:
- менять IP-сервера без изменения доменного имени;
- балансировать нагрузку с помощью нескольких A/AAAA-записей (simple round-robin);
- использовать CNAME-записи для делегирования имен.
- Примеры:
- миграция сервера:
- меняем IP в DNS-записи → пользователи продолжают ходить по тому же домену.
- балансировка:
service.example.com→ несколько IP,- клиенты распределяются по ним.
- миграция сервера:
- DNS позволяет:
-
Типы DNS-записей (важно знать на собеседовании):
- A — имя → IPv4-адрес.
- AAAA — имя → IPv6-адрес.
- CNAME — алиас одного доменного имени на другое.
- MX — почтовые сервера домена.
- TXT — произвольные данные (SPF, верификации, метаданные).
- NS — серверы, ответственные за зону.
- SRV, CAA и др. — для специфичных сервисов и политик.
-
Как работает запрос DNS (упрощенно):
- Клиент (браузер/приложение) вызывает системный резолвер:
- проверяется локальный кеш;
- при отсутствии записи → запрос на DNS-сервер (обычно провайдерский или публичный).
- Резолвер при необходимости опрашивает:
- корневые DNS-серверы (root),
- TLD-серверы (.com, .net, .org...),
- авторитетные DNS-серверы конкретного домена.
- Результат кэшируется на всех уровнях (TTL), чтобы снизить нагрузку и ускорить ответы.
- Клиент (браузер/приложение) вызывает системный резолвер:
-
Связь с практикой 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 используется для:
Кратко:
- DNS нужен, чтобы связать доменные имена с IP-адресами и скрыть динамику инфраструктуры за стабильными именами.
- Он обеспечивает удобство для людей, гибкость и масштабируемость для систем, и является критически важным компонентом сети: если ломается DNS — «ломается Интернет» для конечного пользователя, даже если сами сервисы работают.
Вопрос 24. Какие типы DNS-записей вы знаете, в частности для сопоставления доменного имени с IPv4-адресом?
Таймкод: 00:19:24
Ответ собеседника: неполный. Не называет тип записи самостоятельно; после подсказки собеседника упоминается тип A.
Правильный ответ:
В DNS используется множество типов записей, каждый из которых решает свою задачу. Для собеседования важно уверенно знать базовые типы и уметь объяснить их назначение.
Ключевой ответ на уточняющий вопрос:
- Для сопоставления доменного имени с IPv4-адресом используется запись типа A (Address record).
Далее — краткий, но содержательный обзор основных записей:
-
A (Address)
- Назначение:
- сопоставляет доменное имя с IPv4-адресом.
- Пример:
example.com. 300 IN A 203.0.113.10 - Использование:
- клиенты (браузеры, приложения) получают IP по имени и устанавливают TCP/UDP-соединения.
- Назначение:
-
AAAA (Quad-A)
- Назначение:
- сопоставляет доменное имя с IPv6-адресом.
- Пример:
example.com. 300 IN AAAA 2001:db8::1
- Назначение:
-
CNAME (Canonical Name)
- Назначение:
- делает одно доменное имя алиасом другого.
- Пример:
www.example.com. 300 IN CNAME example.com. - Особенности:
- имя с CNAME не должно иметь одновременно A/AAAA-записи;
- удобно для маршрутизации на внешние сервисы (CDN, балансировщики).
- Назначение:
-
MX (Mail eXchanger)
- Назначение:
- указывает почтовые серверы для домена.
- Пример:
example.com. 300 IN MX 10 mail1.example.com.
- Назначение:
-
TXT
- Назначение:
- хранит произвольный текст.
- Типичные применения:
- SPF/DKIM/DMARC для почты;
- валидация домена у внешних сервисов.
- Пример:
example.com. 300 IN TXT "v=spf1 include:_spf.example.net -all"
- Назначение:
-
NS (Name Server)
- Назначение:
- указывает авторитетные DNS-серверы для зоны.
- Пример:
example.com. 300 IN NS ns1.example.net.
- Назначение:
-
PTR (Pointer)
- Назначение:
- обратное разрешение: IP → доменное имя.
- Используется:
- в rDNS-запросах, часто для почтовых серверов.
- Пример:
10.113.0.203.in-addr.arpa. 300 IN PTR example.com.
- Назначение:
-
SRV
- Назначение:
- описывает расположение сервисов (порт, приоритет, вес).
- Используется:
- в некоторых протоколах (SIP, XMPP, AD и др.).
- Назначение:
-
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
- 255.255.255.0 =
- Это значит:
- первые 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 — удобно для линков между роутерами.
Как определяется, в одной ли сети два адреса:
- Берём IP и маску.
- Считаем сетевой адрес: IP & MASK (битовое И).
- Если у двух 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 (важно знать на собеседовании):
-
Static NAT (один-к-одному)
- Один внутренний IP ↔ один внешний IP.
- Применение:
- проброс конкретного сервера из внутренней сети наружу.
- Пример:
- 10.0.0.10 ↔ 203.0.113.10.
-
Dynamic NAT (многие-к-многим из пула)
- Внутренние адреса динамически сопоставляются с пулом внешних адресов.
- Менее популярен в сравнении с PAT.
-
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.
- SNAT/DNAT в iptables/ipvs:
- Диагностика проблем:
- приложение не получает внешние запросы:
- нужно проверить 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, обработке ошибок, логировании и мониторинге.
Группы статус-кодов:
-
1xx — Informational (информационные)
- Означают: запрос принят, продолжается обработка, но финального ответа ещё нет.
- Используются относительно редко.
- Примеры:
- 100 Continue — сервер говорит клиенту, что начальные заголовки приняты, можно отправлять тело.
- 101 Switching Protocols — переход на другой протокол (например, WebSocket).
-
2xx — Success (успешные)
- Означают: запрос успешно обработан.
- Ключевые:
- 200 OK:
- успешный ответ для большинства GET/POST, когда есть тело ответа.
- 201 Created:
- используется при создании ресурса (обычно на POST), желательно с Location заголовком.
- 202 Accepted:
- запрос принят, но обработка асинхронна (например, задача в очереди).
- 204 No Content:
- успешно, но без тела (часто для DELETE или PUT без содержимого ответа).
- 200 OK:
-
3xx — Redirection (перенаправления)
- Означают: для завершения запроса клиенту нужно сделать новый запрос по другому URI.
- Ключевые:
- 301 Moved Permanently:
- постоянный редирект; клиенты и поисковики могут кешировать.
- 302 Found:
- временный редирект (исторически используется широко).
- 303 See Other:
- после POST перейти по другому URI с GET.
- 307 Temporary Redirect / 308 Permanent Redirect:
- более корректные варианты 302/301 с сохранением метода (для 307) и жесткой семантикой.
- 301 Moved Permanently:
-
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).
- 400 Bad Request:
-
5xx — Server Error (ошибки сервера)
- Означают: запрос был корректный, но сервер не смог его корректно обработать по своей вине.
- Ключевые:
- 500 Internal Server Error:
- общая ошибка сервера (panic, необработанное исключение, и т.п.).
- 502 Bad Gateway:
- неверный ответ от следующего узла (upstream), часто на балансировщиках/gateway.
- 503 Service Unavailable:
- сервис временно недоступен (перегрузка, maintenance), желательно с Retry-After.
- 504 Gateway Timeout:
- не дождались ответа от upstream за отведённое время.
- 500 Internal Server Error:
Практика для разработки и эксплуатации:
-
Группы важны для:
- алертинга:
- рост 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.
Ключевые методы:
-
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
}
// вернуть список пользователей
})
- Назначение:
-
POST
- Назначение:
- создать ресурс;
- запустить действие (команда, обработка формы, старт задачи);
- отправить данные, которые сервер интерпретирует.
- Характеристики:
- не является идемпотентным:
- два одинаковых POST-запроса могут создать два ресурса или запустить действие дважды;
- обычно не кешируется по умолчанию.
- не является идемпотентным:
- Примеры:
POST /users— создать пользователя.POST /tasks/{id}/run— запустить задачу.
- Типичный ответ при создании:
- 201 Created + Location.
- Назначение:
-
PUT
- Назначение:
- полная замена ресурса по указанному URI.
- Характеристики:
- идемпотентен:
- повторный одинаковый PUT приводит к тому же состоянию ресурса;
- клиент присылает полное представление ресурса.
- идемпотентен:
- Примеры:
PUT /users/123— заменить данные пользователя 123.
- Если ресурса не существует:
- в некоторых дизайнах допустимо создавать (upsert), но это требует явной договоренности.
- Назначение:
-
PATCH
- Назначение:
- частичное обновление ресурса.
- Характеристики:
- не гарантированно идемпотентен по смыслу (зависит от реализации);
- тело содержит изменения (diff), а не полный ресурс.
- Примеры:
PATCH /users/123— изменить только email или имя.
- Часто предпочтителен для API, чтобы не передавать весь объект.
- Назначение:
-
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, кеширования, идемпотентности и безопасности.
Основные различия:
- Семантика
-
GET:
- Используется для чтения/получения ресурса.
- Не должен изменять состояние на сервере (safe-метод).
- Примеры:
GET /users/123GET /products?page=2&size=20
-
POST:
- Используется для создания ресурса или выполнения действий, которые изменяют состояние.
- Не является безопасным и не является идемпотентным:
- два одинаковых POST-запроса могут создать два разных ресурса или дважды выполнить действие.
- Примеры:
POST /users— создание пользователя;POST /orders— создание заказа;POST /tasks/{id}/run— запуск задачи.
- Идемпотентность
- GET:
- Идемпотентен:
- многократное выполнение одного и того же GET-запроса не должно изменять состояние ресурса.
- Идемпотентен:
- POST:
- Не идемпотентен по определению:
- повторные запросы могут приводить к повторным операциям (двойная оплата, повторное создание).
- Не идемпотентен по определению:
- Передача данных
- GET:
- Данные передаются в URL (query-параметры, часть пути).
- Ограничения по длине URL (зависят от клиента/серверов/прокси).
- Не подходит для больших объёмов данных и чувствительной информации.
- POST:
- Данные передаются в теле запроса (body).
- Нет практического ограничения размера на уровне протокола (ограничения задаются сервером/инфраструктурой).
- Подходит для форм, JSON, файлов, сложных структур.
- Кеширование и прокси
- GET:
- Может кешироваться браузером, CDN, прокси:
- при наличии соответствующих заголовков (
Cache-Control,ETag,Last-Modified).
- при наличии соответствующих заголовков (
- Может кешироваться браузером, CDN, прокси:
- POST:
- Обычно не кешируется по умолчанию;
- кеширование POST — редкий и специфичный кейс, требует явных заголовков и поддержки.
- Идём через инфраструктуру (безопасность/логирование)
- GET:
- Параметры запроса видны в URL:
- логи веб-сервера,
- история браузера,
- прокси.
- Не стоит передавать через query токены, пароли, персональные данные.
- Параметры запроса видны в URL:
- POST:
- Данные в теле:
- не попадают в URL и историю адресной строки,
- но всё ещё могут логироваться сервером/прокси.
- Для секретных данных в любом случае обязателен HTTPS.
- Данные в теле:
- Практика для API-дизайна
- Рекомендуемый подход (RESTful-стиль):
- GET:
- получение ресурса или коллекции.
- POST:
- создание ресурса;
- запуск операций, которые не ложатся на стандартные CRUD-семантики.
- Не использовать GET для операций, изменяющих состояние:
- нельзя делать "GET /do-delete-all";
- это ломает ожидания клиентов, кешей, поисковиков и может быть опасно.
- GET:
Пример на 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:
- https://app.example.com
- https://api.example.com
- http://app.example.com:8080 Все три — разные origin.
Как это работает на практике:
-
Базовая модель безопасности браузера — Same-Origin Policy (SOP)
- По умолчанию браузер запрещает JavaScript-коду на странице делать многие типы запросов к другому origin и читать их ответы.
- Это не мешает самому запросу физически уйти в сеть — блокируется доступ к результату на стороне браузера.
-
CORS как контролируемое ослабление SOP
- CORS позволяет серверу явно сказать браузеру:
- «этим origin я доверяю, им можно читать ответы».
- Управляется через специальные HTTP-заголовки в ответе сервера.
- CORS позволяет серверу явно сказать браузеру:
Ключевые заголовки CORS:
- Access-Control-Allow-Origin
- Определяет, какому origin разрешён доступ к ответу.
- Примеры:
- Разрешить одному домену:
Access-Control-Allow-Origin: https://app.example.com - Разрешить всем (часто опасно для приватных API):
Access-Control-Allow-Origin: *
- Разрешить одному домену:
- Access-Control-Allow-Methods
- Какие HTTP-методы разрешены при cross-origin-запросах.
- Пример:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
- Access-Control-Allow-Headers
- Какие нестандартные заголовки клиент может отправить.
- Пример:
Access-Control-Allow-Headers: Content-Type, Authorization, X-Request-ID
- Access-Control-Allow-Credentials
- Разрешено ли отправлять куки/авторизационные данные в cross-origin-запросах.
- Пример:
Access-Control-Allow-Credentials: true - При этом:
- нельзя использовать
*вAccess-Control-Allow-Origin, - нужно указать конкретный origin.
- нельзя использовать
Preflight-запрос (OPTIONS):
- Для потенциально «опасных» запросов браузер сначала делает preflight:
OPTIONS /endpoint- с заголовками:
OriginAccess-Control-Request-MethodAccess-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:
- 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.
- Тот же сервис во 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:
-
Docker Desktop для Windows
- Современный Docker на Windows (особенно для Linux-контейнеров) работает через:
- WSL2 backend или
- легковесную Linux-VM (раньше через Hyper-V).
- Для Linux-контейнеров:
- фактически используется Linux-ядро внутри WSL2/VM;
- контейнеры остаются Linux-контейнерами (то есть поведение близко к Linux-серверу).
- Для Windows-контейнеров:
- Docker Desktop переключается в режим Windows Containers;
- используется ядро Windows, поддерживаются только совместимые образы.
- Современный Docker на Windows (особенно для Linux-контейнеров) работает через:
-
WSL2 (Windows Subsystem for Linux 2)
- Это полноценное Linux-ядро, работающее внутри Windows.
- Варианты:
- запуск Docker Engine внутри дистрибутива WSL2 (Ubuntu/Alpine);
- использование WSL2 как backend для Docker Desktop.
- Преимущества:
- поведение окружения очень близко к реальному Linux-серверу;
- удобно для разработки backend’ов на Go, тестирования Dockerfile, CI/CD-скриптов.
-
Почему это важно для разработки и 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/номад/на сервера.
- CI:
- Пример команды:
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-состояние: процессы, файловая система, сеть).
Подробно:
- Образ (Image)
- Что это:
- Набор слоёв файловой системы + метаданные:
- базовый образ (например,
alpine,ubuntu,gcr.io/distroless/base); - ваши бинарники, зависимости, конфиги, переменные окружения,
ENTRYPOINT/CMD.
- базовый образ (например,
- Иммутабелен:
- после сборки не меняется; любые изменения при запуске происходят уже в контейнерном слое.
- Набор слоёв файловой системы + метаданные:
- Где хранится:
- локальный Docker registry/кеш;
- удаленные registry: Docker Hub, GitHub Container Registry, GitLab Registry, ECR, GCR, Harbor и т.п.
- Идентификация:
- по имени и тегу:
myapp:latestmyapp:1.2.3myapp:git-sha
- по имени и тегу:
- Роль:
- стандартный, воспроизводимый артефакт, который используется в CI/CD и оркестраторах.
- единая «упаковка» приложения: то, что собрали, точно так же запустится в любом окружении.
- Контейнер (Container)
- Что это:
- Запущенный экземпляр образа:
- собственный слой поверх образа (writable layer);
- собственное состояние:
- процессы,
- открытые файлы,
- сетевые интерфейсы/порты,
- runtime-конфигурация (env, volume-монты).
- Запущенный экземпляр образа:
- Свойства:
- изолирован от других контейнеров и хоста через namespaces/cgroups;
- эфемерен:
- его файловая система (writable-слой) обычно не должна считаться долговечной;
- при удалении контейнера изменения внутри него пропадают (если не вынесены в volumes).
- Жизненный цикл:
- создаётся из образа:
docker run -d --name myapp -p 8080:8080 myapp:1.0.0 - пока контейнер жив — внутри него крутится процесс (обычно один главный);
- когда процесс завершается — контейнер останавливается.
- создаётся из образа:
- Аналогия:
- Образ — как бинарник + набор зависимостей и конфиг (read-only шаблон).
- Контейнер — как запущенный процесс этого бинарника в изолированном окружении.
- Практика в терминах 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} - они:
- используют один и тот же образ (код и зависимости идентичны),
- но являются разными инстансами с разным состоянием.
- запускаем множество контейнеров из одного и того же образа:
- В Kubernetes:
- Deployment указывает:
- какой image использовать (например,
myapp:1.0.0);
- какой image использовать (например,
- Kubernetes:
- скачивает image на node;
- создаёт один или несколько Pod’ов;
- в каждом Pod’е запускает контейнер(ы) на основе этого image.
- Обновление версии:
- меняем image с
myapp:1.0.0наmyapp:1.0.1; - создаются новые контейнеры с новой версией образа.
- меняем image с
Кратко, как стоит отвечать на интервью:
- «Образ — это неизменяемый шаблон (слои файловой системы + метаданные), в котором описано, что и как запускать. Контейнер — это запущенный экземпляр образа с собственным состоянием (процессы, сетевые namespace, writable-слой). Из одного образа можно запустить много контейнеров, что делает деплой предсказуемым и масштабируемым.»
Вопрос 36. С какой инструкции начинается Dockerfile?
Таймкод: 00:30:31
Ответ собеседника: правильный. Правильно называет инструкцию FROM, задающую базовый образ.
Правильный ответ:
В корректном Dockerfile первая обязательная инструкция — FROM (за исключением специального случая ARG до первого FROM в современных версиях Docker, но базовый образ всё равно задаётся через FROM).
Инструкция FROM:
- Определяет базовый образ, от которого будет «наследоваться» ваш образ.
- Может использоваться один или несколько раз (multi-stage build).
- Синтаксис:
FROM <image>[:tag] [AS name]
Примеры:
- Простой Dockerfile для Go-приложения:
FROM golang:1.22-alpine
WORKDIR /app
COPY . .
RUN go build -o app ./cmd/app
CMD ["./app"]
- 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определяет контекст: какая ОС/окружение будут использованы для последующих инструкций. - Без
FROMDocker не знает, на какой файловой системе и окружении выполнять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, плюс:
-
Если источник — локальный tar-архив, ADD автоматически распакует его содержимое в целевую директорию.
ADD app.tar.gz /app/- В результате в /app будет распакован архив.
-
Может использовать URL как источник:
ADD https://example.com/file.tar.gz /tmp/file.tar.gz- Docker сам скачает файл при сборке.
-
-
Почему с этим осторожно:
- URL в ADD:
- ухудшает воспроизводимость:
- содержимое по URL может измениться;
- сборка станет недетерминированной;
- возможны проблемы с кешированием.
- сложнее контролировать кеш Docker’а:
- изменение содержимого по URL не всегда инвалидирует слой, если имя/URL те же.
- безопасность:
- вынос сетевого поведения в Dockerfile усложняет анализ.
- ухудшает воспроизводимость:
- Автораспаковка tar:
- иногда полезна, но скрытая «магия»:
- по Dockerfile не всегда очевидно, что произойдёт распаковка;
- сложнее контролировать структуру слоёв.
- иногда полезна, но скрытая «магия»:
- URL в ADD:
Best practices (как правильно отвечать на собеседовании):
-
COPY — использовать по умолчанию:
- для копирования исходников, бинарников, конфигов:
COPY . /app
- для копирования исходников, бинарников, конфигов:
-
ADD — использовать только когда:
- осознанно нужна распаковка локального tar-архива;
- и вы точно понимаете эффект.
-
Для скачивания внешних файлов:
- рекомендуется не использовать ADD, а делать это через RUN + curl/wget:
RUN curl -fsSL https://example.com/tool -o /usr/local/bin/tool - так:
- явно видно сетевую операцию;
- проще управлять кешем;
- можно проверить checksum, версионирование и т.д.
- рекомендуется не использовать ADD, а делать это через RUN + curl/wget:
Мини-пример для 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 — в том, как они формируют/переопределяют команду запуска и как ведут себя при передаче аргументов.
Разберём по порядку.
- RUN (для контекста)
- Выполняется во время сборки образа.
- Результат (изменения файловой системы) попадает в слой образа.
- Пример:
RUN apk add --no-cache ca-certificates - К запуску контейнера напрямую не относится.
- CMD
- Определяет команду (и/или её аргументы) по умолчанию для контейнера.
- Выполняется при запуске контейнера, если не была переопределена в
docker runили оркестратором. - Может быть в двух формах:
- shell-форма:
CMD echo "hello" - exec-форма (рекомендуется):
CMD ["./app", "-config", "/etc/app.yaml"]
- shell-форма:
- Особенности:
- В Dockerfile может быть только один активный CMD — последний.
- Любые аргументы, переданные в
docker run <image> ..., перезаписывают CMD (если ENTRYPOINT не задан или задан особым образом). - Часто используется как «значения по умолчанию» для ENTRYPOINT.
- ENTRYPOINT
- Определяет «главный» процесс контейнера — то, что всегда будет выполняться при старте.
- Также имеет две формы:
- shell-форма:
ENTRYPOINT echo "Hello" - exec-форма:
ENTRYPOINT ["/usr/local/bin/app"]
- shell-форма:
- Особенности:
- ENTRYPOINT сложнее «полностью заменить» при
docker run:- чаще всего аргументы
docker run image arg1 arg2добавляются как аргументы к ENTRYPOINT (в exec-форме).
- чаще всего аргументы
- Используется, когда контейнер должен вести себя как конкретный бинарник:
- например,
docker run myapp ...всегда запускает/usr/local/bin/myapp.
- например,
- ENTRYPOINT сложнее «полностью заменить» при
- Взаимодействие 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 переопределён аргументами из командной строки).
Примеры паттернов:
- Только 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(и это часто не то, что нужно).
- полностью заменит CMD → выполнит
- 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 задаёт дефолтное поведение.
- ENTRYPOINT (shell-форма) — когда не стоит так делать:
ENTRYPOINT /usr/local/bin/app
- Такая форма завёрнута в
/bin/sh -c:- сложнее с сигналами (PID 1, обработка SIGTERM/SIGINT);
- хуже интеграция с Kubernetes и корректным graceful shutdown.
- В production почти всегда стоит использовать exec-форму.
- Практические рекомендации:
- Для сервисов (Go API, worker и т.п.):
- использовать exec-форму ENTRYPOINT и при необходимости CMD для аргументов:
ENTRYPOINT ["/usr/local/bin/app"]
CMD ["serve"]
- использовать exec-форму ENTRYPOINT и при необходимости CMD для аргументов:
- Для утилит/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"]
Что это даёт:
-
Меньший размер образа:
- Без multi-stage:
- в образе остаются Go toolchain, исходники, кеши, dev-пакеты.
- С multi-stage:
- финальный образ содержит только:
- бинарник,
- минимальный runtime (alpine или даже scratch),
- нет компилятора, исходников, тестовых данных.
- финальный образ содержит только:
- Это:
- ускоряет доставку образа по сети (CI/CD, k8s);
- уменьшает время раскатки;
- сокращает использование диска на нодах.
- Без multi-stage:
-
Безопасность:
- Меньше инструментов внутри контейнера → меньше потенциальных точек атаки.
- Нет исходников → сложнее анализировать код в случае компрометации контейнера.
- Нет package manager’ов → атакующему сложнее «доставить» себе инструменты.
-
Чистота и reproducibility:
- Окружение сборки изолировано:
- можно использовать любые dev-зависимости, не боясь протащить их в runtime.
- Финальный образ детерминированный, минимальный, лучше управляемый.
- Окружение сборки изолировано:
-
Удобство в CI/CD:
- Один Dockerfile описывает весь pipeline сборки:
- не нужно отдельно подготавливать builder-образ вручную;
- легко читать и ревьюить.
- Один Dockerfile описывает весь pipeline сборки:
Расширенный пример (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-инфраструктурой.
Ключевые инструменты, которые важно знать и уметь описать:
- 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
- Docker Swarm
Встроенный в Docker режим оркестрации (меньше распространен сейчас, но знать стоит):
- Позволяет объединить несколько хостов в swarm-кластер.
- Понятия:
- service, stack, replica.
- Преимущества:
- проще старт, чем Kubernetes;
- Недостатки:
- менее богатый функционал, меньшая экосистема, существенно проигрывает Kubernetes в гибкости и распространенности.
- HashiCorp Nomad
Легковесный оркестратор задач (jobs):
- Поддерживает:
- контейнеры (Docker),
- бинарники,
- JVM и др.
- Сильные стороны:
- простая архитектура;
- хорош для хетерогенных workload’ов.
- Часто используется вместе с:
- Consul (service discovery),
- Vault (секреты).
- Rancher / OpenShift и подобные платформы
Не отдельные оркестраторы, а платформы поверх Kubernetes:
- Rancher:
- мульти-кластер менеджмент, UI, интеграции.
- OpenShift:
- enterprise-платформа на базе Kubernetes (своё мнение по security, образам, registry, pipelines).
- 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-образа на другой сервер без пересборки:
- через образ в registry (рекомендуется);
- через экспорт/импорт локального tar-архива.
Оба варианта должны быть уверенно озвучены на собеседовании.
- Использование реестра образов (Docker Registry) — основной и правильный способ
Алгоритм:
-
На машине, где образ уже собран:
- Тегируем образ:
docker tag myapp:latest registry.example.com/myteam/myapp:1.0.0 - Логинимся (если приватный реестр):
docker login registry.example.com - Публикуем образ:
docker push registry.example.com/myteam/myapp:1.0.0
- Тегируем образ:
-
На целевом сервере:
- Логинимся (если нужно):
docker login registry.example.com - Загружаем образ:
docker pull registry.example.com/myteam/myapp:1.0.0 - Запускаем контейнер:
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;
- обеспечивает версионирование образов;
- не требует ручного копирования файлов;
- работает для десятков/сотен серверов.
- Экспорт/импорт образа через файл (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.
- Что не является ответом
- 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 и оркестраторами.
Ключевые инструменты (важно уметь назвать и знать базовые отличия):
- 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.
- Puppet
- Agent-based:
- на каждом хосте — агент, который регулярно тянет конфигурацию с Puppet Master.
- Декларативный DSL.
- Силен в крупных энтерпрайз-окружениях:
- централизованный контроль,
- отчётность,
- политика комплаенса.
- Chef
- Использует Ruby DSL.
- Agent-based модель (Chef Client / Server).
- Гибкий, но порог входа выше из-за языка и экосистемы.
- SaltStack
- Может работать как в push (master → minion), так и в pull-режиме.
- YAML-описания (state files).
- Подходит для быстрого массового применения состояний.
- Связь с Terraform и Kubernetes
Важно уметь различать классы инструментов:
- Terraform:
- управляет ресурсами (VM, сети, LB, БД, Kubernetes-кластеры) через API провайдеров;
- создаёт «железо» и облачную инфраструктуру.
- Конфигурационные менеджеры (Ansible, Puppet и др.):
- настраивают ОС и софт внутри этих ресурсов:
- пакеты, файлы, юзеры, systemd, конфиги сервисов.
- настраивают ОС и софт внутри этих ресурсов:
- Kubernetes/Helm:
- оркестрируют контейнеры и приложение внутри кластера.
В продвинутых практиках:
- Terraform поднимает VM + сети.
- Ansible (или аналог) настраивает базовую ОС, агент, systemd, прокси, runtime.
- Kubernetes/Helm управляют приложениями в контейнерах.
- Всё это — код, ревью, CI/CD.
Критические качества хорошего конфигурационного менеджмента:
- Идемпотентность:
- Playbook/manifest можно запускать много раз:
- если состояние уже совпадает с описанным, изменений не будет.
- Playbook/manifest можно запускать много раз:
- Декларативность:
- описываем целевое состояние, а не последовательность «скриптов по шагам».
- Аудит и откат:
- изменения через Git;
- можно увидеть diff, вернуть предыдущую версию.
Краткий ответ для собеседования:
- «Инструменты управления конфигурацией позволяют описывать целевое состояние серверов как код и применять его массово, идемпотентно и воспроизводимо. Типичные примеры: Ansible (agentless, YAML), Puppet и Chef (agent-based), SaltStack. Они дополняют Terraform и Kubernetes: Terraform создает ресурсы, CM-инструменты настраивают ОС и софт, оркестраторы управляют приложениями. Ansible — один из самых удобных и широко применяемых инструментов, им обычно автоматизируют установку пакетов, деплой бинарников, настройку systemd-сервисов, пользователей, прав и шаблонов конфигов.»
Вопрос 43. Использовали ли вы роли Ansible и как реализовать кросс-дистрибутивное обновление пакетов в плейбуках?
Таймкод: 00:37:44
Ответ собеседника: неправильный. Сообщает, что писал только простые плейбуки, не работал с ролями и условным выполнением задач для разных дистрибутивов.
Правильный ответ:
Для зрелого использования Ansible необходимо:
- структурировать конфигурации через роли;
- уметь писать кросс-дистрибутивные плейбуки с использованием фактов и условий.
Роли 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_familyansible_distributionansible_distribution_major_version
- Условные выражения (
when). - Унифицированные модули:
- для Ansible 2.10+ предпочтителен модуль
package, который абстрагирует менеджер пакетов.
- для Ansible 2.10+ предпочтителен модуль
Простой и правильный способ (через 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"
Через роли это оформляется лучше:
- Роль
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"
- Использование роли в 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 (права и транзакции). Для уверенной работы с базами в реальных сервисах важно не только знать названия, но и правильно применять команды с учетом индексов, транзакций и нагрузок.
Основные группы и ключевые команды:
- 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 с индексируемыми полями для производительности.
- 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 или обновленных значений.
- 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-миграции или стратегию восстановления).
- 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.
- 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, подзапросы.
- SQL как декларативный язык:
- Целостность данных:
- ограничения (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-хранилищ:
-
Key-Value (ключ-значение)
- Простая модель: ключ → значение (blob/строка/JSON).
- Очень быстрые операции.
- Примеры:
- Redis, Amazon DynamoDB, etcd.
- Юз-кейсы:
- кеш, сессии, конфигурации, метаданные, feature flags.
-
Document Store (документные)
- Документы (обычно JSON/ BSON) с гибкой структурой.
- Есть индексы по полям документов, запросы по вложенным структурам.
- Примеры:
- MongoDB, Couchbase.
- Юз-кейсы:
- когда сущности естественно представимы как документы (user profile, настройки);
- когда схема эволюционирует и не хочется мигрировать таблицы.
-
Column-Family / Wide-Column
- Логическая модель: строки с очень большим числом потенциальных колонок, сгруппированных в column families.
- Оптимизированы под большие объемы данных и распределенные записи/чтения.
- Примеры:
- Apache Cassandra, HBase.
- Юз-кейсы:
- таймсерии, логирование, метрики, огромные event-стримы.
-
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, с аналогами у других):
- Compute (вычисления)
- Виртуальные машины:
- AWS: EC2
- GCP: Compute Engine
- Azure: Virtual Machines
- Используются для:
- запуска приложений (в т.ч. Go-сервисов),
- хостинга Docker/Kubernetes,
- stateful-сервисов (БД, очереди, внутренние сервисы).
- Managed Kubernetes / контейнеры
- AWS: EKS, ECS/Fargate
- GCP: GKE
- Azure: AKS
- Позволяют:
- запускать контейнеры без ручного управления нодами или с его минимизацией;
- интеграцию с сетями, балансировщиками, секретами, IAM.
- Storage (хранение данных)
- Объектное хранилище:
- AWS: S3
- GCP: Cloud Storage
- Azure: Blob Storage
- Свойства:
- durable, версионирование, дешево, подходит для логов, бэкапов, статики.
- Блочное/файловое хранилище:
- диски для виртуалок и Kubernetes.
- Managed базы данных
- Relational:
- AWS RDS (PostgreSQL, MySQL, etc.),
- Cloud SQL (GCP),
- Azure Database.
- NoSQL:
- DynamoDB, Cloud Bigtable, Cosmos DB и т.п.
- Преимущества:
- резервные копии, репликация, обновления, мониторинг — берёт на себя облако;
- команда фокусируется на схеме и запросах, а не на ручном администрировании.
- Сети и балансировка
- VPC (Virtual Private Cloud):
- изолированная сеть, подсети, маршруты, security groups.
- Балансировщики:
- L4/L7, интеграция c Kubernetes ingress/service.
- Важные навыки:
- CIDR, подсети, NAT, security groups/firewall;
- понимание, как сервисы в облаке общаются между собой и с внешним миром.
- Identity & Access Management (IAM)
- Централизованное управление правами:
- кто и что может делать в облаке;
- роли для сервисов (instance roles, service accounts).
- Критично для безопасности:
- отказ от «root-ключей в коде»;
- минимально необходимые полномочия (least privilege).
- Инфраструктура как код (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).
Инструменты мониторинга и наблюдаемости, которые важно знать:
- Prometheus — сбор и хранение метрик
-
Модель:
- time-series база для метрик;
- pull-модель: Prometheus сам опрашивает endpoints (
/metrics); - метрики в формате key/value с лейблами (labels).
-
Использование:
- сбор метрик приложений, системных метрик, метрик Kubernetes и т.п.;
- написание alert-правил (Alertmanager).
-
Примеры метрик:
http_requests_total{service="api", method="GET", code="200"}go_goroutinesprocess_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.
- Grafana — визуализация и дашборды
- Используется как UI для:
- Prometheus,
- Loki, Tempo,
- Elasticsearch, InfluxDB и многих других источников.
- Возможности:
- дашборды для сервисов, БД, инфраструктуры;
- алертинг (в современных версиях — централизованный).
- Типичный практический подход:
- собирать метрики в Prometheus;
- строить Grafana-дашборды:
- RPS, p95/p99 latency;
- error rate (4xx/5xx);
- CPU/RAM, количество goroutine;
- состояние БД (connections, slow queries).
- Логи
Важно упомянуть стек для логирования:
- Elastic stack (ELK: Elasticsearch + Logstash + Kibana) или OpenSearch + Kibana/OS Dashboards.
- Loki (от Grafana Labs):
- лог-агрегация с моделью, похожей на Prometheus (labels), дешево по хранению.
- Подход:
- приложения логируют в stdout/stderr;
- агенты (Filebeat/Fluent Bit/Promtail) собирают и отправляют в центральное хранилище;
- поиск и корелляция логов через Kibana/Grafana.
- Tracing (распределённые трассировки)
При микросервисной архитектуре и сложных запросах важны трассировки:
- Jaeger, Zipkin, Tempo.
- OpenTelemetry как стандарт:
- для метрик, логов и трейсинга.
- Применение:
- отслеживание пути запроса через несколько сервисов;
- поиск «узких мест» и долгих вызовов;
- диагностика проблем в проде.
- Инфраструктурный мониторинг
Инструменты:
- node_exporter, cAdvisor, kube-state-metrics:
- метрики нод, контейнеров, Kubernetes-объектов.
- Cloud-native решения:
- AWS CloudWatch, GCP Cloud Monitoring, Azure Monitor.
- Интеграция:
- в продакшене часто комбинируют:
- Prometheus/Grafana для приложений и k8s;
- облачные мониторинги для инфраструктуры и managed-сервисов.
- в продакшене часто комбинируют:
- Алертинг и SLO
Важная часть ответа — понимание, что мониторинг не только про «красивые графики»:
- Настройка алертов:
- по метрикам (Prometheus Alertmanager, Grafana Alerting):
- рост 5xx,
- падение RPS,
- увеличение latency (p95/p99),
- нехватка памяти/CPU,
- падение реплик в Kubernetes.
- по метрикам (Prometheus Alertmanager, Grafana Alerting):
- 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 недостаточно, нужны централизованные решения. Основные подходы и инструменты:
- ELK / OpenSearch-стек
- Состав:
- Elasticsearch / OpenSearch — хранилище и поиск по логам.
- Logstash / Fluentd / Fluent Bit — сбор и преобразование логов.
- Kibana / OpenSearch Dashboards — визуализация и поиск.
- Схема:
- контейнеры → stdout/stderr;
- лог-агенты на нодах собирают логи Docker/Kubernetes;
- отправляют в Elasticsearch/OpenSearch;
- аналитика и поиск через Kibana.
- Loki (Grafana Loki)
- Лог-хранилище, оптимизированное под модель «как Prometheus, но для логов».
- Метки (labels) вместо тяжёлых индексов:
- дешёвое хранение;
- отлично интегрируется с Grafana.
- Агенты:
- Promtail, Fluent Bit.
- Плюс:
- единый стек: Prometheus (метрики) + Loki (логи) + Grafana (дашборды и запросы).
- Облачные решения
- 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 и какие направления важны.
Правильный ответ:
Если говорить о требованиях к стартовому уровню, то важен не формальный «титул», а то, насколько человек умеет:
- понимать, как код превращается в работающий, наблюдаемый и управляемый сервис;
- опираться на автоматизацию, а не на ручные действия;
- разбираться в основных инфраструктурных примитивах и сетевых концепциях.
Ниже — ориентир, на который стоит равняться при подготовке. Это не «чеклист из теории», а практичный минимум, который позволяет уверенно встраиваться в команду.
- Базовый, но уверенный Linux
- Навыки:
- работа в терминале (bash/sh);
- файловая система, права (chmod/chown), пользователи и группы;
- процессы (ps/top/htop), systemd (systemctl, unit-файлы);
- базовая диагностика:
ping,curl,ss/netstat,journalctl,dmesg.
- Ожидание:
- можешь подключиться к серверу по SSH и:
- проверить, жив ли сервис;
- посмотреть логи;
- перезапустить сервис;
- разобраться с наиболее очевидными проблемами.
- можешь подключиться к серверу по SSH и:
- Сетевые основы
- Понимание:
- модель OSI на прикладном уровне;
- IP, порты, TCP/UDP;
- DNS (A/AAAA/CNAME, как работает резолвинг);
- маска подсети, маршрутизация в базовом виде;
- что такое NAT, балансировщик, reverse proxy.
- Ожидание:
- умеешь объяснить, что происходит при запросе к https-сервису;
- можешь применить
curl,dig,traceroute,pingдля диагностики.
- Контейнеры и Docker
- Навыки:
- понимать разницу образ/контейнер;
- писать Dockerfile для простого сервиса:
- выбрать базовый образ;
- собрать бинарник (multi-stage build — большой плюс);
- корректно настроить CMD/ENTRYPOINT;
- запускать контейнеры:
- маппинг портов, volume, переменные окружения.
- Ожидание:
- можешь упаковать небольшое приложение (например, Go/Node.js/Python API) в корректный образ и запустить на другом хосте.
- Базовое понимание CI/CD
- Концепции:
- что такое CI, CD (delivery vs deployment);
- какие шаги типичного pipeline:
- линтеры,
- тесты,
- сборка артефакта/образа,
- деплой.
- Навыки:
- прочитать и понять
.gitlab-ci.ymlили GitHub Actions workflow; - при минимальном guidance — собрать простой CI: проверка кода + тесты + сборка Docker-образа.
- прочитать и понять
- Ожидание:
- не обязательно глубокий опыт, но чёткая ментальная модель и готовность писать пайплайны под присмотром.
- Инфраструктура как код и конфигурации
- Начальный уровень:
- понимание идеи IaC: инфраструктура, описанная декларативно и хранимая в Git;
- базовое знакомство с Ansible:
- плейбуки, инвентори, идеи идемпотентности;
- базовое представление о Terraform:
- ресурсы, провайдеры, состояние.
- Ожидание:
- можешь прочитать и слегка поправить простые playbook/terraform-конфиг;
- понимаешь, зачем это лучше, чем «SSH + руками».
- Мониторинг и логирование
- Понимание:
- зачем нужны метрики (RPS, latency, error rate, CPU/RAM);
- базовая модель Prometheus + Grafana;
- идея централизованного логирования (ELK/Loki/Cloud Logging).
- Ожидание:
- можешь:
- навесить простую /metrics на сервис;
- открыть Grafana и понять, хорошо или плохо по графикам;
- использовать
docker logs/kubectl logsдля первичной диагностики.
- можешь:
- Работа с БД и основами приложений
- Навыки:
- базовые SQL-команды: SELECT/INSERT/UPDATE/DELETE/CREATE/ALTER;
- понимание разницы между рел. БД и NoSQL;
- базовая работа с PostgreSQL.
- Ожидание:
- можешь:
- проверить коннект к БД;
- выполнить простой запрос;
- не «ронять» БД случайным
DELETEбез WHERE.
- можешь:
- Язык скриптов и основ программирования
- Обязательно:
- уверенный bash/sh;
- умение писать маленькие утилиты/скрипты для автоматизации.
- Желательно:
- базовый опыт на одном из языков: Go, Python.
- Ожидание:
- можешь:
- автоматизировать рутину (деплой, health-check, бэкапы);
- понимать код сервисов достаточно, чтобы разбираться в том, как они конфигурируются, логируются, стартуют.
- можешь:
- Мышление и подход
То, что критически важно для старта:
- системное мышление:
- понимать цепочку: код → сборка → образ → деплой → сеть → логи/метрики → пользователь;
- аккуратность:
- не делать опасных изменений без плана и бэкапа;
- любознательность:
- разбирать инциденты и читать документацию;
- привычка автоматизировать:
- если делаешь одно и то же второй раз руками — задуматься о скрипте/плейбуке.
Если переводить в конкретный roadmap (что выучить первым):
- Linux (CLI, systemd, сеть).
- Docker + написание Dockerfile.
- Базовый CI (GitHub Actions/GitLab CI для простого сервиса).
- Основы сетей, HTTP, DNS.
- PostgreSQL (минимум CRUD + простая схема).
- Prometheus + Grafana (снять метрики с тестового сервиса).
- Ansible (один-два простых playbook для настройки сервера).
Такой набор делает вас кандидатом, которого можно брать в команду, подключать к реальным задачам и растить дальше уже в сторону более сложных тем: Kubernetes, продвинутая безопасность, GitOps, сервис-меши, сложные пайплайны, отказоустойчивые архитектуры.
