🚀

Kafka, Docker, K8s & CI/CD

для QA

Полное руководство: от теории до практики и собеседований

Нажмите → или Пробел для начала
👨‍💻

Григорий Звягинцев

QA Engineer

  • Специализируюсь на автоматизации и интеграциях
  • Люблю разбирать архитектуру "по винтикам"
  • Автор базы знаний "my_zametki"
📋

План полета

🔹 Brokers: Kafka vs RabbitMQ
🔹 Docker: Контейнеры
🔹 K8s: Оркестрация
🔹 CI/CD: Автоматизация
Brokers
Docker
K8s
CI/CD

👶 Junior QA

Хочет выйти за рамки "кнопки"

🔄 Switchers

Переход из ручного в авто/DevOps

🎯 Interview Preppers

Систематизация знаний для технических собеседований

Эволюция

Что это даст?

📬

Брокеры Сообщений

Зачем они нужны и как они спасают микросервисы

Архитектура

🏰

Монолит

Всё в одном процессе.

  • Тяжело масштабировать
  • Ошибка валит всё
  • Долгий деплой
🏙️

Микросервисы

Много маленьких сервисов.

  • Сложная коммуникация
  • Нужна надежная сеть
  • Как передать данные?

Синхронно vs Асинхронно

Зачем они нужны?

Синхронно (HTTP)

Service A ➡️ Service B

Если B упал, A тоже висит. Жесткая сцепка.

Асинхронно (Broker)

Service A ➡️ 📬 ➡️ Service B

Service A положил письмо и пошел дальше. Service B прочитает, когда сможет.

Брокер — это посредник 📬

🏭 ➡️ 📬 Broker ➡️ 🏘️

Принимает от отправителей, хранит и отдает получателям.

Задачи:

  • ✓ Маршрутизация данных
  • ✓ Буферизация (сглаживание пиков)
  • ✓ Развязывание сервисов (Decoupling)
  • ✓ Гарантия доставки

Типологии

Hub & Spoke

Центральный сервер управляет всем. Все сообщения через единый узел.

Message Bus

Распределенная шина. "Умные" эндпоинты и "глупая" труба. Масштабируемо.

Зачем это QA? 🤔

🐰

RabbitMQ

"Умный брокер, глупый потребитель"

  • Тип: Очередь (Queue)
  • Логика: Smart Server, Dumb Client
  • Механика: Push (Сам проталкивает)
  • Главное: Сообщение удаляется после обработки (Ack).
🚀

Apache Kafka

"Глупый брокер, умный потребитель"

  • Модель: Распределенный лог\журнал (Log)
  • Логика: Dumb Server, Smart Client
  • Доставка: Pull (Клиент сам читает, что ему нужно).
  • Жизнь: Хранится (Retention policy)
  • Главное: Сообщения хранятся на диске (Retention) и не удаляются при чтении.

Примеры из жизни: Кто для чего?

🐰 RabbitMQ (Задачи)

Кейс: Регистрация на сайте

  • Отправить Email подтверждения
  • Сгенерировать PDF отчет

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

🚀 Kafka (Потоки)

Кейс: Uber / Netflix

  • Сбор GPS координат всех такси (100к в сек)
  • Сбор логов просмотров фильмов

Нужна бешеная скорость и история событий. Потоковая обработка.

Сравнение

Критерий 🐰 RabbitMQ 🚀 Apache Kafka
Тип Очередь Журнал (Log)
Доставка Push Pull
Хранение Удаляет после Ack Хранит (Retention)
Масштаб Вертикальное Горизонтальное

Глобальная схема Kafka 🗺️

Producer 🏭
1. Публикует (Push)
Всегда дописывает в конец (Append Only)
➡️

Topic "Orders" 📂

Partition 0 📄 [0, 1, 2...]
Partition 1 📄 [0, 1, 2...]
Partition 2 📄 [0, 1, 2...]
➡️

Consumer Group 👥

App A (Instance 1) 🏗️
App A (Instance 2) 🏗️
2. Читают (Pull)
Параллельная обработка без конфликтов

