Skip to content

Interview

Preparing to Interview

Resources: 1. https://github.com/Swfuse/devops-interview?tab=readme-ov-file 2. https://github.com/rmntrvn/adm_linux_ops_questions


Общее

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

Подается сигнал о подаче питания, запускается BIOS/UEFI (программа подготовки к загрузке ОС). Загрузчик проверяет целостность своей программы, инициализирует устройства. После успешного прохождения тестов BIOS ищет загрузчик ОС:

  • ищется загрузочный сектор (MBR)
  • считывается код из этого раздела и управление передается ему,
  • этот код ищет активный раздел ("system partition"), в первом разделе которого находится загрузочный код GRUB,
  • GRUB загружает и выполняет образы ядра и initrd,
  • передает управление ядру,
  • ядро монтируется в файловую систему и выполняет процесс init,
  • init загружает программы уровней выполнения (runlevel) из /etc/rc.d/rc*.d/.

Пример программ:

  • S12syslog — запуск демона syslog,
  • S80sendmail — запуск демона sendmail.

Таким образом, syslog будет запущен перед sendmail.

Источник: https://habr.com/ru/post/518972/

Открыли linux консоль, ввели curl www.tinkoff.ru - нажали энтер. Что происходит, максимально подробно.

curl — утилита для взаимодействия с хостами по протоколам: FTP, FTPS, HTTP, HTTPS, TFTP, SCP, SFTP, Telnet, DICT, LDAP, POP3, IMAP и SMTP.

Без дополнительных параметров curl выполняется с методом GET по протоколу HTTP (уровень L7). Перед этим:

  1. происходит разрешение DNS имени в IP-адрес по протоколу DNS,
    • Протокол: DNS
    • Уровни OSI:
      • Прикладной (7) — DNS
      • Транспортный (4) — UDP (или TCP)
      • Сетевой (3) — IP
  2. запрос уходит на DNS-сервер, далее по цепочке — к корневым DNS и авторитетным,
    • Протоколы: DNS → через UDP или TCP (53 порт)
    • Уровни OSI:
      • Прикладной (7) — DNS
      • Транспортный (4) — UDP/TCP
      • Сетевой (3) — IP
      • Канальный (2) — Ethernet/Wi-Fi
      • Физический (1)
  3. получив IP, устанавливается соединение по TCP (трехстороннее рукопожатие),
    • TCP (3-way handshake: SYN, SYN-ACK, ACK)
    • Уровни OSI:
      • Транспортный (4) — TCP
      • Сетевой (3) — IP
      • Канальный (2), Физический (1)
  4. используется стек TCP/IP и таблицы маршрутизации,
    • Протоколы: IP (routing), TCP (session)
    • Уровни OSI:
      • Сетевой (3) — IP
      • Транспортный (4) — TCP
      • Канальный (2), Физический (1)
  5. далее отправляется HTTP-запрос на порт 80,
    • Протоколы: HTTP
    • Уровни OSI:
      • Прикладной (7) — HTTP
      • Транспортный (4) — TCP
      • Сетевой (3) — IP
  6. получаем ответ от сервера.
    • Протоколы: HTTP (ответ), TCP (доставка)
    • Уровни OSI:
      • Прикладной (7) — HTTP
      • Транспортный (4) — TCP
      • Сетевой (3) — IP

Что такое LoadAverage?

LoadAverage — средняя загрузка CPU за 1, 5 и 15 минут.

  • Что означает? Процент утилизируемого времени от общего времени простоя.
  • В чем измеряется? Измеряется в тиках (единицы времени CPU), сколько тиков отдано на выполнение процессов. Если > 100%, значит есть очередь из ожидающих процессов.
  • Какое нормальное значение? До 1 на 1 CPU (1.0 на одно ядро).
  • Если LA = 1000 — это нормально? Нет, это плохо, если только не 1000 ядер в системе.
  • Что влияет на LA? Например, iowait.

Высокие значения wa (wait I/O) и LA часто говорят о D-state (ожидание ввода/вывода), может быть связано с дисками, сетью, USB, сокетами и др.

Упрощенная модель состояний процесса в Linux

  • D — ожидание ввода/вывода
  • R — выполняется
  • S — спящий, ждет события
  • T — остановлен
  • Z — зомби

Посмотреть процессы: ps aux

Если wa (wait I/O) > 10-30%, желательно найти причину.

Источник: https://firstvds.ru/technology/nagruzka-na-server-opredelenie-prichin

Кто и при каких условиях может менять группу у файла?

Команда: chown

  • Требуется: либо быть владельцем файла, либо иметь права root.

Пример:

-rw------- 1 user1 user1 147746 мар 27 12:19 somefile
  • chown user1:user2 somefile

  • chown user1:www-data somefile

  • Если не root: сработает, если user1 состоит в этих группах.

  • Если root: сработает, если группы и пользователи существуют.

Свободное место #1

df (disk free) - Показывает использование файловой системы в целом, на уровне блочного устройства. - Смотрит на таблицу inodes и блоков, чтобы узнать, сколько данных физически занято. - Учитывает все данные, включая: - удалённые, но всё ещё открытые файлы; - данные в сокетах, pipes, tmpfs и т.п. 🔸 Типичный источник правды — ядро, через системные вызовы statfs().

du (disk usage) - Считает файлы и папки, доступные в текущем namespace. - Обходит файловую систему и суммирует размеры только доступных (видимых) файлов. - Не считает: - удалённые, но ещё открытые файлы; - tmpfs, сокеты, pipes и другие неклассические файловые объекты. 🔸 Типичный источник правды — просмотр содержимого директорий.

lsof (List Open Files) — это утилита в Linux/Unix, которая показывает все открытые файлы и сокеты в системе. В Linux всё — это файл, включая:

  • обычные файлы (/var/log/...)
  • директории
  • устройства (/dev/sda)
  • сокеты (/tmp/.X11-unix)
  • каналы (pipes)
  • сетевые соединения (TCP, UDP)

df -Th показывает, что место есть. Но touch file — ошибка "No space left on device".

Причины:

  • Файлы были удалены, но заняты процессами.

  • Проверка: sudo lsof / | grep deleted

  • Исчерпаны inodes — df -i

  • Плохие блоки — использовать fsck -vcck /dev/sdX

  • Ограничения файловой системы (например, FAT — макс. файл 4GB)

Источник: https://rtfm.co.ua/unix-df-i-du-raznye-znacheniya/ https://zalinux.ru/?p=3001

Свободное место #2

