BÀI VIẾT // [DEVOPS] Quản lý hệ thống với Bash: Theo dõi tài nguyên và can thiệp tiến trình trong DevOps
Quản lý hệ thống với Bash: Theo dõi tài nguyên và can thiệp tiến trình trong DevOps

Quản lý hệ thống với Bash: Theo dõi tài nguyên và can thiệp tiến trình trong DevOps

Hướng dẫn sử dụng Bash để giám sát CPU, RAM, disk, quản lý tiến trình và can thiệp hệ thống DevOps với ps, top, kill, systemctl, journalctl, df, free, lsof, ss.

DEVOPS BASH

Quản lý hệ thống với Bash trong DevOps

Trong bài viết trước, chúng ta đã tìm hiểu cách tích hợp Bash vào các pipeline CI/CD, từ việc kích hoạt Strict Mode đến bảo vệ secret và chống Command Injection. Bây giờ, chúng ta sẽ quay lại “mặt đất” — nơi Bash phát huy sức mạnh trực tiếp trên server: quản lý hệ thống và giám sát tài nguyên.

Khi vận hành hạ tầng DevOps, bạn không thể lúc nào cũng ngồi trước màn hình quan sát server. Đôi khi một tiến trình ngốn RAM bất thường, một ổ disk đầy ảnh hưởng đến log rotation, hay một dịch vụ quan trọng bị crash giữa đêm. Những lúc như vậy, một script Bash đúng chuẩn có thể phát hiện sự cố trước khi bạn kịp nhận cuộc gọi khẩn cấp, và thậm chí tự động can thiệp để khôi phục dịch vụ.

Bài viết này sẽ hướng dẫn bạn các lệnh cốt lõi để giám sát tài nguyên hệ thống (CPU, RAM, Disk, Network), quản lý tiến trình và dịch vụ, cùng một script thực hành DevOps kết hợp cron để theo dõi server liên tục.


Giám sát tài nguyên hệ thống

CPU và tải hệ thống

Lệnh top là công cụ đầu tiên bạn nghĩ đến khi cần xem CPU đang chạy gì:

1
2
# Chạy top 1 lần rồi thoát (phù hợp cho script)
top -bn1 | head -5

Để lấy đúng phần trăm CPU trống, dùng grepawk:

1
2
3
4
# CPU usage (tính từ idle)
cpu_idle=$(top -bn1 | grep "Cpu(s)" | awk '{print $8}' | tr -d ',')
cpu_usage=$(echo "100 - $cpu_idle" | bc)
echo "CPU usage: ${cpu_usage}%"

Hoặc dùng cách chính xác hơn với mpstat (từ gói sysstat):

1
2
# CPU usage trung bình trong 1 giây
mpstat 1 1 | tail -1 | awk '{print "CPU usage: " 100 - $NF "%"}'

uptime cho bạn cái nhìn nhanh về tải hệ thống (load average) trong 1, 5, 15 phút:

1
uptime | awk -F'load average:' '{print "Load average:" $2}'

Lưu ý load average: Nếu load average lớn hơn số CPU cores, hệ thống đang bị quá tải. Ví dụ server 4 cores mà load trung bình là 5.2 nghĩa là có tiến trình đang chờ xử lý.

RAM và swap

Lệnh free hiển thị nhanh trạng thái bộ nhớ:

1
free -h

Để tính phần trăm RAM đã dùng:

1
2
# RAM usage percentage
free | awk '/Mem:/ {printf "RAM: %.1f%% used\n", $3/$2*100}'

Kiểm tra swap có đang bị “dùng” không — dấu hiệu cảnh báo khi hệ thống cạn RAM:

1
2
3
4
5
# Kiểm tra swap usage
swap_used=$(free | awk '/Swap:/ {print $3}')
if [[ "$swap_used" -gt 0 ]]; then
    echo "Cảnh báo: Đang sử dụng ${swap_used}KB swap"
fi

Disk và inode

df hiển thị dung lượng ổ cứng:

1
2
# Dung lượng partition theo %
df -h --output=target,pcent | tail -n +2

Kiểm tra partition nào vượt ngưỡng 80%:

1
2
3
4
5
6
while read -r mount usage; do
    pct=$(echo "$usage" | tr -d '%')
    if [[ "$pct" -ge 80 ]]; then
        echo "Cảnh báo: $mount đang dùng $usage dung lượng"
    fi
