DevOps

Troubleshooting VPS Ubuntu 24.04: xử lý 502, SSL, permission, disk, load

troubleshooting VPS Ubuntu

Sau khi đã dựng VPS, cài Nginx, nối PHP-FPM, cấu hình MariaDB, bật SSL, hardening SSH, Fail2ban, backup, monitoring, logging và tối ưu hiệu năng, bạn đã có một nền tảng khá đầy đủ để vận hành server. Nhưng production không bao giờ “yên bình mãi”. Sẽ có lúc website báo 502, SSL lỗi, quyền file sai, disk đầy, load tăng cao, database chậm hoặc deploy mới làm hỏng app.

Bài Troubleshooting VPS Ubuntu 24.04: xử lý 502, SSL, permission, disk, load là sổ tay xử lý sự cố cho VPS Ubuntu 24.04 LTS chạy stack LEMP. Mục tiêu không phải là liệt kê mọi lỗi có thể xảy ra, mà là đưa ra quy trình xử lý thực tế: bình tĩnh khoanh vùng, đọc log đúng chỗ, kiểm tra service đúng thứ tự, sửa ít nhất có thể, rollback khi cần, và ghi lại nguyên nhân sau sự cố.

Điểm quan trọng nhất của troubleshooting không phải là nhớ thật nhiều lệnh. Điểm quan trọng là không làm tình hình tệ hơn. Khi website đang lỗi, đừng chỉnh 5 file cùng lúc, đừng restart mọi service theo cảm tính, đừng xóa log, đừng xóa backup, đừng chạy lệnh nguy hiểm khi chưa hiểu tác động. Hãy đi theo playbook.

1. Nguyên tắc troubleshooting VPS production

1.1. Ổn định trước, tối ưu sau

Khi website đang lỗi, mục tiêu đầu tiên là khôi phục dịch vụ hoặc giảm ảnh hưởng. Đừng tối ưu sâu trong lúc sự cố đang diễn ra. Nếu bản deploy mới làm lỗi, rollback trước rồi điều tra sau. Nếu disk đầy, giải phóng dung lượng an toàn trước rồi mới tìm nguyên nhân. Nếu SSL hết hạn, khôi phục HTTPS trước rồi mới tối ưu cấu hình TLS.

Thứ tự ưu tiên nên là:

  • Xác định website có đang down thật không.
  • Xác định phạm vi ảnh hưởng: một domain, một endpoint hay toàn server.
  • Kiểm tra thay đổi gần nhất.
  • Đọc log liên quan.
  • Thực hiện thay đổi nhỏ nhất để khôi phục.
  • Ghi lại nguyên nhân và việc cần phòng tránh.

1.2. Không chỉnh nhiều thứ cùng lúc

Một lỗi rất phổ biến khi troubleshooting là vừa sửa Nginx, vừa restart PHP-FPM, vừa đổi permission, vừa chỉnh database, vừa xóa cache. Nếu website chạy lại, bạn không biết thứ gì đã sửa lỗi. Nếu website tệ hơn, bạn không biết thứ gì gây thêm lỗi.

Quy tắc thực chiến:

  • Mỗi lần chỉ thay đổi một nhóm nhỏ.
  • Chạy lệnh kiểm tra trước khi reload/restart.
  • Ghi lại file đã chỉnh.
  • Biết cách rollback thay đổi đó.

1.3. Đừng xóa log khi đang điều tra

Log là bằng chứng. Khi disk đầy vì log, bạn có thể rotate, vacuum journal, nén hoặc di chuyển log một cách có kiểm soát. Nhưng đừng xóa bừa toàn bộ log trước khi đọc. Nếu xóa mất log, bạn có thể khôi phục website tạm thời nhưng mất luôn nguyên nhân gốc.

1.4. Luôn hỏi: chuyện gì vừa thay đổi?

Nhiều sự cố production không tự nhiên xuất hiện. Hãy hỏi:

  • Vừa deploy code mới không?
  • Vừa cập nhật package không?
  • Vừa đổi Nginx/PHP-FPM/MariaDB config không?
  • Vừa renew SSL hoặc đổi DNS không?
  • Vừa chỉnh permission hoặc owner file không?
  • Vừa bật plugin/cache/CDN rule không?
  • Vừa hết disk do backup/log tăng không?

Nếu có thay đổi rõ ràng ngay trước lỗi, đó là điểm điều tra đầu tiên.

2. Bộ lệnh triage 5 phút đầu tiên

2.1. Kiểm tra website từ bên ngoài

Trước khi SSH vào server, hãy kiểm tra từ phía người dùng:

curl -I https://example.com
curl -I http://example.com
curl -o /dev/null -s -w "http=%{http_code} ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/