Анатомия: Topic & Partition 📂

Topic — это папка. Partition — это файл внутри папки.

Topic "Orders" 📂

📄
Part 0
Leader
📄
Part 1
Leader
📄
Part 2
Leader
Данные (Offset) пишутся последовательно

Что такое "Логи" в Kafka?

❌ Это НЕ текстовые логи (`error.log`)
Log = Партиция на диске (Append Only)
  • 💾 Суть: Это физическое "тело" партиции.
  • 📂 Где лежат: Сегменты на диске.
    /tmp/kafka-logs или
    /var/lib/kafka/data
  • Retention: Время жизни (TTL).
    Дефолт: 168 часов (7 дней).
  • ⚙️ Настройка: log.retention.hours

Как чекать?

  1. CLI:
    kafka-console-consumer
  2. UI: Kafdrop, Kafka UI, AKHQ
  3. Смотрим offset (отставание) и payload.

Про дисковое пространство

Kafka Flow: Жизнь сообщения 🔄

1. App (Producer)

🏭
POST /orders
{
  "id": 101,
  "item": "Laptop"
}
Send(JSON)
➡️

2. Broker

📬
[0] id:99
[1] id:100
[2] id:101 (New)
Append to Log
➡️

3. Consumer

🐢
Processing...
✅ Commit Offset 2
Read & Ack

📝 Логи (как это выглядит в консоли)

[INFO] Producer: Sending message key=101 value={"item":"Laptop"} to topic 'orders'
[INFO] Consumer: Received record offset=2 key=101 value={"item":"Laptop"}
[DEBUG] Consumer: Processing order 101... Success. Committing offset.

Анатомия: Offset 🔖

Offset — это просто "закладка" в книге (партиции).

Topic "Orders" / Partition 0

0
Done
1
Done
2
Now
3
Wait
4
Wait
⬆️ Current Offset (2)
Брокер запомнил: 0 и 1 уже обработаны.
Если сервис упадет, он вернется и начнет с 2.
(Гарантия "At least once")

Когда забыл Offset...

Способы доставки (Semantics)

At-most-once

🔥 Fire & Forget

Может потеряться. Дубликатов нет.

At-least-once

🛡️ Стандарт

Гарантируем доставку. Но могут быть дубликаты (нужна идемпотентность).

Exactly-once

🦄 Идеал

Сложно (Транзакции). Каждое сообщение строго 1 раз.

Consumer Groups (Масштабирование) 👥

⚠️ Золотое правило

1 Партиция = Максимум 1 Консьюмер (в рамках одной группы).

  • Параллелизм: Читаем партиции одновременно.
  • Лимит: Нельзя добавить консьюмеров больше, чем партиций.
📂 Topic (2 Partition)
P0
P1
⬇️   ⬇️
👥 Group "Analytics" (3 Consumers)
😋
Con 1
😋
Con 2
😴
Con 3
(Idle)

Репликация (High Availability) 🛡️

  • 👑 Leader:
    Только он обрабатывает Запись и Чтение.
  • 🐑 Follower:
    Пассивно копирует данные у Лидера (фоновая синхронизация).
  • 🔥 Failover (Отказоустойчивость):
    Лидер упал? → Один из Фолловеров моментально выбирается новым Лидером.
Broker 1 (Server)
👑 P-0 (Leader)
➡️
Broker 2 (Server)
🐑 P-0 (Replica)
Broker 3 (Server)
🐑 P-0 (Replica)

Control Plane: Мозг Кластера 🧠

Зачем нужно? Хранение метаданных (где чья партиция) и выборы Лидера (Consensus).

  • 📍 Метаданные: Кто лидер? Где лежат партиции?
  • 🗳️ Консенсус: Выбор главного (Leader Election).
  • 👮 Health Check: Кто жив, а кто умер?
🗺️ 🗳️ 👮

Эволюция: Zookeeper vs KRaft 🔄

🐘 Zookeeper (Legacy)

Zookeeper Cluster
⬇️ ⬆️
Kafka Brokers
(Dumb Storage)
  • ❌ Внешняя зависимость
  • ❌ Двойные метаданные