Корень 100ГБ, df показывает занято всё, а du -sh /* — только 10ГБ.

Возможные причины:

  • Файлы удалены, но заняты процессами.
  • Найти процессы: lsof | grep deleted

Пояснение: файловые дескрипторы и inodes

  • inode — уникальный ID файла, метаданные.

  • fd (file descriptor) — "билет" на доступ к файлу для конкретного процесса, с конкретными правами.

  • Один файл может быть открыт многими fd.

  • После закрытия fd доступ к файлу теряется (если нет других открытых).

Гипервизор

Гипервизор — это низкоуровневая оболочка (программа или аппаратная схема), обеспечивающая параллельное выполнение нескольких ОС на одном хост-компьютере.

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

Гипервизор можно считать микроядром — минимальной ОС, управляющей виртуальными машинами.

Как работает контейнеризация и чем отличается от виртуализации?

Контейнеризация: изоляция приложений с использованием общего ядра ОС (через namespaces, cgroups). Контейнер — это изолированный процесс, а не отдельная ОС.

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

Контейнеры легче, запускаются быстрее, используют меньше ресурсов.


Linux

Виды памяти

Основные термины

Вид памяти Что это такое Где смотрим
VSS (VSZ) – Virtual Set Size Вся виртуальная память процесса (вкл. библиотеки, mmap, кэш, зарезервированную). ps, top, smem
RSS – Resident Set Size Реально загруженная в RAM память процесса (без свопа). ps, top, smem, /proc/[pid]/statm
PSS – Proportional Set Size RSS, но shared-страницы делятся пропорционально между процессами. smem, /proc/[pid]/smaps
USS – Unique Set Size Только та память, которую использует только данный процесс. smem, /proc/[pid]/smaps
Shared memory Общая память, используемая несколькими процессами (например, библиотеки). smem, pmap, /proc/[pid]/maps
Swap Страницы, выгруженные на диск (в swap). free, vmstat, top, smem
Page Cache Кэш файловых данных, недавно прочитанных с диска. Используется ядром. free, vmstat, /proc/meminfo
Buffers Кэш метаданных файловой системы и блочных устройств. free, vmstat, /proc/meminfo
Slab Кэш структур ядра (inode, dentry, файловые дескрипторы и пр.). /proc/meminfo, slabtop
PageTables Память, занимаемая таблицами страниц виртуальной памяти процессов. /proc/meminfo, smem, top
KernelStack Стек ядра, выделенный для каждого потока. /proc/meminfo, smem
#### Области памяти процесса
Область Назначение
Text (code) Исполняемый код
Data Глобальные переменные
Heap Динамическая память (malloc, new)
Stack Локальные переменные, вызовы функций
mmap Отображения файлов, shared libraries, анонимная память
Можно посмотреть с помощью:
cat /proc/<pid>/maps

📊 Пример smem

sudo smem -r
PID User USS PSS RSS Command
123 root 10M 12M 14M nginx

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

Высокий VSS — не всегда проблема (может быть много mmap’ов, но физически не используется). • Высокий RSS — показывает реальное использование RAM. • PSS — хорош для понимания, сколько “в среднем” процесс реально занимает. • USS — полезен для оценки того, что можно выгрузить без ущерба другим процессам. • Swap > 0 — может сигнализировать о нехватке RAM.

Namespaces and Cgroups

namespaces и cgroups — это два столпа изоляции в Linux. Они лежат в основе контейнеризации (например, Docker, Kubernetes) и вообще любой виртуализации на уровне ОС.

Давай разложим на понятном уровне:


🧱 Что такое Linux namespaces?

Namespaces позволяют изолировать системные ресурсы между процессами.

То есть каждый процесс “видит” только то, что разрешено его namespace’у.

📦 Основные типы namespace
Namespace Изолирует Пример
mnt файловую систему отдельный /, /proc
pid процессы и PID’ы ps покажет “свои” PID
net сетевые интерфейсы, IP отдельный eth0
uts имя хоста (hostname) hostname будет разным
ipc очереди сообщений, semaphors ipcs
user пользователей и UID/GID root внутри, но не снаружи
cgroup иерархию cgroups отдельная область ресурсов

📌 Каждый namespace изолирует свою подсистему.

🔧 Пример изоляции с unshare:

sudo unshare --pid --fork --mount-proc bash

Теперь ты в новом PID namespace → ps покажет только текущий процесс.


🔒 Что такое Linux cgroups?

Cgroups (Control Groups) — это способ ограничить и контролировать ресурсы, которые может потреблять процесс.

📌 Возможности cgroups:

• Ограничение CPU, RAM, disk IO, pids • Приоритет (через weight) • Мониторинг использования ресурсов • Группировка процессов в иерархию

Примеры ресурсов:

Тип Что ограничивает
cpu использование CPU
memory объём RAM
pids число процессов
blkio доступ к диску
devices доступ к устройствам
Пример ограничения памяти:
mkdir /sys/fs/cgroup/memory/mygroup
echo 100000000 > /sys/fs/cgroup/memory/mygroup/memory.limit_in_bytes
echo $$ > /sys/fs/cgroup/memory/mygroup/tasks

📌 Теперь текущий процесс не может использовать больше 100Мб RAM.


📦 Как работают вместе

Контейнер (например, docker run) использует:

Namespaces → чтобы изолировать окружение (видимость процессов, файлов, сети) • Cgroups → чтобы ограничить, сколько он может потреблять ресурсов


🐳 Пример: как работает Docker контейнер под капотом
  1. Создаётся новый PID, NET, UTS, MNT, USER namespace → у контейнера “своя” ОС
  2. Создаются cgroups для CPU, memory и pids
  3. Монтируется overlayfs → своя корневая файловая система
  4. Внутри запускается твой ENTRYPOINT в изолированной среде

✅ Сравнение:
Namespaces Cgroups
Что делают? Изолируют Ограничивают
Применение Видимость ресурсов Потребление ресурсов
Пример свой hostname максимум 512Mi памяти

Bash переменные $?, $0, $1, $@, $\$

$\$

echo $$

Это команда, которая выводит PID (Process ID) текущего shell-процесса.


🔍 Подробно:

• $ — это префикс для переменной окружения в bash.

• $\$ — специальная переменная в bash, которая содержит PID текущего процесса, в котором выполняется команда.


📦 Пример:

$ echo $$
12345

Это означает, что оболочка (shell), в которой ты работаешь — имеет PID 12345.


🧠 Когда полезно?

  1. Чтобы:

• добавить текущий процесс в cgroup:

echo $$ > /sys/fs/cgroup/memory/mygroup/tasks
  1. Отлаживать скрипты:
echo "This script is running with PID $$"
  1. Смотреть, какие процессы запущены от конкретного shell:
ps -fp $$

Special variables short

Иногда их также называют:

параметрами окружения shell’апозиционными параметрами ($0, $1, $2, …, $@, $) • встроенными переменными shell (builtin shell variables)*


📘 Расшифровка самых важных:

Переменная Значение
$$ PID текущего shell-процесса
$? Код возврата последней команды (0 = успех, ≠0 = ошибка)
$0 Имя скрипта (или имя текущего shell)
$1, $2, … Параметры, переданные скрипту или функции
$@ Все аргументы как список ("$1", "$2"…)
$* Все аргументы как одна строка ("$1 $2 ...")

🔍 Примеры:

#!/bin/bash
echo "Script name: $0"
echo "First arg: $1"
echo "Exit code of last cmd: $?"
echo "All args (\$@): $@"
echo "All args (\$*): $*"

Если ты вызовешь:

./myscript.sh hello world

То получишь:

Script name: ./myscript.sh
First arg: hello
Exit code of last cmd: 0
All args ($@): hello world
All args ($*): hello world

🧠 Резюме

Имя Что делает Категория
$$ PID текущего shell спец. переменная
$? Код выхода последней команды спец. переменная
$0 Имя скрипта или shell позиционный параметр
$1, $2 Аргументы позиционные параметры
$@, $* Все аргументы позиционные параметры

Что такое zombie процесс и чем это плохо, как его убить?

Zombie (зомби) — завершившийся процесс, чья запись ещё хранится в системе (ожидает, пока родитель вызовет wait() для чтения exit-кода).

  • Не использует CPU/RAM, но потребляет PID.
  • При большом числе зомби — может закончиться пул PID.

Убить: нельзя напрямую, нужно убить родителя:

ps -ef | grep defunct
# найти родителя, затем:
sudo kill -9 <PID>

Чем опасно хранение большого количества маленьких файлов на файловой системе?

  • Заканчиваются inodes — метаданные для файлов.
  • Медленное выполнение операций (поиск, open/read).
  • Проблемы с резервным копированием и производительностью.

Что делать: - Использовать базы данных (например, SQLite, PostgreSQL). - Разбивать по подпапкам (sharding по префиксу ID). - Использовать объектное хранилище (S3, GCS).


GIT

Как внести изменения в последний коммит?

Можно использовать команду git commit с флагом --amend.

Как добавить забытые файлы в последний коммит?

Если забыли включить в коммит часть файлов, можно:

git add dir1
git commit

# забыли dir2:
git add dir2
git commit --amend --no-edit

Флаг --no-edit позволяет оставить прежнее сообщение коммита.

Чем отличается git revert от git reset?

  • git revert отменяет один конкретный коммит, создав новый коммит с обратными изменениями.
  • git reset возвращает проект к прошлому состоянию, удаляя все последующие коммиты (если hard).

Сеть

OSI

Open Systems Interconnection

📶 Модель OSI — 7 уровней сетевого взаимодействия

Уровень Описание Примеры
7 Прикладной (Application) То, что видит пользователь HTTP, FTP, DNS, SSH
6 Представления (Presentation) Формат данных, кодировка, шифрование TLS/SSL, JPEG, MP3
5 Сеансовый (Session) Управление сессиями SSH-сессия, RPC, NetBIOS
4 Транспортный (Transport) Доставка данных, надёжность TCP, UDP
3 Сетевой (Network) Адресация и маршрутизация IP, ICMP
2 Канальный (Data Link) Доставка кадров между узлами Ethernet, Wi-Fi, PPP
1 Физический (Physical) Биты, кабели, сигналы RJ-45, оптоволокно, Wi-Fi
Мнемоника:

🔽 Снизу вверх:

Please Do Not Throw Sausage Pizza Away

(Physical, Data Link, Network, Transport, Session, Presentation, Application)


🔍 Пример передачи HTTP-запроса:

  1. HTTP-запрос (Application)
  2. Шифруется в TLS (Presentation)
  3. Управление сеансом TLS (Session)
  4. Делится на сегменты TCP (Transport)
  5. Достаётся через IP-адрес (Network)
  6. Отправляется по Ethernet/Wi-Fi (Data Link)
  7. Через физический кабель или сигнал (Physical)

L4/L7 LB

Разберём L4, L7 и другие типы балансировщиков, и в чём их особенности.

🌐 Уровни балансировки нагрузки (по OSI-модели)

✅ L4-балансировка (транспортный уровень)

📌 Основана на: IP-адресах + TCP/UDP-портах

🛠 Как работает:

• Перенаправляет трафик, не заглядывая внутрь пакета.

• Работает по 5-tuple: src IP, src port, dst IP, dst port, protocol.

🔧 Примеры:

• iptables / ipvs • Linux LVS • HAProxy (L4 режим) • AWS Network Load Balancer (NLB) • MetalLB в Kubernetes

➕ Плюсы:

• Быстро • Низкие накладные расходы • Подходит для TCP/UDP

➖ Минусы:

• Не видит содержимого (например, URL, cookie) • Не может принимать решения по содержимому запроса


✅ L7-балансировка (прикладной уровень)

📌 Основана на: содержимом HTTP/HTTPS, gRPC, WebSocket и т.д.

🛠 Как работает:

• Анализирует запросы полностью: путь, заголовки, куки, тело и т.п. • Может делать маршрутизацию по URL, хосту, методу, и даже по содержимому.

🔧 Примеры:

• NGINX, Envoy, Traefik, HAProxy (L7) • Kubernetes Ingress Controllers • AWS Application Load Balancer (ALB) • Istio / Linkerd (в сервисной mesh’е)

➕ Плюсы:

• Гибкая маршрутизация • Поддержка канареек, A/B, blue/green • Можешь делать rate limiting, аутентификацию, логирование

➖ Минусы:

• Выше нагрузка • Медленнее, чем L4 • Не подходит для “сырого” TCP/UDP


📦 Есть ли другие уровни?

✅ L3-балансировка (сетевой уровень)

📌 Маршрутизация по IP без портов

• Очень базовая, почти не используется в приложениях напрямую • Пример: BGP + ECMP (Equal-cost multi-path routing), IP-routing

✅ L2-балансировка (канальный уровень)

📌 Балансировка через MAC-адреса и ARP

• Используется в некоторых виртуализированных или железных решениях • Пример: балансировка через L2-мосты, ARP spoofing/rewriting (редкость)

🧠 Сравнение коротко

Уровень Что видит? Примеры Особенности
L7 HTTP, gRPC, Headers ALB, Ingress, NGINX Умная маршрутизация
L4 TCP/UDP NLB, LVS, iptables Быстро, не читает запросы
L3 IP-адреса BGP, ECMP Очень низкий уровень, routing
L2 MAC-адреса ARP proxy, мосты Почти не используется в клауде

🧪 В Kubernetes

Service type: LoadBalancer — обычно L4 (NLB или аналог) • Ingress Controller — L7 (работает с HTTP) • Service Mesh (Istio, Linkerd) — L7 прокси с deep inspection

В Kubernetes уровни L4 и L7 чаще всего используются вместе, потому что они решают разные задачи на пути трафика к приложению. Давай разберём пошагово и наглядно, как они сочетаются.

🧭 Кратко: как трафик проходит от клиента к приложению
Client → L4 LoadBalancer → NodePort / ClusterIP (L4) → Ingress Controller (L7) → App

🔷 1. L4 в Kubernetes

L4 балансировка основана на IP + TCP/UDP портах. В Kubernetes это:

✅ Примеры L4-объектов:

Тип объекта Что делает Уровень
Service: ClusterIP L4 внутри кластера L4
Service: NodePort Открывает порт на всех нодах L4
Service: LoadBalancer Интеграция с внешним L4 балансером (например, NLB) L4

📌 Важно: LoadBalancer — это внешний L4-балансер, например, AWS NLB или GCP TCP balancer, который умеет “раздавать” трафик на NodePort сервиса.


🔶 2. L7 в Kubernetes

✅ Как только L4-трафик доходит до Ingress Controller (например, NGINX, Traefik, Envoy):

• Начинается L7-балансировка:

Разбор HTTP-запросов → маршрутизация по:

• host (например, api.example.com)

• path (например, /v1/ → один сервис, /admin → другой)

• заголовкам, методам, и т.п.

✅ Примеры L7:

• Ingress ресурс

• IngressRoute (Traefik CRD)

• VirtualService (в Istio)


🧪 Пример трафика:

👉 Ты открываешь браузер:

https://app.example.com/login

Что происходит:

  1. 🌍 Запрос уходит на внешний IP LoadBalancer’а (например, AWS NLB) — L4

  2. 🚪 Он перенаправляет трафик на NodePort внутри кластера (все ноды слушают нужный порт) — L4

  3. 📦 Попадает на pod с Ingress Controller’ом (например, NGINX) — теперь включается L7

  4. 🧠 NGINX смотрит:

• Host: app.example.com

• Path: /login

  1. 🛣 Направляет трафик на нужный backend-сервис (например, auth-service) — уже внутри кластера

🔁 Схема

    [ Client ]
        |
    [ L4 LoadBalancer ]  ← external (AWS NLB, GCP LB)
        |
    [ NodePort Service ] ← kube-proxy, iptables (L4)
        |
    [ Ingress Controller Pod ] ← nginx, envoy (L7)
        |
    [ Target App Service / Pod ]

🤓 А если Service Mesh (например, Istio)?

• Istio вставляет L7-прокси (Envoy) перед каждым pod’ом

• Трафик идёт:

• L4 → Ingress Gateway (L7)

• Потом между подами тоже L7 → полный контроль, трассировка, канареечные релизы и т.п.


🧠 Резюме
Компонент Уровень Назначение
LoadBalancer Service L4 Доступ снаружи кластера
NodePort / ClusterIP L4 Роутинг на pod’ы внутри
Ingress Controller L7 HTTP/HTTPS маршрутизация
Service Mesh L7 Продвинутая маршрутизация между подами

TCP vs UDP. Разница

  • TCP — протокол с гарантией доставки, устанавливает соединение перед передачей пакетов.
  • UDP — не устанавливает соединение, работает быстрее, не гарантирует доставку. Используется для потокового видео, DNS и других приложений, где важна скорость.

Как происходит TCP-handshake?

Кратко: 1. Клиент отправляет SYN с seq=A. 2. Сервер отвечает SYN+ACK с seq=B и ack=A+1. 3. Клиент отправляет ACK с seq=A+1 и ack=B+1.

Подробно: 1. Клиент отправляет сегмент с SYN и случайным seq (A). 2. Сервер запоминает seq клиента, выделяет сокет, отправляет SYN+ACK (seq=B, ack=A+1) и переходит в SYN-RECEIVED. При ошибке — RST. 3. Клиент получает SYN+ACK, запоминает seq сервера и отправляет ACK. Переходит в ESTABLISHED. Если получил RST — завершает попытку. При таймауте — повтор. 4. Сервер при получении ACK переходит в ESTABLISHED. Если нет ACK — закрывает соединение.

Как работают сертификаты? Как подтверждается соединение? (https соединение)

Процесс работает следующим образом:

  1. Браузер или сервер пытается подключиться к веб-сайту (веб-серверу), защищенному с помощью SSL.
  2. Браузер или сервер запрашивает идентификацию у веб-сервера.
  3. В ответ веб-сервер отправляет браузеру или серверу копию своего SSL-сертификата и публичный ключ.
  4. Браузер или сервер проверяет, является ли этот SSL-сертификат доверенным. У него уже зашиты сервера с помощью которых нужно производить проверку, с помощью центров сертификации. Если это так, он сообщает об этом веб-серверу. Генерирует сенасовый ключ, шифрует пебличным ключом и отправляет на сервер.
  5. Сеервер расшифровывает сообщение и сохраняет сеансовый ключ. Затем веб-сервер возвращает подтверждение с цифровой подписью и начинает сеанс, зашифрованный с использованием SSL.
  6. Зашифрованные данные используются совместно браузером или сервером и веб-сервером.

Опиши принцип работы ssl-шифрования

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

Публичный ключ зашифровывает сообщение.

Браузер использует его, когда нужно отправить пользовательские данные серверу. Например, после того как вы ввели данные банковской карты и нажали «Оплатить». Этот ключ виден всем, браузер прикрепляет его к сообщению.

Приватный ключ расшифровывает сообщение.

Его использует сервер, когда получает сообщение от браузера. Этот ключ хранится на сервере и никогда не передаётся вместе с сообщением.

Сеансовый ключ одновременно зашифровывает и расшифровывает сообщения.

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

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

Шифрование с двумя разными ключами называют асимметричным. Использовать такой метод более безопасно, но медленно. Поэтому браузер и сервер используют его один раз: чтобы создать сеансовый ключ.

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

Симметричное и асимметричное шифрование — это два основных метода шифрования данных. Они отличаются механизмом использования ключей, безопасностью и производительностью.

Симметричное шифрование

🔑 Один ключ для шифрования и дешифрования

В симметричном шифровании используется один и тот же ключ для шифрования и дешифрования данных.

Как работает?

  1. Отправитель и получатель обмениваются секретным ключом.
  2. Отправитель шифрует сообщение с помощью этого ключа.
  3. Получатель расшифровывает сообщение, используя тот же ключ.

Примеры алгоритмов:

AES (Advanced Encryption Standard) – широко используется в современных системах. • DES (Data Encryption Standard) – устарел, но был стандартом в прошлом. • 3DES (Triple DES) – улучшенная версия DES. • RC4 – потоковый алгоритм, сейчас считается небезопасным.

Плюсы

✅ Быстрое шифрование и дешифрование. ✅ Эффективно при работе с большими объемами данных.

Минусы

❌ Необходимо безопасно передавать ключ между отправителем и получателем. ❌ Если ключ украдут, злоумышленник сможет расшифровать данные.

Асимметричное шифрование

🔑 Два разных ключа: публичный и приватный

В асимметричном шифровании используются пара ключей:

Публичный ключ – используется для шифрования. • Приватный ключ – используется для дешифрования.

Как работает?

  1. Отправитель получает публичный ключ получателя.
  2. Отправитель шифрует данные с помощью публичного ключа.
  3. Получатель дешифрует данные с помощью приватного ключа.

Примеры алгоритмов:

RSA (Rivest-Shamir-Adleman) – один из самых распространенных алгоритмов. • ECC (Elliptic Curve Cryptography) – более безопасный при меньших размерах ключей. • DSA (Digital Signature Algorithm) – используется для цифровых подписей.

Плюсы

Не нужно передавать секретный ключ, только публичный. ✅ Безопаснее, так как взлом требует вычисления обратного преобразования (например, факторизации больших чисел).

Минусы

Медленнее, чем симметричное шифрование (из-за сложных математических операций). ❌ Менее эффективно при обработке больших объемов данных.

Основные отличия

Симметричное шифрование Асимметричное шифрование
🔑 Ключи Один общий ключ Пара ключей (публичный + приватный)
Скорость Быстрее Медленнее
🔐 Безопасность Уязвимо при передаче ключа Более безопасно
🔁 Использование Хорошо для шифрования больших данных Используется для ключевого обмена и цифровых подписей
### Когда что использовать?

📌 Симметричное шифрование – для быстрого и эффективного шифрования больших объемов данных (например, файлов, дисков, VPN).

📌 Асимметричное шифрование – для безопасного обмена ключами, цифровых подписей, шифрования электронной почты (PGP), TLS/SSL в интернете.

💡 Гибридный подход: в реальных системах часто используется комбинация. Например, в HTTPS клиент и сервер сначала обмениваются ключами через асимметричное шифрование (RSA), а затем используют симметричное шифрование (AES) для передачи данных.

Вывод

Симметричное шифрование быстрее, но требует безопасного обмена ключами. • Асимметричное шифрование медленнее, но решает проблему передачи ключей. • В реальных сценариях они часто используются вместе!

Что такое таблица маршрутизации?

Это таблица, содержащая маршруты для отправки пакетов в разные подсети и сети. Используется для выбора интерфейса и следующего хопа (gateway).

Проверка маршрута до IP 1.2.3.4:

traceroute 1.2.3.4
ip route get 1.2.3.4

IPTables: SNAT, DNAT, Маскарадинг

Основные таблицы:

1. Filter Table

  • Используется по умолчанию.
  • Для фильтрации пакетов (DROP, LOG, ACCEPT, REJECT).
  • Цепочки:
  • INPUT (пакеты для сервера)
  • OUTPUT (исходящие локальные)
  • FORWARD (транзитные)

2. NAT Table

  • Для преобразования сетевых адресов.
  • Только первый пакет потока проходит через эту таблицу.
  • Цепочки:
  • PREROUTING — DNAT
  • POSTROUTING — SNAT
  • OUTPUT — NAT для локально сгенерированных

3. Mangle Table

  • Для изменения заголовков: TOS, TTL, MARK и др.
  • Цепочки:
  • PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING

4. Raw Table

  • До connection tracking (state machine)
  • Цепочки: PREROUTING, OUTPUT

Цепочки IPTables:

  • PREROUTING — до маршрутизации
  • INPUT — для сервера
  • FORWARD — транзитные
  • OUTPUT — локальные исходящие
  • POSTROUTING — после маршрутизации

Что означает запись 10.12.15.35/22?

CIDR-блок /22: - Маска: 255.255.252.0 - Диапазон: 10.12.12.0 – 10.12.15.255 - Пример частной сети с 1024 адресами, 64 подсети.

DNS

Структура записи DNS (RR):

  • NAME — домен

  • TYPE — тип записи (A, AAAA, MX, CNAME...)

  • CLASS — тип сети (обычно IN)

  • TTL — срок жизни в кэше

  • RDLEN — длина данных

  • RDATA — содержимое

Типы записей:

  • A — IPv4 адрес

  • AAAA — IPv6 адрес

  • CNAME — псевдоним (каноническое имя)

  • MX — почтовый обменник

  • NS — DNS сервер зоны

  • PTR — обратная запись IP → имя

  • SOA — начальная запись зоны

  • SRV — серверы сервисов (например, для Jabber, Active Directory)

Рекурсивный и не рекурсивный запрос

При рекурсивном запросе Вы просто обращаетесь к серверу, а он, если не найдет у себя нужной записи, идет к другим серверам и спрашивает у них.

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

Сетевые утилиты

Проверка открытых портов и сокетов

ss -tlpn

Проверка сетевых интерфейсов

ip a

Проверка маршрутов

ip route

NetCat (nc)

Проверка порта:

nc -vn 192.168.1.100 12345

Сканирование портов:

nc -vnz 192.168.1.100 20-24

Источник: https://habr.com/ru/post/336596/

Nmap

Поиск всех хостов в подсети:

nmap -sP 192.168.1.0/24
nmap -sn 192.168.1.0/24

Tcpdump

Пример:

tcpdump -i any -s0
  • -i — интерфейс
  • -s — размер захвата пакета (0 — весь)

Docker

Как внутри Docker понять что ты в Docker?

1. Проверка файла /.dockerenv
test -f /.dockerenv && echo "Inside Docker"

Плюсы: Надёжно, этот файл создаёт Docker при запуске контейнера. • Минусы: Некоторые среды (например, Kubernetes) могут не создавать этот файл.

Также, если ты используешь другой runtime (containerd, Podman), файл может отсутствовать.


2. Чтение cgroup
cat /proc/1/cgroup

Если ты видишь что-то вроде:

0::/docker/abc123...

или

1:name=systemd:/docker/abc123

— то ты, скорее всего, в Docker.

Пример проверки в bash:

grep -qE "/docker/|/kubepods/" /proc/1/cgroup && echo "Inside container"

• kubepods → ты внутри Kubernetes.

• docker → в Docker напрямую.


3. Проверка hostname

Иногда можно увидеть, что hostname = короткий случайный hash (Docker генерирует его как container ID). Пример:

hostname

Может быть что-то вроде 0a1b2c3d4e5f.

📌 Не надёжный способ, но может быть косвенным признаком.


⚠️ 4. Проверка ограничений cgroups

Если внутри контейнера у тебя:

cat /sys/fs/cgroup/memory/memory.limit_in_bytes

И значение сильно меньше, чем у твоей машины (например, 512MB или 1GB), ты, вероятно, в контейнере с ограничением ресурсов.


🧠 Какой способ лучше?
Метод Надёжность Комментарий
/.dockerenv ✅ Очень высокая Стандарт для Docker
/proc/1/cgroup ✅ Очень высокая Работает и в k8s
hostname ⚠️ Низкая Косвенный признак
cgroup limits Полезно для анализа, но не строго

🛠 Рекомендуемый способ (универсальный скрипт)

if [ -f /.dockerenv ] || grep -qa 'docker\|kubepods' /proc/1/cgroup; then
    echo "Running inside a container"
else
    echo "Not in container"
fi

В чем отличие CMD и ENTRYPOINT

Эти инструкции Dockerfile задают команду, исполняемую при запуске контейнера. При их использовании есть несколько правил, например:

  • Должна быть минимум одна из них, CMD или ENTRYPOINT, в Dockerfile.
  • Если контейнер используется как исполняемый файл — ENTRYPOINT должна быть определена.
  • Если контейнер запускается с другими аргументами — CMD будет переопределена.

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

Хорошей иллюстрацией инструкции RUN может быть установка нескольких пакетов систем контроля версий:

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion

Обратите внимание, что apt-get updateи apt-get installвыполняются в одной инструкции RUN. Это делается для того, чтобы убедиться, что будут установлены самые последние пакеты. Если бы apt-get installэто была отдельная инструкция RUN, то она бы повторно использовала слой, добавленный apt-get update, который мог быть создан давным-давно.

Инструкция CMD позволяет вам установить команду по умолчанию , которая будет выполняться только тогда, когда вы запускаете контейнер без указания команды. Если контейнер Docker запускается с командой, команда по умолчанию будет игнорироваться. Если Dockerfile содержит более одной инструкции CMD, все инструкции CMD, кроме последней, игнорируются.

CMD имеет три формы:

  • CMD ["executable","param1","param2"]

    (исполнительная форма, предпочтительнее)

  • CMD ["param1","param2"]exec

    (устанавливает дополнительные параметры по умолчанию для ENTRYPOINT в форме

  • CMD command param1 param2

    (форма оболочки)

    docker run -it <image> /bin/bash - тут при наличии CMD он будет проигнорирован, и будет запущен баш

Инструкция ENTRYPOINT позволяет настроить контейнер, который будет работать как исполняемый файл. Он похож на CMD, потому что также позволяет указать команду с параметрами. Разница заключается в том, что команда ENTRYPOINT и параметры не игнорируются, когда контейнер Docker запускается с параметрами командной строки. (Есть способ игнорировать ENTTRYPOINT, но вряд ли вы это сделаете.)

Докерфайл

ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]

когда контейнер запускается, как docker run -it <image>будет производиться вывод

Hello world

но когда контейнер запускается, docker run -it <image> Johnэто приведет к

Hello John
  • Краткий ответ

cmd подставится после entrypoint при запуске. Тем самым можно запускать контейнер с нужными параметрами.

То есть в entrypoint можно передать бинарь, а в cmd передать параметры.

CMD может перетереться просто.

Что такое Docker? Как он устроен? На каких технологиях основывается?

Docker — это платформа контейнеризации, позволяющая запускать приложения в изолированной среде (контейнере).

Технологии: - cgroups и namespaces (пространства имён) для изоляции - union файловые системы (OverlayFS) - контейнерный движок containerd/runc

Пространства имён, используемые Docker:

  • pid — изоляция процессов
  • net — отдельные сетевые интерфейсы
  • ipc — управление IPC-ресурсами (взаимодействие процессов)
  • mnt — управление точками монтирования
  • uts — имя хоста и домена (hostname)

Разница между Docker и VMware

  • Docker — уровень ОС (использует ядро хоста, быстрая и лёгкая изоляция)
  • VMware — полноценная виртуализация (каждая ВМ — своя ОС, медленнее, ресурсоёмко)

Зачем нужен Docker? Какие проблемы решает?

  • Решает проблему "работает у меня, не работает у тебя"
  • Позволяет создавать воспроизводимую среду
  • Быстрое развёртывание и масштабирование приложений

Основные директивы Dockerfile

  • FROM — базовый образ
  • RUN — выполнение команд в контейнере при сборке
  • COPY / ADD — копирование файлов
  • CMD / ENTRYPOINT — команда запуска
  • EXPOSE — открытие портов
  • ENV — переменные окружения
  • WORKDIR — рабочая директория

Разница между Dockerfile и docker-compose

  • Dockerfile — инструкция для создания образа
  • docker-compose.yml — инструмент для описания и запуска многоконтейнерных приложений (описывает сервисы, сети, тома)

Как перенести docker image с одной машины на другую без registry?

docker save myimage > image.tar
scp image.tar user@remote:/tmp/
docker load < image.tar

Или:

docker export <container> > file.tar
# и далее docker import

Best practices по написанию Dockerfile и безопасности контейнеров

  • Использовать минимальные образы (например, alpine)
  • Указывать конкретные версии пакетов
  • Не использовать root внутри контейнера
  • Минимизировать количество слоёв
  • Удалять временные файлы после установки пакетов
  • Не хранить секреты в образах
  • Использовать USER для запуска приложений
  • Ограничивать доступ к хосту (AppArmor, seccomp, read-only FS)

Сетевые драйверы Docker

  • bridge — дефолтная сеть, контейнеры в одной подсети, могут общаться между собой через виртуальный bridge-интерфейс
  • host — контейнер использует сетевой стек хоста
  • macvlan — каждому контейнеру назначается отдельный MAC-адрес, прямой доступ к сети
  • overlay — сеть между несколькими хостами (используется в Swarm)
  • swarm — кластерная сеть для контейнеров, работающих в Swarm-режиме

В чем отличие ARG от ENV?

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

ARG позволяет закинуть переменные, которые будут доступны во время сборки. Но они недоступны в контейнере. Однако через ARG можно задавать значения переменных по умолчанию для ENV

🧱 Как работают слои в Docker

Docker использует многоуровневую файловую систему (Union FS) — это означает, что каждый RUN, COPY, ADD создаёт отдельный слой, который накладывается поверх предыдущих.

✔️ Основные моменты:

• Каждый слой неизменяемый (immutable). • Новый слой ссылается на предыдущие и добавляет или переопределяет файлы. • Итоговый образ — это stack слоёв:

FROM python:3.10      # базовый слой
RUN apt install curl  # + новый слой с curl
COPY . /app           # + слой с приложением

🔗 Где тут хардлинки и симлинки?

⚙️ При сборке Docker образа:

• Docker использует copy-on-write и может применять хардлинки на уровне хостовой файловой системы (например, OverlayFS).

• Это позволяет экономить диск — одинаковые файлы не дублируются, а представляются как ссылки на один и тот же inode.


🧷 Разница между хардлинками и симлинками:

Тип ссылки Хардлинк Симлинк
Что ссылается На inode (сам файл) На путь (имя файла)
После удаления оригинала Файл остаётся Ссылка “битая”
Видна в ls -li Да, имеет одинаковый inode Нет, свой inode
Используется в Docker Да, но внутренне, на уровне слоёв Иногда в COPY или вручную

📁 Пример использования симлинков в Docker

RUN ln -s /usr/bin/python3 /usr/bin/python

• Создаётся симлинк внутри образа.

• Работает как обычный путь, но при копировании между слоями — это просто символическая ссылка.


🧠 Почему это важно на собеседовании

Ты можешь сказать примерно так:

Docker использует слоистую файловую систему, где каждый слой строится на предыдущем. Это обеспечивает кэширование и повторное использование слоёв. На уровне системы, Docker может использовать хардлинки между слоями, чтобы экономить диск и ускорить сборку. Симлинки используются внутри образов, но не влияют на кэш слоёв напрямую.


💡 Хинт для оптимизации:

Чтобы кэш Docker работал эффективно, нужно:

• Сначала ставить зависимости (requirements.txt) → потом копировать исходники.

• Объединять RUN-команды.

• Удалять ненужные файлы внутри одного слоя.

Хочешь, покажу пример Dockerfile до и после оптимизации с учётом слоёв и кэша?


Bash

Обработка access логов (CSV): timestamp, status, url, user-agent

Количество уникальных URL:

cut -d',' -f3 access.csv | sort | uniq | wc -l

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

cut -d',' -f3 access.csv | sort | uniq -c | sort -nr

Количество 400-404 по каждому URL:

awk -F',' '$2 ~ /^40[0-4]$/ {print $3}' access.csv | sort | uniq -c | sort -nr

Что делает set -eu -o pipefail?

Используется в начале bash-скриптов для надёжности: - set -e — завершает скрипт, если любая команда завершилась с ошибкой. - set -u — завершает скрипт, если используется необъявленная переменная. - set -o pipefail — если одна из команд в пайпе завершилась с ошибкой, скрипт завершится. - set -x (дополнительно) — выводит команды в stdout перед выполнением (для отладки).

Как выполнить одну и ту же команду на 50 серверах?

Варианты: - С помощью ssh в цикле:

for host in $(cat hosts.txt); do
  ssh user@$host 'команда' &
done
wait
  • Использовать pssh (parallel-ssh):
pssh -h hosts.txt -l user -i 'команда'
  • Использовать Ansible:
ansible all -i hosts.ini -m shell -a 'команда'
  • Использовать tmux, dsh, fabric и другие инструменты массового управления.

Балансировщики нагрузки

Зачем нужны?

  • Распределение нагрузки между приложениями
  • Высокая доступность и отказоустойчивость
  • Масштабируемость приложений

Nginx

Ошибки по статус-кодам

  • 5xx — ошибка на стороне сервера (например, 500)
  • 4xx — ошибка на стороне клиента (например, 400, 404, 413 — превышен размер запроса)

HTTP-методы, изменяющие состояние

  • POST, PUT, DELETE

Как Nginx решает проблему C10K?

За счёт событийно-ориентированной архитектуры: - неблокирующая модель - master + многопроцессная модель workers - каждый worker — однопоточный, обрабатывает тысячи соединений - минимизация переключений контекста (в отличие от Apache)

Важные параметры

worker_processes auto;
worker_connections 1024;
# max_clients = worker_processes * worker_connections

Как выбирается location?

Порядок: 1. = — точное совпадение 2. ^~ — приоритетный префикс 3. ~ / ~* — регулярные выражения (учитывает/не учитывает регистр) 4. Без модификаторов — префиксные совпадения

Как дебажить location?

  • Логи: access.log, error.log
  • Временный вывод переменных через return, add_header
  • Консоль разработчика браузера
  • Проверка фактического URI и проксируемого пути

Что реально приходит на приложение?

Проверить на стороне приложения (например, на localhost:3000) фактический URL запроса, заголовки и путь (X-Forwarded-For, X-Real-IP, Host)

Преимущества Nginx перед Apache

  • Лучшая производительность при отдаче статики (x2.5)
  • Эффективная архитектура (event loop против процессов)
  • Меньшее потребление памяти
  • Часто используется как frontend прокси к Apache/php-fpm и др.

Могут ли работать вместе?

Да. Пример связки: - Nginx — фронтенд, отдает статику, проксирует - Apache — бэкенд, генерирует динамику

HAProxy

  • Высокопроизводительный L4/L7-балансировщик
  • Поддержка TCP и HTTP
  • Поддержка ACL, health checks, sticky sessions
  • Часто используется в продакшене как фронтенд балансер

Envoy

  • Прокси и сервис-меш от Lyft
  • Поддерживает HTTP/2, gRPC, TLS termination
  • Используется в Istio
  • Расширенные возможности маршрутизации и мониторинга

Google Cloud Load Balancer

  • Балансировка на уровне L4 и L7
  • Автоматическое масштабирование и Anycast IP
  • Интеграция с Cloud CDN, Cloud Armor
  • Управляется через GCP Console или gcloud

AWS Load Balancers

Classic Load Balancer (CLB)

  • Устаревший, L4/L7

Application Load Balancer (ALB)

  • L7 балансировка (HTTP/HTTPS)
  • Поддержка path-based, host-based routing

Network Load Balancer (NLB)

  • L4 балансировка (TCP/UDP)
  • Высокая пропускная способность и низкая задержка

Обычно используется вместе с Auto Scaling и ECS/EKS

Прокси vs Реверс-прокси

Что такое прокси (forward proxy)?

Прокси-сервер действует от имени клиента: - Клиент отправляет запрос не напрямую к целевому серверу, а через прокси. - Прокси перенаправляет запрос и возвращает ответ обратно клиенту. - Пример: сотрудник офиса выходит в интернет через корпоративный прокси-сервер.

Используется для: - Контроля доступа (фильтрация) - Кеширования - Анонимизации - Обхода блокировок

Что такое реверс-прокси (reverse proxy)?

Реверс-прокси действует от имени сервера: - Клиент делает запрос на один публичный адрес. - Реверс-прокси принимает запрос и перенаправляет его одному из внутренних серверов. - Ответ возвращается клиенту от имени прокси.

Используется для: - Балансировки нагрузки - SSL termination - Кеширования - Ограничения доступа и аутентификации - Защиты внутренних сервисов

Сравнение

Характеристика Прокси Реверс-прокси
Работает для кого? От имени клиента От имени сервера
Кто знает о нём? Клиент Сервер (а клиент — нет)
Основная задача Доступ к внешним ресурсам Защита и маршрутизация к внутренним ресурсам
Пример Squid Nginx, HAProxy, Envoy
## Kubernetes

Architecture

Kubernetes-кластер построен по распределённой архитектуре и состоит из двух основных типов компонентов: Control Plane (плоскость управления) и Worker Nodes (рабочие узлы). Эти части взаимодействуют между собой, обеспечивая автоматизированное развертывание контейнеров, масштабирование, балансировку нагрузки и восстановление после сбоев.

  • Control Plane — отвечает за глобальное управление кластером. Он принимает решения о размещении Pod’ов, отслеживает состояние системы и обеспечивает соответствие текущего состояния — желаемому. Включает в себя несколько ключевых компонентов:

    • kube-apiserver — центральная точка входа в кластер. Обрабатывает все REST-запросы (kubectl, контроллеры и т.д.), валидирует их и взаимодействует с другими компонентами через API.
    • etcd — распределённое key-value хранилище, где сохраняется всё состояние кластера: манифесты объектов, конфигурации, секреты и др.
    • kube-scheduler — определяет, на какие ноды размещать новые Pod’ы, основываясь на доступных ресурсах и заданных правилах.
    • kube-controller-manager — управляет контроллерами (Deployment, ReplicaSet, Node и др.), следя за тем, чтобы система соответствовала желаемому состоянию.
    • (опционально) cloud-controller-manager — взаимодействует с API облачного провайдера (например, AWS или GCP), управляя такими ресурсами, как балансировщики, маршруты и диски.
  • Worker Nodes — это узлы, на которых фактически запускаются контейнеры. Каждый node включает:

    • kubelet — агент, взаимодействующий с API Server и отвечающий за запуск и мониторинг Pod’ов на узле.
    • container runtime (например, containerd, cri-o) — низкоуровневый компонент, запускающий контейнеры.
    • kube-proxy — обеспечивает сетевую маршрутизацию между Pod’ами и доступ к сервисам, настраивая правила iptables/ipvs.

Таким образом, Control Plane управляет кластером как системой, а Worker Nodes исполняют рабочие нагрузки (контейнеры), подчиняясь этим правилам и решениям.


Что происходит при kubectl apply

  1. 💬 Клиент kubectl считывает манифест:

bash kubectl apply -f my-deployment.yaml

Считывается YAML-файл, описывающий желаемое состояние объекта (например, Deployment, Service и т.д.)

  1. 📤 kubectl отправляет запрос в API Server:

Манифест отправляется через HTTP-запрос к API Server — это центральная точка взаимодействия в Kubernetes.

  1. 🔍 API Server сравнивает текущее и желаемое состояние:

  2. API Server проверяет, существует ли объект уже в etcd (через свой внутренний слой хранения).

  3. Если объект уже существует — выполняется \"patch\" (обновление), сохраняются только изменения.
  4. Если объекта ещё нет — он создаётся с нуля.

kubectl apply использует \"server-side apply\" или \"client-side apply\" (в зависимости от версии и флагов) — это способ вычислить, какие поля нужно обновить.

  1. 📦 etcd обновляет хранилище состояния:

API Server сохраняет обновлённое состояние объекта в etcd — распределённой key-value базе.

  1. ⚙️ Контроллеры приводят кластер к нужному состоянию:

Если применён объект типа Deployment: - Deployment Controller обнаруживает изменения. - Создаёт или удаляет ReplicaSet. - ReplicaSet создаёт или удаляет Pod'ы.

  1. 🔁 Scheduler размещает новые Pod'ы:

Если нужно создать новые Pod'ы: - Scheduler выбирает подходящий Worker Node. - Назначает Pod на выбранный узел.

  1. 🧩 Kubelet запускает контейнеры:

На выбранной ноде: - Kubelet получает инструкции от API Server. - Запускает контейнеры через Container Runtime (например, containerd, cri-o, Docker).

  1. 🌐 Kube Proxy настраивает сеть:

Если объект включает в себя сервис или взаимодействие по сети: - Kube Proxy обновляет сетевые правила (iptables/ipvs). - Обеспечивает доступ к Pod'ам по сервису.

QOS (Pod Quality of Service Classes)

QoS-класс определяет приоритет Pod’а при вытеснении (eviction), а также влияет на поведение в случае давления по CPU/памяти. (QoS) в Kubernetes — это один из ключевых механизмов, который влияет на то, как kubelet управляет Pod-ами, особенно при нехватке ресурсов.

В Kubernetes есть три класса QoS:

1. Guaranteed

Наивысший приоритет, вытесняется в последнюю очередь.

Условия:

Для всех контейнеров в Pod-е заданы requests и limits, и они равны друг другу.

resources:
  requests:
    memory: "512Mi"
    cpu: "500m"
  limits:
    memory: "512Mi"
    cpu: "500m"

Такой Pod получает гарантированный кусок ресурсов и защищён от вытеснения при OOM (Out of Memory) до последнего.

2. Burstable

⚠️ Средний приоритет.

Условия:

• У некоторых контейнеров заданы requests, и:

• либо они не равны limits,

• либо limits не заданы вовсе.

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "1"

Такой Pod получает доступ к гарантированным ресурсам (requests), но может использовать больше при наличии свободных ресурсов на узле.


3. BestEffort

Наименьший приоритет, вытесняется первым.

Условия:

• У ни одного контейнера нет requests и limits.

resources: {}

Этот Pod работает «на удачу» — использует то, что осталось на узле. Он первым попадает под OOM killer.


Как проверить QoS класс у уже запущенного Pod?

kubectl get pod <pod-name> -o jsonpath='{.status.qosClass}'

Управление шедулингом в Kubernetes: Tolerations, Affinity, Labels и назначение Pod'ов на ноды

В Kubernetes автоматическое размещение Pod'ов на ноды происходит через scheduler. Чтобы управлять этим процессом, можно использовать labels, tolerations, taints, node affinity, pod affinity/anti-affinity.

1. Labels (лейблы)

Лейблы — это пары ключ/значение, которые можно назначать как Pod'ам, так и нодам. Они служат основой для: - выбора подходящих нод (через nodeAffinity); - группировки и фильтрации ресурсов (например, при kubectl get pods -l app=frontend); - определения соседства между Pod'ами (podAffinity, antiAffinity); - назначения сервисов (selector).

metadata:
  labels:
    app: \"frontend\"
    disktype: \"ssd\"

2. Taints и Tolerations

  • Taint — это метка на ноде, которая не позволяет размещать на ней Pod'ы, если только у Pod'а нет соответствующей toleration.
  • Используются для защиты нод, на которых должны работать только определённые приложения (например, GPU-ноды или системные).
kubectl taint nodes my-node key=value:NoSchedule
  • Это значит: не размещай Pod'ы на my-node, если у них нет toleration для key=value.

Пример Toleration в Pod-манифесте:

tolerations:
  - key: \"key\"
    operator: \"Equal\"
    value: \"value\"
    effect: \"NoSchedule\"

3. Node Affinity

Позволяет предпочесть или требовать размещение Pod'а на ноде с определёнными лейблами.

Типы Node Affinity: - requiredDuringSchedulingIgnoredDuringExecutionобязательное условие. - preferredDuringSchedulingIgnoredDuringExecutionжелательное, но не строгое.

Пример:

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: \"disktype\"
              operator: \"In\"
              values:
                - \"ssd\"

Этот Pod может быть размещён только на нодах с меткой disktype=ssd.

4. Pod Affinity / Anti-Affinity

Управление тем, рядом с какими Pod'ами может (или не может) быть размещён новый Pod. Использует лейблы других Pod'ов.

Pod Affinity (соседство):
affinity:
  podAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
            - key: \"app\"
              operator: \"In\"
              values:
                - \"frontend\"
        topologyKey: \"kubernetes.io/hostname\"

Под будет размещён на той же ноде, где уже есть Pod с меткой app=frontend.

Pod Anti-Affinity (разделение):
affinity:
  podAntiAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
            - key: \"app\"
              operator: \"In\"
              values:
                - \"frontend\"
        topologyKey: \"kubernetes.io/hostname\"

Под не будет размещён на той же ноде, где уже есть другой frontend Pod.

Kubernetes предоставляет гибкие механизмы управления размещением Pod'ов: - Labels — фундаментальный инструмент для выбора, группировки и фильтрации ресурсов. - Taints/Tolerations — позволяют исключать неподходящие ноды. - Affinity/Anti-Affinity — дают контроль над предпочтениями и ограничениями по нодам и другим Pod'ам.

Грамотная настройка этих параметров позволяет добиться надёжности, предсказуемости и оптимального использования кластера.

Pod Lifecycle

Жизненный цикл Pod-а. Это основа для понимания, как Pod-ы создаются, переходят между состояниями, завершаются и удаляются.

🚦 Жизненный цикл Pod-а

📌 Основные состояния (PodPhase):

Это высокоуровневые стадии, которые можно увидеть через kubectl get pod.

  1. Pending — Pod был принят API сервером, но ещё не запущен (например, ждёт скачивания образов или размещения на ноде).
  2. Running — хотя бы один контейнер запущен, и Pod успешно размещён.
  3. Succeeded — все контейнеры завершились успешно (с кодом 0), и Pod не будет перезапускаться.
  4. Failed — хотя бы один контейнер завершился неуспешно, и Pod не может быть перезапущен.
  5. Unknown — kubelet не может определить статус (обычно из-за сетевых проблем).

📌 Состояния контейнеров (ContainerState)

Каждый контейнер внутри Pod-а может быть в:

Waiting — ожидает запуска. Причины можно увидеть в .state.waiting.reason (например, CrashLoopBackOff, ImagePullBackOff).

Running — контейнер успешно запущен.

Terminated — контейнер завершил выполнение (по exit code или сигналу).

Пример:

kubectl get pod mypod -o jsonpath='{.status.containerStatuses[*].state}'

🔁 Restart Policy

Указывается в манифесте Pod-а. Возможные значения:

• Always (по умолчанию, используется в Deployment)

• OnFailure (для Job)

• Never

Это влияет на то, будет ли kubelet перезапускать контейнер при сбое.


🔄 Container Lifecycle Hooks

Можно задать обработчики событий:

lifecycle:
  postStart:
    exec:
      command: ["/bin/sh", "-c", "echo Hello from postStart"]
  preStop:
    exec:
      command: ["/bin/sh", "-c", "echo Goodbye from preStop"]

postStart — вызывается после запуска контейнера, но до его marked as Running. • preStop — вызывается до завершения контейнера (например, при kubectl delete pod или Scale down).


⏱ Termination Grace Period

• По умолчанию: 30 секунд.

• Kubernetes отправляет SIGTERM, ждёт terminationGracePeriodSeconds, затем — SIGKILL.

terminationGracePeriodSeconds: 10

📉 Final Pod Termination Flow

  1. kubelet отправляет SIGTERM контейнерам.
  2. Ждёт terminationGracePeriodSeconds.
  3. Выполняется preStop hook (если есть).
  4. По истечении времени — SIGKILL.
  5. Pod удаляется из etcd (если без finalizer).

preStop && finalizer

Иногда выполняют похожие задачи (что-то “почистить перед удалением”), но они работают на совершенно разных уровнях. Давай разберёмся по-честному, без воды:


🔁 Кратко: разница между preStop и finalizer

preStop hook finalizer
Уровень Уровень контейнера внутри Pod-а Уровень Kubernetes-объекта (Pod, PVC, CRD и т.п.)
Когда вызывается Перед остановкой контейнера (например, SIGTERM) При удалении объекта, но до физического удаления
Кто исполняет kubelet на ноде Контроллер (например, твой оператор/внешний процесс)
Для чего нужен Подготовить контейнер к остановке (напр., завершить соединения) Выполнить финальную логику: удалить ресурсы, почистить связи
Можно ли “зависнуть” Нет — ограничен terminationGracePeriod Да — пока не удалишь finalizer, объект не удалится
Работает при kill -9? ❌ Нет — SIGKILL игнорирует preStop ✅ Да, потому что finalizer выше по уровню

🔍 Примеры сценариев

preStop:

• Подключено 100 клиентов к веб-серверу. preStop может:

• отправить сигнал приложению: «заверши активные соединения»;

• дождаться 10 секунд;

• завершиться сам.

lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "echo 'shutting down' >> /log"]

Это выполняется в контейнере, как скрипт или команда.


finalizer:

• У тебя Pod, при удалении которого нужно удалить запись из внешней БД, освободить IP-адрес, уведомить систему логирования.

• Эти действия нельзя сделать изнутри Pod-а, ты хочешь делать это в контроллере.

metadata:
  finalizers:
    - cleanup.mycompany.com

Пока контроллер не уберёт этот finalizer — Pod не удалится.


🎯 Ключевые различия в использовании

Вопрос Использовать
Нужно что-то сделать внутри контейнера перед его остановкой? ✅ preStop
Нужно удалить внешний ресурс или логически “попрощаться” с объектом на уровне кластера? ✅ finalizer

🩺 Probes

Probes — это специальные проверки, которые Kubernetes выполняет, чтобы понять:

жив ли контейнер (livenessProbe)

готов ли контейнер принимать трафик (readinessProbe)

запустился ли контейнер корректно (startupProbe)

Они помогают kubelet принимать решения:

• Перезапустить ли контейнер?

• Начать ли направлять на него трафик?

• Подождать ли подольше, если сервис стартует долго?


🔍 1. livenessProbe

“Контейнер завис? Надо перезапустить.”

Если livenessProbe неудачна — kubelet перезапускает контейнер.

Пример:

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10

🕒 2. readinessProbe

“Готов ли контейнер принимать запросы?”

Если readinessProbe неудачна — Pod выводится из service endpoints (на него не будет идти трафик).

Пример:

readinessProbe:
  exec:
    command: ["cat", "/tmp/ready"]
  initialDelaySeconds: 2
  periodSeconds: 5

🐢 3. startupProbe

“Дай контейнеру больше времени на запуск — не убивай его раньше времени.”

Полезно для медленно стартующих приложений. До тех пор, пока startupProbe не прошла, Kubernetes не выполняет livenessProbe. То есть она защищает от ложных срабатываний.

startupProbe:
  httpGet:
    path: /startup
    port: 8080
  failureThreshold: 30
  periodSeconds: 10

В этом примере даётся ~5 минут на запуск (30 × 10 секунд).


🔧 Типы probe’ов:

• httpGet — делает HTTP GET-запрос и проверяет код ответа (200-399 — успех).

• exec — выполняет команду внутри контейнера.

• tcpSocket — проверяет TCP-порт на доступность.


🧠 Когда использовать что:

Задача Использовать
Перезапустить зависший контейнер livenessProbe
Управлять трафиком к Pod’у readinessProbe
Дать приложению время на запуск startupProbe
#### В какой последовательности работают пробы (startupProbe, readinessProbe, livenessProbe)

✅ Очередность и логика работы

  1. startupProbe — первая запускается

• Используется, чтобы дать приложению время “проснуться” (например, если долго стартует).

• Пока startupProbe не успешна, ни liveness, ни readiness не запускаются.

• Если startupProbe проваливается, контейнер считается “неживым” и перезапускается.

  1. readinessProbe — включается после startupProbe

Проверяет, готов ли контейнер обслуживать запросы (например, подключился к БД, прогрел кэш и т.д.).

• Пока readinessProbe не проходит, Pod не получает трафик от сервисов.

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

  1. livenessProbe — также запускается после startupProbe

• Проверяет, жив ли контейнер (например, не завис ли, не утёк ли в вечную обработку).

• Если livenessProbe проваливается, контейнер перезапускается.


📌 Важные детали

• После того как startupProbe прошла, она больше не запускается.

• readinessProbe и livenessProbe продолжают выполняться периодически всё время, пока контейнер жив.

• readinessProbe может возвращать “не готов”, например, если БД недоступна — и в это время сервис перестаёт слать трафик на этот pod.

• livenessProbe провалилась → kubelet рестартит контейнер.


🔁 Тайминги

Ты можешь задать параметры для всех проб:

• initialDelaySeconds — сколько подождать перед первой проверкой.

• periodSeconds — интервал между проверками.

• timeoutSeconds — сколько ждать ответа.

• failureThreshold — сколько раз подряд должно не пройти, чтобы считать контейнер неработающим.


🧠 На собесе можно сказать:

Kubernetes запускает startupProbe, чтобы убедиться, что контейнер успеет инициализироваться. Пока она не прошла, readiness и liveness не работают. После успешного старта readinessProbe управляет трафиком, а livenessProbe следит, чтобы контейнер не повис. Это разделение позволяет избегать лишних рестартов при долгом старте приложения.

InitContainers

initContainer — это специальный контейнер, который выполняется до основного контейнера в Pod-е. Он завершает свою работу, и только после этого запускаются обычные containers.


🔑 Ключевые особенности:

• Выполняются последовательно, в порядке их объявления. • Каждый должен успешно завершиться (exit 0) — иначе Pod остаётся в состоянии Init. • Имеют отдельные образы и команды от основного контейнера. • Могут монтировать те же тома, что и основной контейнер.


🧪 Зачем использовать initContainers?

• Задержка старта, пока не готов внешний сервис (БД, Redis).

• Копирование файлов в volume перед стартом приложения.

• Генерация конфигураций, ключей, секретов.

• Прогрев кэша или база данных миграций.


🔧 Пример: Ожидание БД перед стартом приложения

apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
spec:
  initContainers:
  - name: wait-for-db
    image: busybox
    command: ['sh', '-c', 'until nc -z db 5432; do echo waiting for db; sleep 2; done']
  containers:
  - name: main-app
    image: myapp:latest
    ports:
    - containerPort: 80

Этот initContainer будет проверять доступность базы данных на порту 5432. Как только она станет доступна — приложение стартует.


📁 Пример: копирование файлов в том

spec:
  volumes:
  - name: config-volume
    emptyDir: {}

  initContainers:
  - name: copy-config
    image: busybox
    command: ['sh', '-c', 'cp /etc/config/* /config/']
    volumeMounts:
    - name: config-volume
      mountPath: /config
    - name: config-source
      mountPath: /etc/config

  containers:
  - name: app
    image: myapp
    volumeMounts:
    - name: config-volume
      mountPath: /app/config

🧠 Советы: • Лучше использовать initContainers, чем городить логику ожидания или подготовки в основном контейнере. • Они хорошо сочетаются с emptyDir и другими volume-типами. • Можно использовать разные образы, в т.ч. alpine, busybox, curlimages/curl для утилит.

Service Mesh

Это слой инфраструктуры, который управляет сетевыми коммуникациями между сервисами в распределённой системе.

Он отвечает за:

• 📦 Service-to-Service communication (обмен трафиком) • 🔒 Шифрование трафика (MTLS) • 🎯 Routing & Canary (маршрутизация, A/B, Canary) • 📊 Наблюдаемость (метрики, трассировки, логи) • 🔁 Retry, timeout, circuit breaker (механики надёжности) • 🚦 Policy control (авторизация, ACL)


🔧 Как работает?

Ключевая идея — Sidecar Proxy.

Каждому Pod-у автоматически добавляется прокси-контейнер (обычно Envoy), через который весь трафик между сервисами идёт:

[service A] <=> [Envoy A] <===> [Envoy B] <=> [service B]

Управляется централизованно через control-plane (например, Istiod в Istio).


🎯 Зачем нужен?

Без Service Mesh С Service Mesh
Сам пишешь retry, timeout Задаёшь в YAML, работает из коробки
TLS надо встраивать в код MTLS автоматом между сервисами
Логика маршрутизации в коде Traffic shifting через CRD
Метрики в app Авто-интеграция с Prometheus/Grafana/Jaeger
Manual discovery Встроенное сервис-обнаружение

🛠 Примеры популярных Service Mesh

Istio (де-факто стандарт, мощный, но тяжеловат)

• Control-plane: istiod

• Data-plane: Envoy

• CRD’ы: VirtualService, DestinationRule, Gateway, AuthorizationPolicy, и др.

• Сильная поддержка MTLS, трассировки, observability.


🧪 Пример: управление маршрутом в Istio

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews-route
spec:
  hosts:
    - reviews
  http:
    - route:
        - destination:
            host: reviews
            subset: v1
          weight: 90
        - destination:
            host: reviews
            subset: v2
          weight: 10

90% трафика пойдёт в v1, 10% — в v2. Это пример canary deployment без изменения кода.


🔐 MTLS — всё шифруется автоматически

Трафик между Pod-ами автоматически зашифрован, и можно включать политику Zero Trust — только определённые сервисы могут общаться.

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
spec:
  mtls:
    mode: STRICT

📊 Observability из коробки

Сбор метрик, логов, трассировок:

• 📈 Prometheus / Grafana

• 🔎 Kiali (визуализация mesh-а)

• 🧵 Jaeger / Zipkin (трассировки)

🔧 Service

Тип Внешний доступ Где используется Пример подключения
ClusterIP ❌ Нет Внутри кластера curl my-svc.default.svc.cluster.local
NodePort ✅ Да Bare-metal, Minikube curl http://<\NodeIP>:30080
LoadBalancer ✅ Да (облако) GKE, EKS, AKS, MetalLB curl http://\<\EXTERNAL-IP>
ExternalName ⚠️ Через DNS Доступ к внешним сервисам curl http://external-service.local (CNAME)
Headless ❌ Нет (IP нет) StatefulSet, Service Discovery dig my-svc.default.svc.cluster.local → IP подов

Это базовый ресурс Kubernetes, который обеспечивает:

• сервис-дискавери по имени (DNS), • стабильный IP, • маршрутизацию трафика на Pod-ы по label’ам.

apiVersion: v1
kind: Service
metadata:
  name: reviews
spec:
  selector:
    app: reviews
  ports:
    - port: 80
      targetPort: 8080

Все Pod-ы с app=reviews получат трафик через этот Service.


🧱 ClusterIP (по умолчанию)

apiVersion: v1
kind: Service
metadata:
  name: my-svc
spec:
  selector:
    app: my-app
  ports:
    - port: 80        # Внутри кластера
      targetPort: 8080
  • Описание: Доступен только внутри кластера.
  • Применение: Для связи между подами разных приложений.
  • Пример использования: Бэкенд-сервис, к которому обращается фронтенд.
spec:
  type: ClusterIP

🌐 NodePort

apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-svc
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080

Доступ: http://<NodeIP>:30080

  • Описание: Открывает порт на всех нодах кластера.
  • Доступ извне: Да, по IP-адресу ноды и указанному порту.
  • Диапазон портов: 30000–32767 (по умолчанию).
  • Применение: Простой способ опубликовать приложение наружу без Ingress.

🌎 LoadBalancer

apiVersion: v1
kind: Service
metadata:
  name: my-lb-svc
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080

В облаках — выдаст EXTERNAL-IP через kubectl get svc

  • Описание: Создает внешний балансировщик нагрузки (обычно в облаке).
  • Применение: Доступ к сервису из интернета, production-ready подход.
  • Зависимость: Работает при наличии интеграции с облачным провайдером (GKE, EKS, AKS и др).

🔗 ExternalName

spec:
  type: ExternalName
  externalName: my.external.service.com
  • Описание: Проксирует запросы к внешнему DNS-имени.
  • Применение: Используется для обращения к внешним сервисам через DNS, как будто это обычный сервис в кластере.
  • Особенность: Не создает собственный IP, работает через CNAME.

🧪 Headless Service

spec:
  clusterIP: None
  • Описание: Не назначает IP-адрес; возвращает список IP подов.
  • Применение: Используется для сервисов, где важен прямой доступ к подам, например, StatefulSet, сервис discovery.
  • Особенность: Вместо баланса — полное управление с клиентской стороны.

🔧 Дополнительные сценарии

Не являются новыми типами, но часто используются

  • Ingressне сервис, а контроллер маршрутизации. Используется для HTTP/HTTPS входа и более гибкого роутинга.
  • Service Mesh (например, Istio, Linkerd) — добавляют абстракции над сервисами, например, виртуальные сервисы, маршруты на уровне L7.
  • MetalLB / Kube-vip — расширяют возможности LoadBalancer в on-premise кластерах, где нет встроенного облачного балансировщика.

🕹 Istio VirtualService

Это ресурс уровня Istio, который определяет:

Как именно маршрутизировать трафик внутри Service • Делать canary, A/B, header-based routing и т.д. • Управлять retry, timeout, fault injection и т.п.

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: reviews-routing
spec:
  hosts:
    - reviews
  http:
    - match:
        - headers:
            end-user:
              exact: test-user
      route:
        - destination:
            host: reviews
            subset: v2
    - route:
        - destination:
            host: reviews
            subset: v1

Это правило: если юзер — test-user, отдать reviews:v2, иначе — v1.


🔄 Как они работают вместе

• Service — отвечает за “куда”: набор Pod-ов (endpoints)

• VirtualService — отвечает за “как”: правила маршрутизации, версии, условия

Трафик идёт так:

Client → Kubernetes Service → Istio Proxy (Envoy) → VirtualService → Target Pod

📦 А что такое subset?

Чтобы VirtualService мог выбрать v1, v2, ты должен создать DestinationRule:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: reviews-dest
spec:
  host: reviews
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

Теперь Istio сможет направлять трафик только на те Pod-ы, у которых version: v1 или v2.


Ingress

Kubernetes ресурс, который описывает правила входа HTTP/HTTPS-трафика в кластер и маршрутизацию к сервисам.

Пример:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
    - host: mysite.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-service
                port:
                  number: 80

📌 Но сам Ingress ничего не делает без контроллера!

🎮 Ingress Controller — что это?

Приложение (под/деплоймент), которое читает ресурсы Ingress и настраивает прокси-сервер (например, NGINX) для обработки внешнего трафика.

Популярные контроллеры:

• nginx-ingress ✅ (де-факто стандарт)

• traefik

• HAProxy

• Istio Gateway (в рамках Istio)

• AWS ALB Controller (в AWS)


🔁 Как это работает:

[Браузер] 
  ↓ HTTP/HTTPS
[Ingress Controller Pod (например, NGINX)]
  ↓
[Ingress rules → Service → Pod]

📌 Что делает Ingress:

• Правила маршрутизации (host + path)

• Поддержка HTTPS (через TLS-секреты)

• Может использовать аннотации для настроек (timeouts, redirects и т.д.)

📌 Что делает Ingress Controller:

• Слушает порт (80/443)

• Считывает Ingress-объекты

• Настраивает внутренний прокси (например, nginx.conf)

• Обрабатывает входящие соединения


🧠 Кратко:

Компонент Роль
Ingress Правила маршрутизации
Ingress Controller Прокси, который эти правила реализует

ReplicaSet (низкоуровневый)

Поддерживает заданное количество Pod-ов. Сам по себе редко используется напрямую.

apiVersion: apps/v1
kind: ReplicaSet
spec:
  replicas: 3

• Обеспечивает: N реплик • Не поддерживает обновления, стратегии, rollback • Используется внутри Deployment

📌 Не используй напрямую — почти всегда используешь Deployment.


Deployment (стандарт для stateless приложений)

Самый популярный контроллер. Обеспечивает масштабируемость, обновления, откат.

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 3
  strategy:
    type: RollingUpdate

• Позволяет обновлять версии без даунтайма

• Есть стратегии обновления (RollingUpdate, Recreate)

• Поддерживает rollback, pause, resume

📌 Используется для web-приложений, API, сервисов.


DaemonSet

Запускает по одному Pod-у на каждой Node.

apiVersion: apps/v1
kind: DaemonSet

• Полезен для: логгера, мониторинга, агента (Prometheus, Fluentd)

• Новые ноды автоматически получают Pod

• Без replicas — он сам знает, сколько нод

📌 Используется для инфраструктурных компонентов.


StatefulSet

Используется для stateful-приложений (БД, кэш, очереди), где важен порядок и имена Pod-ов.

apiVersion: apps/v1
kind: StatefulSet
spec:
  serviceName: my-headless

• Поддерживает стабильные имена Pod-ов: mongo-0, mongo-1

• Использует PersistentVolumeClaim templates

• Запускает/удаляет Pod’ы поочерёдно

📌 Используется для: PostgreSQL, Cassandra, Kafka, Redis Cluster


Job

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

apiVersion: batch/v1
kind: Job
spec:
  completions: 1

• Создаёт Pod для выполнения задачи

• Повторяет, если задача неуспешна

• Сам удаляется по завершении (или можно сохранить логи)

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


CronJob

Периодически запускает Job по расписанию (cron-выражение)

apiVersion: batch/v1
kind: CronJob
spec:
  schedule: "*/5 * * * *"