done < <(df -h --output=target,pcent | tail -n +2)

du giúp tìm thư mục chiếm nhiều dung lượng nhất:

1
2
# Top 10 thư mục lớn nhất từ /
du -h --max-depth=1 / 2>/dev/null | sort -rh | head -10

Kiểm tra inode — nhiều khi dung lượng trống nhưng inode đã hết:

1
df -i --output=target,ipcent | tail -n +2

Quản lý tiến trình

Liệt kê và tìm tiến trình

ps là lệnh cơ bản nhất để xem tiến trình đang chạy:

1
2
3
4
5
# Liệt kê tất cả tiến trình với CPU và RAM
ps aux --sort=-%cpu | head -15

# Tìm tiến trình theo tên
ps aux | grep -E '[n]ginx' | awk '{print $2, $4"%CPU", $11}'

Mẹo: Dùng [n]ginx thay vì nginx để grep không bắt chính dòng lệnh grep itself.

Để lấy PID của một tiến trình cụ thể:

1
2
3
4
# Lấy PID của Nginx master process
pidof nginx
# Hoặc
pgrep -f "nginx: master"

Xử lý tiến trình

kill gửi tín hiệu đến tiến trình. Hiểu rõ các tín hiệu giúp bạn can thiệp đúng cách:

Tín hiệuSốÝ nghĩaKhi nào dùng
SIGTERM15Yêu cầu dừng lịch sựThử dừng trước, cho tiến trình dọn tài nguyên
SIGKILL9Dừng ngay lập tứcChỉ dùng khi SIGTERM không hiệu quả
SIGHUP1Tải lại cấu hìnhRestart Nginx, Apache không cần dừng process
SIGSTOP19Tạm dừngTạm dừng tiến trình để điều tra
1
2
3
4
5
6
7
8
# Thử dừng lịch sự trước
kill "$PID"

# Nếu sau 5 giây không chết, force kill
sleep 5
if kill -0 "$PID" 2>/dev/null; then
    kill -9 "$PID"
fi

pkill giúp dừng nhiều tiến trình theo mẫu tên:

1
2
# Dừng tất cả tiến trình python cũ (trừ script hiện tại)
pkill -f "python.*old_script"

Xem tiến trình theo thời gian thực

top hoặc htop (nếu cài thêm) cho phép quan sát realtime:

1
2
# Chạy top với 3 lần cập nhật, mỗi lần cách nhau 1 giây
top -bn3 -d1 | head -30

Nếu server chỉ có CLI và bạn muốn giao diện đẹp hơn top, htop là lựa chọn phổ biến:

1
2
# Cài htop trên Debian/Ubuntu
apt-get install -y htop

Quản lý dịch vụ với systemctl

Trong các hệ thống Linux hiện đại (systemd), systemctl là lệnh chính để kiểm soát dịch vụ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Kiểm tra trạng thái dịch vụ
systemctl status nginx

# Khởi động / dừng / restart
systemctl start nginx
systemctl stop nginx
systemctl restart nginx

# Bật / tắt tự khởi động khi boot
systemctl enable nginx
systemctl disable nginx

Để liệt kê tất cả dịch vụ đang chạy:

1
systemctl list-units --type=service --state=running

Tìm dịch vụ đã fail:

1
systemctl list-units --state=failed

Xem log của dịch vụ bằng journalctl:

1
2
3
4
5
6
7
8
# Log của Nginx trong 50 dòng gần nhất
journalctl -u nginx -n 50 --no-pager

# Log realtime (tương tự tail -f)
journalctl -u nginx -f

# Log từ 1 giờ trước
journalctl -u nginx --since "1 hour ago"

Kiểm tra port và kết nối mạng

ss (Socket Statistics) là lệnh hiện đại thay thế netstat:

1
2
3
4
5
6
7
8
# Liệt kê tất cả port đang lắng nghe
ss -tlnp

# Kiểm tra port cụ thể (ví dụ 80)
ss -tlnp | grep ':80'

# Đếm kết nối theo trạng thái
ss -s

lsof giúp tìm tiến trình đang sử dụng file hoặc port:

1
2
3
4
5
# Tìm tiến trình đang giữ port 443
lsof -i :443

# Tìm tiến trình đang mở file nào đó
lsof /var/log/syslog

