VPS сервер з Docker контейнерами періодично ставав недоступним – не відповідав SSH, сайт не працював. Після перезавантаження через хостинг-панель все запускалося нормально, але проблема повторювалася.
Характеристики сервера:
- RAM: 4.8GB
- CPU: 4 ядра
- Диск: 34GB
- OS: Ubuntu 22.04 LTS
- Контейнери: Traefik, Nginx, PHP, MySQL, Redis, IoT система (Node.js)
Крок 1: Первинна діагностика після перезапуску
Перевірка системних ресурсів
|
1 2 3 |
# Інформація про систему free -h |
Результат:
|
1 2 3 4 |
total used free shared buff/cache available Mem: 4.8Gi 1.9Gi 1.7Gi 19Mi 1.2Gi 2.6Gi Swap: 0B 0B 0B |
⚠️ Перша підозра: SWAP = 0 – немає резервної пам’яті!
Перевірка використання диску
|
1 2 |
df -h |
Результат:
|
1 2 3 |
Filesystem Size Used Avail Use% Mounted on /dev/vda1 34G 14G 21G 41% / |
✅ Диск в нормі (41% використання).
Перевірка запущених контейнерів
|
1 2 |
docker ps --format "table {{.Names}}\t{{.Status}}" |
Результат:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
NAMES STATUS nginx Up 4 minutes laravel-cron Up 3 minutes ai-queue-worker Up 3 minutes telegram-posts-worker Up 3 minutes php83 Up 4 minutes redis_container Up 4 minutes mysql_container Up 4 minutes (healthy) traefik Up 4 minutes iot-backend-server Up 4 minutes (unhealthy) iot-admin-panel Up 4 minutes (health: starting) iot-pgadmin Up 4 minutes iot-redis Up 4 minutes (healthy) iot-database Up 4 minutes (healthy) |
⚠️ Підозра: Багато контейнерів, деякі unhealthy.
Крок 2: Пошук “винуватця” – моніторинг ресурсів
Детальна статистика Docker контейнерів
|
1 2 |
docker stats --no-stream |
КРИТИЧНИЙ РЕЗУЛЬТАТ:
|
1 2 3 4 5 |
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % 8a34296ff9ee iot-admin-panel 195.22% 3.058GiB / 4.799GiB 63.72% 3412300963a3 iot-pgadmin 68.76% 146.1MiB / 4.799GiB 2.97% 7e396394e0bd mysql_container 0.37% 387MiB / 4.799GiB 7.87% |
🔴 ЗНАЙДЕНО ПРОБЛЕМУ!
iot-admin-panel:
- CPU: 195% (майже 2 ядра з 4!)
- RAM: 3.06GB (64% всієї пам’яті сервера!)
iot-pgadmin:
- CPU: 68.76% (додатково навантажує)
- RAM: 146MB
Топ процесів по пам’яті
|
1 2 |
ps aux --sort=-%mem | head -10 |
Результат:
|
1 2 3 4 5 |
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND deploy 2832 0.2 2.7 21877892 138452 ? Sl 07:10 0:00 node next dev 5050 3409 2.5 3.9 232392 199352 ? Sl 07:10 0:07 python3 gunicorn deploy 2391 0.2 2.0 776376 104956 ? Sl 07:10 0:00 node index.js |
📊 Аналіз:
node next dev– Next.js в development режиміpython3 gunicorn– pgAdmin- Обидва процеси активно використовують ресурси
Крок 3: Пошук причини в логах
Перевірка системних логів
|
1 2 |
sudo journalctl -p err -b | tail -50 |
Результат:
|
1 2 3 |
Dec 27 07:10:27 dockerd: time="..." level=warning msg="error locating sandbox id..." Dec 27 07:10:27 dockerd: time="..." level=info msg="Deleting nftables IPv4 rules" error="exit status 1" |
⚠️ Помилки Docker після перезавантаження – контейнери не коректно зупинилися.
Перевірка OOM Killer (Out Of Memory)
|
1 2 |
sudo dmesg | grep -i "out of memory\|oom" |
Результат: Порожній вивід.
💡 Висновок: OOM Killer НЕ спрацював, але це лише тому що сервер завис раніше.
Крок 4: Розуміння причини
Чому Next.js dev режим з’їв всі ресурси?
Next.js в development режимі:
- ✅ Hot Module Replacement (HMR) – перекомпіляція при зміні коду
- ✅ Source maps для дебагу
- ✅ Детальні логи помилок
- ❌ Постійна компіляція TypeScript
- ❌ Webpack Dev Server в пам’яті
- ❌ Відсутність оптимізації коду
Що сталося:
- Next.js компілював код при старті
- Webpack зберігав всі модулі в RAM
- HMR слухав зміни файлів (додаткове навантаження)
- CPU 195% – постійна компіляція
- RAM 3GB – весь build в пам’яті
- Інші контейнери потребували пам’яті
- SWAP = 0 – немає резерву
- Система зависла – kernel не міг виділити пам’ять
Крок 5: Рішення проблеми
Рішення 1: Додати SWAP (негайно)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# Створити файл підкачки 2GB sudo fallocate -l 2G /swapfile # Встановити права доступу sudo chmod 600 /swapfile # Ініціалізувати swap sudo mkswap /swapfile # Увімкнути swap sudo swapon /swapfile # Додати в fstab для автозапуску echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab # Перевірити free -h |
Результат після:
|
1 2 3 4 |
total used free shared buff/cache available Mem: 4.8Gi 1.9Gi 1.7Gi 19Mi 1.2Gi 2.6Gi Swap: 2.0Gi 0B 2.0Gi |
✅ Тепер є резерв 2GB!
Рішення 2: Обмежити ресурси для контейнерів
Відкрити docker-compose.yml IoT проєкту:
|
1 2 3 4 5 6 |
iot-admin-panel: image: your-image # ДОДАТИ ЦІ РЯДКИ: cpus: 1.0 # Максимум 1 CPU ядро (100%) mem_limit: 1g # Максимум 1GB RAM |
Застосувати зміни:
|
1 2 3 |
cd ~/iot_system docker compose up -d --force-recreate iot-admin-panel |
Рішення 3: Перейти на Production режим
У docker-compose.yml:
|
1 2 3 4 5 6 7 |
iot-admin-panel: command: sh -c "npm run build && npm start" # Замість npm run dev cpus: 1.0 mem_limit: 1g environment: - NODE_ENV=production # Production режим |
Production режим Next.js:
- ✅ Один раз компілює при старті
- ✅ Оптимізований код (minified, tree-shaking)
- ✅ Кешування build
- ✅ CPU ~5% замість 195%
- ✅ RAM ~300MB замість 3GB
Рішення 4: Налаштувати swappiness
|
1 2 3 4 |
# Встановити агресивність використання swap echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf sudo sysctl -p |
Що це означає:
swappiness=60(default) – використовує swap коли RAM 40% зайнятаswappiness=10– використовує swap тільки коли RAM 90% зайнята- Для серверів рекомендується 10-20
Крок 6: Моніторинг для запобігання в майбутньому
Скрипт автоматичного моніторингу пам’яті
|
1 2 3 4 5 6 7 8 9 10 11 |
cat > ~/monitor-memory.sh << 'EOF' #!/bin/bash MEMORY_USAGE=$(free | grep Mem | awk '{print ($3/$2) * 100.0}') if (( $(echo "$MEMORY_USAGE > 90" | bc -l) )); then docker stats --no-stream >> /var/log/docker-memory.log echo "$(date): Memory usage critical: $MEMORY_USAGE%" >> /var/log/memory-alert.log fi EOF chmod +x ~/monitor-memory.sh |
Додати в cron (кожні 5 хвилин):
|
1 2 |
(crontab -l 2>/dev/null; echo "*/5 * * * * ~/monitor-memory.sh") | crontab - |
Скрипт моніторингу CPU
|
1 2 3 4 5 6 7 8 9 10 11 12 |
cat > ~/monitor-cpu.sh << 'EOF' #!/bin/bash CPU_LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//') if (( $(echo "$CPU_LOAD > 4.0" | bc -l) )); then echo "$(date): High CPU load: $CPU_LOAD" >> /var/log/cpu-alert.log docker stats --no-stream >> /var/log/docker-cpu.log fi EOF chmod +x ~/monitor-cpu.sh (crontab -l 2>/dev/null; echo "*/5 * * * * ~/monitor-cpu.sh") | crontab - |
Візуальний моніторинг з Netdata (опціонально)
|
1 2 3 4 5 6 |
# Встановити Netdata bash <(curl -Ss https://my-netdata.io/kickstart.sh) # Після встановлення доступ: # http://your-server-ip:19999 |
Netdata покаже:
- Real-time CPU, RAM, Disk, Network
- Графіки за останні години/дні
- Алерти при перевищенні порогів
- Статистика по кожному контейнеру
Крок 7: Результати після виправлення
До виправлення
|
1 2 |
docker stats --no-stream |
|
1 2 3 4 |
NAME CPU % MEM USAGE / LIMIT iot-admin-panel 195.22% 3.058GiB / 4.799GiB ❌ КРИТИЧНО iot-pgadmin 68.76% 146.1MiB / 4.799GiB |
Проблеми:
- Сервер періодично недоступний
- SSH не відповідає
- Сайти не працюють
- Потрібен ручний перезапуск
Після виправлення
|
1 2 |
docker stats --no-stream |
|
1 2 3 4 |
NAME CPU % MEM USAGE / LIMIT iot-admin-panel 5.12% 298.5MiB / 1.0GiB ✅ ВІДМІННО iot-pgadmin 0.02% 257.1MiB / 4.799GiB ✅ НОРМАЛЬНО |
Результат:
- ✅ Сервер стабільний 24/7
- ✅ CPU знизився з 195% до 5% (в 39 разів!)
- ✅ RAM знизилась з 3GB до 300MB (в 10 разів!)
- ✅ Є резерв SWAP 2GB
- ✅ Обмеження на контейнери
- ✅ Автоматичний моніторинг
Алгоритм діагностики (checklist)
Коли VPS недоступний після перезапуску, використовуй цю послідовність:
1️⃣ Базова перевірка ресурсів
|
1 2 3 4 5 |
free -h # RAM та SWAP df -h # Диск uptime # CPU Load docker ps # Статус контейнерів |
Що шукати:
- SWAP = 0? → Додати swap
- Диск > 90%? → Очистити
- Load Average > кількість ядер? → Перевантаження CPU
2️⃣ Знайти проблемний контейнер
|
1 2 |
docker stats --no-stream |
Що шукати:
- CPU > 100%? → Оптимізувати або обмежити
- RAM > 50% від ліміту? → Обмежити mem_limit
- Unhealthy статус? → Перевірити логи
3️⃣ Перевірити логи
|
1 2 3 4 5 6 7 8 9 |
# Системні помилки sudo journalctl -p err -b | tail -50 # OOM Killer sudo dmesg | grep -i "out of memory\|oom" # Логи контейнера docker logs <container_name> --tail 50 |
Що шукати:
- “Out of memory” → Додати RAM або SWAP
- “killed” → OOM Killer вбив процес
- Постійні перезапуски → Помилка в додатку
4️⃣ Виправити проблему
Якщо нестача RAM:
|
1 2 3 4 5 6 7 |
# Додати SWAP sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab |
Якщо високий CPU:
|
1 2 3 4 5 6 |
# Додати обмеження в docker-compose.yml services: problematic-service: cpus: 1.0 mem_limit: 1g |
Якщо dev режим:
|
1 2 3 4 5 6 7 |
# Перейти на production services: app: command: npm run build && npm start environment: - NODE_ENV=production |
5️⃣ Налаштувати моніторинг
|
1 2 3 4 5 6 7 8 9 |
# Створити скрипти моніторингу ~/monitor-memory.sh ~/monitor-cpu.sh # Додати в cron crontab -e */5 * * * * ~/monitor-memory.sh */5 * * * * ~/monitor-cpu.sh |
Корисні команди для діагностики
Моніторинг в реальному часі
|
1 2 3 4 5 6 7 8 9 |
# Топ процесів (інтерактивно) htop # Docker статистика (live) docker stats # Системна інформація top |
Пошук проблем
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# Який контейнер найбільше використовує CPU docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" | sort -k2 -hr # Який контейнер найбільше використовує RAM docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.CPUPerc}}" | sort -k2 -hr # Unhealthy контейнери docker ps --filter "health=unhealthy" # Логи за останню годину sudo journalctl --since "1 hour ago" | grep -i error |
Очистка ресурсів
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# Видалити зупинені контейнери docker container prune # Видалити невикористані образи docker image prune -a # Видалити невикористані volumes docker volume prune # Очистити все docker system prune -a --volumes |
Висновки
Причина проблеми
- Next.js dev режим використовував 195% CPU і 3GB RAM
- Відсутність SWAP – немає резерву пам’яті
- Немає обмежень на контейнери – могли з’їсти всі ресурси
- Відсутність моніторингу – не бачили проблему до краху
Рішення
- ✅ Додали SWAP 2GB
- ✅ Обмежили CPU до 1 ядра та RAM до 1GB
- ✅ Перейшли на production режим Next.js
- ✅ Налаштували автоматичний моніторинг
Результат
- 📉 CPU: 195% → 5% (в 39 разів менше)
- 📉 RAM: 3GB → 300MB (в 10 разів менше)
- 📈 Стабільність: періодичні крахи → 24/7 uptime
- 🔍 Моніторинг: немає → алерти кожні 5 хвилин
Додаткові рекомендації
Для production серверів завжди:
- Додавай SWAP = 2x RAM (мінімум 2GB)
- Обмежуй ресурси контейнерів через
cpusіmem_limit - Використовуй production режим для Node.js, Python, PHP
- Налаштуй моніторинг (cron скрипти або Netdata)
- Автозапуск Docker:
sudo systemctl enable docker - Регулярні бекапи через cron
Не роби:
- ❌ Не запускай dev режим на production
- ❌ Не ігноруй unhealthy контейнери
- ❌ Не залишай сервер без SWAP
- ❌ Не запускай багато контейнерів без обмежень