Kiểm tra DNS:

dig +short A example.com
dig +short AAAA example.com
dig +short CNAME www.example.com

Nếu chỉ máy bạn lỗi nhưng công cụ ngoài internet vẫn truy cập được, vấn đề có thể nằm ở DNS cache, mạng cá nhân, ISP, firewall local hoặc CDN edge.

2.2. Kiểm tra service chính trên VPS

SSH vào server và chạy:

sudo systemctl status nginx --no-pager
sudo systemctl status php8.3-fpm --no-pager
sudo systemctl status mariadb --no-pager
sudo systemctl status ssh --no-pager

Kiểm tra port đang listen:

sudo ss -ltnp | grep -E ':80|:443|:9000|php|mysql|maria|nginx' || true

2.3. Kiểm tra tài nguyên nền

uptime
free -h
df -h
journalctl --disk-usage
top -b -n 1 | head -n 30

Nếu disk 100%, ưu tiên xử lý disk trước. Nếu RAM cạn và swap tăng mạnh, ưu tiên xử lý OOM/load. Nếu service chết, đọc log service trước khi restart.

2.4. Kiểm tra log lỗi gần nhất

sudo tail -n 100 /var/log/nginx/example.com.error.log
sudo tail -n 100 /var/log/nginx/example.com.access.log
sudo journalctl -u nginx --since "30 minutes ago" --no-pager
sudo journalctl -u php8.3-fpm --since "30 minutes ago" --no-pager
sudo journalctl -u mariadb --since "30 minutes ago" --no-pager
sudo journalctl -p err --since "30 minutes ago" --no-pager

Đừng đọc log quá rộng ngay từ đầu. Hãy bắt đầu bằng 30 phút gần nhất, sau đó mở rộng nếu cần.

3. Tạo script triage nhanh cho VPS

3.1. Vì sao nên có script triage?

Khi sự cố xảy ra, bạn không muốn phải nhớ lại từng lệnh. Một script triage giúp gom thông tin cơ bản: service, disk, RAM, load, Nginx error log, journal lỗi, port listen và các file lớn. Script này không sửa gì cả, chỉ đọc thông tin.

3.2. Tạo script devnook-triage.sh

sudo nano /usr/local/sbin/devnook-triage.sh

Nội dung:

#!/usr/bin/env bash
set -euo pipefail

DOMAIN="${1:-example.com}"
LOG_PREFIX="${2:-example.com}"

echo "== Time =="
date -Is

echo
echo "== System =="
uptime
free -h
df -h

echo
echo "== Top processes =="
ps -eo pid,ppid,user,cmd,%cpu,%mem --sort=-%cpu | head -n 15

echo
echo "== Listening ports =="
ss -ltnp | grep -E ':80|:443|:3306|:9000|nginx|php|mysql|maria' || true

echo
echo "== Services =="
systemctl --no-pager --full status nginx php8.3-fpm mariadb fail2ban 2>/dev/null || true

echo
echo "== HTTP check =="
curl -I "https://${DOMAIN}" 2>/dev/null || true
curl -o /dev/null -s -w "http=%{http_code} ttfb=%{time_starttransfer} total=%{time_total}\n" "https://${DOMAIN}/" || true

echo
echo "== Nginx config test =="
nginx -t 2>&1 || true

echo
echo "== Recent Nginx errors =="
tail -n 80 "/var/log/nginx/${LOG_PREFIX}.error.log" 2>/dev/null || true

echo
echo "== Recent Nginx access =="
tail -n 40 "/var/log/nginx/${LOG_PREFIX}.access.log" 2>/dev/null || true

echo
echo "== Recent service errors =="
journalctl -p err --since "1 hour ago" --no-pager || true

echo
echo "== Biggest files in /var/log =="
find /var/log -type f -printf '%s %p\n' 2>/dev/null \
  | sort -nr \
  | head -n 15 \
  | awk '{printf "%.2f MB %s\n", $1/1024/1024, $2}' || true

Cấp quyền:

sudo chmod +x /usr/local/sbin/devnook-triage.sh

Chạy:

sudo devnook-triage.sh example.com example.com

Khi gặp sự cố, chạy script này trước khi chỉnh cấu hình. Output sẽ giúp bạn biết nên đi vào playbook nào.

4. Playbook xử lý lỗi 502 Bad Gateway Nginx

4.1. 502 nghĩa là gì trong stack Nginx + PHP-FPM?

