DevOps / SRE — шпаргалка к собеседованию

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

1. Linux (уровень администратора)

Загрузка системы

Последовательность: BIOS/UEFI → загрузчик (GRUB2 / systemd-boot) → ядро + initramfs → PID 1 (systemd) → target. UEFI работает с ESP (FAT32, /boot/efi), видит файлы напрямую; BIOS — через MBR/stage1. initramfs нужен, чтобы смонтировать корень, если на нём LVM/LUKS/NFS/RAID.

systemd: юниты лежат в /lib/systemd/system/ (ванильные) и /etc/systemd/system/ (админские overrides). Таргеты заменяют runlevels: multi-user.target ≈ rl3, graphical.target ≈ rl5. Текущий: systemctl get-default.

systemctl list-units --failed
systemctl cat  myapp.service       # весь собранный unit-файл
systemctl edit myapp.service       # drop-in override (в /etc/...d/override.conf)
journalctl -u myapp -f --since "15 min ago"
journalctl -p err -b               # только errors в текущем boot

Процессы и сигналы

  • fork/exec: fork() клонирует процесс, exec() заменяет адресное пространство образом бинаря. В контейнерах важно, что PID 1 должен обрабатывать SIGTERM и жать ребёнкам (zombie reaping). Для shell-скриптов используют tini/dumb-init.
  • Сигналы: SIGTERM — вежливый (можно обработать, задать timeout), SIGKILL — ядро убивает немедленно (нельзя поймать), SIGHUP — обычно «перечитать конфиг» (nginx, haproxy), SIGINT ≈ Ctrl+C, SIGSTOP/SIGCONT — заморозка.
  • Zombie — дочерний процесс завершился, но родитель не сделал wait() — в PID-таблице остаётся запись; не ест CPU, но ест PID. Orphan — родитель умер, ребёнка усыновляет PID 1. В docker/k8s если контейнер как PID 1 не reap'ит — получаем zombies внутри pod.
ps -eo pid,ppid,stat,cmd   # stat=Z → зомби, stat=D → uninterruptible sleep (обычно IO)
kill -l                    # список сигналов
pgrep -a nginx; pkill -HUP nginx
timeout 30 long-job        # хорошая практика в cron/CI

Права и изоляция

МеханизмДля чегоКак посмотреть
rwx / modeБазовые права user/group/otherls -l, stat
SUID (s вместо x на user)Исполнять с правами владельца (напр. passwd, sudo)find / -perm -4000 2>/dev/null
SGIDНа файле — запускать с группой; на папке — новые файлы наследуют группу-perm -2000
sticky bit (t на other)/tmp — удалять может только владелец-perm -1000
ACLТонкие права на пользователя помимо 3+3+3getfacl, setfacl -m u:deploy:rwx path
capabilitiesРазбить root на гранулы (CAP_NET_BIND_SERVICE, CAP_SYS_ADMIN и т.д.)getcap, setcap cap_net_bind_service+ep /usr/bin/node
umaskМаска вычитается из 0666/0777 при создании файла/каталогаumask (часто 022 → 644/755)

Файловые системы и диск

  • ext4 — дефолт в большинстве Debian/Ubuntu, журналируемая, шустрая, простая. XFS — лучше на больших объёмах и параллельных записях, не умеет shrink. btrfs/ZFS — CoW, снапшоты, но сложнее.
  • inode — запись о файле (метаданные, блоки), имя лежит в каталоге как {name → inode}. Переполнение inode → «No space left on device» при свободных блоках. Смотри df -i.
  • LVM: PV → VG → LV. Снапшоты, resize на лету, thin-provisioning. pvs/vgs/lvs, lvextend -L+10G -r /dev/vg0/data (-r ресайзит ФС сразу).
  • /etc/fstab: UUID → точка монтирования → тип → опции (noatime, nofail — не вешать загрузку, x-systemd.automount).
  • Swap — файл или partition, включать через swapon. vm.swappiness регулирует «охоту» ядра свопить. На БД-хостах часто swappiness=1.