🚤 KRaft (Future)

Kafka Cluster
👑 Controller (Internal)
  • All-in-One: Единый процесс.
  • Скорость: Миллионы партиций.
Kafka 4.0 без ZK!

Log Compaction (Уплотнение) 📚

🧐 В чем суть?

Kafka удаляет старые значения для ключа, оставляя последнее.

"Мне не важна история, важен текущий статус."

Где нужно:

  • 👤 Профили пользователей.
  • ⚙️ Конфигурации сервисов.
  • 💰 Текущие цены товаров.

📋 До Compaction

K: 101 | Val: "Alex"
K: 101 | Val: "Alexander"
K: 101 | Val: "Sasha"
⬇️

📦 После (Snapshot)

Key: 101 | Val: "Sasha"
⚠️ Для обычных логов (Traffic, Clicks) это не подходит!
"Kafka — это распределенный, отказоустойчивый журнал событий."
🐳

Docker

Контейнеры, Слои и Сборка

📦

Контейнеризация

Способ упаковки приложения вместе с его окружением (библиотеки, конфиги). Гарантирует, что код работает одинаково и у разработчика, и в тестировании, и на проде.

VM 🆚 Container

🐢 Virtual Machine

Полная изоляция (OS + Libs + App)
App A
Libs / Bins
Guest OS (Heavy) 🏋️
Hypervisor
Infrastructure
Boot: Minutes ⏳

🐇 Container

Изоляция процессов (Shared Kernel)
App A
App B
Libs
Libs
Docker Engine
Host OS Kernel (Shared) 🤝
Infrastructure
Boot: Milliseconds ⚡

Image vs Container 💿📦

💿 Image (Образ)

Неизменяемый файл (Snapshot).

  • 🔒 Read-Only: Нельзя менять.
  • 🏗️ Build time: Результат сборки.
  • 📐 Аналогия: Чертеж дома / Класс.
➡️ docker run

📦 Container

Запущенный процесс.

  • 📝 Read-Write: Можно писать файлы.
  • 🏃 Run time: Живая система.
  • 🏠 Аналогия: Дом / Объект.
Суть: Из одного Образа можно запустить сотни Контейнеров.

Ожидание vs Реальность

Dockerfile & Image 🏗️

Dockerfile — это инструкция (рецепт) для повара 👨‍🍳.

Image (Образ) — это готовое блюдо 🥘 (упакованный код + ОС).

Container — это когда блюдо едят 🍽️ (запущенный процесс).

# 1. Берем готовый Образ (Base Image)
FROM python:3.9-slim # <-- Linux + Python внутри

# 2. Создаем папку внутри образа
WORKDIR /app

# 3. Копируем файлы (Ингредиенты)
COPY . .             # <-- Из нашего ПК в Образ

# 4. Устанавливаем либы (Готовка)
RUN pip install flask # <-- Работает при BUILD

# 5. Команда старта (Подача)
CMD ["python", "app.py"] # <-- Работает при RUN

Docker Layers (Слои) 🥪

Layer 1: Base OS (Ubuntu/Alpine) 🔒
Layer 2: Deps (pip install) �
Layer 3: App Code (copy . .) 🔒
Writable Layer (Container) 📝
Новые файлы, логи, изменения БД

🔒 Read-Only (Image)

Нижние слои (Образ) нельзя менять. Это как кирпичи фундамента.

📝 Writable Layer

Когда контейнер запускается, Docker кладет сверху тонкий прозрачный лист для записи.

* Все изменения (создание файлов) пишутся СЮДА.
* Если контейнер удалить — этот слой исчезнет!

Вопрос: А если приложение пишет в память (RAM)?
Ответ: RAM — это оперативная память CPU, она не тут. Слои — это только про Файловую Систему (Диск).

Что такое YAML? (.yml) 📄

"Yet Another Markup Language"

Формат для конфигов. Популярен из-за читаемости.

  • ✅ Отступы (пробелы) вместо скобок
  • ✅ Где: K8s, Docker Compose, GitLab CI