Ví dụ thực hành: Script giám sát và can thiệp hệ thống

Dưới đây là một script hoàn chỉnh kết hợp các lệnh trên, thiết kế để chạy qua cron định kỳ, phát hiện sự cố và tự động can thiệp khi cần:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env bash
# ==============================================================================
# Script : system-monitor.sh
# Mục đích: Giám sát tài nguyên hệ thống, cảnh báo và can thiệp tự động
# Chạy qua cron: */5 * * * * /opt/scripts/system-monitor.sh
# ==============================================================================

set -euo pipefail
IFS=$'\n\t'

readonly LOG_FILE="/var/log/system-monitor.log"
readonly MAX_LOG_SIZE=10485760  # 10MB
readonly CPU_THRESHOLD="${CPU_THRESHOLD:-90}"
readonly RAM_THRESHOLD="${RAM_THRESHOLD:-85}"
readonly DISK_THRESHOLD="${DISK_THRESHOLD:-80}"

log() {
    local level="$1"; shift
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a "$LOG_FILE" 2>/dev/null

    # Xoay vòng log nếu quá lớn
    if [[ -f "$LOG_FILE" ]]; then
        local size
        size=$(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null || echo 0)
        if [[ "$size" -gt "$MAX_LOG_SIZE" ]]; then
            mv "$LOG_FILE" "${LOG_FILE}.1"
            touch "$LOG_FILE"
        fi
    fi
}

# Kiểm tra CPU — kill tiến trình ngốn CPU nhất nếu vượt ngưỡng
check_cpu() {
    local cpu_idle cpu_usage top_pid
    cpu_idle=$(top -bn1 | grep "Cpu(s)" | awk '{print $8}' | tr -d ',' || echo "0")
    cpu_usage=$(echo "100 - $cpu_idle" | bc 2>/dev/null || echo "0")

    if [[ "$(echo "$cpu_usage > $CPU_THRESHOLD" | bc 2>/dev/null || echo 0)" -eq 1 ]]; then
        log "WARN" "CPU usage: ${cpu_usage}% (threshold: ${CPU_THRESHOLD}%)"

        # Lấy PID tiến trình消耗 CPU cao nhất
        top_pid=$(ps -eo pid,%cpu --sort=-%cpu | awk 'NR==2 {print $1}')
        if [[ -n "$top_pid" && "$top_pid" != "PID" ]]; then
            local top_cmd
            top_cmd=$(ps -p "$top_pid" -o comm= 2>/dev/null || echo "unknown")
            log "WARN" "Killing PID $top_cmd ($top_pid) — high CPU"

            # Thử SIGTERM trước, chờ 5s rồi SIGKILL
            kill "$top_pid" 2>/dev/null || true
            sleep 5
            if kill -0 "$top_pid" 2>/dev/null; then
                kill -9 "$top_pid" 2>/dev/null || true
                log "WARN" "Force killed PID $top_pid"
            fi
        fi
    else
        log "INFO" "CPU: ${cpu_usage}% — OK"
    fi
}

# Kiểm tra RAM
check_ram() {
    local ram_pct
    ram_pct=$(free | awk '/Mem:/ {printf "%.1f", $3/$2*100}')

    if [[ "$(echo "$ram_pct > $RAM_THRESHOLD" | bc 2>/dev/null || echo 0)" -eq 1 ]]; then
        log "WARN" "RAM usage: ${ram_pct}% (threshold: ${RAM_THRESHOLD}%)"

        # Dọn bộ nhớ cache (an toàn, không mất dữ liệu)
        sync
        echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true
        log "INFO" "Dropped page cache"
    else
        log "INFO" "RAM: ${ram_pct}% — OK"
    fi
}

# Kiểm tra Disk
check_disk() {
    while read -r mount usage; do
        local pct
        pct=$(echo "$usage" | tr -d '%')
        if [[ "$pct" -ge "$DISK_THRESHOLD" ]]; then
            log "WARN" "Disk $mount usage: ${pct}% (threshold: ${DISK_THRESHOLD}%)"

            # Tự dọn file log cũ hơn 30 ngày
            find /var/log -name "*.gz" -mtime +30 -delete 2>/dev/null || true
            log "INFO" "Cleaned old compressed logs (>30 days)"
        fi
    done < <(df -h --output=target,pcent 2>/dev/null | tail -n +2)
}