📌 Используется для: бэкапов, проверок, периодических отчётов


Pod controllers comparation

Контроллер Масштабируемость Хранит стейт Периодичность Одна задача Пример использования
Deployment ✅ Да ❌ Нет ❌ Нет ❌ Нет web-app, API, worker
ReplicaSet ✅ Да ❌ Нет ❌ Нет ❌ Нет (внутри Deployment)
DaemonSet ❌ 1 на Node ❌ Нет ❌ Нет ❌ Нет node exporter, log-агенты
StatefulSet ✅ Да ✅ Да ❌ Нет ❌ Нет PostgreSQL, Redis cluster
Job ❌ (один запуск) ❌ Нет ❌ Нет ✅ Да миграция, ETL, init-task
CronJob ❌ Нет ✅ Да ✅ Да бэкап каждые N минут

PVC

PVC (PersistentVolumeClaim) — это заявка от Pod-а на хранилище. Это как “бронирование диска”: ты говоришь, какой объём и доступ тебе нужен — а Kubernetes сам выделяет подходящее хранилище (PersistentVolume) или создаёт его (в случае dynamic provisioning).


🧱 Компоненты хранения в Kubernetes

  1. PersistentVolume (PV) — физический или логический диск (долговечный том).
  2. PersistentVolumeClaim (PVC) — запрос от Pod-а: “дай мне диск на 1Gi, с RW доступом”.
  3. StorageClass — шаблон, описывающий, как именно создавать PV (например, тип диска в облаке: SSD, HDD, reclaim policy и т.п.).