Trong stack LEMP, lỗi 502 thường có nghĩa là Nginx không nhận được phản hồi hợp lệ từ upstream. Với PHP app, upstream thường là PHP-FPM. Nguyên nhân phổ biến:

  • PHP-FPM không chạy.
  • Nginx trỏ sai socket PHP-FPM.
  • PHP-FPM socket không tồn tại.
  • Permission socket sai.
  • PHP-FPM chạm pm.max_children.
  • PHP app bị fatal error hoặc timeout.
  • Server hết RAM, OOM kill PHP-FPM.
  • Disk đầy khiến PHP/session/cache/log lỗi.

4.2. Kiểm tra nhanh lỗi 502

curl -I https://example.com
sudo tail -n 100 /var/log/nginx/example.com.error.log
sudo systemctl status php8.3-fpm --no-pager
sudo journalctl -u php8.3-fpm --since "30 minutes ago" --no-pager

Tìm các dấu hiệu trong Nginx error log:

connect() to unix:/run/php/php8.3-fpm.sock failed
upstream timed out
recv() failed
permission denied
no live upstreams
Primary script unknown

4.3. Nếu PHP-FPM không chạy

sudo systemctl status php8.3-fpm --no-pager
sudo journalctl -u php8.3-fpm --since "30 minutes ago" --no-pager
sudo php-fpm8.3 -t

Nếu config hợp lệ, restart:

sudo systemctl restart php8.3-fpm
sudo systemctl status php8.3-fpm --no-pager

Nếu PHP-FPM không start được, đọc lỗi trong journal. Đừng restart lặp lại nhiều lần mà không đọc log.

4.4. Nếu Nginx trỏ sai socket PHP-FPM

Kiểm tra socket thật:

ls -la /run/php/
sudo ss -lxnp | grep php || true

Kiểm tra Nginx config:

sudo nginx -T | grep -n "fastcgi_pass"

Nếu socket thật là:

/run/php/php8.3-fpm.sock

thì server block nên có:

fastcgi_pass unix:/run/php/php8.3-fpm.sock;

Sau khi sửa:

sudo nginx -t
sudo systemctl reload nginx

4.5. Nếu PHP-FPM chạm max_children

Kiểm tra log:

sudo journalctl -u php8.3-fpm --since "24 hours ago" --no-pager | grep -Ei 'max_children|server reached|pool' || true

Nếu thấy cảnh báo chạm pm.max_children, đừng tăng ngay. Kiểm tra RAM trước:

free -h
ps -o rss= -C php-fpm8.3 2>/dev/null | awk '{sum+=$1; n++} END {if (n>0) printf "avg_php_child=%.1f MB\n", sum/n/1024; else print "no php-fpm process found"}'

Nếu RAM còn nhiều, có thể tăng nhẹ pm.max_children. Nếu RAM thấp hoặc swap tăng, cần tìm request chậm, bật slowlog hoặc giảm tải bot.

4.6. Nếu upstream timed out

Kiểm tra access log có timing:

sudo tail -n 100 /var/log/nginx/example.com.access.log | grep -E 'rt=|urt=' | tail -n 30

Kiểm tra PHP và database:

sudo journalctl -u php8.3-fpm --since "30 minutes ago" --no-pager
sudo journalctl -u mariadb --since "30 minutes ago" --no-pager
sudo mariadb -e "SHOW PROCESSLIST;"

Nếu request chậm vì database, đi sang playbook MariaDB. Nếu request chậm vì app/plugin/API ngoài, xử lý ở lớp ứng dụng.

4.7. Checklist 502

  • php8.3-fpm đang active.
  • Nginx trỏ đúng socket PHP-FPM.
  • Socket tồn tại trong /run/php/.
  • nginx -t thành công.
  • php-fpm8.3 -t thành công.
  • Không có OOM kill trong journal.
  • Disk chưa đầy.
  • Không chạm pm.max_children liên tục.
  • Nginx error log không còn upstream error mới.

5. Playbook xử lý lỗi SSL/HTTPS Let’s Encrypt

5.1. Phân loại lỗi SSL trước khi sửa

Lỗi SSL có nhiều loại khác nhau:

  • Certificate hết hạn.
  • Certificate không khớp domain.
  • Thiếu intermediate chain.
  • Nginx đang dùng certificate cũ.
  • Certbot renew fail.
  • Port 80 bị chặn làm HTTP-01 challenge fail.
  • DNS trỏ sai server.
  • Cloudflare/CDN SSL mode gây redirect loop.

Không nên chạy certbot liên tục khi chưa biết lỗi là gì. Có thể chạm rate limit hoặc làm cấu hình rối hơn.

5.2. Kiểm tra certificate bằng curl và OpenSSL

curl -Iv https://example.com 2>&1 | head -n 80

Kiểm tra bằng OpenSSL:

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Kiểm tra SAN:

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName

Nếu SAN không có domain bạn đang truy cập, certificate không khớp domain.

5.3. Kiểm tra Certbot

