ps aux
Все запущенные процессы. Колонки: USER, PID, %CPU, %MEM, COMMAND
ps aux | grep nginx
Найти процессы по имени. grep отфильтрует строки
top -bn1 | head -20
Снимок нагрузки прямо сейчас. %CPU — кто жрёт процессор
pgrep -f miner
Найти PID по части имени команды. Вернёт число(а)
kill 1234
Вежливо попросить завершиться (сигнал 15 SIGTERM)
kill -9 1234
Принудительно убить, нельзя проигнорировать (SIGKILL)
pkill -f miner.py
Убить процесс по имени, без поиска PID
Порядок: сначала kill, если не помогло — kill -9
df -h
Место на всех дисках. Use% — заполненность. Если >90% — проблема
df -h /
Место только на корневом разделе
du -sh /var/*
Размер каждой папки в /var. -s=итого, -h=читаемо
du -sh /* | sort -rh | head -10
Топ-10 самых больших папок в системе
truncate -s 0 /var/log/syslog
Очистить файл не удаляя его. Безопаснее чем rm для логов
ls -lh /var/log/
Список файлов с размерами. Ищем подозрительно большие
Filesystem Size Used Avail Use% Mounted
/dev/vda1 20G 18G 824M 96% / ← проблема!
/dev/vda2 500M 312M 152M 68% /boot ← норм
ss -tlnp
Какие порты слушают и какая программа. t=TCP l=listen n=числа p=процесс
ss -tlnp | grep 8080
Кто занял конкретный порт
netstat -tlnp
То же что ss, старый вариант
curl -s http://localhost:8080/
Проверить что сервис отвечает. -s = без прогресс-бара
curl -I http://localhost/
Только заголовки ответа (HTTP-статус, сервер...)
ping -c 3 8.8.8.8
Проверить доступность хоста
Iptables (файрвол)
iptables -L INPUT -n
Правила файрвола для входящего трафика
iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
Открыть порт 2222
netfilter-persistent save
Сохранить правила iptables после перезагрузки
Timeout vs Refused: timeout = файрвол DROP (молча выбросил). connection refused = порт закрыт, сервис не слушает
systemctl status nginx
Состояние сервиса + последние логи. Главная команда диагностики
systemctl start nginx
Запустить прямо сейчас
systemctl stop nginx
Остановить
systemctl restart nginx
Перезапустить (stop + start)
systemctl reload nginx
Перечитать конфиг без остановки (если поддерживается)
systemctl enable nginx
Добавить в автозапуск при загрузке
systemctl disable nginx
Убрать из автозапуска
systemctl --failed
Показать все упавшие сервисы. Первое что смотреть
systemctl daemon-reload
Перечитать unit-файлы после редактирования. Обязательно после изменений
journalctl -u nginx -n 50
Последние 50 строк лога сервиса
journalctl -u nginx -f
Следить за логом в реальном времени (-f = follow)
● nginx.service - nginx web server
Active: active (running) since ... ← хорошо
Active: failed (Result: exit-code) ← плохо, смотрим лог ниже
enable ≠ start. enable = будет стартовать при загрузке. start = запустить сейчас. Нужны оба!
# /etc/systemd/system/myapp.service
[Unit]
Description=My Web Application
After=network.target # ждать сети
[Service]
Type=simple
User=www-data # не root!
WorkingDirectory=/opt/app
ExecStart=/opt/app/start.sh
Restart=on-failure # перезапуск при падении
RestartSec=5
[Install]
WantedBy=multi-user.target
После создания файла
systemctl daemon-reload
1. Перечитать файлы
systemctl enable --now myapp
2. Включить И запустить одной командой
nginx -t
Проверить конфиг на ошибки. Запускать ВСЕГДА перед reload/restart
nginx -T | grep error
Показать весь конфиг с включёнными файлами
systemctl reload nginx
Применить конфиг без прерывания соединений
Шаблон конфига /etc/nginx/conf.d/app.conf
server {
listen 80;
server_name _; # любой домен
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Частые ошибки
unknown directive
Опечатка в названии директивы. Смотри точное написание в документации
Address already in use
Порт 80 уже занят. ss -tlnp | grep :80 — кем?
Permission denied /var/log/nginx
Неправильный владелец папки логов. chown -R www-data /var/log/nginx
Алгоритм: nginx -t → исправить → nginx -t → systemctl reload nginx → curl localhost/
sshd -t
Проверить конфиг sshd на ошибки (как nginx -t)
systemctl restart sshd
Применить изменения. Текущая сессия не обрвётся
Два порта в /etc/ssh/sshd_config
# было:
#Port 22
# стало:
Port 22
Port 2222
Проверка авторизованных ключей
cat ~/.ssh/authorized_keys
Ключи которым разрешён вход. Чужой ключ = backdoor!
cat /root/.ssh/authorized_keys
Ключи для root. Особенно подозрительно
> /root/.ssh/authorized_keys
Очистить файл (записать пустоту)
Параметры sshd_config
PermitRootLogin no
Запретить вход напрямую под root
PasswordAuthentication no
Только ключи, без паролей
⚠ Осторожно: не отключай PasswordAuthentication пока не убедился что ключ работает!
fstab — диски при загрузке
cat /etc/fstab
Список дисков которые монтируются при старте
blkid
Реальные UUID всех дисков. Сверяй с fstab!
findmnt --verify
Проверить fstab на ошибки. Должно быть "0 errors"
mount -a
Примонтировать всё из fstab прямо сейчас (тест)
# ОПАСНАЯ строка в fstab:
UUID=99999999-DEAD-BEEF /mnt/data ext4 defaults 0 2
↑ такого UUID нет → система зависнет при загрузке!
# Исправление — закомментировать:
#UUID=99999999-DEAD-BEEF /mnt/data ext4 defaults 0 2
GRUB — загрузчик
cat /etc/default/grub
Настройки GRUB. Смотри GRUB_TIMEOUT
GRUB_TIMEOUT=5
Показывать меню 5 секунд. 0 = опасно (нельзя выбрать запасное ядро)
update-grub
Применить изменения GRUB. Запускать после редактирования
Порядок действий при boot-проблеме: 1) Проверить fstab через blkid 2) Проверить GRUB_TIMEOUT 3) findmnt --verify 4) update-grub
python3 -m venv venv
Создать виртуальное окружение в папке venv
source venv/bin/activate
Войти в окружение. Появится (venv) в приглашении
pip install -r requirements.txt
Установить все зависимости из файла
pip list
Посмотреть что установлено
deactivate
Выйти из окружения
apt-get install -y python3-pip python3-venv
Установить pip и venv если нет
gunicorn
gunicorn --bind 0.0.0.0:8080 --workers 2 app:application
Запустить приложение. app=имя файла, application=переменная Flask
Cron — планировщик задач
crontab -l
Показать задачи текущего пользователя
crontab -l -u root
Показать задачи root
crontab -e
Редактировать свои задачи
crontab -r -u root
Удалить ВСЕ задачи root. Осторожно!
# Формат cron:
* * * * * команда
│ │ │ │ └─ день недели (0-7)
│ │ │ └─── месяц (1-12)
│ │ └───── день (1-31)
│ └─────── час (0-23)
└───────── минута (0-59)
*/5 * * * * — каждые 5 минут
0 3 * * * — каждый день в 03:00
@reboot — при каждом старте системы
Где искать зловреда (persistence)
crontab -l -u root
1. Cron root
ls /etc/cron.d/ /etc/cron.daily/
2. Системные крон-задачи
cat /root/.ssh/authorized_keys
3. Чужие SSH-ключи
find /tmp /var/tmp -name "*.py" -o -name "*.sh" 2>/dev/null
4. Скрипты во временных папках
systemctl list-units --type=service | grep -v "loaded active"
5. Подозрительные сервисы
cat /etc/rc.local
6. Старый способ автозапуска
Структура скрипта
#!/bin/bash # обязательно первая строка
set -e # останавливаться при ошибке
VARIABLE="значение" # переменная (без пробелов у =)
RESULT=$(команда) # результат команды в переменную
DATE=$(date +"%Y%m%d_%H%M%S") # дата/время
echo "$VARIABLE" # вывод переменной
echo "[$(date)] сообщение" # лог с временем
Условия
if [ $? -eq 0 ]; then # если код выхода = 0 (успех)
echo "OK"
else
echo "Ошибка" >&2 # писать в stderr
exit 1
fi
# Операторы сравнения чисел:
-eq равно -ne не равно
-gt больше -lt меньше
-ge ≥ -le ≤
# Строки:
[ "$A" = "$B" ] равны
[ "$A" != "$B" ] не равны
[ -z "$A" ] пустая строка
[ -f "/path" ] файл существует
[ -d "/path" ] директория существует
Циклы
# for
for i in {1..5}; do
echo $i
done
# while + read (читать строки)
ls -1 /var/backups/*.gz | while read FILE; do
rm -f "$FILE"
done
tar — архивы
tar -czf arch.tar.gz /opt/app
c=создать, z=сжать, f=файл
tar -xzf arch.tar.gz
x=распаковать
tar -tzf arch.tar.gz
t=посмотреть содержимое
Права доступа
chmod +x script.sh
Дать право на выполнение (запуск)
chmod 755 file
rwxr-xr-x: владелец всё, остальные читают и запускают
chmod 644 file
rw-r--r--: владелец читает/пишет, остальные только читают
chown www-data:www-data /opt/app
Сменить владельца файла/папки
chown -R www-data /opt/app
-R = рекурсивно (вся папка со вложениями)
ls -la
Подробный список с правами, владельцем, размером
Поиск файлов
find /tmp -name "*.py" 2>/dev/null
Найти все .py файлы в /tmp
find / -name "authorized_keys" 2>/dev/null
Найти все authorized_keys в системе
grep -r "miner" /etc/ 2>/dev/null
Найти слово miner во всех файлах в /etc
sed — редактирование файлов
sed -i 's/старое/новое/' file
Заменить первое вхождение в файле. -i = изменить файл
sed -i 's/старое/новое/g' file
Заменить ВСЕ вхождения (g = global)
sed -i '/строка/d' file
Удалить строку содержащую «строка»
journalctl -u webapp -n 100
Последние 100 строк лога сервиса
journalctl -xe
Общий системный лог с объяснениями. Смотреть при падении
tail -f /var/log/nginx/error.log
Следить за ошибками nginx в реальном времени
tail -100 /var/log/syslog
Последние 100 строк системного лога
dmesg | tail -20
Сообщения ядра. Ошибки железа, диски
strace -p 1234
Следить за системными вызовами процесса. Глубокая отладка
lsof -p 1234
Какие файлы открыл процесс
lsof -i :8080
Кто использует порт 8080
Алгоритм диагностики упавшего сервиса
1. systemctl status myapp ← статус + последние строки лога
2. journalctl -u myapp -n 50 ← полный лог
3. cat /etc/systemd/system/myapp.service ← конфиг unit
4. bash /opt/app/start.sh ← запустить вручную, видеть ошибку
5. ss -tlnp | grep 8080 ← не занят ли порт
#!/bin/bash
SOURCE="/opt/app"
DEST="/var/backups/app"
MAX=5
TS=$(date +"%Y%m%d_%H%M%S")
FILE="${DEST}/backup_${TS}.tar.gz"
mkdir -p "$DEST"
tar -czf "$FILE" -C "$(dirname $SOURCE)" "$(basename $SOURCE)"
if [ $? -ne 0 ]; then
echo "ОШИБКА: бэкап не создан" >&2; exit 1
fi
# Ротация: удалить старые если > MAX
COUNT=$(ls -1 "$DEST"/backup_*.tar.gz 2>/dev/null | wc -l)
if [ "$COUNT" -gt "$MAX" ]; then
EXCESS=$((COUNT - MAX))
ls -1t "$DEST"/backup_*.tar.gz | tail -n "$EXCESS" | xargs rm -f
fi
echo "OK: $FILE (всего: $(ls -1 $DEST/backup_*.tar.gz | wc -l))"
chmod +x /opt/backup.sh && /opt/backup.sh
Сделать исполняемым и запустить
0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1
В cron: каждый день в 03:00 с логированием
# Прогони перед тем как написать "готово"
### Сервисы живые?
systemctl status nginx webapp sshd | grep -E "Active:|●"
### Сайт отвечает?
curl -s http://localhost/ | head -1
### SSH на двух портах?
ss -tlnp | grep sshd
### Автозапуск настроен?
systemctl is-enabled nginx webapp sshd
### Диск в норме?
df -h / | tail -1
### Зловред мёртв?
ps aux | grep -i miner | grep -v grep
crontab -l -u root
### fstab валиден?
findmnt --verify
### Файрвол настроен?
iptables -L INPUT -n | grep -E "22|80|2222"
Правило: каждое исправление → проверь результат сразу. Не копи изменения вслепую.
✓ Сначала осмотр — потом действие
ps aux, df -h, systemctl --failed, top
✓ Пиши в Slack КАЖДЫЙ шаг
"Вижу что nginx упал, смотрю journalctl..."
"Нашёл занятый порт 8080, убиваю PID 2101"
"Проверил — curl возвращает 200"
✓ Если застрял — напиши об этом
"Смотрю /etc/fstab, пока не понимаю почему..."
✓ После каждого исправления — проверь
Починил nginx → nginx -t → systemctl status nginx → curl
✓ Ищи больше чем просят
Нашёл что-то лишнее? Напиши. Это плюс.
✗ Не молчи и не торопись
✗ Не применяй изменения без проверки
✗ Не удаляй файлы без понимания зачем
✗ Не делай rm -rf без крайней необходимости
docker ps
Запущенные контейнеры. -a = все включая остановленные
docker images
Локальные образы
docker run -d --name app -p 8080:80 nginx:alpine
-d=фон, --name=имя, -p=порт хост:контейнер
docker logs -f app
Следить за логами. --tail 100 = последние 100 строк
docker exec -it app sh
Войти в контейнер. Если нет sh — попробуй bash
docker stop app && docker rm app
Остановить и удалить. docker rm -f app = принудительно
docker build -t myapp:1.0 .
Собрать образ из Dockerfile в текущей папке
docker inspect app
Подробная информация: сети, volumes, env, IP
Очистка
docker system prune -f
Удалить остановленные контейнеры + dangling образы
docker system prune -af
+ все неиспользуемые образы. Осторожно!
docker volume prune -f
Удалить неиспользуемые volumes
Dockerfile — минимальный шаблон
FROM python:3.11-slim # базовый образ
WORKDIR /app # рабочая директория
COPY requirements.txt .
RUN pip install -r requirements.txt # слой кэшируется
COPY . .
ENV PORT=8080 # переменная окружения
EXPOSE 8080 # документация (не открывает порт!)
CMD ["python", "app.py"] # команда запуска
Порядок в Dockerfile важен: редко меняющееся — выше (кэш переиспользуется), часто меняющееся — ниже.
docker compose up -d
Запустить все сервисы в фоне
docker compose down
Остановить и удалить контейнеры
docker compose down -v
+ удалить volumes. Данные БД пропадут!
docker compose logs -f app
Логи конкретного сервиса
docker compose ps
Состояние всех сервисов
docker compose exec app sh
Войти в контейнер сервиса
docker compose build --no-cache
Пересобрать образы без кэша
docker compose pull
Обновить образы из registry
Минимальный docker-compose.yml
services:
app:
build: .
ports:
- "8080:8080"
environment:
- DB_HOST=db
depends_on:
- db
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
kubectl get pods -n ns
Поды в неймспейсе. -A = во всех сразу
kubectl logs -f pod-name -n ns
Логи пода в реальном времени. -c = конкретный контейнер
kubectl describe pod pod-name -n ns
Подробный статус: причина CrashLoop/Pending, события
kubectl exec -it pod-name -n ns -- sh
Войти в под (или bash)
kubectl apply -f file.yaml
Применить манифест (создать или обновить)
kubectl delete -f file.yaml
Удалить ресурсы из манифеста
kubectl rollout restart deployment/myapp -n ns
Перезапустить деплоймент (rolling update)
kubectl port-forward pod/myapp 8080:80 -n ns
Пробросить порт пода на localhost:8080
kubectl get events -n ns --sort-by='.lastTimestamp'
События неймспейса по времени. Главный инструмент отладки
kubectl top pods -n ns
CPU и память подов (нужен metrics-server)
Диагностика упавшего пода
1. kubectl get pods -A | grep -v Running ← что не ок?
2. kubectl describe pod POD -n NS ← причина в Events
3. kubectl logs POD -n NS --previous ← лог прошлого запуска
4. kubectl get events -n NS --sort-by='.lastTimestamp'
git status
Изменённые и неотслеживаемые файлы
git log --oneline -10
Последние 10 коммитов в одну строку
git diff
Что изменилось (не staged). --staged = что в индексе
git stash
Спрятать незакоммиченные изменения
git stash pop
Достать последний stash
git checkout -b feature/branch
Создать и переключиться на новую ветку
git reset --soft HEAD~1
Отменить последний коммит, оставить изменения
git reset --hard HEAD~1
Отменить коммит и выбросить изменения. Необратимо!
git cherry-pick abc1234
Применить конкретный коммит в текущую ветку
git fetch origin && git rebase origin/main
Обновить ветку поверх main без merge-коммита
git reset --hard опасна: изменения пропадут. Сначала сделай git stash если не уверен.
2xx — Успех
200 OK — всё хорошо
201 Created — ресурс создан (POST)
204 No Content — успех, тела нет (DELETE)
3xx — Редирект
301 Moved Permanently — постоянный редирект (меняй ссылки)
302 Found — временный редирект
304 Not Modified — кэш актуален, тело не отправляется
4xx — Ошибка клиента
400 Bad Request — кривой запрос (синтаксис, параметры)
401 Unauthorized — нужна авторизация
403 Forbidden — авторизован, но доступа нет
404 Not Found — ресурса нет
405 Method Not Allowed — метод (POST/GET) не поддерживается
429 Too Many Requests — rate limit
5xx — Ошибка сервера
500 Internal Server Error — упал backend, смотри логи
502 Bad Gateway — nginx не достучался до backend
503 Service Unavailable — сервис перегружен или упал
504 Gateway Timeout — backend ответил слишком медленно
502 в nginx = upstream не отвечает. Проверь: запущен ли сервис, правильный ли порт в proxy_pass.
free -h
RAM: total / used / available. available — реально свободна для приложений
vmstat 1 5
CPU, память, I/O каждую секунду, 5 раз. wa=ожидание диска
iostat -x 1 3
Дисковый I/O. %util близко к 100% = диск перегружен
uptime
Load average за 1 / 5 / 15 минут
nproc
Количество CPU-ядер. Норма load average < этого числа
cat /proc/meminfo | grep -E "MemTotal|MemAvailable"
Память в байтах напрямую из ядра
# Load average — что это:
uptime → load average: 0.5, 0.7, 0.4
1м 5м 15м
# Число = длина очереди к CPU
1.0 на 4 ядра ← 25% нагрузки, отлично
8.0 на 4 ядра ← очередь в 2× длиннее ядер, тормоза
env
Все переменные окружения текущего процесса
printenv PATH
Значение конкретной переменной
export MY_VAR=value
Установить для текущей сессии и дочерних процессов
echo $MY_VAR
Прочитать значение
unset MY_VAR
Удалить переменную
source .env
Загрузить переменные из файла в текущий шелл
MY_VAR=test ./script.sh
Передать переменную только этому запуску (не в сессию)
В systemd unit-файле
[Service]
Environment="DB_HOST=localhost" "PORT=8080" # прямо в unit
EnvironmentFile=/opt/app/.env # или из файла
# Формат .env для systemd (без кавычек, без export):
DB_HOST=localhost
DB_PASSWORD=secret
PORT=8080
whoami && id
Кто я и в каких группах
useradd -m -s /bin/bash username
-m=создать домашнюю папку, -s=shell
passwd username
Установить пароль пользователю
usermod -aG sudo username
Добавить в группу. -a=append (не затирать другие группы!)
usermod -aG docker username
Добавить в группу docker (нужен re-login)
su - username
Переключиться на пользователя (с его окружением)
sudo -u www-data whoami
Выполнить команду от имени другого пользователя
visudo
Редактировать /etc/sudoers безопасно (проверяет синтаксис)
grep username /etc/passwd
Запись: логин:x:UID:GID:коммент:home:shell
⚠ usermod без -a: usermod -G docker user удалит все остальные группы! Всегда используй -aG.
curl -v http://example.com
Verbose: все заголовки запроса и ответа
curl -L http://example.com
Следовать редиректам (301/302)
curl -w "%{http_code}" -o /dev/null -s http://
Только HTTP-код ответа. Удобно в скриптах
curl -s http://api/ | jq .
Красивый вывод JSON (apt install jq)
curl -o file.tar.gz http://example.com/file
Скачать файл
POST запросы
# JSON POST
curl -X POST \
-H "Content-Type: application/json" \
-d '{"key":"value"}' \
http://api/endpoint
# С авторизацией
curl -H "Authorization: Bearer TOKEN" http://api/
# Форма
curl -d "user=admin&pass=secret" http://example.com/login
# Тест без DNS (обход резолвинга)
curl --resolve example.com:443:1.2.3.4 https://example.com/
tmux new -s work
Создать именованную сессию
tmux attach -t work
Подключиться к сессии. tmux ls = список
tmux kill-session -t work
Завершить сессию
Сочетания клавиш (префикс Ctrl+B)
Ctrl+B, D — отсоединиться (сессия продолжает жить)
Ctrl+B, C — новое окно
Ctrl+B, N / P — следующее / предыдущее окно
Ctrl+B, 0..9 — переключиться на окно по номеру
Ctrl+B, % — разделить вертикально
Ctrl+B, " — разделить горизонтально
Ctrl+B, ← → ↑ ↓ — переключиться между панелями
Ctrl+B, Z — развернуть/свернуть панель на весь экран
Ctrl+B, [ — режим прокрутки (выход — Q)
Сессии переживают disconnect. Запусти долгую команду в tmux — можно закрыть терминал и вернуться через attach.