🔄 Жизненный цикл: как всё связано

Pod → PersistentVolumeClaim (PVC) → PersistentVolume (PV) → Physical disk (GCE, EBS, NFS, Ceph, etc)

🔧 Пример 1: Простой PVC + Pod

📄 PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

📄 Под, использующий этот PVC:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: app
      image: nginx
      volumeMounts:
        - name: my-storage
          mountPath: /usr/share/nginx/html
  volumes:
    - name: my-storage
      persistentVolumeClaim:
        claimName: my-pvc

📦 Контейнер получает путь /usr/share/nginx/html, примонтированный из PVC.


📚 Ключевые параметры PVC

Параметр Значение
storage Сколько нужно (например, 1Gi)
accessModes ReadWriteOnce, ReadOnlyMany, ReadWriteMany
volumeMode Filesystem (по умолчанию) или Block
storageClassName Какой StorageClass использовать

🔁 Static vs Dynamic provisioning

Тип Что происходит
Static Админ заранее создаёт PV, ты делаешь PVC и ждёшь совпадения по параметрам
Dynamic PVC автоматически вызывает StorageClass, который создаёт PV на лету (например, EBS в AWS, Persistent Disk в GCP)

📄 Пример: PVC с StorageClass

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: fast-storage
spec:
  storageClassName: "fast-ssd"
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