sudo certbot certificates
sudo certbot renew --dry-run
sudo tail -n 200 /var/log/letsencrypt/letsencrypt.log

Nếu dry-run fail, đọc log trước khi chạy renew thật.

5.4. Kiểm tra port 80 và HTTP-01 challenge

sudo ufw status verbose
sudo ss -ltnp | grep -E ':80|:443'
curl -I http://example.com
dig +short A example.com
dig +short AAAA example.com

HTTP-01 cần Let’s Encrypt truy cập được domain qua port 80. Nếu UFW, cloud firewall hoặc CDN rule chặn port 80/path challenge, renew có thể fail.

5.5. Kiểm tra Nginx SSL config

sudo nginx -t
sudo nginx -T | grep -nE 'server_name|ssl_certificate|ssl_certificate_key|listen 443|http2'

Nginx nên dùng fullchain.pem, không chỉ cert.pem:

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Sau khi sửa:

sudo nginx -t
sudo systemctl reload nginx

5.6. Nếu SSL lỗi sau Cloudflare/CDN

Kiểm tra theo thứ tự:

  • Origin server có HTTPS hoạt động trực tiếp không.
  • Cloudflare/CDN đang dùng SSL mode nào.
  • DNS record có proxy không.
  • Origin certificate có khớp hostname không.
  • Nginx có redirect loop không.

Nếu origin đã có Let’s Encrypt hợp lệ, không nên dùng chế độ Flexible. Với production, nên dùng chế độ mã hóa đầy đủ đến origin.

5.7. Checklist SSL

  • DNS trỏ đúng VPS.
  • Port 80 và 443 mở đúng.
  • certbot certificates hiển thị certificate đúng domain.
  • Certificate chưa hết hạn.
  • SAN có đủ root domain và www nếu cần.
  • Nginx dùng fullchain.pem.
  • certbot renew --dry-run thành công.
  • Không có redirect loop sau CDN.

6. Playbook xử lý lỗi permission Nginx/PHP

6.1. Dấu hiệu lỗi permission

Lỗi permission thường xuất hiện dưới dạng:

  • 403 Forbidden.
  • 404 dù file tồn tại.
  • Upload file fail.
  • App không ghi cache/session.
  • Nginx error log có permission denied.
  • PHP báo không thể mở file hoặc không ghi được thư mục.

6.2. Kiểm tra log trước

sudo tail -n 100 /var/log/nginx/example.com.error.log
sudo journalctl -u php8.3-fpm --since "30 minutes ago" --no-pager

Nếu thấy đường dẫn cụ thể bị denied, dùng đường dẫn đó để kiểm tra permission.

6.3. Kiểm tra toàn bộ đường dẫn bằng namei

namei -l /srv/apps/example.com/current/public/index.php

Lệnh này cho thấy quyền của từng thư mục trên đường dẫn. Với Nginx/PHP-FPM, không chỉ file cuối cùng cần đọc được; các thư mục cha cũng cần quyền execute phù hợp.

6.4. Kiểm tra user chạy Nginx và PHP-FPM