df -hT        # размер + тип ФС
df -i         # inode usage
du -xsh /var/* | sort -h
ncdu /var     # интерактивно «что съело место»
iostat -xz 1  # %util, await, wKB/s — подсветит медленный диск
lsblk -f      # дерево блочных устройств с UUID/ФС

LVM — подробно

LVM — слой между блочными устройствами и файловой системой. Позволяет объединять диски, ресайзить «на лету», делать снапшоты, thin-provisioning, миграцию данных между устройствами без downtime.

Иерархия

УровеньЧто этоКоманды
PV (Physical Volume)Подготовленное блочное устройство: диск целиком (/dev/sdb) или партиция (/dev/sdb1 с типом 8e/lvm). На него кладётся LVM-метадатаpvcreate /dev/sdb, pvs, pvdisplay, pvscan
VG (Volume Group)«Пул» ёмкости из одного или нескольких PV. Единица аллокации — PE (Physical Extent), обычно 4 MiBvgcreate vg0 /dev/sdb, vgs, vgdisplay, vgextend vg0 /dev/sdc
LV (Logical Volume)«Виртуальная партиция» внутри VG. На неё кладётся ФС (ext4/xfs), и она монтируетсяlvcreate -L 50G -n var vg0, lvs, lvdisplay, lvextend, lvreduce
pvs    # PV      VG  Fmt  Attr PSize   PFree
vgs    # VG      #PV #LV  Attr VSize   VFree
lvs    # LV  VG  Attr      LSize   ...
lsblk  # дерево видно с именами LV (/dev/mapper/vg0-var)

Снапшоты и thin pool

  • Thick snapshot: lvcreate -s -L 10G -n snap /dev/vg0/var — CoW копия. Размер снапшота должен вмещать дельту изменений; переполнится — снапшот станет invalid.
  • Thin provisioning: из thin pool выделяются тонкие LV — заявленный размер больше физического. Быстрые снапшоты «бесплатно». Риск: переполнение пула = все thin-LV встанут read-only. Обязателен мониторинг Data% в lvs -a.
  • Striping / mirroring: lvcreate --stripes N (RAID0-подобный, быстрее), lvconvert --type raid1.

Подводные камни

  • ext4 умеет и online grow, и offline shrink. XFSтолько grow, shrink невозможен; если надо уменьшить — backup + mkfs + restore.
  • После lvextend ФС сама не растёт — нужен resize2fs / xfs_growfs. Флаг -r у lvextend делает это автоматически (знает, какой ФС имеем).
  • Корень в LVM — нужен lvm2 в initramfs (обычно уже есть). После ресайза корня всё делается на живую, без перезагрузки.
  • SSD: включить issue_discards=1 в /etc/lvm/lvm.conf, чтобы при lvremove/lvreduce шёл TRIM.
  • Боевой риск с thin pool: забыли настроить алерты → пул заполнился → все БД разом встали. Включить thin_pool_autoextend_threshold + thin_pool_autoextend_percent.

Кейс: «/var забит логами, сервер при смерти»

Фаза 1. Остановить кровотечение (0 ресурсов, критично)

# Смотрим общую картину
df -h
df -i              # и inodes — на мелких файлах inodes закончатся раньше байтов!

# Что именно съело — не уходим в /proc, /sys, бинды
sudo du -xh --max-depth=1 /var 2>/dev/null | sort -h | tail
# Популярные виновники:
#   /var/log (journal, nginx access, приложенческий stdout)
#   /var/lib/docker (слои, volumes, overlay2/diff)
#   /var/lib/containerd
#   /var/cache/apt
#   /var/lib/mysql, /var/lib/postgresql
#   /var/spool (mail, cron, cups)

# journalctl: проверить текущее использование и срезать
journalctl --disk-usage
sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=7d

# «классика»: большой файл открыт удалённым процессом — df видит полный диск,
# но du этого файла не показывает (inode освободится только после закрытия)
sudo lsof +L1 | head         # DEL/LINK=0 — файлы «удалены», но держатся fd
# фикс: рестарт соответствующего сервиса (иногда это единственный путь)

# docker — не чистить «руками» /var/lib/docker, использовать prune
sudo docker system df
sudo docker system prune -af --volumes

# rotate и сжатие старых логов nginx/app
sudo logrotate -f /etc/logrotate.conf
На «забитом под 100%» диске systemd-journald, rsyslogd, часто и sshd начинают сбоить. Не перезагружай до того как освободил ≥ 1-2% — можно не вернуться. Сначала vacuum-size, truncate -s 0 больших активных логов через тот же процесс (не rm!), потом разбираться.

Фаза 2. Расширить /var через LVM (основной сценарий)

Допустим, /var — это LV /dev/vg0/var на ext4, и мы хотим добавить 50 GB.

Предполётная проверка — какой у нас сценарий:

df -h /var                      # точка монтирования → какое устройство
findmnt /var                    # TARGET SOURCE FSTYPE OPTIONS
mount | grep ' /var '
lsblk -f                        # дерево, тип ФС каждого LV
sudo vgs                        # есть ли свободное место в VG (VFree)?
sudo lvs vg0/var                # текущий размер LV

Вариант A — в VG уже есть свободное место (VFree > 50G). Это самый частый кейс, если при инсталляции «оставили запас».

# ext4 — online grow; -r resize2fs делается автоматически
sudo lvextend -L +50G -r /dev/vg0/var
# или «съесть всё свободное место»
sudo lvextend -l +100%FREE -r /dev/vg0/var

# XFS — тоже online
sudo lvextend -L +50G /dev/vg0/var
sudo xfs_growfs /var

df -h /var                      # убеждаемся — размер вырос, данные на месте

Сеть

ip a; ip r; ip -s link show eth0
ss -tulpn                # слушающие сокеты с PID
ss -ti '( dport = :443 )'
ethtool eth0             # speed/duplex, ring buffer
ethtool -S eth0 | grep -i err

# Маршрутизация
ip route add 10.0.0.0/24 via 192.168.1.1 dev eth0
ip route get 8.8.8.8

# VLAN / bridge / bonding
ip link add link eth0 name eth0.10 type vlan id 10
ip link add br0 type bridge
ip link add bond0 type bond mode=active-backup

MTU: по умолчанию 1500; в VPN/overlay часто 1400 (иначе фрагментация и «странные» таймауты). Проверить: ping -M do -s 1472 host (1472+28=1500).

NetworkManager vs systemd-networkd: NM — десктоп/ноутбуки, много GUI; networkd — сервера, декларативные .network файлы, легковесный.

Firewall

Стек: netfilter в ядре, iptables (legacy) / nftables (современный синтаксис). Большинство дистрибутивов перешли на nftables, но iptables остаётся как wrapper. conntrack отслеживает состояния (NEW/ESTABLISHED/RELATED) — именно это позволяет firewall-у корректно работать с ответными пакетами.

# nftables
nft list ruleset
nft add rule inet filter input tcp dport 22 accept

# iptables
iptables -L -n -v --line-numbers
iptables -I INPUT -p tcp --dport 80 -j ACCEPT

# SNAT / DNAT
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

# conntrack
cat /proc/net/nf_conntrack | wc -l
sysctl net.nf_conntrack_max
Типовой инцидент: nf_conntrack: table full, dropping packet. Увеличить nf_conntrack_max и hashsize, либо уменьшить таймауты (nf_conntrack_tcp_timeout_established).

Инструменты траблшутинга (must-know)

СимптомКоманды в первую очередь
Всё медленноtop/htop, uptime, vmstat 1, iostat -xz 1, pidstat 1
Load avg высокийСмотреть %wa (IO-wait) в top; dstat -tam; iotop; проверить swap-in/out
«Какой-то процесс жрёт диск»iotop -oPa, pidstat -d 1, lsof +D /var
Процесс виситstrace -f -p PID, strace -e openat,connect -p PID, ltrace, gdb -p PID
Что слушает порт / какой процессss -tulpn, lsof -i :443, fuser -n tcp 80
Сетевые проблемыtcpdump -ni eth0 host X and port 80 -w /tmp/a.pcap, mtr host, traceroute, dig +trace, curl -vk
Ядро / perfperf top, perf record -p PID -g, bpftrace -e '...'
Памятьfree -h, /proc/meminfo, slabtop, smem -k
OOM killsdmesg | grep -i oom, journalctl -k --grep oom

systemd — свой сервис

# /etc/systemd/system/myapp.service
[Unit]
Description=My App
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp.env
ExecStart=/opt/myapp/bin/server
Restart=on-failure
RestartSec=5
# Хардening:
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
systemctl daemon-reload && systemctl enable --now myapp

Таймеры vs cron: systemd-таймеры лучше интегрированы (persistent, OnBootSec, RandomizedDelaySec, логи в journald, можно зависеть от других unit-ов). Cron проще и везде есть. На K8s/контейнерах — CronJob.

SSH — best practices

  • Ключ на ed25519: ssh-keygen -t ed25519 -C "me@laptop". RSA — только ≥3072 бит.
  • ProxyJump (-J) — ходить через bastion без копирования ключей на него: ssh -J user@bastion user@target.
  • Agent forwarding (-A) — удобно, но опасно: на промежуточном хосте рутовый юзер может попросить ваш агент подписать challenge. Для CI лучше scp/deploy keys.
  • known_hosts — защита от MITM. Очищать старые: ssh-keygen -R host.
  • Hardening /etc/ssh/sshd_config: PermitRootLogin no, PasswordAuthentication no, AllowUsers, MaxAuthTries 3, PubkeyAcceptedAlgorithms.
Типовой вопрос: «Что такое load average и почему 4 при 8 ядрах — это норма?» LA — скользящее среднее числа runnable + uninterruptible процессов за 1/5/15 мин. Делить на число ядер, чтобы понять «нагружена ли система». 4/8 = 50% — ок; 16/8 — перегруз (очередь).

Типовые вопросы с ответами

Что такое права каталога в Linux?

У каталога те же три бита rwx, что и у файла, но значат они другое — именно это и проверяют на собеседовании:

БитНа файлеНа каталоге
rЧитать содержимое файлаЛистать имена в каталоге (ls). Без rls dir даст «Permission denied», но если знаешь имя — можешь к файлу обратиться (при наличии x)
wИзменять содержимое файлаСоздавать / удалять / переименовывать записи в каталоге. Важно: удаление файла зависит от прав на каталог, а не на сам файл. w на каталог + отсутствие w на файл = всё равно удалишь
xИсполнять (бинарь/скрипт)Входить (cd) и обращаться по имени к inode-ам внутри. Это самый важный бит для каталога: без x каталог становится «чёрной коробкой»

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

  • r-- — видно имена (ls), но ни открыть файлы, ни cd — нельзя. Редкий и почти бесполезный случай.
  • --x — «drop-box»: cd можно, файлы по известному имени доступны, но ls запрещён. Часто на /home/<user>.
  • r-x — стандартный read-only каталог: и ls, и cd, но не создать/удалить.
  • rwx — полный доступ.

Дополнительные биты на каталоге:

  • SGID (chmod g+s dir) — новые файлы/подкаталоги наследуют группу каталога (а не primary group пользователя). Классика для shared-директорий команды.
  • sticky (chmod +t dir, буква t в ls -ld) — удалять/переименовывать запись в каталоге может только её владелец, даже если у других есть w на каталог. Так устроен /tmp.
  • SUID на каталоге игнорируется Linux.
$ ls -ld /tmp /home
drwxrwxrwt  14 root root  /tmp      # t — sticky
drwxr-xr-x   5 root root  /home

# Shared-папка команды: все пишут, новые файлы — в группу devs
sudo chgrp devs /srv/shared
sudo chmod 2775 /srv/shared         # 2xxx = SGID; 775 = rwxrwxr-x
# SGID + umask 002 у пользователей — и все файлы автоматически
# создаются с правами rw-rw-r-- и группой devs.

Проверка «могу ли я это сделать» идёт на каждый компонент пути: чтобы прочитать /a/b/c/file, нужен x на /a, /a/b, /a/b/c и r на сам файл. Отсутствие x на промежуточном каталоге сломает доступ, даже если на конечный файл права есть.

Чем отличаются df и du?

Обе показывают «сколько занято», но смотрят с разных сторон и часто не сходятся — на этом расхождении обычно и ловят инциденты «диск забит, а du не видит кто».

df (disk free)du (disk usage)
Источник данныхМетаданные файловой системы: сколько блоков всего / свободно / занято на устройствеОбход дерева каталогов: суммирует размеры файлов, которые видно
ЕдиницаPer filesystem (точка монтирования / блочное устройство)Per path (файл / каталог)
СкоростьМгновенно (один syscall statfs())Долго — надо обойти и stat() каждый файл
Нужны праваЧтение метаданных, на смонтированные ФС видно всемПрава на чтение/обход каждого каталога (иначе «Permission denied» и заниженный результат)
Смотрит ли «скрытые» файлыДа — учитывает весь занятый block space, включая удалённые-но-открытые и скрытые монтированияНет — только то, что видит в дереве поверх текущих mount-ов

Когда df и du расходятся — и почему

1. Удалённый, но открытый файл
Классика. Приложение пишет в /var/log/app.log, кто-то сделал rm. Inode удалён из каталога (du его не видит), но блоки не освободятся, пока процесс держит файл открытым — df показывает занято.
sudo lsof +L1 | head       # файлы, у которых link count = 0 (LINK=0)
sudo ls -l /proc/$PID/fd/  | grep deleted
# фикс: truncate -s 0 /proc/$PID/fd/N  (не rm!) или рестарт сервиса
2. Mount поверх непустого каталога
/var/log был набит на 10 GB, потом сверху примонтировали отдельный диск. df видит новую ФС (пустую). du /var/log видит только новое содержимое, но старые 10 GB живы «под» mount'ом и учтены в df корня.
sudo mount --bind / /mnt
sudo du -xsh /mnt/var/log  # то, что скрыто mount'ом, становится видно
3. Резерв ФС для root (ext2/3/4)
По умолчанию ext4 резервирует 5% блоков для root. df показывает «100% use» заметно раньше фактического конца, а du этого резерва не видит.
sudo tune2fs -m 1 /dev/sda2   # снизить резерв до 1% (на /var/log разумно)
4. Sparse-файлы / «дыры»
du показывает реально занятые блоки (ключ --apparent-size — показать «заявленный» размер). df отражает физику. У больших qcow2/vmdk/огромных sparse .log — цифры «размер на диске» и «размер файла» радикально разные.
5. Разные ФС под одним путём
du / без -x уходит в /proc, /sys, смонтированные сетевые шары, bind-mounts. Всегда — du -x (не пересекать границы ФС).

Практические команды

# df — размер/тип всех ФС, сразу с человечными единицами
df -hT
df -hT /var                          # только по одной точке

# inode-exhaustion (миллионы мелких файлов — блоки есть, inode-ов нет)
df -i

# du — топ виновников под /var, без вылезания в /proc и другие ФС
sudo du -xh --max-depth=1 /var 2>/dev/null | sort -h | tail
sudo du -xh --max-depth=2 /var/lib 2>/dev/null | sort -h | tail

# быстрее на больших деревьях:
sudo ncdu -x /var

# Почему df показывает полный диск, а du — нет? Deleted-but-open:
sudo lsof +L1 | awk '$NF ~ /deleted/ || $(NF-1) == 0'
Короткая формулировка для интервьюера: df спрашивает у файловой системы, сколько блоков занято, — отвечает мгновенно и видит всё (включая удалённые-но-открытые файлы и данные под mount-ами). du обходит дерево каталогов и суммирует видимые файлы — медленно, зависит от прав и не видит того, что «под» mount'ом или «удалено, но ещё открыто».
Что такое символическая и жёсткая ссылка, чем отличаются?

Обе — способ иметь «ещё одно имя» для файла, но устроены принципиально по-разному.

Hard link (ln file hl)Symlink / soft link (ln -s target sl)
Что это физическиЕщё одна запись «имя → inode» в каталоге, указывающая на тот же inode, что и оригинальное имя. Равноправна с ним — «оригинала» нетОтдельный файл (свой inode) с маленьким содержимым — строкой пути до target
InodeСовпадает с оригиналом (ls -i)Свой собственный
Link countУ inode растёт на 1 (ls -l, второй столбец). Файл удалится, когда count дойдёт до 0 и ни один процесс его не держит открытымУ target не меняется
Пересечение ФСНельзя — inode существует только внутри одной ФСМожно — это просто путь
На каталогНельзя (только ядро создаёт . и .., чтобы не ломать обход дерева)Можно
Что если оригинал удалить/переместить«Оригинала» нет, данные живы, пока есть ещё хоть одна ссылкаСимлинк становится dangling (битым) — указывает на путь, которого больше нет
ПраваHard link = тот же inode → те же права и владелец (они у inode, не у имени)У самого симлинка обычно lrwxrwxrwx; проверяются права target при разыменовании
Видно в ls -lКак обычный файлС префиксом l и name -> target
Типичное применениеSnapshot-like бэкапы (rsync --link-dest), дедупликация одинаковых файлов«Текущая версия» (/opt/app/current -> /opt/app/releases/2026-04-22/), cross-mount shortcuts, /etc/alternatives
# Создание
ln        /data/orig  /data/hl        # жёсткая (оригинал и hl равноправны)
ln -s     /data/orig  /data/sl        # символическая

ls -li /data/
# 131073 -rw-r--r-- 2 u u   0 ...  orig       <- link count = 2, inode 131073
# 131073 -rw-r--r-- 2 u u   0 ...  hl         <- тот же inode, та же «двойка»
# 131090 lrwxrwxrwx 1 u u  10 ...  sl -> /data/orig

rm /data/orig
cat /data/hl      # ок — данные живы
cat /data/sl      # No such file or directory — dangling symlink

# Полезное
readlink -f /path/sl        # куда разыменовывается (рекурсивно)
find / -samefile /x         # все имена, ссылающиеся на тот же inode
find /dir -xtype l          # найти битые симлинки
stat file                   # Links: N  (link count inode)
Подводный камень: в tar, rsync, скриптах часто путают -L (follow symlinks → архивирует содержимое target) и поведение по умолчанию (сохранить симлинк как есть). Не ту ноту → либо дублируешь данные, либо восстанавливаешь битые ссылки.

Аналогия для объяснения интервьюеру: hard link — это второй паспорт на того же человека (тот же гражданин/inode); symlink — это бумажка «он живёт по адресу X», и если человек переехал — бумажка врёт.

2. Kubernetes

Ключевой блок. Ожидай глубоких вопросов про архитектуру, сеть, сторадж, отладку подов.

Архитектура кластера

Control plane (master):
  • kube-apiserver — единственный entrypoint, REST-фасад к etcd. Без него кластер «не управляется» (но поды продолжают жить).
  • etcd — distributed KV store, источник истины кластера. Raft, нужен нечётный кворум (3/5). Backup обязателен.
  • kube-scheduler — назначает поды узлам (учитывая requests, taints, affinity).
  • kube-controller-manager — reconcile-циклы (Deployment, ReplicaSet, Node, Endpoint и т.д.).
  • cloud-controller-manager — интеграция с облаком (LB, volume, route).
Worker node:
  • kubelet — агент узла, общается с apiserver, запускает контейнеры через CRI.
  • kube-proxy — программирует правила iptables/ipvs/eBPF для Service→Endpoint.
  • Container runtime (containerd/CRI-O) — запускает контейнеры через OCI.
  • CNI-plugin (Calico/Cilium/Flannel) — сеть подов, NetworkPolicy.

etcd

  • Кворум = N/2 + 1. 3 ноды — терпит 1 падение, 5 — 2.
  • Backup: etcdctl snapshot save /backup/etcd-$(date +%F).db. Восстановление — snapshot restore + перезапуск членов из того же snapshot.
  • Deprecated: split-brain в случае сетевого разделения — Raft-лидер остаётся у большинства, меньшинство — read-only.
  • Размер рекомендуют ≤ 8 ГБ; defrag при фрагментации.

Основные объекты

Pod
Минимальная планируемая единица. 1+ контейнер, общий network namespace и volume-ы. Обычно НЕ создают «руками» — через контроллеры.
ReplicaSet
Поддерживает N реплик Pod-ов по селектору. Редко создаётся напрямую — Deployment оборачивает.
Deployment
Stateless-приложения. Поддерживает rolling update (maxSurge, maxUnavailable), rollback (kubectl rollout undo), ревизии.
StatefulSet
Для stateful (БД, Kafka). Стабильные имена (mysql-0, mysql-1), по PVC на под (headless-сервис для DNS), упорядоченный rollout.
DaemonSet
По одному поду на (выбранных) узлах. Логи, сетевые агенты, мониторинг (node-exporter, fluent-bit).
Job
Запуск до успешного завершения (batch). backoffLimit, parallelism, completions.
CronJob
Job по расписанию. concurrencyPolicy: Forbid/Allow/Replace, startingDeadlineSeconds, successfulJobsHistoryLimit.

Сеть

  • CNI: Calico (BGP + iptables/eBPF, NetworkPolicy из коробки), Cilium (eBPF, L7-policies, замена kube-proxy), Flannel (простой VXLAN, без NetworkPolicy по умолчанию).
  • Service types:
    • ClusterIP — дефолт, L4, доступен внутри кластера.
    • NodePort — открывает порт (30000-32767) на всех нодах.
    • LoadBalancer — внешний LB от облака/MetalLB.
    • Headless (clusterIP: None) — без виртуального IP, DNS возвращает IP подов; для StatefulSet.
    • ExternalName — CNAME на внешний хост.
  • Ingress — L7 (HTTP/S), host/path routing, TLS termination. Контроллеры: nginx, Traefik, HAProxy. Gateway API — новый стандарт, богаче (trafficMirror, header modification, cross-namespace refs), постепенно замещает Ingress.
  • NetworkPolicy — L3/L4 firewall по лейблам. По умолчанию в k8s — allow all, NetworkPolicy переводит pod в «default deny» только для тех направлений, где есть хотя бы одна Policy.
  • CoreDNS — DNS внутри кластера. Сервисы: svc.namespace.svc.cluster.local. ndots: по умолчанию 5 — любой доменный запрос с <5 точками проходит через search list; отсюда знаменитое «DNS lookups slow» на облаках.

Storage

  • PV — ресурс кластера (кусок стораджа). PVC — заявка пода на этот ресурс.
  • StorageClass — шаблон для динамического provisioning (EBS, RBD, NFS, CSI-driver).
  • ReclaimPolicy: Retain (после удаления PVC PV остаётся → ручной cleanup), Delete (удаляется underlying), Recycle (deprecated).
  • AccessMode: RWO (single node), ROX (many read), RWX (many read-write — не всякий сторадж умеет), RWOP (single pod).
  • VolumeMode: Filesystem (дефолт) vs Block (raw device).
  • CSI — современный стандарт драйверов (EBS/CSI, rook-ceph, longhorn, openEBS).

Конфигурация

  • ConfigMap — plain-text параметры. Инжектят как env, volume (файлы), command args.
  • Secret — то же, но base64, не шифрование. Шифрование — этаж ниже (encryption at rest в etcd через --encryption-provider-config), либо внешние вольты (SOPS, Sealed Secrets, ESO).
  • Обновление: mounted file обновится автоматически (~1-2 минуты), env — НЕ обновится, нужно пересоздать под.
kubectl create secret generic db-creds \
  --from-literal=user=admin \
  --from-literal=pass='s3cr3t' \
  --dry-run=client -o yaml | kubectl apply -f -

Планирование

  • requests / limits: requests — «гарантия» для scheduler-а и cgroup-а, limits — потолок. CPU можно throttle; memory — OOMKill.
  • QoS-классы:
    • Guaranteed — limits = requests на все ресурсы.
    • Burstable — requests < limits (хоть у одного).
    • BestEffort — ни requests, ни limits. Убивают первыми при нехватке.
  • Taints/Tolerations: taint на узле отталкивает поды, toleration на поде позволяет «терпеть». Типичный кейс: GPU-узлы, spot-ноды, master без workload.
  • Affinity: nodeAffinity (требовать зоны/labels ноды), podAffinity (держать вместе), podAntiAffinity (разносить реплики на разные ноды/зоны).
  • TopologySpreadConstraints — равномерно разложить поды по топологическим ключам (zone, hostname).
  • PriorityClass + preemption — высокоприоритетные поды могут «сбросить» низкоприоритетные.

Автоскейлинг

  • HPA — по CPU/memory/custom metric. Читает из metrics.k8s.io (metrics-server) / custom.metrics.k8s.io (adapter → Prometheus). kubectl autoscale deploy web --min=2 --max=10 --cpu-percent=70.
  • VPA — перестраивает requests/limits. Три режима: Off (только рекомендует), Auto (рестартит pod с новыми значениями), Initial (только при создании).
  • Cluster Autoscaler — скейлит сами ноды (через провайдера облака) на основании unschedulable pending-подов.
  • Karpenter — альтернатива CA от AWS, гибче (подбирает нужный instance type под нагрузку, не ограничен node groups).

Безопасность

  • RBAC: Role/ClusterRole (набор verbs на resource'ы) + RoleBinding/ClusterRoleBinding (привязка к Subject — User/Group/ServiceAccount).
  • ServiceAccount — identity для пода. Токен монтируется в /var/run/secrets/kubernetes.io/serviceaccount/.
  • PodSecurityAdmission (заменил PSP в 1.25+): уровни privileged/baseline/restricted, включается label'ом namespace.
  • seccomp/AppArmor — фильтрация syscall-ов. Включается на уровне пода/контейнера.
  • OPA/Gatekeeper и Kyverno — admission controllers для политик («все image'ы только из trusted registry», «ни одного privileged», «resources обязательны»). Kyverno проще (YAML-native), Gatekeeper — Rego.

Обновление / drain

# Узел
kubectl cordon node-1                 # пометить unschedulable
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
# апгрейдим, возвращаем
kubectl uncordon node-1

# Deployment
kubectl rollout status deploy/web
kubectl rollout history deploy/web
kubectl rollout undo deploy/web --to-revision=3

PodDisruptionBudget (PDB) — гарантия «минимум X реплик всегда живых» при добровольных eviction (drain). Без PDB drain спокойно укатает все реплики.

Probes (liveness / readiness / startup)

ProbeЧто делает failКогда нужна
livenessРестартит контейнерПриложение в deadlock, зависло — надо перезапустить
readinessИсключает pod из endpoint'ов ServiceWarm-up, temporary dep отказ — не гасим pod, но и трафик не лить
startupДо её success остальные не запускаютсяДолгий старт (Java) — нельзя дать liveness убить pod раньше времени
Типовая ошибка: liveness бьёт в /health, который зависит от БД. БД моргнула → liveness fail → pod рестарт → всё стадо рестартит. Liveness должна проверять только «жив ли сам процесс»; зависимости — в readiness.

Деплой кластера

ИнструментПлюсыМинусы
kubeadmОфициальный, гибкий, идемпотентныйНет LB, etcd и upgrade-процесса «из коробки»
kubesprayAnsible-based, много плагинов, prod-readyМедленный, много параметров
Managed (EKS/GKE/AKS)Control plane не ваш, auto-upgradeVendor lock, цена, меньше гибкости
k3s / k0sЛёгкий, быстро, edge/devНе всё супер-стандартно

Troubleshooting поды

СтатусПричинаЧто смотреть
PendingНет ресурсов / PVC не bind / taintkubectl describe pod — events внизу
CrashLoopBackOffПроцесс падает сразу после стартаkubectl logs --previous, liveness probe, entrypoint
ImagePullBackOffКредов нет / тег не существует / registry недоступенdescribe events; imagePullSecret; crictl pull с ноды
OOMKilledПроцесс превысил memory limitdescribe → Last State: Terminated, Reason: OOMKilled. Смотри memory-метрики, тюнь limits или код
EvictedНода в memory/disk pressure, k8s выселилdescribe pod + kubectl describe node → conditions
Error/CompletedJob завершился; не рестартится по спекеПроверить restartPolicy и reason
kubectl describe pod web-xxx
kubectl logs -f web-xxx -c sidecar
kubectl logs web-xxx --previous
kubectl exec -it web-xxx -- sh
kubectl get events --sort-by=.lastTimestamp -A | tail
kubectl top pod -A; kubectl top node

3. Docker и контейнеры

Образ и слои

Образ = набор read-only слоёв + manifest + config. Каждый RUN/COPY/ADD создаёт слой. Слои общие между образами → пул-экономия.

overlayfs (дефолтный storage driver): lower (read-only слои образа) + upper (writable layer контейнера) + merged (что видит контейнер). Копирование-при-записи: изменение файла из нижнего слоя «копирует» его в upper.

Кеш сборки: докер считает слой тем же, если инструкция совпадает И base не изменился. Поэтому COPY go.mod go.sum ./ && go mod download идёт раньше COPY . . — зависимости кешируются и не скачиваются при каждом изменении кода.

Dockerfile best practices

  • Минимальная база: alpine, distroless, debian:bookworm-slim. Чем меньше — тем меньше CVE и размер.
  • Multi-stage: собираем в builder-образе, копируем артефакт в runtime-образ.
  • Non-root: USER 10001:10001. Если требуется bind на :80 — лучше слушать :8080 и маппить снаружи.
  • Один процесс на контейнер (12-factor). Несколько — только через supervisor в спец. кейсах.
  • Порядок команд: часто меняющиеся — вниз; COPY package.json перед COPY ..
  • .dockerignore: уменьшить build context (node_modules, .git, логи).
  • HEALTHCHECK: полезно для swarm; в k8s игнорируется (есть probes).
# multi-stage пример (Go)
FROM golang:1.22-alpine AS build
WORKDIR /src
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/app

FROM gcr.io/distroless/static:nonroot
COPY --from=build /out/app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]

Изоляция: namespaces + cgroups

  • Namespaces (что контейнер «видит»): pid, net, mnt, uts (hostname), ipc, user, cgroup.
  • cgroups (сколько ресурсов контейнер «может»): CPU shares/quota, memory, blkio, pids. v2 — единое дерево, лучше accounting.
  • Дополнительно: capabilities (root-привилегии разбиты на флажки), seccomp (whitelist syscall), AppArmor/SELinux (MAC).

Runtime: containerd vs Docker vs CRI-O

Исторически Docker = docker-cli + dockerd + containerd + runc. Kubernetes ходил в Docker через dockershim. В 1.24 shim удалили — теперь k8s говорит напрямую с containerd/CRI-O по CRI. Образы и опыт разработчика этим не затрагивается; для админа k8s это значит, что docker ps на ноде не покажет контейнеры — используй crictl ps.

Сети Docker

DriverКогда
bridge (default)Дефолтный bridge docker0; кастомные bridge-сети дают DNS по именам контейнеров
hostКонтейнер делит сеть с хостом (без NAT, лучшая perf, нет изоляции портов)
noneБез сети вообще (для изолированных задач)
overlaySwarm: мультихост-сеть через VXLAN
macvlanКонтейнер — отдельный MAC в физической сети (legacy-приложения, ожидающие L2)

Volumes vs bind mounts vs tmpfs

  • named volume — Docker управляет, лежит в /var/lib/docker/volumes/. Переносимо между хостами (бекап через docker run --volumes-from). Правильный выбор для state.
  • bind mount — конкретный путь хоста. Удобно для dev, рискованно для prod (разные FS/uids).
  • tmpfs — только в RAM, эфемерно.

Registry, подпись, сканирование

  • Registry: Docker Hub, GitLab Registry, Harbor (on-prem), Nexus, Artifactory, GHCR.
  • Подпись: cosign sign/verify (Sigstore) — подписывает digest (SHA256), а не tag. Проверяется в admission controller (Kyverno/OPA).
  • Сканирование: Trivy (самый популярный, CVE + misconfig + secrets + SBOM), Grype, Clair, Snyk. В CI блокируем HIGH/CRITICAL.
  • SBOM (Software Bill of Materials) — список всех зависимостей образа. syft генерит, grype скормит.
trivy image --severity HIGH,CRITICAL myapp:latest
cosign sign --key cosign.key myrepo/app:1.2.3
cosign verify --key cosign.pub myrepo/app:1.2.3

4. CI/CD (GitLab CI и Jenkins)

GitLab CI — якорные понятия

  • Stages — последовательные фазы (build → test → deploy). Job-ы в одном stage идут параллельно.
  • Jobs — единица выполнения, привязывается к runner.
  • Runners: shared (GitLab.com), group, specific (project). Executors: shell, docker, docker-in-docker, kubernetes, ssh, parallels, virtualbox, custom.
  • cache vs artifacts: cache — для ускорения между job-ами в одной project (node_modules, .m2), expire не задан; artifacts — результаты job-а, доступны следующим stage-ам/пользователям, версионируются с pipeline.
  • rules / only / except: современный синтаксис — rules с if, changes, exists. only/except — legacy.
  • needs / DAG: разрывает строгое порядковое выполнение stages — job может стартовать сразу как готовы его needs. needs: с artifacts: true.
  • Parent-child / multi-project pipelines: trigger dynamic pipeline, trigger в другом проекте (trigger: project: group/other).
  • Environments — dev/staging/prod, отслеживание деплоев; manual action → one-click deploy.
  • Protected branches/tags + masked variables → секреты только в защищённых контекстах.
stages: [build, test, deploy]

variables:
  DOCKER_TLS_CERTDIR: ""

build:
  stage: build
  image: docker:24
  services: [docker:24-dind]
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
    - docker push  $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == "main"'

deploy:prod:
  stage: deploy
  needs: [build]
  environment: { name: prod, url: https://app.example.com }
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual
  script:
    - helm upgrade --install web ./charts/web --set image.tag=$CI_COMMIT_SHORT_SHA

Jenkins

  • Declarative (рекомендуется) vs Scripted (полная мощь Groovy, больше соблазнов ошибиться).
  • Shared libraries — вынести общий Groovy-код в отдельный репо (@Library('my-lib')).
  • Agents — master orchestrates, jobs идут на agents (static, docker, kubernetes plugin). Kubernetes-plugin создаёт pod-agent на лету.
  • Credentials — binding через withCredentials / credentialsId. Stored в Jenkins encrypted store.
  • Blue Ocean — UI для визуализации pipelines.
pipeline {
  agent { kubernetes { yamlFile 'jenkins-agent.yaml' } }
  options { timeout(30) }
  stages {
    stage('Test')   { steps { sh 'make test' } }
    stage('Build')  { steps { sh 'make build' } }
    stage('Deploy') {
      when { branch 'main' }
      steps {
        withCredentials([string(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
          sh 'helm upgrade --install web charts/web'
        }
      }
    }
  }
  post { always { junit 'reports/*.xml' } }
}

Стратегии деплоя

СтратегияСутьПлюсМинус
RollingПостепенно заменяем реплики старой версии новойПростая, без extra ресурсовОбе версии одновременно живут; совместимость обязательна
Blue-greenПолностью развернуть «green», переключить LBМгновенный rollback2x ресурсов во время смены
Canary5% → 25% → 100% трафика на новую версию, с метрикамиОграничивает blast radiusНужен тулчейн (Argo Rollouts / Flagger) + метрики
A/BРоутинг по признаку (header/cookie/user segment)Product-experimentsТребует feature-flags / service mesh

В k8s: native rolling — из Deployment. Canary/Blue-green — Argo Rollouts, Flagger (с Istio/Linkerd/Nginx).

Секреты в пайплайнах

  • GitLab: masked + protected variables, file-type variables для key-файлов, integration с Vault (secrets: в job).
  • Jenkins: credentials plugin, HashiCorp Vault plugin, AWS Secrets Manager plugin.
  • Общее правило: никогда не print'ать секреты в логи. set -x + секрет в env = утечка.

Артефакты / репозитории

  • Nexus / Artifactory — универсальные (Maven, npm, PyPI, Docker, generic). Прокси для внешних реестров + self-host.
  • GitLab Package & Container Registry — встроены.

Безопасность пайплайна

КлассИнструментыЧто ловит
SASTSonarQube, Semgrep, Checkmarx, Bandit (Py), gosec (Go)Уязвимости в исходниках
DASTOWASP ZAP, BurpУязвимости работающего приложения (XSS, SQLi)
SCASnyk, Dependency-Track, Trivy (fs), RenovateУязвимости в зависимостях
Secret scanninggitleaks, trufflehog, GitLab Secret DetectionУтечки API-ключей в репо
Lint / confighadolint, kube-linter, tflint, yamllint, ansible-lintBad practices
SBOMsyft, cyclonedxПрозрачность зависимостей, соответствие требованиям

5. GitOps и пакет-менеджеры K8s

Helm

  • Chart — пакет шаблонов. Структура: Chart.yaml, values.yaml, templates/*.yaml, charts/ (subcharts).
  • Values — параметры, override через -f values-prod.yaml и --set.
  • Templates — Go templates + sprig-функции (default, quote, toYaml, include, tpl).
  • Release — инстанс чарта с именем/namespace. История в Secret/ConfigMap.
  • Hooks (helm.sh/hook: pre-install,pre-upgrade) — job-ы на разных стадиях.
  • Dependencies — subcharts в Chart.yaml, helm dep update.
  • helmfile — декларативный wrapper: описать набор releases и параметры в одном YAML.
helm template web ./charts/web -f values-prod.yaml   # отрендерить без установки
helm upgrade --install web ./charts/web -n prod --create-namespace
helm diff upgrade web ./charts/web                   # plugin helm-diff
helm rollback web 2
helm history web

Kustomize

  • Base + overlays. Без шаблонизации — только наложение patch-ей на базовые manifest-ы.
  • Плюсы: прозрачность (всё — валидный YAML), встроен в kubectl -k, нет runtime-зависимостей.
  • Минусы: нет if/else/loop, сложные чарты писать больно.
  • Комбинируется с Helm через helmCharts: в kustomization.yaml.

Helm vs Kustomize

HelmKustomize
ШаблоныGo tpl, powerful, но сложно читатьPlain YAML + patches
УпаковкаChart = артефакт (репо)Просто каталог в Git
Lifecycleinstall/upgrade/rollback, historyApply-only, состояние в Git
ДляCторонних приложений (operators)Собственные манифесты

ArgoCD vs Flux

  • ArgoCD — UI богатый, App/AppProject модель, ApplicationSet для генерации, sync waves (argocd.argoproj.io/sync-wave), self-heal, SSO из коробки, App of Apps для больших кластеров.
  • Flux — CRDs GitRepository / HelmRepository / Kustomization / HelmRelease. Декларативнее, без UI (есть Capacitor/Weave GitOps); хорошо интегрируется с Flagger для canary.
  • Общие принципы: Git — source of truth, контроллер периодически ресинкает кластер, расхождения (drift) подсвечиваются.

Секреты в GitOps

Положить «чистый» Secret в Git нельзя (base64 != encryption). Варианты:

SOPS
Шифрование YAML значений (age/GPG/KMS). Хранится в Git, расшифровывается controller-ом (ArgoCD sops-plugin / kustomize-sops).
Sealed Secrets (Bitnami)
Controller в кластере с парой ключей. kubeseal шифрует Secret публичным ключом → получается SealedSecret, кладётся в Git. Расшифровка только в этом кластере.
External Secrets Operator (ESO)
CRD ExternalSecret подтягивает значения из Vault/AWS SM/GCP SM/1Password. В Git — только ссылка, значения живут в external store.
Vault CSI Provider / Vault Agent Injector
Секреты инжектятся в pod как файлы/env из Vault прямо на лету.

Принципы GitOps

  1. Желаемое состояние описано декларативно.
  2. Хранится в Git (единый source of truth, аудит, PR-based approval).
  3. Агент автоматически синхронизирует кластер с Git.
  4. Расхождения детектируются и алертят (drift detection).

6. Мониторинг и Observability

Три столпа (+ два)

СтолпЧто даётИнструменты
MetricsАгрегированные числа во времени — CPU, latency, error ratePrometheus, VictoriaMetrics, Datadog
LogsДискретные события с контекстомELK/EFK, Loki, Splunk
TracesПуть одного запроса через сервисы (spans)Jaeger, Tempo, Zipkin
EventsДеплои, pod evictions (k8s events), алерты — «что вообще происходит»k8s, EventBridge
ProfilesCPU/memory профили во времениPyroscope, Parca, Grafana Phlare

Prometheus

  • Pull-модель: Prometheus сам ходит по /metrics таргетов. Для кратковременных job-ов — Pushgateway.
  • Exporters: node_exporter (Linux host), postgres_exporter, blackbox_exporter (ping/HTTP), kube-state-metrics (state объектов k8s).
  • Data model: time series = metric_name{label="v", ...}, тип: Counter (только растёт, reset при рестарте), Gauge (может туда-сюда), Histogram (buckets + _sum + _count), Summary (квантили клиентского подсчёта).
  • Remote write: отправляем «сырые» метрики в Thanos/VictoriaMetrics/Mimir для долгосрочного хранения.
  • Federation: один Prometheus scrape'ит /federate другого (иерархия dc → global).

PromQL essentials

# Rate запросов за 5 мин, по service
sum by (service) (rate(http_requests_total[5m]))

# 99-й перцентиль latency из histogram
histogram_quantile(0.99,
  sum by (le, service) (rate(http_request_duration_seconds_bucket[5m]))
)

# Error rate в процентах
100 * sum(rate(http_requests_total{status=~"5.."}[5m]))
     / sum(rate(http_requests_total[5m]))

# Сколько pod-ов в CrashLoop
sum by (namespace) (kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"})

# rate vs irate: rate — сглажённое по окну, irate — по двум последним точкам (шумнее, для алертов)
Правила по histogram_quantile: нужен rate() под sum(... by le), иначе врёт. Без by (le) — бессмыслица.

Правила

  • Recording rules — предрасчёт часто используемых выражений (job:http_errors:rate5m).
  • Alerting rulesALERT по выражению + for: 10m (чтобы не сработать от спайка).

Alertmanager

  • Routes — дерево маршрутизации по labels (team, severity) в receivers (Slack, Pagerduty, email).
  • Grouping — склеивать похожие (по group_by) в одно уведомление.
  • Inhibition — «если сработал X, не шлём Y» (напр. node_down → не шлём «pod unreachable» для каждого pod-а на этой ноде).
  • Silences — временное подавление по label-matcher'у.

Grafana

  • Datasources: Prometheus, Loki, Tempo, PostgreSQL, ClickHouse, ElasticSearch.
  • Variables (template variables) — параметризуют дашборды ($instance).
  • Unified Alerting — новая модель (замена legacy). Поддерживает многошаговые алерты (Grafana-managed и Prometheus-managed).
  • Provisioning — дашборды и datasources как YAML/JSON, через GitOps.

Zabbix vs Prometheus

ZabbixPrometheus
МодельPush (agent) + pull; «хостоцентричная»Pull, «метрикоцентричная», dimensional
StorageMySQL/PostgresTSDB on disk (+ remote_write)
Откуда взятIT-monitoring 2001-го; агент-basedCloud-native (2012), SoundCloud
Лучшее применение«Железо», SNMP, сетевое оборудование, enterprise-оповещенияСервисы, k8s, микросервисы, Service-level metrics

Tracing — OpenTelemetry

  • OTel = стандарт (API + SDK + collector + протоколы). Один SDK в приложении, экспорт в любой backend.
  • OTel collector: receivers (OTLP, Prometheus scrape) → processors (batch, memory_limiter, attributes) → exporters (Jaeger, Tempo, Prometheus).
  • Trace context (traceparent, W3C) пробрасывается через заголовки.

Методологии

RED
Rate (RPS), Errors (rate ошибок), Duration (latency). Для request-driven сервисов.
USE
Utilization, Saturation, Errors. Для ресурсов (CPU, диск, сеть).
Four Golden Signals (Google SRE)
Latency, Traffic, Errors, Saturation.

SLI / SLO / SLA / Error budget

  • SLI — измеряемая величина (availability, latency p99).
  • SLO — целевой уровень SLI (99.9% за 30 дней).
  • SLA — договор с клиентом, обычно слабее SLO (чтобы SLO был guard-rail-ом).
  • Error budget = 1 − SLO. На 99.9% это 43m за 30 дней. Пока бюджет есть — катаем релизы, «сгорел» — замораживаем фичи и чиним надёжность.

Long-term storage

  • Thanos — sidecar + object storage + querier. Prometheus-native, долгая история.
  • Cortex / Grafana Mimir — multi-tenant, horizontally scalable.
  • VictoriaMetrics — один бинарь, шустрее, меньше ресурсов, MetricsQL (расширение PromQL). cluster version на большие объёмы.

7. Логирование (EFK/ELK)

Архитектура стеков

приложение → [сборщик] → [обработчик] → [хранилище] → [UI]
              Filebeat      Logstash        ES/OS      Kibana
              Fluent Bit    Fluentd         (альтернативы)
              Vector        Vector
  • ELK — Elasticsearch + Logstash + Kibana.
  • EFK — та же связка, но Fluentd/Fluent Bit вместо Logstash (часто в k8s).
  • OpenSearch — форк Elasticsearch от AWS после смены лицензии ES на SSPL. API-совместим, Apache 2.0.

Elasticsearch — ключевое

  • Индекс — логическая коллекция документов. Физически делится на shards (primary + replicas).
  • Шардирование: новые документы распределяются по primary shard-ам по _routing. Число primary фиксируется на создании.
  • Реплики: копии primary на других нодах. 1 replica → на shard отказоустойчивость + больший read throughput.
  • Mapping — «схема» полей (типы, analyzers). Авто-mapping удобен, но в prod лучше явный.
  • Analyzers — токенизация, стемминг для full-text search. Standard/keyword для логов чаще всего достаточно.
  • Heap sizing: 50% RAM, но не больше 32 GB (compressed oops). Остальное — page cache.
  • Split brain — исторически решалось minimum_master_nodes; в 7+ — zen2 discovery, авто.

ILM (Index Lifecycle Management)

Фазы: hot (активно пишется) → warm (меньше ресурсов) → cold (сжатие, читается редко) → frozen (searchable snapshot) → delete.

{
  "policy": {
    "phases": {
      "hot":    { "actions": { "rollover": { "max_size": "50gb", "max_age": "7d" } } },
      "warm":   { "min_age": "7d",  "actions": { "shrink": {"number_of_shards":1}, "forcemerge":{"max_num_segments":1}}},
      "cold":   { "min_age": "30d", "actions": { "searchable_snapshot": {"snapshot_repository":"s3"}}},
      "delete": { "min_age": "90d", "actions": { "delete": {} } }
    }
  }
}

Rollover / curator / retention

  • Rollover — переключение на новый индекс при достижении размера/возраста (logs-000001 → logs-000002) по alias-у logs.
  • Curator — legacy-инструмент; современный путь — ILM.
  • Retention: для логов обычно 7-30 дней «в горячем», остальное в warm/cold/S3 snapshot.

Fluent Bit vs Fluentd

Fluent BitFluentd
ЯзыкCRuby + C-плагины
Потребление~1 МБ RAMдесятки/сотни МБ
Плагиновменьше>500
Для чегоNode-agent (DaemonSet в k8s), edgeAggregator, богатая обработка

Типовой паттерн k8s: Fluent Bit DaemonSet собирает stdout pod-ов → Fluentd aggregator (опц.) → Elastic/OpenSearch.

Парсинг и нормализация

  • Приводим логи к единой схеме — ECS (Elastic Common Schema): @timestamp, service.name, host.name, log.level, event.dataset.
  • Парсинг multiline (stacktrace Java) — multiline.pattern в Filebeat/Fluent Bit.
  • Лучше писать JSON-логи из приложения — не нужен grok.
  • Ingest pipelines в ES — как «мини-Logstash» на уровне ES-кластера (grok, script, rename, date, geoip).

Производительность

  • Bulk API: batch-размер 5-15 МБ, 1000-5000 документов — оптимум.
  • Размер shard-а: 20-50 ГБ. Больше — replay медленный, меньше — overhead на управление.
  • Отключать _source нельзя, если нужен reindex.
  • refresh_interval — при индексации поднять до 30s (дефолт 1s) — меньше IO.

Безопасность

  • X-Pack Security / OpenSearch Security: native users, RBAC, index-level permissions, document-level security, field-masking.
  • TLS везде: node↔node (transport), client↔node (REST), Kibana↔ES.
  • Для PCI — маскирование PAN/CVV в логах на стороне приложения + ещё раз в processor (защита в глубину).

Альтернатива — Loki

Grafana Loki: индексирует только labels (как Prometheus), сами логи — сжатые чанки. Дёшево по storage, но LogQL ограничен по full-text.

8. PostgreSQL

Архитектура процессов

  • postmaster — супервизор, слушает порт, форкает backend при каждом коннекте.
  • backend — один процесс на коннект (дорого! отсюда pool-еры).
  • autovacuum launcher + workers — чистят dead tuples, обновляют stats.
  • wal writer — пишет WAL buffer на диск.
  • checkpointer — сбрасывает грязные страницы shared_buffers на диск и отсекает старые WAL.
  • background writer — помогает checkpointer-у непрерывно.
  • walsender / walreceiver — для репликации.

MVCC, VACUUM, bloat

  • Каждая строка — несколько версий (tuples) с xmin/xmax. UPDATE = insert + mark old as dead.
  • VACUUM освобождает место внутри страниц (для reuse), VACUUM FULL — rewrite table с exclusive lock (простой недопустим).
  • autovacuum срабатывает по порогам: autovacuum_vacuum_scale_factor (0.2 по умолчанию — 20% dead). На больших таблицах хочется меньше (0.02–0.05), чтобы не пропускать.
  • Bloat — раздутые таблицы/индексы. Диагностика: pgstattuple, запросы-checkers из pg_wiki. Починка: REINDEX CONCURRENTLY, pg_repack.
  • Wraparound: xid 32-битный; при достижении transaction-wraparound PostgreSQL уходит в read-only. Следим age(datfrozenxid).

Репликация

ТипЧтоПрименение
Physical streamingПобайтная копия WAL, всё или ничегоHA, read replicas
LogicalПубликация/подписка по таблицам, построчноГетерогенные версии, частичная репликация, миграции
SynchronousCommit ждёт подтверждения standby (synchronous_commit=on + synchronous_standby_names)Нет потери данных, но latency +RTT
AsynchronousStandby отстаёт, возможна потеряPerformance, география

Replication slots — гарантируют, что master не удалит WAL, пока standby/consumer не прочитает. Риск: «забытый» slot → WAL растёт бесконтрольно → pg_wal полон → база OOM по диску.

HA решения

  • Patroni (+ etcd/Consul/ZooKeeper как DCS) — de-facto стандарт. Auto-failover, leader election, REST API для orchestration. Zalando.
  • repmgr — проще, меньше зависимостей, ручной failover или script-driven.
  • pg_auto_failover — MS + monitor, тройка узлов.
  • Для routing — HAProxy + Patroni healthcheck (/master, /replica), либо PgBouncer поверх.

Backup / Restore / PITR

  • pg_dump (logical) — консистентный snapshot; не работает на очень больших БД (часы-дни на сотни ГБ).
  • pg_basebackup — физический бэкап running-кластера. База для PITR.
  • WAL-G / pgBackRest — инкрементальные бэкапы в S3, параллелизм, сжатие, delta, PITR с высокой гранулярностью.
  • PITR: base backup + непрерывная archive WAL → восстановление на любую точку (recovery_target_time).
  • Всегда проверяем восстановление (untested backup = no backup).

Пулеры соединений

SessionTransactionStatement
Когда освобождается соединениеПосле disconnect клиентаПосле COMMIT/ROLLBACKПосле каждого statement
Поддержка prepared statements, advisory locks, session stateДаОграниченно (могут сломаться)Нет
ЭффективностьНизкаяОтличная (дефолт PgBouncer)Экстремальная, но ломает многое

EXPLAIN — что читать

EXPLAIN (ANALYZE, BUFFERS, VERBOSE, FORMAT TEXT)
SELECT ... FROM ...;

-- Seq Scan vs Index Scan / Index Only Scan / Bitmap Heap Scan + Bitmap Index Scan
-- rows=estimate vs actual rows=...  (если сильно расходятся — вакуум/статистика, re-ANALYZE)
-- Filter: ... rows removed (неэффективный индекс, или его нет)
-- Buffers: shared hit=X read=Y  (read = с диска, много → холодный кеш или мало shared_buffers)
-- Sort Method: external merge Disk  (work_mem мал)
-- Hash Join vs Merge Join vs Nested Loop  (NL на больших = беда)

Индексы

  • B-Tree — дефолт, для = < > BETWEEN, ORDER BY.
  • GIN — для массивов, jsonb, full-text. Быстрый поиск, дорогие вставки.
  • GiST — геометрия, полнотекст, ranges.
  • BRIN — «block range», для огромных таблиц с природным порядком (timestamp логов), очень маленький.
  • Partial: CREATE INDEX ... WHERE status = 'active' — индекс только на часть строк.
  • Covering (INCLUDE): CREATE INDEX ... (user_id) INCLUDE (email, name) — Index Only Scan без heap-touch.
  • CREATE INDEX CONCURRENTLY — без блокировки пишущих запросов (дольше, может failать).

Партиционирование

  • Declarative partitioning (PG 10+): RANGE (по дате), LIST, HASH.
  • Partition pruning — planner исключает ненужные партиции.
  • Использование: логи/events по месяцам/неделям; старые партиции drop'ом удаляются мгновенно.
  • Шардинг — горизонтальное масштабирование по нодам: Citus (distributed tables с distribution_column), FDW + партиции (ручной шардинг).

Тюнинг (отправные точки для 64 GB RAM)

ПараметрЗначениеСмысл
shared_buffers25% RAM (16 GB)Внутренний кеш страниц
effective_cache_size50-75% RAMPlanner: сколько ОС-кеша доступно
work_mem16-64 MB (на каждый sort/hash!)Поднятие = меньше disk sort, но ×N операций ×M коннектов
maintenance_work_mem1-2 GBVACUUM/CREATE INDEX
wal_levelreplica (или logical)Нужен для streaming/logical репликации
max_wal_size4-16 GBReduce checkpoint frequency
checkpoint_completion_target0.9Размазать checkpoint
random_page_cost1.1 (SSD)Planner friendlier к index scan

Locks и deadlocks

-- Что сейчас блокируется
SELECT blocked.pid AS blocked_pid, blocked.query AS blocked_query,
       blocking.pid AS blocking_pid, blocking.query AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.wait_event_type = 'Lock';

-- Убить
SELECT pg_cancel_backend(pid);   -- попросить
SELECT pg_terminate_backend(pid); -- жёстко

-- Долгие транзакции (виновники bloat + wraparound)
SELECT pid, state, xact_start, query FROM pg_stat_activity
WHERE state = 'idle in transaction' AND xact_start < now() - interval '10 min';
idle in transaction — тихий убийца: держит locks, блокирует VACUUM, раздувает таблицы. Нужен idle_in_transaction_session_timeout.

Observability БД

  • pg_stat_statements — топ-запросы по времени/вызовам.
  • auto_explain — логировать план для медленных запросов.
  • postgres_exporter → Prometheus → Grafana (стандартные дашборды: commits/rollbacks, cache hit, replication lag, disk space).

9. Configuration Management (Ansible / Salt)

Ansible базово

  • Inventory — static (hosts.yml, hosts.ini) или dynamic (плагины: AWS EC2, GCP, k8s, custom script).
  • Playbook — список plays (hosts + tasks + handlers + vars). YAML.
  • Role — папка со стандартной структурой (tasks/, handlers/, templates/, defaults/, vars/, files/, meta/). Переиспользуемая единица.
  • Handlers — «ленивые» таски: вызываются notify, запускаются один раз в конце play (обычно restart service).
  • Templates — Jinja2 ({{ var }}, {% if %}, {% for %}), filters (default, to_nice_json, b64encode), tests.
  • Lookup — получение данных вне inventory: {{ lookup('env','HOME') }}, {{ lookup('file','cert.pem') }}, vault, etcd.
  • when / loop / tags: условия, циклы, группировка тасков для выборочного запуска.
  • Ansible Vault: ansible-vault encrypt_string, --vault-password-file. Шифрует YAML значения (AES256).
# Простой role task
- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present
    update_cache: true
    cache_valid_time: 3600

- name: Render nginx config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    mode: "0644"
    validate: "nginx -t -c %s"   # проверка синтаксиса перед тем как положить
  notify: Restart nginx

Идемпотентность

  • Задача — описывать состояние, а не действие. «Пакет установлен», а не «запусти apt install».
  • Декларативные модули (apt, copy, template, systemd_service, user, lineinfile) — идемпотентны из коробки.
  • command/shell — не идемпотентны; обязательно creates:, removes:, changed_when:, failed_when:.
  • Проверка: второй прогон должен давать changed=0. Это «zen of Ansible».

Push vs pull

  • Push (ansible): запускаешь с control-машины, идёт по SSH. Простая модель, нужно SSH-доступ к узлам.
  • ansible-pull: узел сам клонирует репо и применяет. Годно для больших флотов (edge, IoT), cron-based.

Тестирование ролей — Molecule

  • Scenarios: create → prepare → converge (применить роль) → verify (тесты: testinfra, ansible, shell) → destroy.
  • Драйверы: docker, podman, vagrant, ec2.
  • Идеально в CI: каждый PR к роли — Molecule + lint + syntax-check.

Salt — обзор

  • Модель: master/minion (push-event-driven). Salt-ssh — agentless (как Ansible).
  • Grains — факты о minion-е (как Ansible facts).
  • Pillar — приватные данные, назначаемые per-minion (параллель к Ansible vars/Vault).
  • States (SLS) — YAML + Jinja, задают желаемое состояние.
  • Reactor — реакция на события (подписался на event bus → запустил state).
  • Плюс: скорость, масштаб (event-bus ZeroMQ). Минус: сложнее, меньше community, debug тяжёлый.

Сравнение CM-инструментов

AnsibleSaltPuppetChef
МодельAgentless, pushMaster/minion (event bus)Master/agent pullServer/client pull
ЯзыкYAML + JinjaYAML + Jinja (или Py)Puppet DSLRuby DSL
Порог входаНизкийСреднийВысокийВысокий
Сильная сторонаПростота, ad-hocСкорость, event-drivenEnterprise complianceInfrastructure-as-code «по-настоящему»

10. Микросервисная инфраструктура

Nginx (reverse proxy)

upstream api {
    least_conn;                       # round_robin | least_conn | ip_hash | hash $var
    server api1:8080 max_fails=3 fail_timeout=30s;
    server api2:8080;
    keepalive 64;                     # пул upstream-соединений (HTTP/1.1 keep-alive)
}

limit_req_zone $binary_remote_addr zone=api:10m rate=50r/s;

server {
    listen 443 ssl http2;
    server_name api.example.com;
    ssl_certificate /etc/ssl/fullchain.pem;
    ssl_certificate_key /etc/ssl/privkey.pem;

    location / {
        limit_req zone=api burst=100 nodelay;
        proxy_pass http://api;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_read_timeout 60s;
    }

    location /static/ {
        proxy_cache static_cache;
        proxy_cache_valid 200 1h;
        proxy_pass http://api;
    }
}
  • Балансировка: round_robin (default), least_conn, ip_hash (sticky), hash $cookie_sid consistent.
  • Keepalive к upstreamkeepalive N в upstream + proxy_http_version 1.1 + обнулить Connection.
  • Rate limiting: limit_req (leaky bucket), limit_conn.
  • TLS: Mozilla intermediate профиль, OCSP stapling, HSTS; сертификаты через certbot/cert-manager.
  • Graceful reload — nginx -s reload (SIGHUP): worker процессы дорабатывают запросы, новые принимают новые worker-ы.

HAProxy

  • L4 (tcp) и L7 (http) — один из лучших балансировщиков.
  • Active health checks: check, httpchk, agent-check. Можно смотреть ответ (http-check expect).
  • Stick tables — хранилище per-IP/cookie для rate-limit, stickiness, abuse-detection.
  • ACL — условная маршрутизация: acl is_api path_beg /apiuse_backend api_be if is_api.
  • Отличный stats endpoint (stats uri /haproxy?stats), Prometheus exporter встроен.

Traefik

  • Dynamic providers: Docker labels, Kubernetes Ingress/CRD (IngressRoute), Consul, File. Конфиг обновляется без рестартов.
  • Middlewares: auth, headers, rate-limit, redirect, strip-prefix — цепочкой.
  • Автоматический ACME/Let's Encrypt (TLS-ALPN, HTTP-01, DNS-01).
  • Сильный UI (dashboard), хорошо в k8s для быстрой интеграции.

Kafka

  • Topic делится на partitions. Порядок гарантирован только внутри partition.
  • Replication factor: сколько реплик каждой partition (обычно 3). ISR (In-Sync Replicas) — те, что не отстают сверх replica.lag.time.max.ms.
  • Producer: acks=0/1/all (all = подтверждение от всех ISR, самая надёжная), retries, idempotent.
  • Consumer group: разбивка partitions между consumer-ами группы. Одна partition → один consumer в группе.
  • Offset commit: auto (рискованно — потеря/дубли) vs manual (sync/async).
  • Exactly-once: idempotent producer + transactions + read_committed на consumer-е.
  • Kafka Connect — коннекторы (source/sink) для БД, S3, Elastic, без собственного кода.
  • KRaft (Kafka Raft) — в 3.x заменяет ZooKeeper (метаданные внутри Kafka).

Redis

  • Типы данных: String, List, Hash, Set, Sorted Set (ZSET), Stream, Bitmap, HyperLogLog, Geo, JSON (модуль).
  • Persistence:
    • RDB — snapshot на диск (fork + save). Компактно, быстрый restart, потеря последнего окна.
    • AOF — append-only file всех записей. Надёжнее, большие файлы, rewrite периодически.
    • Комбинация AOF+RDB (aof-use-rdb-preamble yes) — snapshot + инкременты.
  • Репликация master→replica (async). Sentinel — failover для одномастерной реплики.
  • Cluster — шардирование на 16384 hash slots, multi-master.
  • Eviction (maxmemory-policy): noeviction, allkeys-lru, allkeys-lfu, volatile-ttl.
  • Кейсы: cache-aside, session store, pub/sub, rate limiter (INCR + EXPIRE), leaderboard (ZSET), distributed lock (SET NX EX, Redlock-алгоритм).

Keycloak

  • Realm — изолированная tenant-зона (свои юзеры, клиенты, роли).
  • Client — приложение, которое интегрируется (confidential/public/bearer-only).
  • Протоколы: OAuth2 (authorization_code, client_credentials, device_code), OIDC (расширение над OAuth2 с ID-token JWT), SAML.
  • Federation — LDAP/AD/Kerberos.
  • Brokering — вход через Google/GitHub/другой IdP.
  • Fine-grained permissions: roles, groups, attributes, token-exchange, impersonation (с аудитом).
  • HA: база (Postgres) + infinispan распределённый cache + shared cache stack; реплика без sticky session может терять session если cache не shared.

11. Безопасность и PCI-DSS

PCI-DSS — 12 требований (v4.0 — мнемоника)

  1. Установить и поддерживать сетевую защиту (firewall/NGFW, сегментация).
  2. Применять безопасные конфигурации (harden OS/Apps, не использовать vendor defaults).
  3. Защищать stored account data (шифрование, truncation, tokenization; CVV нельзя хранить после авторизации).
  4. Защищать CHD при передаче через публичные сети (TLS 1.2+, strong ciphers).
  5. Защищать системы от malware (AV/EDR).
  6. Разрабатывать и поддерживать secure software (SDLC, code review, SAST).
  7. Ограничить доступ к CHD по принципу need-to-know.
  8. Идентифицировать и аутентифицировать пользователей (MFA обязателен для админ-доступа и удалённого).
  9. Ограничить физический доступ к CHD.
  10. Логировать и мониторить доступ к сети и CHD (retention ≥ 1 год, 3 месяца «hot»).
  11. Регулярно тестировать безопасность (vuln scan, pentest, wireless scan).
  12. Поддерживать политику информационной безопасности (процессы, роли, обучение).

Сегментация и CDE

  • CDE (Cardholder Data Environment) — сегмент, где обрабатываются/хранятся/передаются карточные данные.
  • Сегментация снижает объём аудита (scope): всё вне CDE можно вывести из-под части требований, если firewall корректно изолирует.
  • Jump host (bastion): единственная точка входа в CDE из admin-сегмента, с MFA, логированием, session recording.
  • В k8s — отдельный кластер/namespace + NetworkPolicy default-deny + admission (Kyverno), можно усилить service mesh mTLS (Istio/Linkerd).

Шифрование

  • At rest: LUKS (диск), dm-crypt, AWS EBS/KMS, pgcrypto/transparent data encryption.
  • In motion: TLS 1.2/1.3, strong ciphers (Mozilla intermediate), HSTS.
  • Key management: HSM (аппаратный security module), KMS (AWS/GCP/Azure), HashiCorp Vault (transit engine = crypto-as-a-service).
  • Rotation: PCI — минимум ежегодно, при увольнении/компрометации — немедленно.

Identity / MFA

  • Пароли: сложность, unique per account, password managers.
  • MFA обязателен: TOTP (Google Authenticator/Authy), FIDO2/WebAuthn, SMS — не рекомендуется (SIM-swap).
  • Least privilege: отдельные admin-аккаунты, just-in-time access (Teleport, Boundary, AWS IAM Identity Center).

Logging and monitoring

  • Требуется хранить не менее 1 года; 3 месяца — быстрый доступ.
  • Логируем: все доступы к CHD, admin-действия, изменения permissions, создание/удаление аккаунтов, access/init/stop audit-лога.
  • Integrity: write-once хранилище, подписанные sinks (WORM S3 Object Lock).
  • SIEM (Splunk/Elastic SIEM/Wazuh) для корреляции.

Vulnerability & patch management

  • Внешние ASV-сканы (Approved Scanning Vendor) — ежеквартально.
  • Внутренние — после «significant change».
  • Инструменты: Nessus, Qualys, OpenVAS.
  • SLA на патчи: critical — 30 дней, остальные — по risk matrix.

SAST / DAST / SCA

Что анализируетИнструменты
SASTИсходный код (white-box)SonarQube, Semgrep, Checkmarx, Fortify
DASTРаботающее приложение (black-box)OWASP ZAP, Burp Suite, Nikto
IASTИнструментация runtimeContrast, Checkmarx
SCAЗависимости и их CVESnyk, Dependency-Track, Trivy (fs), OWASP Dep-Check
Secrets scanningУтечки в gitgitleaks, trufflehog, GitLab Secret Detection

Container security

  • Минимальные базы, non-root, read-only root FS (readOnlyRootFilesystem: true).
  • Подпись образов (cosign) и проверка в admission (Kyverno verifyImages).
  • SBOM в pipeline + сравнение с CVE DB.
  • Runtime security: Falco (syscall anomalies), Tracee, Sysdig Secure.
  • Registry scanning: Trivy, Harbor встроенный сканер.

HashiCorp Vault

  • KV secrets (v2 с versioning).
  • Dynamic secrets: Vault генерит временного юзера БД/AWS при запросе — TTL, auto-revoke. Убийца static creds.
  • Transit engine: crypto-as-a-service — приложение посылает данные, Vault возвращает шифртекст, ключи никогда не покидают Vault.
  • Auth methods: AppRole, Kubernetes (SA token), JWT/OIDC, AWS IAM.

mTLS / Service Mesh

  • mTLS — взаимная аутентификация сертификатами на обоих концах TLS. В микросервисах — must-have для zero-trust.
  • Istio: богатый, sidecar Envoy, L7 политики, traffic mirroring, большая сложность.
  • Linkerd: проще, легче, Rust-микропрокси. mTLS из коробки без конфиг-кошмара.
  • Cilium Service Mesh (eBPF): без sidecar, одной нодой на каждом узле.

12. Scripting (Bash / Python)

Bash — каркас рабочего скрипта

#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'

trap 'rc=$?; echo "ERR on line $LINENO: $BASH_COMMAND (rc=$rc)" >&2; exit $rc' ERR
trap 'tmp=${TMP:-}; [[ -n "$tmp" && -d "$tmp" ]] && rm -rf "$tmp"' EXIT

log()  { printf '%(%F %T)T  %s\n' -1 "$*" >&2; }
die()  { log "FATAL: $*"; exit 1; }

usage() { cat <"$TMP/health"
jq -e '.status=="ok"' "$TMP/health" || die "unhealthy"

Гвозди, которые надо знать

  • set -e — exit на первой ошибке. -u — unset var = ошибка. -o pipefail — fail всего pipe при fail любого звена. -E — ERR trap наследуется в функции.
  • Разница: [ ] (test, POSIX), [[ ]] (bash — regex, глоб, && || внутри), (( )) (арифметика, C-like).
  • Heredoc: cat <<EOF (с подстановкой), <<'EOF' (буквально), <<-EOF (обрезка leading TAB).
  • Process substitution: diff <(cmd1) <(cmd2).
  • Массивы: arr=(a b c); echo "${arr[@]}", ${#arr[@]} — длина, ${arr[*]} — склеенная строка.
  • Trap: trap 'cmd' EXIT ERR INT TERM.
  • Параллельность: xargs -P N, GNU parallel, wait.
  • Не забываем LC_ALL=C для стабильного лексикографического sort в скриптах.

Полезные one-liners

# Топ-10 IP в access.log
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

# Грохнуть старые файлы
find /var/log -type f -mtime +30 -name '*.log' -print0 | xargs -0 rm -f

# Считать 5xx в nginx за последний час
awk '$4 > "'"$(date -d '1 hour ago' +'[%d/%b/%Y:%H:%M:%S')"'"' access.log \
  | awk '$9 ~ /^5/' | wc -l

# Массовые curl с лимитом параллелизма
xargs -a hosts.txt -P 10 -I {} curl -s -o /dev/null -w "{} %{http_code}\n" https://{}/health

# jq: вытащить поле и привести массив объектов к CSV
jq -r '.items[] | [.metadata.name, .status.phase] | @csv' pods.json

Python — когда переключаться

Если скрипт:

  • > 100 строк ветвлений и data-structure manipulation — Python.
  • Работает с JSON/YAML/XML глубоко — Python (bash+jq начинает душить).
  • Требует хороших ошибок и тестов — Python.
  • Работает с REST API, k8s API, БД, SSH-туннелями — Python.

Python — типовой стек

# venv + requirements
python3 -m venv .venv && source .venv/bin/activate
pip install requests pyyaml kubernetes psycopg2-binary paramiko

# HTTP
import requests
r = requests.get("https://api.example.com/v1/items",
                 headers={"Authorization": f"Bearer {token}"},
                 timeout=10)
r.raise_for_status()
data = r.json()

# K8s
from kubernetes import client, config
config.load_kube_config()   # или load_incluster_config() внутри pod
v1 = client.CoreV1Api()
for p in v1.list_pod_for_all_namespaces(watch=False).items:
    print(p.metadata.namespace, p.metadata.name, p.status.phase)

# SSH/паралелльно
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("host", username="core", key_filename="~/.ssh/id_ed25519")
_, out, err = ssh.exec_command("uptime")
print(out.read().decode())
# Аргументы
import argparse
ap = argparse.ArgumentParser()
ap.add_argument("--host", required=True)
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()

# Логирование
import logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)-7s %(name)s  %(message)s",
)
log = logging.getLogger("deploy")

13. Сети и DNS

OSI / TCP/IP (кратко, но ТОЧНО)

OSITCP/IPПример
7 ApplicationApplicationHTTP, DNS, SSH, SMTP
6 PresentationTLS (иногда уровень 5/6)
5 SessionTLS handshake state
4 TransportTransportTCP, UDP
3 NetworkInternetIP, ICMP, routing
2 Data LinkLinkEthernet, ARP, VLAN, MAC
1 PhysicalLinkкабели, 802.11

Классика собеседования: «что происходит, когда ты вводишь https://x в браузере?» — DNS (recursive → authoritative) → TCP handshake (SYN, SYN-ACK, ACK) → TLS handshake (ClientHello + SNI → ServerHello + cert → key exchange → Finished) → HTTP-запрос → парсинг HTML → подгрузка ресурсов и т.д.

TCP — состояния и ловушки

  • Handshake: SYN → SYN-ACK → ACK.
  • 4-way teardown: FIN → ACK → FIN → ACK.
  • TIME_WAIT: у инициатора закрытия, длится 2×MSL (≈60-120s). Защита от поздних пакетов и переиспользования портов. Сотни тысяч TIME_WAIT — симптом ephemeral-port exhaustion (настроить net.ipv4.ip_local_port_range, tcp_tw_reuse, keep-alive).
  • CLOSE_WAIT: у того, кто получил FIN, но ещё не закрылся сам. Копящиеся CLOSE_WAIT — баг в приложении: не закрывает socket.
  • Retransmit — потерянные пакеты → повторная передача с экспоненциальным backoff. Metric: TcpExtRetransSegs в /proc/net/netstat.
  • Nagle's algorithm и TCP_NODELAY — аккуратно с latency-sensitive.

DNS

ЗаписьДля чего
A / AAAAIP v4 / v6
CNAMEАлиас к другому имени (нельзя на apex зоны)
MXMail exchanger
TXTПроизвольный текст (SPF, DKIM, ACME-challenge)
SRVService record (порт + target), часто для XMPP, SIP, k8s
PTRОбратная зона (IP → имя)
NS / SOAДелегирование / метаданные зоны
CAAКакие CA могут выдавать сертификаты на домен
  • Рекурсивный резолвер — ходит за клиента (8.8.8.8, 1.1.1.1, локальный).
  • Авторитативный — отдаёт данные своей зоны.
  • TTL — время кеширования. Перед миграцией за день-два снижают до 60s.
  • Split-horizon — разные ответы внутренним vs внешним клиентам (одно имя — private IP внутри, public снаружи).
  • DNSSEC — криптоподпись записей (RRSIG) + цепочка доверия до корня.

CoreDNS в Kubernetes

  • Сервисы резолвятся как <svc>.<ns>.svc.cluster.local.
  • /etc/resolv.conf в pod: search <ns>.svc.cluster.local svc.cluster.local cluster.local + options ndots:5.
  • Из-за ndots:5 запрос api.example.com (3 точки) сначала пройдёт через search-list → 4 запроса на NXDOMAIN + один успешный. Типовая причина «DNS лагает».
  • Решение: в приложении делать абсолютные имена с точкой в конце (api.example.com.) или настроить dnsConfig.options: ndots=2 в поде.

HTTP/1.1 vs 2 vs 3

HTTP/1.1HTTP/2HTTP/3
ТранспортTCP + TLSTCP + TLS (ALPN)QUIC поверх UDP (встроен TLS 1.3)
МультиплексНет (pipelining провальный)Да, streams в одном соединенииДа, без head-of-line блокировки TCP
Сжатие заголовковНетHPACKQPACK
Head-of-lineНа соединениеНа TCP-уровне (при потере пакета)Нет (UDP streams)
Server pushНетДа (обесценено)Нет (убран)

Keep-alive: держим TCP/TLS соединение открытым для нескольких HTTP-запросов. В HTTP/2 это базовая модель.

Chunked encoding (HTTP/1.1): Transfer-Encoding: chunked — стримим ответ неизвестной длины (хекс-длина каждого чанка + CRLF + данные). В HTTP/2/3 — нативный framing.

TLS / сертификаты

  • Handshake (TLS 1.2): ClientHello → ServerHello + Certificate + ServerKeyExchange → ClientKeyExchange → ChangeCipherSpec → Finished.
  • TLS 1.3: один round-trip, 0-RTT опция; убраны RSA key exchange и слабые шифры.
  • SNI (Server Name Indication) — клиент сообщает hostname в ClientHello → сервер выбирает cert для этого имени.
  • Цепочка сертификатов: server cert → intermediate → root. Клиент должен получить полный fullchain без root (root в trust store).
  • Let's Encrypt / cert-manager: ACME HTTP-01 (/.well-known/acme-challenge/) или DNS-01 (TXT в зоне). Обновление автоматом.
  • Диагностика: openssl s_client -servername X -connect X:443, curl -v, testssl.sh, SSLLabs.

L4 vs L7 балансировка

L4 (TCP/UDP)L7 (HTTP/S)
ВидитIP, портыHTTP method, URL, headers, cookies
ПроизводительностьВыше (меньше parsing)Ниже, зато богаче роутинг
TLSОбычно passthroughОбычно terminate на LB
ПримерыHAProxy (tcp), AWS NLB, IPVSnginx, Traefik, AWS ALB, Istio

TLS termination: на LB — проще управлять сертификатами, есть возможность WAF/rate-limit. Passthrough — end-to-end шифрование нужно для PCI в CDE.

14. Highload, HA, масштабируемость

Вертикаль vs горизонталь

  • Scale-up — больший инстанс. Предел железа, единая точка отказа, дёшево в начале.
  • Scale-out — больше инстансов. Требует stateless/shared-state; лучше для отказоустойчивости.

Stateless дизайн

  • Каждый запрос самодостаточный, любое состояние — во внешнем store (БД, Redis, S3).
  • Sticky session — компромисс, когда нельзя сразу переписать.
  • JWT для auth — stateless identity (есть нюансы revocation).

Кеширование (слои)

СлойПримерЭффект
ClientBrowser cache, Service Worker0 запросов на backend
CDNCloudflare, Fastly, AkamaiРазгрузка egress + TLS, edge close to user
Reverse proxyNginx proxy_cache, VarnishРазгрузка backend
ApplicationRedis/MemcachedДорогие вычисления и запросы
DBshared_buffers, materialized views, result cacheРазгрузка IO

Cache invalidation — одна из двух сложных вещей в CS. Паттерны: TTL, write-through, write-behind, cache-aside (lazy), read-through.

Очереди и async

  • Kafka — distributed log, высокая пропускная способность, долгое хранение, replay.
  • RabbitMQ — брокер с routing (direct/topic/fanout), DLX, priorities; меньше throughput, богаче семантика.
  • NATS — очень быстрый, at-most-once (core) / at-least-once (JetStream).
  • Зачем нужны: развязка сервисов (loose coupling), сглаживание нагрузки, retry с персистентностью, event sourcing.

CAP, PACELC

  • CAP: при сетевом разделении (P) система может быть либо C (consistent, отказывать в записи), либо A (available, но с возможным split-brain).
  • PACELC: если Partition — P vs C; Else (в норме) — L (latency) vs C.
  • Примеры: Postgres/Spanner — CP (CAP), линеаризуемы; DynamoDB/Cassandra — AP; Redis — зависит от конфигурации.
  • Eventual consistency: записи постепенно сходятся; устраивает для лент, счётчиков; плох для денег.

Репликация и шардинг

  • Master-slave (primary-replica) — пишет один, читают многие. Риск: отставание, split-brain при failover.
  • Multi-master: конфликты записей → нужен conflict resolution (Cassandra LWW, CRDT).
  • Шардинг: hash (ровно), range (locality, hotspot risk), directory (lookup). Rebalance — боль.

Надёжность вызовов

  • Rate limiting: токен-бакет / leaky bucket. Защита от перегруза и abuse.
  • Circuit breaker: если upstream сыпется, не бить по нему (открыть цепь), пробовать иногда (half-open). Библиотеки: resilience4j, Hystrix (depr), Envoy outlier detection.
  • Retry с backoff + jitter: иначе «thundering herd» после сбоя.
  • Idempotency keys: клиент отправляет Idempotency-Key: uuid, сервер дедуплицирует. Must-have в платежах.
  • Timeout-ы на всех уровнях: connect, read, total. Без них — каскадный отказ.
  • Bulkhead: отдельные пулы ресурсов по сервисам/приоритетам, чтобы отказ одного не топил остальных.

Chaos engineering, graceful degradation

  • Регулярные «game days» с инжекцией сбоев (Chaos Mesh, Litmus, Gremlin, AWS Fault Injection Simulator).
  • Graceful degradation: при падении некритичного сервиса приложение работает в сокращённой функциональности (feature flag → «recommendations недоступны»).
  • Feature flags (LaunchDarkly, Unleash, Flagsmith) — на A/B и аварийное выключение фич.

15. Incident management и SRE-практики

Ops vs DevOps vs SRE

Ops
Традиционная эксплуатация: разделение dev/ops, изменения через тикеты, ручные процедуры.
DevOps
Культура и практика: общая ответственность, автоматизация, CI/CD, «you build it, you run it».
SRE
Google-подход: инженерный взгляд на надёжность, SLI/SLO/error budget, toil reduction, post-mortem культура, «treat ops as a software problem».

On-call

  • Ротации: primary/secondary, follow-the-sun (разные часовые пояса).
  • Alert fatigue: убирать/тихо-хоронить алерты, которые не ведут к action. «Page-worthy» — только то, что требует немедленного вмешательства и что можно что-то сделать.
  • Time-to-ack, escalation ladder, runbook-и обязательны.

Postmortem (blameless)

  • Фокус на системе, не на людях. «What happened, why, what's next», без поиска виноватых.
  • Структура: summary, impact (кто и как страдал), timeline (UTC, минута-в-минуту), root cause (5 whys), contributing factors, action items с owner/deadline, lessons learned.
  • Action items должны быть конкретные и отслеживаемые до закрытия.

Метрики инцидентов

МетрикаЧто меряет
MTTD (Mean Time To Detect)От появления до обнаружения
MTTA (Mean Time To Acknowledge)От алерта до ack-а on-call-ом
MTTR (Mean Time To Restore/Repair/Recover)От начала до восстановления
MTBF (Mean Time Between Failures)Среднее между инцидентами

Runbooks vs Playbooks

  • Runbook — пошаговая инструкция «если алерт X — сделать 1-2-3». Ссылка прямо в alert annotation.
  • Playbook — более общий сценарий (деплой, failover, DR).
  • Цель — довести до автоматизации: сначала runbook → потом auto-remediation (кто-то должен реально это жать, пока не поймут, что безопасно).

Работа на инциденте — роли

  • IC (Incident Commander) — координирует, принимает решения.
  • Ops / Subject matter — фиксят.
  • Comms — общается с наружным миром (status page, клиенты).
  • Scribe — ведёт таймлайн, позже отдаст на postmortem.

Расскажи про самый сложный инцидент — шаблон ответа

  1. Контекст: что за продукт, масштаб, моя роль.
  2. Симптомы: что заметил, какие метрики/алерты.
  3. Первые гипотезы и как их проверил (методично отбрасывая, не «залочились» на одной).
  4. Что оказалось root cause — и чего не хватало, чтобы это увидеть раньше.
  5. Фикс: короткий (останавливает пожар) + долгий (устраняет класс проблемы).
  6. Уроки и действия (мониторинг, runbook, тест, архитектурное решение).

16. Живые кейсы — сценарные вопросы

Под в CrashLoopBackOff — с чего начнёшь?
  1. kubectl describe pod — events (ImagePullBackOff? OOMKilled? FailedScheduling?). Last State / Reason / Exit code.
  2. kubectl logs --previous — логи предыдущего запуска.
  3. Проверить probes — не бьёт ли liveness раньше, чем приложение успело стартовать (→ добавить startupProbe или поднять initialDelaySeconds).
  4. Entrypoint/args — совпадают ли с ожидаемым. kubectl exec в sleep-image с тем же образом, посмотреть файлы.
  5. Ресурсы: describe pod → requests/limits; kubectl top pod; частое OOM — поднять memory limit или поправить heap.
  6. Сеть/зависимости: не может достучаться до БД/Vault? NetworkPolicy? DNS? kubectl exec ... -- nslookup mysql.
  7. ConfigMap/Secret: правильно ли смонтированы? kubectl exec ... -- env | grep.
В кластере 5xx — как локализовать?
  1. Смотрим на слой Ingress/LB: метрики по status (5xx rate), апстримы (какой именно сервис).
  2. Grafana — RED-дашборд сервиса: rate/errors/duration; сравнение per-pod (может быть «больной» pod).
  3. Логи (Kibana/Loki) по trace_id или timestamp-корреляции.
  4. Traces (Jaeger/Tempo): путь запроса → какой хоп кидает error. Часто это таймаут на удалённый сервис/БД.
  5. Сама БД: long-running queries, locks, replication lag.
  6. Событийное: проверяем — был ли деплой в это время (rollout revision), autoscaling, eviction нодом, network policy change.
  7. Если подозрение на один pod — kubectl delete pod (scheduler пересоздаст); воспроизводится ли.
Postgres вдруг стал медленным — что смотришь?
  1. Ресурсы хоста: CPU/IO-wait/диск (iostat, df — не кончилось ли место → switch в read-only), memory pressure.
  2. pg_stat_activity: сколько активных коннектов, есть ли «idle in transaction», долгие queries.
  3. pg_stat_statements: топ по total_time / mean_time / calls — новичок ли в топе, изменилось ли что-то.
  4. Плейны: EXPLAIN (ANALYZE, BUFFERS) подозрительного запроса; rows estimate vs actual; seq scan на большой таблице.
  5. VACUUM: pg_stat_user_tables — когда последний раз vacuum/analyze, dead_tup, n_live_tup.
  6. Blocking: pg_blocking_pids(), long transactions.
  7. Replication lag (pg_stat_replication).
  8. Внешние изменения: деплой приложения (новые запросы?), обновление версии, auto-tuning параметров, smaller instance.
Нужно раскатить новую версию сервиса без даунтайма — как организуешь?
  1. Совместимость API: новая версия должна принимать запросы/данные от старой и наоборот (expand-contract для БД-миграций — сначала добавили колонку nullable, задеплоили писателей, потом бекфилл, потом NOT NULL).
  2. Strategy: rolling (дешевле) или canary (безопаснее). В k8s: Deployment strategy, maxSurge/maxUnavailable; Argo Rollouts / Flagger для canary с автометриками.
  3. PDB — гарант «не менее N реплик».
  4. Readiness probe — чтобы не пустить трафик на not-ready pod. PreStop hook + terminationGracePeriodSeconds — корректно завершать активные запросы.
  5. Запас мощности: HPA не «жмёт», пулы до БД/Redis выдерживают.
  6. Мониторинг во время релиза: error rate / latency / saturation на обеих версиях. Автосгон canary при деградации (Flagger metrics-check).
  7. Процедура rollback заранее протестирована.
В логах Kibana ничего нет, а приложение пишет — где искать?
  1. Выходит ли приложение в stdout/stderr (12-factor) или в файл? Если в файл — собирает ли его агент (маунт, путь)?
  2. На ноде: docker logs/crictl logs подтверждают, что runtime видит логи.
  3. На collector'е (Fluent Bit/Filebeat DaemonSet): kubectl logs — есть ли ошибки (not authorized to ES, backpressure, parse error).
  4. Logstash/ingest pipeline: не дропает ли события grok-ом/условием.
  5. Elasticsearch: cluster health (/_cluster/health), disk watermark (read-only при high watermark!), права роли, ILM не переместил ли индекс в cold/frozen без UI-дефолта.
  6. Kibana: data view / index pattern совпадает с реальным именем индекса; выбран правильный timestamp field; time range корректный.
  7. Network: TLS-ошибки, firewall, k8s NetworkPolicy.
Как спроектируешь мониторинг нового микросервиса с нуля?
  1. Определить SLO — что значит «работает» с точки зрения клиента: availability, latency p95/p99, error rate. Пороги.
  2. RED метрики (rate/errors/duration) на HTTP-интерфейсе — стандартные Prometheus-дашборды.
  3. USE на ресурсах: CPU (node/container), memory, сеть, диск.
  4. Бизнес-метрики — что-то доменное (payments_succeeded_total, orders_in_queue).
  5. Логи в JSON с trace_id, уровнями, PII-маскированием; отгрузка в EFK/Loki.
  6. Tracing через OpenTelemetry — instrument HTTP-клиент и серверы, пропагируем traceparent.
  7. Health endpoints: /livez (только сам процесс), /readyz (зависимости); k8s probes.
  8. Алерты только на SLO-burning и жёсткие инфраструктурные события. Линки на runbook, owner, severity.
  9. Дашборд для сервиса (Grafana): RED, USE, SLO/error budget burn rate, deploy markers.
  10. Game day / load test — проверить, что метрики реально отражают проблему.

Поведенческое — что часто спрашивают

  • Расскажи о сложной миграции / крупном инфраструктурном изменении.
  • Конфликт с разработчиком/командой — как разрулил.
  • Как учишься новому (курсы, книги, play-projects)?
  • Какая твоя самая «любимая» неудача?
  • Как принимаешь решение «fix vs rewrite»?

Что спросить у интервьюера (сигнал зрелости команды)

  • Как устроен on-call, сколько page-ов в неделю?
  • Есть ли формальные SLO и error budget?
  • Процесс post-mortem — blameless? публикуются?
  • Как идёт обновление кластеров / OS / БД — график и кто делает?
  • Что за toil съедает основное время DevOps-команды сейчас, и план по его сокращению?
  • Как устроен onboarding — документация, shadow-on-call, mentor?

Сгенерировано как подготовка к собеседованию на вакансию DevOps. Обновляй/правь прямо в /srv/cheatsheet/index.html — nginx отдаёт файл на лету, без перезапусков.