🧠 Как посмотреть:

kubectl get pvc
kubectl describe pvc my-pvc
kubectl get pv

Статус PVC должен быть Bound, если всё ок.


🗑 Reclaim Policy (что делать после удаления PVC)

• Delete — PV будет удалён вместе с PVC (часто в динамическом сценарии)

• Retain — диск останется, нужно очищать вручную

• Recycle (устаревший)


📦 Где используются PVC?

• Базы данных (PostgreSQL, MongoDB)

• CMS (WordPress)

• Кэш (Redis с persistence)

• Хостинг статики в Nginx

• Kafka, MinIO, Ceph, и т.д.


GitOps vs Helm

🎯 Суть обоих подходов:

Подход Что делает
Helm CLI Ты вручную применяешь Helm чарты с локальной машины или CI/CD
GitOps Ты хранишь всё в Git, а оператор в кластере сам синхронизирует состояние

⚙️ Как работает каждый подход

📦 1. Helm deployment (классический)

• Ты запускаешь команду:

helm upgrade --install my-app ./chart --values values.yaml

• Или через CI (например, GitLab CI).

• Helm устанавливает/обновляет всё напрямую в кластер.

Состояние — в Helm и в кластере, не обязательно в Git.

🔻 Проблемы:

• Нет 100% гарантий, что Git = кластер.