ps aux | grep '[n]ginx'
ps aux | grep '[p]hp-fpm'
grep -R "^user\|^group" /etc/php/8.3/fpm/pool.d/*.conf

Trên Ubuntu, Nginx/PHP-FPM thường chạy với user/group như www-data. Nhưng nếu bạn đã custom pool, cần kiểm tra thực tế.

6.5. Kiểm tra quyền đọc/ghi như user www-data

sudo -u www-data test -r /srv/apps/example.com/current/public/index.php && echo "read ok"
sudo -u www-data test -x /srv/apps/example.com/current/public && echo "dir execute ok"
sudo -u www-data test -w /srv/apps/example.com/current/storage && echo "storage write ok"

Thay đường dẫn storage bằng thư mục app thật cần ghi, ví dụ uploads, cache, sessions.

6.6. Sửa permission an toàn

Không dùng:

chmod -R 777 /srv/apps/example.com

Thay vào đó, đặt owner/group rõ ràng. Ví dụ với app deploy bởi user deploy và web server cần đọc:

sudo chown -R deploy:www-data /srv/apps/example.com
sudo find /srv/apps/example.com -type d -exec chmod 750 {} \;
sudo find /srv/apps/example.com -type f -exec chmod 640 {} \;

Với thư mục cần ghi bởi PHP:

sudo chmod -R 770 /srv/apps/example.com/current/storage
sudo chmod -R 770 /srv/apps/example.com/current/public/uploads

Cấu trúc thật tùy app. Mục tiêu là cấp đúng quyền cần thiết, không mở toàn bộ.

6.7. Checklist permission

  • Nginx error log chỉ ra đúng path lỗi.
  • Thư mục cha có quyền execute phù hợp.
  • File PHP/static có quyền đọc phù hợp.
  • Thư mục upload/cache/session có quyền ghi phù hợp.
  • User/group PHP-FPM được xác định rõ.
  • Không dùng chmod 777 để chữa cháy lâu dài.

7. Playbook xử lý disk đầy

7.1. Dấu hiệu disk đầy

Disk đầy có thể gây nhiều lỗi không rõ ràng:

  • MariaDB không ghi được dữ liệu.
  • Nginx/PHP không ghi log/cache/session.
  • Certbot renew fail.
  • Backup fail.
  • Deploy fail.
  • Service crash hoặc không start được.

7.2. Kiểm tra disk

df -h
df -ih
sudo du -xh / | sort -h | tail -n 30

df -h kiểm tra dung lượng. df -ih kiểm tra inode. Có trường hợp disk chưa đầy GB nhưng hết inode vì quá nhiều file nhỏ.

7.3. Tìm thư mục lớn

sudo du -xh /var | sort -h | tail -n 30
sudo du -xh /var/log | sort -h | tail -n 30
sudo du -xh /srv | sort -h | tail -n 30
sudo du -xh /home | sort -h | tail -n 30

Nếu có ncdu:

sudo ncdu /

7.4. Dọn journal và log an toàn

journalctl --disk-usage
sudo journalctl --vacuum-size=500M
sudo logrotate -d /etc/logrotate.conf
sudo logrotate -f /etc/logrotate.conf

Kiểm tra file log lớn:

sudo find /var/log -type f -printf '%s %p\n' | sort -nr | head -n 30 | awk '{printf "%.2f MB %s\n", $1/1024/1024, $2}'

7.5. Kiểm tra file đã xóa nhưng process còn giữ

sudo lsof | grep deleted | head -n 50

Nếu một process vẫn giữ file log đã xóa, disk có thể chưa giảm. Hãy reload/restart service tương ứng trong khung giờ phù hợp.

7.6. Dọn package/cache tạm

sudo apt autoremove --purge -y
sudo apt clean
sudo find /tmp -mindepth 1 -mtime +3 -delete

Cẩn thận khi xóa trong /tmp. Không xóa file đang được app dùng nếu bạn không chắc.

7.7. Kiểm tra backup và Prometheus retention

Nếu đã làm bài C3/C4, kiểm tra:

sudo du -sh /var/backups/devnook 2>/dev/null || true
sudo du -sh /backup 2>/dev/null || true
sudo du -sh /var/lib/prometheus 2>/dev/null || true
systemctl list-timers | grep devnook || true

Backup local, Prometheus retention, log cũ và upload file thường là các nguồn tăng disk lớn.

7.8. Checklist disk đầy

  • Biết filesystem nào đầy.
  • Biết inode có đầy không.
  • Tìm được thư mục/file lớn nhất.
  • Dọn journal/log bằng cách có kiểm soát.
  • Kiểm tra file deleted còn bị process giữ.
  • Không xóa database/upload/backup quan trọng khi chưa xác minh.
  • Sau khi dọn, tìm nguyên nhân tăng disk để tránh lặp lại.

8. Playbook xử lý load cao, CPU cao, RAM thiếu

8.1. Load cao không phải lúc nào cũng do CPU

Kiểm tra:

uptime
nproc
top
mpstat -P ALL 1 5
iostat -xz 1 5

Nếu %iowait cao, bottleneck có thể là disk. Nếu %steal cao trên VPS, có thể do hạ tầng provider/noisy neighbor. Nếu CPU user cao, xem process.

8.2. Tìm process gây tải

ps -eo pid,ppid,user,cmd,%cpu,%mem --sort=-%cpu | head -n 20
ps -eo pid,ppid,user,cmd,%mem,rss --sort=-rss | head -n 20
pidstat 1 5

Nếu process là PHP-FPM, kiểm tra request và PHP-FPM pool. Nếu là MariaDB, kiểm tra slow query. Nếu là backup, cân nhắc đổi lịch backup. Nếu là bot traffic, kiểm tra access log và Fail2ban/CDN rule.

8.3. Kiểm tra OOM

journalctl -k --since "24 hours ago" --no-pager | grep -Ei 'oom|out of memory|killed process' || true

Nếu có OOM, server đã thiếu RAM nghiêm trọng. Không nên tăng PHP-FPM children hoặc MariaDB buffer lúc này. Cần giảm tải, giảm worker, thêm swap hợp lý hoặc nâng cấp VPS.

8.4. Kiểm tra bot traffic gây tải

sudo awk '{print $1}' /var/log/nginx/example.com.access.log | sort | uniq -c | sort -nr | head -n 30
sudo awk '$9 ~ /^[45]/ {print $7}' /var/log/nginx/example.com.access.log | sort | uniq -c | sort -nr | head -n 30

Nếu nhiều request vào endpoint nhạy cảm như /wp-login.php, /xmlrpc.php, /.env, hãy dùng Fail2ban, Nginx rate limit hoặc CDN rule để giảm tải.

8.5. Giảm tải tạm thời

Tùy tình huống, có thể:

  • Bật maintenance page tạm thời.
  • Chặn IP bot rõ ràng bằng firewall/CDN.
  • Tạm dừng job nặng như backup/import.
  • Restart service bị leak memory nếu đã xác định rõ.
  • Rollback deploy mới nếu gây load tăng.

Không restart toàn bộ server nếu chưa cần. Restart có thể che mất bằng chứng và làm downtime dài hơn.

8.6. Checklist load cao

  • Biết load cao do CPU, I/O, RAM hay steal time.
  • Biết process gây tải.
  • Kiểm tra OOM.
  • Kiểm tra bot traffic.
  • Kiểm tra job nền như backup, cron, import.
  • Không tune config khi chưa biết bottleneck.

9. Playbook xử lý MariaDB chậm hoặc lỗi kết nối

9.1. Dấu hiệu lỗi database

  • Website báo database connection failed.
  • Request chậm, TTFB cao.
  • Nginx báo upstream timed out.
  • MariaDB service restart liên tục.
  • Disk đầy làm database không ghi được.
  • Too many connections.

9.2. Kiểm tra service MariaDB

sudo systemctl status mariadb --no-pager
sudo journalctl -u mariadb --since "1 hour ago" --no-pager
sudo mariadb -e "SELECT VERSION();"

Nếu không kết nối được, kiểm tra socket/port:

sudo ss -ltnp | grep 3306 || true
sudo ls -la /run/mysqld/ || true

9.3. Kiểm tra connection và processlist

sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Threads_connected';"
sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Max_used_connections';"
sudo mariadb -e "SHOW VARIABLES LIKE 'max_connections';"
sudo mariadb -e "SHOW PROCESSLIST;"

Nếu nhiều query ở trạng thái giống nhau hoặc chạy lâu, copy query để phân tích bằng slow log/EXPLAIN.

9.4. Bật slow query log khi cần

Nếu chưa bật slow query log, tạo file:

sudo nano /etc/mysql/mariadb.conf.d/60-devnook-troubleshooting.cnf

Nội dung:

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mariadb-slow.log
long_query_time = 1
log_queries_not_using_indexes = 0

Restart:

sudo systemctl restart mariadb
sudo systemctl status mariadb --no-pager

Xem slow log:

sudo tail -f /var/log/mysql/mariadb-slow.log

9.5. Dùng EXPLAIN cho query chậm

EXPLAIN SELECT ...;

Cần chú ý:

  • Query có dùng index không?
  • Có full table scan không?
  • Có filesort hoặc temporary table không?
  • JOIN có cột index phù hợp không?
  • WHERE có lọc trên cột không có index không?

Rất nhiều lỗi database chậm được xử lý bằng index/query/schema tốt hơn, không phải tăng RAM mù.

9.6. Checklist MariaDB

  • MariaDB service active.
  • Disk chưa đầy.
  • Không có OOM kill MariaDB.
  • Không chạm max_connections.
  • Slow query log có dữ liệu nếu nghi query chậm.
  • Query chậm được phân tích bằng EXPLAIN.

10. Playbook rollback sau deploy lỗi

10.1. Khi nào nên rollback?

Nên rollback nếu:

  • Lỗi xuất hiện ngay sau deploy.
  • Nhiều endpoint quan trọng báo 500/502.
  • Database migration lỗi.
  • CPU/RAM tăng bất thường sau release.
  • Không thể xác định nguyên nhân trong thời gian ngắn.

Production không phải nơi để debug quá lâu nếu bản trước vẫn chạy ổn.

10.2. Rollback nếu dùng cấu trúc releases/current

Kiểm tra release:

ls -lah /srv/apps/example.com/releases/
readlink -f /srv/apps/example.com/current

Rollback symlink:

sudo ln -sfn /srv/apps/example.com/releases/PREVIOUS_RELEASE /srv/apps/example.com/current
sudo systemctl reload nginx
sudo systemctl restart php8.3-fpm

Kiểm tra:

curl -I https://example.com
curl -I https://example.com/healthz
sudo tail -n 50 /var/log/nginx/example.com.error.log

10.3. Cẩn thận với database migration

Rollback code không phải lúc nào cũng rollback database. Nếu release mới đã chạy migration thay đổi schema, bản cũ có thể không tương thích. Vì vậy trước migration lớn cần có snapshot/backup và kế hoạch rollback rõ.

10.4. Checklist rollback

  • Biết release trước đang ở đâu.
  • Biết migration có chạy không.
  • Biết backup/snapshot gần nhất.
  • Rollback symlink/code trước nếu an toàn.
  • Kiểm tra health-check.
  • Ghi lại release lỗi để điều tra sau.

11. Playbook DNS, firewall và network

11.1. Kiểm tra DNS

dig +short A example.com
dig +short AAAA example.com
dig +short NS example.com
dig +trace example.com

Nếu domain có AAAA record nhưng VPS chưa cấu hình IPv6 đúng, một số người dùng có thể lỗi qua IPv6 dù IPv4 vẫn chạy.

11.2. Kiểm tra firewall

sudo ufw status verbose
sudo ss -ltnp | grep -E ':22|:80|:443'

Nếu service listen local nhưng bên ngoài không truy cập được, kiểm tra thêm cloud firewall ở provider hoặc rule CDN.

11.3. Kiểm tra từ chính server

curl -I -H "Host: example.com" http://127.0.0.1/
curl -I -H "Host: example.com" https://127.0.0.1/ -k

Nếu local OK nhưng bên ngoài lỗi, vấn đề có thể ở DNS, firewall, CDN hoặc network. Nếu local cũng lỗi, vấn đề nằm ở app/Nginx/service trên server.

12. Ghi incident note sau sự cố

12.1. Vì sao cần ghi lại?

Nếu không ghi lại, sự cố sẽ lặp lại. Một incident note ngắn giúp bạn biết lần sau phải kiểm tra gì trước, config nào đã chỉnh, nguyên nhân gốc là gì và cần phòng tránh thế nào.

12.2. Mẫu incident note

# Incident: YYYY-MM-DD - Website 502

## 1. Thời gian
- Bắt đầu:
- Phát hiện:
- Khôi phục:
- Tổng downtime:

## 2. Ảnh hưởng
- Domain:
- Endpoint:
- Người dùng bị ảnh hưởng:

## 3. Dấu hiệu
- HTTP status:
- Log chính:
- Metrics:

## 4. Nguyên nhân gốc
- Root cause:

## 5. Cách khôi phục
- Lệnh đã chạy:
- File đã chỉnh:
- Rollback nếu có:

## 6. Việc cần làm sau sự cố
- Monitoring/alert:
- Backup/restore:
- Config cần sửa:
- Tài liệu cần cập nhật:

Không cần viết dài như báo cáo doanh nghiệp nếu đây là VPS cá nhân. Nhưng nên có đủ thông tin để lần sau xử lý nhanh hơn.

13. Checklist tổng hợp khi website down

13.1. Checklist 10 phút đầu

  • Kiểm tra website bằng curl -I.
  • Kiểm tra DNS A/AAAA.
  • Kiểm tra nginx, php8.3-fpm, mariadb.
  • Kiểm tra df -h, free -h, uptime.
  • Đọc Nginx error log.
  • Đọc journal service lỗi.
  • Hỏi thay đổi gần nhất là gì.
  • Rollback nếu lỗi xuất hiện ngay sau deploy.

13.2. Checklist lỗi 502/504

  • PHP-FPM active.
  • Socket PHP-FPM đúng.
  • Nginx config hợp lệ.
  • Không OOM.
  • Không disk full.
  • Không chạm max_children liên tục.
  • Không có query database quá chậm.

13.3. Checklist lỗi SSL

  • Certificate chưa hết hạn.
  • Certificate khớp domain.
  • Nginx dùng fullchain.pem.
  • Port 80/443 mở.
  • Certbot dry-run thành công.
  • Không redirect loop sau CDN.

13.4. Checklist disk/load

  • Filesystem chưa đầy.
  • Inode chưa đầy.
  • Journal/log không phình bất thường.
  • Không có file deleted bị process giữ.
  • Biết process ăn CPU/RAM.
  • Biết load cao do CPU hay I/O.

14. Những lỗi cần tránh khi troubleshooting

14.1. Restart mọi thứ theo cảm tính

Restart có thể khôi phục tạm thời, nhưng cũng có thể xóa dấu vết lỗi trong runtime. Trước khi restart, hãy đọc log và trạng thái service.

14.2. Xóa log khi chưa đọc

Khi disk đầy, log thường là nghi phạm. Nhưng hãy kiểm tra và rotate/vacuum có kiểm soát, không xóa toàn bộ bằng rm -rf.

14.3. chmod 777 để chữa permission

chmod 777 có thể làm lỗi biến mất, nhưng mở ra rủi ro lớn. Hãy sửa owner/group/quyền đúng nhu cầu.

14.4. Chạy Certbot liên tục khi DNS sai

Nếu DNS, port 80 hoặc Nginx server block sai, chạy Certbot nhiều lần không giúp gì. Hãy sửa điều kiện xác minh trước.

14.5. Tăng timeout để che app chậm

Timeout cao hơn có thể làm lỗi 504 ít xuất hiện hơn, nhưng request vẫn chậm và worker vẫn bị giữ. Hãy tìm nguyên nhân gốc.

14.6. Không rollback dù deploy mới gây lỗi

Nếu lỗi xuất hiện ngay sau deploy và bản trước ổn, rollback là lựa chọn hợp lý. Debug sau khi dịch vụ đã khôi phục.

15. FAQ

15.1. Gặp 502 Nginx nên kiểm tra gì đầu tiên?

Kiểm tra Nginx error log, trạng thái PHP-FPM, socket PHP-FPM và disk/RAM. Với stack LEMP, 502 thường liên quan đến upstream PHP-FPM không chạy, socket sai, timeout, max_children hoặc server thiếu tài nguyên.

15.2. SSL lỗi thì nên chạy certbot renew ngay không?

Không nên chạy ngay nếu chưa biết lỗi. Trước tiên hãy kiểm tra certificate bằng OpenSSL, xem certbot certificates, kiểm tra DNS, port 80/443, Nginx config và log Certbot. Sau đó mới renew hoặc sửa cấu hình.

15.3. Disk đầy thì có nên xóa /var/log không?

Không nên xóa bừa. Hãy kiểm tra file lớn, journal size, logrotate, file deleted còn bị process giữ, rồi dọn có kiểm soát. Nếu xóa log trước khi đọc, bạn có thể mất nguyên nhân gốc.

15.4. Load cao thì nên restart server không?

Chưa nên nếu chưa biết nguyên nhân. Hãy kiểm tra CPU, RAM, I/O, process, OOM, bot traffic và service log. Restart có thể là biện pháp cuối để khôi phục tạm thời, nhưng không nên là phản xạ đầu tiên.

15.5. Permission lỗi thì có nên chmod 777 không?

Không. Hãy dùng namei -l, kiểm tra user chạy Nginx/PHP-FPM, rồi sửa owner/group/quyền đúng nhu cầu. chmod 777 chỉ che lỗi và tạo rủi ro bảo mật.

15.6. Khi nào nên restore backup?

Khi dữ liệu bị xóa/hỏng, migration phá schema, rollback code không đủ, hoặc server hỏng nặng không thể khôi phục nhanh. Trước khi restore production, nếu có thể hãy restore thử vào môi trường test để xác minh backup.

15.7. Sau bài này nên làm gì?

Sau khi hoàn tất playbook, bạn nên quay lại rà toàn bộ series: backup có restore drill chưa, monitoring có alert chưa, logrotate có hoạt động chưa, SSL renew dry-run có pass chưa, và tài liệu vận hành có ghi rõ cách rollback chưa.

16. Kết luận

Troubleshooting VPS Ubuntu không phải là đoán lỗi rồi restart service. Một playbook tốt cần giúp bạn bình tĩnh đi theo thứ tự: xác nhận lỗi, khoanh vùng phạm vi, kiểm tra thay đổi gần nhất, đọc log đúng chỗ, kiểm tra tài nguyên, sửa ít nhất có thể, rollback khi cần và ghi lại nguyên nhân sau sự cố.

Trong bài này, bạn đã có playbook thực tế cho các lỗi production phổ biến trên VPS Ubuntu 24.04 LTS: 502/504 Nginx + PHP-FPM, SSL Let’s Encrypt, permission, disk đầy, load cao, MariaDB chậm, deploy lỗi, DNS/firewall/network và incident note sau sự cố.

Đây là bài khép lại nhóm Security + Ops trong series VPS Ubuntu. Khi đi hết chuỗi bài, bạn không chỉ biết dựng server chạy được, mà còn biết bảo vệ, backup, quan sát, đọc log, tối ưu và xử lý sự cố. Đó là khác biệt giữa một VPS “đang chạy” và một VPS “có thể vận hành lâu dài”.

Đừng bỏ lỡ

Theo dõi bản tin mới nhất tại đây

Chào bạn 👋
Rất vui được làm quen.

Đăng ký để nhận nội dung hấp dẫn mỗi tháng qua email.

Bằng việc đăng ký, bạn đồng ý với Chính sách bảo mật của DevNook.

Tác giả

Tuấn Lê

Tại DevNook, mình chia sẻ kiến thức thực chiến, kinh nghiệm làm sản phẩm và những công cụ hữu ích giúp bạn làm việc hiệu quả hơn mỗi ngày.