person:
  name: Grigory
  role: QA
  skills:
    - Docker
    - Kafka

Docker Registry 🏪

Хранилище образов (где живут Image).

🌍 Public

Docker Hub

Официальные образы (Python, Kafka, Postgres). Открыты всем.

🔐 Private

Nexus / GitLab Registry

Внутренние образы компании. Нужен логин/пароль.

💻
My PC
⬆️ docker push (Загрузить)
⬇️ docker pull (Скачать)
☁️
Registry

Docker Compose 🐙

Дирижер для локальной разработки (Multi-Container).

version: '3.8'
services:
  kafka:            # 📦 Контейнер 1
    image: bitnami/kafka
    ports:
      - "9092:9092"
  
  app:              # 📦 Контейнер 2
    build: .
    depends_on:
      - kafka       # 🔗 Связь
App 🐍
Kafka 📨

Вместо 10 команд запуска — одна:

docker-compose up -d

⚠️ Запускать из папки с файлом .yml (и Dockerfile)

Когда запустил Compose

Cheat Sheet 📝

👀
docker ps

Кто живой? (Процессы)

💿
docker images

Список образов

📜
docker logs

Must Have для QA

🚀
docker run

Создать и запустить

🛑
docker stop

Остановить контейнер

🧹
docker system prune

Очистка мусора

Зачем Docker QA?

Docker Swarm? 🤔

Это "родной" инструмент кластеризации от Docker.

Позволяет объединить несколько Docker-хостов в один виртуальный.

Swarm vs Kubernetes

  • 🟢 Swarm: Простой, встроен в Docker, легко поднять.
  • 🔴 Минус: Меньше возможностей, проиграл войну популярности.
  • 🔵 K8s: Стандарт индустрии. Сложнее, но мощнее.
☸️

Kubernetes (K8s)

Оркестрация: Управляем флотом контейнеров.

Дирижер оркестра 🎻

🎻

Дирижер (K8s)

Управляет, когда вступать, громкостью и темпом.

🎺🥁🎸

Оркестр (Containers)

Каждый инструмент (контейнер) делает свою работу, но вместе — симфония.

"Kubernetes makes containers dance."

Learning Curve

Docker 🆚 Kubernetes

🐳 Docker

🧱

Кирпич

  • Запуск 1 приложения
  • Ручное управление
  • "Я создал контейнер"

☸️ Kubernetes

🏙️

Небоскреб

  • Управление тысячами
  • Авто-масштабирование
  • "Я построил систему"

Архитектура Кластера 🏗️

🧠

Control Plane

(Master Node)


  • API Server: Вход 🚪
  • Scheduler: Планировщик 📅
  • Etcd: Память 💾
Управляет рабочими ⬇️

👷 Worker 1

(Server / VM)
Pod 1
Pod 2

👷 Worker 2

(Server / VM)
Pod 3

👷 Worker 3

(Server / VM)
Pod 4
Pod 5
...More

Deep Dive: Worker Node 🐴

🖥️ Node (Сервер)

Железный сервер или виртуальная машина (VM).

  • Kubelet (Daemon): "Капитан". Получает приказы от Control Plane.
  • Kube-proxy (Daemon): "Связной". Отвечает за сеть (IP, iptables).
  • Container Runtime (Daemon): Docker/containerd. Крутит контейнеры.
🏭

"Здесь делается работа"

Deep Dive: Control Plane 🧠

🚪 API Server

Входная дверь. Принимает REST запросы (kubectl). Только он общается с Etcd.

💾 Etcd

Память кластера. Хранит конфигурацию (Deployment, Service, NS). (Критично важно!).

📅 Scheduler

Планировщик. Решает, на какую ноду отправить новый Под (смотрит на CPU/RAM).

🤖 Controller

Завхоз. Читает записи в Etcd и заставляет их исполняться (создает поды).

Pod (Под) 🥚

Минимальная единица K8s.