• Нужно самому следить за порядком обновлений.

• Можно “вмешаться вручную” и не зафиксировать в Git.


🔁 2. GitOps (ArgoCD, FluxCD)

• Ты кладёшь манифесты или Helm values в Git.

• Git = источник правды.

• В кластере работает оператор (ArgoCD/Flux), который:

• Смотрит в Git

• Применяет манифесты/Helm чарты в кластер

• Следит, чтобы кластер всегда соответствовал Git

Git → автосинхронизация → кластер


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

Категория GitOps Helm (ручной)
🧠 Источник правды Git Helm state / CI конфигурация / память
🔁 Автоматичность Да (pull model) Нет (push model)
🔒 Безопасность доступа Не нужен доступ к кластеру — только Git Требуется доступ к кластеру
🛠 Откат изменений Git revert → автооткат Надо вручную запускать helm rollback
👀 Observability UI (ArgoCD/Flux) с дифами и статусами helm status, logs
✍️ Изменения вне Git Автоматически возвращаются к Git-состоянию Сохраняются (drift!)
🔁 Частота обновлений Постоянная синхронизация Только по запуску команды

🤖 ArgoCD + Helm = ❤️

Ты можешь использовать Helm внутри GitOps, просто зафиксировав чарты и values в Git:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
spec:
  source:
    repoURL: https://github.com/my-org/my-repo
    targetRevision: main
    path: charts/my-app
    helm:
      valueFiles:
        - values-prod.yaml