# Kiểm tra dịch vụ critical
check_services() {
    local critical_services=("nginx" "docker" "sshd")
    for svc in "${critical_services[@]}"; do
        if ! systemctl is-active --quiet "$svc" 2>/dev/null; then
            log "WARN" "Service $svc is not running — attempting restart"
            systemctl restart "$svc" 2>/dev/null || true
            sleep 2
            if systemctl is-active --quiet "$svc" 2>/dev/null; then
                log "INFO" "Service $svc restarted successfully"
            else
                log "ERROR" "Failed to restart service $svc"
            fi
        fi
    done
}

# Kiểm tra port critical
check_ports() {
    local critical_ports=(22 80 443)
    for port in "${critical_ports[@]}"; do
        if ! ss -tln | grep -q ":${port} " 2>/dev/null; then
            log "WARN" "Port $port is not listening"
        fi
    done
}

main() {
    log "INFO" "=== System monitoring started ==="
    check_cpu
    check_ram
    check_disk
    check_services
    check_ports
    log "INFO" "=== System monitoring completed ==="
}

main "$@"

Cấu hình cron để chạy định kỳ

1
2
3
# Chạy mỗi 5 phút
crontab -e
*/5 * * * * /opt/scripts/system-monitor.sh >> /var/log/system-monitor-cron.log 2>&1

Kết quả mẫu khi chạy script

1
2
3
4
5
6
7
8
9
[2026-08-29 10:00:01] [INFO] === System monitoring started ===
[2026-08-29 10:00:01] [INFO] CPU: 23.5% — OK
[2026-08-29 10:00:01] [INFO] RAM: 61.2% — OK
[2026-08-29 10:00:02] [WARN] Disk / usage: 87% (threshold: 80%)
[2026-08-29 10:00:02] [INFO] Cleaned old compressed logs (>30 days)
[2026-08-29 10:00:02] [INFO] Service nginx — running
[2026-08-29 10:00:02] [INFO] Service docker — running
[2026-08-29 10:00:02] [INFO] Service sshd — running
[2026-08-29 10:00:02] [INFO] === System monitoring completed ===

Ghi chú triển khai

  • Tránh kill tiến trình quan trọng: Không bao giờ kill -9 một tiến trình mà bạn chưa xác định rõ là gì. Luôn đọc kỹ output của ps aux trước khi can thiệp. Trong script ví dụ ở trên, chúng ta chỉ kill tiến trình消耗 CPU cao nhất — nhưng trong production, hãy thêm whitelist để bảo vệ các PID quan trọng (database, application server).
  • Log rotation: Script tự xoay vòng log khi file vượt 10MB. Trong thực tế, nên kết hợp với logrotate để quản lý log tập trung và giữ lại lịch sử cần thiết.
  • Ngưỡng tùy chỉnh: Sử dụng biến môi trường (CPU_THRESHOLD, RAM_THRESHOLD, DISK_THRESHOLD) thay vì hardcode để dễ thay đổi theo từng server mà không cần sửa code.
  • Xem xét替代 giải pháp: Bash phù hợp cho các script giám sát đơn giản. Khi hệ thống lớn hơn (hàng trăm server), hãy kết hợp với Prometheus + Grafana hoặc các công cụ monitoring chuyên dụng, nhưng Bash vẫn là lựa chọn nhanh chóng để tạo script can thiệp khẩn cấp.
  • Test trước khi deploy: Luôn chạy script trên staging hoặc dùng bash -x để debug trước khi đưa vào cron trên production.

Lời kết

Quản lý hệ thống không đòi hỏi những công cụ phức tạp — nhiều khi chỉ cần ps, top, free, df, systemctlss kết hợp trong một script Bash gọn gàng là đủ để giữ server chạy ổn định. Từ việc giám sát CPU/RAM, kiểm tra disk, theo dõi trạng thái dịch vụ đến việc tự động can thiệp khi phát hiện sự cố, Bash vẫn là người bạn đồng hành đáng tin cậy của DevOps Engineer.

bài tiếp theo, chúng ta sẽ khám phá cách tự động hóa SSH với Bash: quản lý đồng thời nhiều server, chạy lệnh từ xa và truyền file an toàn bằng ssh, scprsync.

THẢO LUẬN & BÌNH LUẬN