Это оболочка для контейнера. K8s не запускает контейнеры напрямую, он запускает Поды.

  • 💀 Смертны: Если умрет, K8s создаст новый (с новым IP).
  • 🏠 Общий дом: Контейнеры в одном Поде делят IP и диски.
📦

1 Pod = 1 Container

(Обычно так)

Deployment 🚀 (Менеджер)

Гарант стабильности. (Запись в Etcd)

🔄 Desired State

Deployment постоянно сверяет:

  • Надо: 3 штуки
  • Есть: 2 штуки
  • Действие: Создать +1
✅ Pod 1 (Running)
✅ Pod 2 (Running)
🆕 Pod 3 (Starting...)

Service 🌐 (Связист)

Проблема: Поды умирают, их IP меняются.

Решение: (Запись в Etcd) Service дает постоянный IP.

⚙️ Механика:
Демон Kube-proxy (на каждой ноде) следит за этой записью и обновляет правила маршрутизации (iptables).

⚠️ Это НЕ API Server. Это "внутренний роутер" для приложений.

💻
Client
➡️
Service (LB)
10.96.0.1
🎯 Sel: app=web
↘️
↗️
Pod A
🏷️ app=web
Pod B
🏷️ app=web

Сетевая магия

Namespaces 🏷️ (Пространства)

Виртуальные "папки" для изоляции проектов. (Запись в Etcd)

📁

Namespace: DEV

Для разработчиков. Можно ломать.

🛡️

Namespace: PROD

Священная земля. Доступ ограничен.

Production Architecture (HA) 🏰

☁️ External Load Balancer
Вход для пользователей (HTTPS)
⬇️
Control Plane VIP
🧠 Master 1
🧠 Master 2
🧠 Master 3
⬇️
🐴 Worker 1
🐴 Worker 2
🐴 Worker 3

Если один Master умрет, кластер продолжит жить. API доступен по Virtual IP.

Big Picture: Как всё связано? 🌍

☸️ Kubernetes Cluster
🖥️ Worker Node (VM)
🥚 Pod (Process)
📦 Docker Container
⚛️
My App
Network
➡️
🚀 Kafka Cluster
📬 Broker 1
📬 Broker 2
📬 Broker 3

Приложение живет "в матрешке" и ходит в Кафку по сети.

Инструменты 🛠️

Как мы управляем всем этим?

⌨️

kubectl

Консольная утилита. База. Must have.

👁️

Lens / k9s

Удобные GUI. Видно логи, графики.

🕸️

Dashboard

Веб-интерфейс (официальный).

Инструменты и хаос

K8s Debugging: Практика 🕵️‍♂️

1. Живы ли мы?

$ kubectl get pods
NAME           STATUS       R
app-web-x8z     Running      0
app-db-9qa      Error        5
job-cleaner     Comple..    0

Смотри на STATUS и RESTARTS.

2. Залезть внутрь (Tunnel)

💻
Local:5432
TUNNEL
🥚
Pod:5432
$ kubectl port-forward pod/db 5432:5432

Прокидываем порт, чтобы подключиться к БД с ноутбука.

Любимый статус

K8s Cheat Sheet 📝

Команда Описание Когда нужно?
kubectl get pods Список подов и статусы "Жив ли пациент?"
kubectl get pods -w Смотреть в реальном времени (Watch) При деплое / рестарте
kubectl logs <pod> Читать логи приложения Ищем "Exception" или ошибки 🔥
kubectl describe pod Подробное досье (Events) Если статус Error / Crash
kubectl port-forward Проброс порта (Туннель) Подключиться к БД/Kafka
🔄

CI/CD

Автоматизация пути от кода до продакшена.

CI/CD ♾️

CI (Integration)

  • Слияние кода
  • Build
  • Unit Tests

CD (Delivery)

  • Деплой на Stage
  • E2E Tests
  • Деплой на Prod

Pipeline

💻
Code
➡️
🏗️
Build
➡️
🧪
Test
➡️
🚧
Stage
➡️
🚀
Prod

Роль QA в CI/CD 🛡️

⬅️ Shift Left

Тестируем требования и дизайн до написания кода.