Таким образом, ты не теряешь гибкость Helm, но получаешь автоматизацию GitOps.


💡 Когда использовать что?

Сценарий Подход
Маленький pet-проект Helm CLI
Команда/прод/много окружений GitOps
Требуется аудит, откат, видимость GitOps
Развёртывание из CI (ручной контроль) Helm CLI
Много компонентов, инфраструктура как код GitOps

RBAC

RBAC (Role-Based Access Control) — это система контроля доступа в Kubernetes, основанная на ролях. Она позволяет разграничить, кто что может делать в кластере.

RBAC управляется через четыре основных ресурса:

• Role • ClusterRole • RoleBinding • ClusterRoleBinding

🧱 Основные сущности

Объект Где действует Что делает
Role в пределах namespace Определяет разрешения на действия
ClusterRole действует кластерно Может применяться глобально или к namespace
RoleBinding в пределах namespace Назначает Role пользователю/группе
ClusterRoleBinding глобально Назначает ClusterRole пользователю/группе на весь кластер или несколько NS

📌 Пример 1: Простой Role и RoleBinding
# role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: dev
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
# rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: dev
subjects:
- kind: User
  name: alice
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

📌 Это даёт пользователю alice доступ на чтение подов в namespace dev.


📌 Пример 2: ClusterRole + ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: node-reader
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "watch", "list"]
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: read-nodes
subjects:
- kind: User
  name: bob
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: node-reader
  apiGroup: rbac.authorization.k8s.io

📌 Пользователь bob получает доступ на чтение нод во всём кластере.