🚧 Quality Gates

Тесты упали = ⛔ Стоп машина. Билд не пройдет дальше.

💨 Smoke Tests

Быстрая проверка после деплоя (жив ли пациент?).

🤖 E2E Tests

Полная имитация пользователя (Selenium/Playwright).

Когда тесты упали...

Процесс: Shift Left & Isolation 🔄

💻

1. Local (Dev+QA)

  • Работа в Feature Branch
  • Docker-compose (App + DB)
  • Пишем тесты сразу
➡️
⚙️

2. CI (Pull Request)

  • Isolation Testing
  • Build App ➡️ Run Tests
  • Тесты упали = ⛔ Block Merge
➡️
🚀

3. Environment

  • Deploy to Dev/Stage
  • Smoke / Integration
  • Регрессия перед релизом

Баги ловятся на этапе PR, а не на продакшене! 🛡️

Инструменты 🧰

🏆 Популярные

🤵
Jenkins
Старый, добрый, мощный.
🦊
GitLab CI
Все в одном (код + пайплайн).

🚀 Modern / GitOps

🐙
ArgoCD
GitOps для K8s.
▶️
GitHub Actions
Просто и рядом с кодом.

Где бегут тесты? (GitLab CI)

Типичный сценарий:

💻 git push

GitLab Runner

Сервер, выполняющий Jobs

Docker Executor
🐳 Test Container
Pytest / Playwright
📊 Allure Report

* Джобы выполняются в изолированных Docker-контейнерах, которые умирают после тестов.

🛠️

Практика

Собираем всё вместе

Наш Проект: Архитектура 🏗️

Order API
Kafka
Consumers (x3)
Postgres

Order Consumers: 2 активных, 1 резервный

Data Flow

Postman → API → 🚀 Kafka → Consumer → 🐘 DB
  1. HTTP POST -> Order API (5000)
  2. JSON event -> Kafka topic "order-events"
  3. Consumer читает сообщение
  4. Consumer пишет данные в PostgreSQL

Тулсет (Docker only)

Docker Compose
Основа
Kafka UI / AKHQ
Визуализация
Postman
Отправка
PgAdmin
Проверка БД

Тест 1: Happy Path 😄

📮
1. POST запрос на API (Postman)
⬇️
📨
2. Сообщение в Kafka UI
⬇️
🐘
3. Запись в PostgreSQL

Тест 2: Идемпотентность (Дубль)

🔴 Действие

Отправляем тот же event_id повторно.

🟢 Ожидание

  • Consumer видит дубль.
  • В базу ничего не пишется.
  • В логах: WARN: Duplicate event.

Тест 3: Партиционирование

Ключ партиционирования = barCode.

Coca-Cola
Code: 111
➡️
Hash()
➡️
Partition 0
Всегда сюда!

Гарантия хронологического порядка для товара.

Тест 3b: Разные ключи

Разные Barcode = Round Robin.

Cola (111)
➡️
P0
Pepsi (222)
➡️
P1

Балансировка нагрузки между партициями.

Тест 4: Отказоустойчивость 🔥

  1. 🚀 1. Запускаем нагрузку (посылаем запросы)
  2. 💀 2. Убиваем Консюмера (docker stop consumer-2)
  3. 📈 3. Смотрим Lag (кратковременный рост)
  4. ⚖️ 4. Kafka делает Rebalance
  5. 5. Резервный консюмер подхватывает работу, Lag -> 0

Consumer Lag 🐢

🐢
Consumer
Offset: 600
🐇
Producer
Offset: 900
LAG = 300 msgs

Большой лаг = Консюмер не справляется с потоком.

Что тестируем? (Checklist)

Функциональное

  • Позитивное (Доставка)
  • Негативное (Битая схема)
  • Идемпотентность (Дубли)

Нефункциональное

  • Хаос (Убийство консюмера)
  • Производительность (Лаг)
  • Целостность (Проверка БД)

Итого 📝

👏

Спасибо за внимание!

Вопросы?

"Quality is not an act, it is a habit."

1 / 57

Финиш!