🧠 Что можно указывать в rules?
rules:
- apiGroups: ["", "apps", "batch"]
  resources: ["pods", "deployments", "jobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

• apiGroups: например, "" — core, "apps" — для deployments и т.д.

• resources: "pods", "services", "configmaps" и т.д.

• verbs: "get", "list", "create", "delete", "update", "patch"…


🔍 Проверка доступа

Хочешь понять, имеет ли пользователь/сервис-аккаунт доступ?

kubectl auth can-i get pods --as alice --namespace=dev

или для сервис-аккаунта:

kubectl auth can-i create deployments --as=system:serviceaccount:dev:sa-name

⚠️ RBAC и сервис-аккаунты

Для pod’ов, которые взаимодействуют с Kubernetes API, используется сервис-аккаунт (SA).

📌 Назначение ClusterRole на SA:

subjects:
- kind: ServiceAccount
  name: my-sa
  namespace: dev

🔐 Используется, например, для Prometheus, ArgoCD, Jenkins, etc.


🎯 Частые паттерны:

• view, edit, admin — встроенные ClusterRole • Least Privilege — даём только то, что нужно • aggregated ClusterRole — сборка нескольких прав в одну роль • Используй kubectl auth reconcile для безопасного обновления RBAC

🔐 1. Предустановленные роли: view, edit, admin

Kubernetes из коробки предоставляет несколько встроенных ClusterRole, которые ты можешь сразу привязать:

Роль Что умеет?
view Только чтение (get, list, watch)
edit Чтение и изменение всех ресурсов (кроме RBAC, NS, finalizers)
admin Всё в пределах namespace, включая RBAC (но не ClusterRole)

Пример:

kind: RoleBinding
metadata:
  name: give-access
  namespace: dev
subjects:
- kind: User
  name: alice
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: view
  apiGroup: rbac.authorization.k8s.io

📌 Очень удобно давать людям или бэку доступ “на посмотреть”.


🧃 2. Least Privilege (минимально необходимые права)

⚠️ Золотое правило безопасности: давай минимум, что нужно.

Никаких * в verbs, resources, apiGroups без острой необходимости!

Плохой пример:

verbs: ["*"]
resources: ["*"]
apiGroups: ["*"]

Хороший пример:

verbs: ["get", "list"]
resources: ["pods"]
apiGroups: [""]

📌 Принцип “чем меньше — тем лучше” особенно важен для сервис-аккаунтов CI/CD, мониторинга и автоматизации.


🧬 3. ServiceAccount + RoleBinding: доступ только Pod’ам

Часто сервис в кластере (например, Prometheus, Argo, Karpenter) нуждается в доступе к API. Вместо привязки прав к пользователю — создаём ServiceAccount + RoleBinding.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-agent
  namespace: monitoring
kind: RoleBinding
metadata:
  name: my-agent-binding
  namespace: monitoring
subjects:
- kind: ServiceAccount
  name: my-agent
  namespace: monitoring
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

📌 Затем у pod’а в spec.serviceAccountName: my-agent.


🧩 4. Aggregated ClusterRoles — комбинация нескольких

Можно объединять несколько ClusterRole через метки, а не писать одну большую.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: my-viewer
  labels:
    rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["batch"]
  resources: ["jobs"]
  verbs: ["get", "list", "watch"]

Если в другом месте уже есть ClusterRole с меткой aggregate-to-view=true, то твоя роль автоматически добавится.

Используется во встроенных admin / edit / view.


🧪 5. Dynamic access (например, через GitOps)

Когда используешь ArgoCD, Flux или Helm — можно генерировать RBAC через шаблоны или Git-папки:

• Сервис argo-server использует SA с ClusterRole admin

• Helm chart устанавливает нужные Role/RoleBinding через values.yaml


📁 6. Мульти-Namespace RBAC

Role работает только внутри одного NS. Чтобы дать доступ в несколько NS — используй ClusterRole + несколько RoleBinding:

# reusable ClusterRole
kind: ClusterRole
metadata:
  name: read-pods
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]

# в dev
kind: RoleBinding
metadata:
  name: bind-in-dev
  namespace: dev
subjects:
- kind: User
  name: alice
roleRef:
  kind: ClusterRole
  name: read-pods

# в staging
kind: RoleBinding
metadata:
  name: bind-in-staging
  namespace: staging
subjects:
- kind: User
  name: alice
roleRef:
  kind: ClusterRole
  name: read-pods

🧠 Финальные советы:

• kubectl auth can-i — твой друг.

• Смотри kubectl describe rolebinding и clusterrolebinding — видно, кто к чему привязан.

• Всегда проверяй права через dry-run или в dev-NS.

• GitOps-friendly подход: все роли и биндинги — в yaml и под версионным контролем.


Terraform

Отличие ansible и terraform

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

Ansible - Предназначен для автоматизации развертывания и конфигурации приложений.

То есть Terraform используется что бы разворачивать инфраструктуру (виртуальные машины, сети, диски, и тп.). Ansible предназначен для работы с существующими ресурсами, чтобы устанавливать и настраивать окружение (программы, операционные системы) на этих виртуалках.

Нюансы: Terraform отслеживает состояние и делает инкрементные изменения, что особенно полезно при работе с облачной инфраструктурой. Ansible не управляет состоянием, и каждая задача считается независимой.

Нужно понимать, что это инструменты. И в ряде случаев с помощью Ansible равзвернуть инфраструктуру всё таки можно. Как и для терраформа написать провайдер, который будет делать что-то свое. Вопрос лишь в применимости инструментов.

Что такое Terraform state?

• State-файл (terraform.tfstate) хранит текущее состояние инфраструктуры и сопоставляет описанные в HCL ресурсы с реальными объектами в облаке.

• Это позволяет Terraform определять, какие изменения нужно внести при terraform apply.

  1. Где его хранят?

Локально (terraform.tfstate в рабочей директории) → не подходит для командной работы.

Удаленно (S3, GCS, Azure Blob, Terraform Cloud) → позволяет совместную работу и контроль версий.

  1. Какие проблемы могут возникнуть?

Конфликты при одновременной работе → решается блокировками (dynamodb для S3 backend).

Компрометация чувствительных данных → решается с terraform state encrypt, terraform_remote_state, использование vault.

Потеря state-файла → если удален без бэкапа, теряется возможность управлять ресурсами Terraform. • Если state удален, но инфраструктура еще есть → terraform import поможет. • Если state удален и инфраструктуры нетпотеряно безвозвратно. • Вывод: Обязательно настраивай бэкапы state-файлов! (S3 + DynamoDB, GCS, Azure Blob).

Что такое провайдер в terraform

Это утилита, прослойка, которая позволяет Terraform взаимодействовать с определенным API облачного провадйера.

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

Что такое ресурс в terraform

Это какой то блок кода, который описывает объект в инфраструктуре. Каждый ресурс имеет свои какие то аргументы, свои параметры, свою конфигурацию. Ресурс имеет свой какой то уникальный идентификатор (ID, ARN и тд).

Виртуальная машина, сеть, диск, порт, балансировщик и тд.

Ansible

Основные примитивы Ansible

Инвентарь (Inventory)

  • cписок хостов, может быть статичным в виде текcтового файла в формате .ini или динамическим в виде скрипта или плагина, который подгружает структуру данных из стороннего источника, например, Openstack API или база LDAP.
$ cat hosts
[web]
nginx01
nginx02
nginx03

[mysql]
mysql01
mysql02
mysql03

[prod:children]
web
mysql

[devel]
dev-nginx01
dev-mysql01

Данный пример описывает группы хостов web, mysql, prod и devel, группа prod наследует содержимое групп web и mysql.

Задание (Task)

  • атомарная операция, выполняемая на управляемом хосте, например:
apt:
package: nginx
state: present

Данный пример аналогичен команде apt install nginx

Сценарий (Play или Плейбук (Playbook)

  • сценарий или скрипт, содержащий одно или несколько заданий на выполнение, например:
$ cat prod-playbook.yml
---
- hosts: nginx
tasks:
- apt:
    package: nginx
    state: present
- shell: whoami

Роль (Role)

  • более сложная абстракция, выглядит как структура директорий и файлов, которые описывают набор дефолтных переменных, зависимостей от других ролей, может содержать файлы и темплейты, содержит задания(Tasks).

Факты (Facts)

  • структура данных, которая содержит информацию о хосте, например, версию дистрибутива, IP адреса и файловые системы. Ansible забирает эту информацию с хоста, и на нее можно ссылаться в коде плейбуков и ролей.

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

Порядок приоритета переменных в Ansible

В Ansible переменные можно задавать в разных местах: в инвентаре, в ролях, через set_fact, в playbook, через командную строку и т.д. Если одна и та же переменная определена в нескольких местах — Ansible использует значение с наивысшим приоритетом.

📊 Порядок от низшего к высшему:

  1. defaults в ролях — значения по умолчанию (roles/myrole/defaults/main.yml)
  2. Инвентарь:
  3. переменные в inventory.ini (host var, group:vars)
  4. group_vars/ — переменные для групп
  5. host_vars/ — переменные для конкретных хостов
  6. ansible_facts — автоматически собранные или заданы через set_fact
  7. vars в playbook — переменные внутри vars: блока
  8. vars в task — локальные переменные в задаче
  9. register — результат выполнения задач
  10. --extra-vars — переменные из CLI (самый высокий приоритет)

🧩 Примеры по уровням

1. defaults в ролях (самый низкий приоритет)
# roles/myrole/defaults/main.yml
app_port: 5000
env: \"dev\"
2. Инвентарь
[web]
web01 ansible_host=192.168.0.10 app_port=8081

[web:vars]
region=eu
3. group_vars/ и host_vars/

Файлы:

  • group_vars/web.yml
  • host_vars/web01.yml

Пример:

# group_vars/web.yml
app_port: 8080
env: \"staging\"
# host_vars/web01.yml
app_port: 9090

→ Значение из host_vars переопределит group_vars.

4. ansible_facts (в том числе set_fact)
- name: Set fact
  set_fact:
    app_port: 7070
5. vars в playbook
- hosts: all
  vars:
    app_port: 6060
6. vars в task
- name: Show port
  debug:
    msg: \"Port is {{ app_port }}\"
  vars:
    app_port: 5050
7. register
- name: Get hostname
  command: hostname
  register: hostname_output

→ Используется как {{ hostname_output.stdout }}.

8. --extra-vars (наивысший приоритет)
ansible-playbook site.yml --extra-vars \"app_port=4040 env=prod\"

🧠 Заключение

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

Рекомендации:

  • Используй defaults в ролях для значений по умолчанию.
  • Задавай хост-специфичные данные в host_vars/ и group_vars/.
  • Для временных и приоритетных значений — используй --extra-vars.

Для чего нужны хендлеры, handlers?

Задачу можно дополнить обработчиками, которые будут срабатывать, если задача была выполнена успешно.

Например, мы установили nginx и хотим его запустить

---
- hosts: testbox
  sudo: yes
  tasks:
   - name: Install Nginx
     yum: pkg=nginx state=latest
     notify:
         - NGINX start

  handlers:
    - name: NGINX start
      service: name=nginx state=started

IAC and Идемпотентность

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

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

Например, нам нужно добавить пользователя на хост, для bash это будет выглядеть так:

useradd newuser -ms /bin/bash

Но если мы запустим команду второй раз, то получим ошибку

useradd newuser -ms /bin/bash
useradd: user 'newuser' already exists

Поэтому нам нужно дополнительно добавлять проверку, например так:

id newuser || useradd newuser -ms /bin/bash

В случае Ansible мы только декларируем состояние, например:

- hosts: localhost
  gather_facts: no
  tasks:
    - user:
        name: newuser
        shell: /bin/bash
        state: present

При первом запуске плейбук Ansible ответит:

TASK [user] ***
changed: [localhost] => {
  "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python3"
  },
  "changed": true,
  "comment": "",
  "create_home": true,
  "group": 1001,
  "home": "/home/newuser",
  "name": "newuser",
  "shell": "/bin/bash",
  "state": "present",
  "system": false,
  "uid": 1001
}

При повторном:

TASK [user] ***
ok: [localhost] => {
  "ansible_facts": {
    "discovered_interpreter_python": "/usr/bin/python3"
  },
  "append": false,
  "changed": false,
  "comment": "",
  "group": 1001,
  "home": "/home/newuser",
  "move_home": false,
  "name": "newuser",
  "shell": "/bin/bash",
  "state":
  "present",
  "uid": 1001
}

Обратите внимание, что статус задания сменился с changed на ok и повторный запуск вернул значение "changed": false

Таким образом, сценарии, написанные с учетом идемпотентности, реализуют ту самую декларативность в описании состояния инфраструктуры, а инфраструктура соответствует состоянию, описанному в коде нашего скрипта.

Это и есть IaС - Infrastructure as Code - инфраструктура как код

Для чего нужен ad hoc в ansible?

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