❤️ AZDIGI chính thức cập nhật hệ thống blog mới hoàn chỉnh. Tuy nhiên có thể một số bài viết bị sai lệch hình ảnh, hãy ấn nút Báo cáo bài viết ở cuối bài để AZDIGI cập nhật trong thời gian nhanh nhất. Chân thành cám ơn.

Nếu bạn đang quản lý VPS, chắc chắn sẽ có những tác vụ phải làm đi làm lại: kiểm tra disk, backup database, restart service khi lỗi, dọn log cũ… Làm tay một lần thì không sao, nhưng làm mỗi ngày thì mệt. Shell script sinh ra để giải quyết chuyện này.

linux b15 scripting

Trong bài này, mình sẽ đi từ cơ bản đến thực tế: viết script đầu tiên, làm quen biến, điều kiện, vòng lặp, rồi xây dựng những script thật sự hữu ích cho việc quản trị VPS hàng ngày.

Shell script là gì?

Shell script đơn giản là một file text chứa các lệnh Linux, được thực thi tuần tự từ trên xuống dưới. Thay vì bạn phải gõ từng lệnh một trong terminal, bạn gom tất cả vào một file rồi chạy một phát.

Ví dụ, mỗi sáng bạn phải chạy 5 lệnh kiểm tra VPS. Viết thành script, bạn chỉ cần chạy 1 lệnh duy nhất. Kết hợp thêm cron thì hoàn toàn tự động, không cần động tay.

Shell script dùng Bash (Bourne Again Shell), đây là shell mặc định trên hầu hết các distro Linux. Bạn có thể kiểm tra bằng lệnh:

echo $SHELL

Nếu kết quả là /bin/bash thì bạn đang dùng Bash.

Viết script đầu tiên

Tạo một file script bằng nano hoặc vim:

nano hello.sh

Thêm nội dung sau:

#!/bin/bash
echo "Xin chao, day la script dau tien!"
echo "Server: $(hostname)"
echo "Thoi gian: $(date)"

Dòng đầu tiên #!/bin/bash gọi là shebang. Nó báo cho hệ thống biết file này sẽ được chạy bằng Bash. Luôn đặt dòng này ở đầu mỗi script.

Lưu file xong, bạn cần cấp quyền thực thi rồi chạy:

chmod +x hello.sh
./hello.sh

Kết quả sẽ hiện ra hostname và thời gian hiện tại của server. Vậy là bạn đã viết xong script đầu tiên.

Bạn cũng có thể chạy script mà không cần chmod bằng cách: bash hello.sh. Nhưng thói quen tốt là luôn chmod +x để chạy trực tiếp bằng ./.

Biến trong shell script

Biến dùng để lưu trữ giá trị và tái sử dụng trong script. Cú pháp khai báo biến rất đơn giản:

#!/bin/bash
# Khai bao bien
SERVER_NAME="web-server-01"
ADMIN_EMAIL="admin@example.com"
MAX_LOAD=80
# Su dung bien bang ky hieu $
echo "Server: $SERVER_NAME"
echo "Admin: $ADMIN_EMAIL"
echo "Nguong canh bao: $MAX_LOAD%"

Lưu ý quan trọng: không có dấu cách xung quanh dấu = khi khai báo biến. Viết NAME="value" chứ không phải NAME = "value". Đây là lỗi rất phổ biến với người mới.

Ngoài biến tự khai báo, Linux có sẵn nhiều biến hệ thống hữu ích:

#!/bin/bash
echo "User dang chay: $USER"
echo "Thu muc home: $HOME"
echo "Thu muc hien tai: $PWD"
echo "Shell dang dung: $SHELL"
echo "PID cua script: $$"

Bạn cũng có thể gán kết quả của một lệnh vào biến bằng cú pháp $(command):

#!/bin/bash
CURRENT_DATE=$(date +%Y-%m-%d)
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}')
TOTAL_RAM=$(free -m | awk 'NR==2 {print $2}')
echo "Ngay: $CURRENT_DATE"
echo "Disk da dung: $DISK_USAGE"
echo "Tong RAM: ${TOTAL_RAM}MB"

Nhận input từ người dùng

Có hai cách phổ biến để truyền dữ liệu vào script: dùng read để hỏi người dùng, hoặc dùng tham số dòng lệnh (command line arguments).

Dùng read

#!/bin/bash
read -p "Nhap ten domain: " DOMAIN
read -p "Nhap thu muc web (mac dinh /var/www): " WEB_DIR
WEB_DIR=${WEB_DIR:-/var/www}
echo "Domain: $DOMAIN"
echo "Thu muc: $WEB_DIR/$DOMAIN"

Cú pháp ${WEB_DIR:-/var/www} nghĩa là: nếu biến WEB_DIR rỗng (người dùng nhấn Enter không nhập gì), dùng giá trị mặc định /var/www.

Command line arguments

Khi chạy script kèm tham số, ví dụ ./deploy.sh production web01, các tham số sẽ được gán vào các biến đặc biệt:

#!/bin/bash
# Chay: ./deploy.sh production web01
echo "Tham so thu 1: $1"    # production
echo "Tham so thu 2: $2"    # web01
echo "Tat ca tham so: $@"   # production web01
echo "So luong tham so: $#" # 2
echo "Ten script: $0"       # ./deploy.sh

Trong thực tế, bạn sẽ dùng arguments nhiều hơn read. Lý do đơn giản: arguments cho phép script chạy tự động (qua cron, gọi từ script khác) mà không cần người ngồi gõ.

Câu lệnh điều kiện if/else

Điều kiện giúp script đưa ra quyết định: nếu thỏa điều kiện thì làm A, không thì làm B. Cú pháp cơ bản:

#!/bin/bash
FILE="/etc/nginx/nginx.conf"
if [ -f "$FILE" ]; then
    echo "File $FILE ton tai"
else
    echo "File $FILE khong ton tai"
fi

Một số toán tử kiểm tra phổ biến:

Toán tửÝ nghĩaVí dụ
-fFile tồn tại[ -f /etc/passwd ]
-dThư mục tồn tại[ -d /var/www ]
-zChuỗi rỗng[ -z "$VAR" ]
=So sánh bằng (chuỗi)[ "$A" = "$B" ]
!=Không bằng (chuỗi)[ "$A" != "$B" ]
-gtLớn hơn (số)[ "$A" -gt 10 ]
-ltNhỏ hơn (số)[ "$A" -lt 10 ]
-geLớn hơn hoặc bằng[ "$A" -ge 10 ]
-leNhỏ hơn hoặc bằng[ "$A" -le 10 ]

Ví dụ thực tế hơn, kiểm tra xem script có được chạy bằng root không:

#!/bin/bash
if [ "$EUID" -ne 0 ]; then
    echo "Script nay can chay bang root!"
    echo "Dung: sudo $0"
    exit 1
fi
echo "Dang chay voi quyen root, tiep tuc..."

Bạn cũng có thể kết hợp nhiều điều kiện bằng elif:

#!/bin/bash
LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk -F',' '{print $1}' | tr -d ' ')
# Chuyen sang so nguyen de so sanh
LOAD_INT=$(echo "$LOAD" | awk '{printf "%d", $1}')
if [ "$LOAD_INT" -gt 10 ]; then
    echo "CRITICAL: Load qua cao ($LOAD)"
elif [ "$LOAD_INT" -gt 5 ]; then
    echo "WARNING: Load dang cao ($LOAD)"
else
    echo "OK: Load binh thuong ($LOAD)"
fi

Vòng lặp

Vòng lặp giúp lặp lại một thao tác nhiều lần. Có hai loại chính: forwhile.

Vòng lặp for

#!/bin/bash
# Loop qua danh sach co dinh
for SERVICE in nginx mysql redis; do
    echo "Dang kiem tra $SERVICE..."
    if systemctl is-active --quiet "$SERVICE"; then
        echo "  $SERVICE: RUNNING"
    else
        echo "  $SERVICE: STOPPED"
    fi
done

Loop qua các file trong thư mục:

#!/bin/bash
# Nen tat ca file log cu hon 7 ngay
for LOGFILE in /var/log/*.log; do
    if [ -f "$LOGFILE" ]; then
        SIZE=$(du -sh "$LOGFILE" | awk '{print $1}')
        echo "Tim thay: $LOGFILE ($SIZE)"
    fi
done

Loop qua danh sách server để kiểm tra kết nối:

#!/bin/bash
SERVERS="192.168.1.10 192.168.1.11 192.168.1.12"
for SERVER in $SERVERS; do
    if ping -c 1 -W 2 "$SERVER" &> /dev/null; then
        echo "$SERVER: OK"
    else
        echo "$SERVER: KHONG PHAN HOI"
    fi
done

Vòng lặp while

While loop chạy liên tục cho đến khi điều kiện không còn đúng:

#!/bin/bash
# Doc file line by line
while IFS= read -r LINE; do
    echo "Xu ly: $LINE"
done < /etc/hosts
#!/bin/bash
# Cho service khoi dong (toi da 30 giay)
TIMEOUT=30
COUNT=0

while ! systemctl is-active --quiet nginx; do COUNT=$((COUNT + 1)) if [ "$COUNT" -ge "$TIMEOUT" ]; then echo "Timeout: nginx khong khoi dong duoc sau ${TIMEOUT}s" exit 1 fi echo "Cho nginx khoi dong... (${COUNT}s)" sleep 1 done

echo "nginx da san sang!"

Functions

Khi script dài và phức tạp, bạn nên tách thành các function. Mỗi function làm một việc cụ thể, giúp code dễ đọc và tái sử dụng.

#!/bin/bash

# Dinh nghia function log_info() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1" }

log_error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" >&2 }

check_service() { local SERVICE_NAME=$1 if systemctl is-active --quiet "$SERVICE_NAME"; then log_info "$SERVICE_NAME dang chay" return 0 else log_error "$SERVICE_NAME khong chay!" return 1 fi }

# Goi function log_info "Bat dau kiem tra he thong" check_service "nginx" check_service "mysql" log_info "Kiem tra hoan tat"

Một vài điểm cần nhớ về function:

  • local khai báo biến chỉ tồn tại trong function đó, không ảnh hưởng ra ngoài
  • return 0 nghĩa là thành công, return 1 nghĩa là thất bại
  • Tham số truyền vào function cũng dùng $1, $2... giống như tham số script
  • Function phải được định nghĩa trước khi gọi (Bash đọc từ trên xuống)

Ví dụ thực tế 1: Kiểm tra disk usage

Script đầu tiên mà mình nghĩ ai quản lý VPS cũng cần: kiểm tra dung lượng disk và cảnh báo khi sắp đầy.

#!/bin/bash
# check_disk.sh - Canh bao khi disk vuot nguong

THRESHOLD=80 HOSTNAME=$(hostname)

echo "=== Kiem tra disk tren $HOSTNAME ===" echo "Nguong canh bao: ${THRESHOLD}%" echo ""

# Kiem tra tung phan vung ALERT=0 while IFS= read -r LINE; do USAGE=$(echo "$LINE" | awk '{print $5}' | tr -d '%') MOUNT=$(echo "$LINE" | awk '{print $6}') TOTAL=$(echo "$LINE" | awk '{print $2}') USED=$(echo "$LINE" | awk '{print $3}')

if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "[CANH BAO] $MOUNT: ${USAGE}% (da dung $USED / $TOTAL)" ALERT=1 else echo "[OK] $MOUNT: ${USAGE}% (da dung $USED / $TOTAL)" fi done < <(df -h --type=ext4 --type=xfs | tail -n +2)

echo "" if [ "$ALERT" -eq 1 ]; then echo "Co phan vung vuot nguong ${THRESHOLD}%! Can xu ly." exit 1 else echo "Tat ca phan vung binh thuong." exit 0 fi

Chạy thử:

chmod +x check_disk.sh
./check_disk.sh

Bạn có thể điều chỉnh biến THRESHOLD theo nhu cầu. Với SSD, mình thường đặt 85%. Với HDD có nhiều log, nên đặt 75% để có thời gian xử lý.

Ví dụ thực tế 2: Backup web + database

Script backup là thứ không thể thiếu. Script dưới đây sẽ backup thư mục web, dump database MySQL, nén lại rồi gửi lên server remote qua SCP.

#!/bin/bash
# backup.sh - Backup website va database

# === CAU HINH === SITE_NAME="mywebsite" WEB_DIR="/var/www/mywebsite" DB_NAME="mywebsite_db" DB_USER="dbuser" DB_PASS="dbpassword"

BACKUP_DIR="/backup" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_NAME="${SITE_NAME}_${DATE}"

REMOTE_HOST="backup-server" REMOTE_DIR="/backup/remote"

# So ngay giu backup cu RETENTION_DAYS=7

# === FUNCTIONS === log() { echo "[$(date '+%H:%M:%S')] $1" }

cleanup_old_backups() { log "Xoa backup cu hon $RETENTION_DAYS ngay..." find "$BACKUP_DIR" -name "${SITE_NAME}_*.tar.gz" -mtime +$RETENTION_DAYS -delete log "Da don dep xong" }

# === BAT DAU BACKUP === log "Bat dau backup $SITE_NAME"

# Tao thu muc backup neu chua co mkdir -p "$BACKUP_DIR/$BACKUP_NAME"

# Backup web files log "Backup thu muc web..." tar -czf "$BACKUP_DIR/$BACKUP_NAME/web.tar.gz" -C "$WEB_DIR" . 2>/dev/null if [ $? -ne 0 ]; then log "LOI: Backup web that bai!" exit 1 fi log "Backup web thanh cong"

# Backup database log "Dump database..." mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$BACKUP_DIR/$BACKUP_NAME/database.sql.gz" if [ ${PIPESTATUS[0]} -ne 0 ]; then log "LOI: Dump database that bai!" exit 1 fi log "Dump database thanh cong"

# Nen tat ca lai log "Nen toan bo backup..." cd "$BACKUP_DIR" tar -czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME" rm -rf "$BACKUP_NAME"

BACKUP_SIZE=$(du -sh "${BACKUP_NAME}.tar.gz" | awk '{print $1}') log "Backup hoan tat: ${BACKUP_NAME}.tar.gz ($BACKUP_SIZE)"

# Gui len remote server log "Gui backup len $REMOTE_HOST..." scp "${BACKUP_NAME}.tar.gz" "${REMOTE_HOST}:${REMOTE_DIR}/" 2>/dev/null if [ $? -eq 0 ]; then log "Gui len remote thanh cong" else log "CANH BAO: Khong gui duoc len remote!" fi

# Don dep backup cu cleanup_old_backups

log "=== BACKUP HOAN TAT ==="

Mật khẩu database đang để trực tiếp trong script cho dễ hiểu. Trong thực tế, bạn nên dùng file ~/.my.cnf hoặc biến môi trường để tránh lộ password. Xem thêm cách cấu hình [mysqldump] section trong ~/.my.cnf.

Ví dụ thực tế 3: Health check VPS + gửi Telegram

Đây là script mình thấy thực tế nhất. Nó kiểm tra toàn bộ các thành phần quan trọng trên VPS rồi gửi kết quả qua Telegram. Nếu có vấn đề, bạn biết ngay mà không cần SSH vào server.

#!/bin/bash
# healthcheck.sh - Kiem tra suc khoe VPS va gui Telegram

# === CAU HINH TELEGRAM === BOT_TOKEN="YOUR_BOT_TOKEN" CHAT_ID="YOUR_CHAT_ID"

# === CAU HINH NGUONG === DISK_THRESHOLD=80 RAM_THRESHOLD=90 HOSTNAME=$(hostname)

# === FUNCTIONS === send_telegram() { local MESSAGE=$1 curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \ -d chat_id="$CHAT_ID" \ -d text="$MESSAGE" \ -d parse_mode="HTML" > /dev/null 2>&1 }

check_service() { local SERVICE=$1 if systemctl is-active --quiet "$SERVICE" 2>/dev/null; then echo "OK" else echo "FAIL" fi }

# === BAT DAU KIEM TRA === STATUS="ok" REPORT="Health Check - $HOSTNAME" REPORT+="%0A$(date '+%Y-%m-%d %H:%M:%S')" REPORT+="%0A"

# 1. Kiem tra Nginx NGINX_STATUS=$(check_service "nginx") if [ "$NGINX_STATUS" = "OK" ]; then REPORT+="%0A✅ Nginx: Running" else REPORT+="%0A❌ Nginx: STOPPED" STATUS="fail" fi

# 2. Kiem tra MySQL/MariaDB MYSQL_STATUS=$(check_service "mysql") if [ "$MYSQL_STATUS" = "FAIL" ]; then MYSQL_STATUS=$(check_service "mariadb") fi if [ "$MYSQL_STATUS" = "OK" ]; then REPORT+="%0A✅ MySQL: Running" else REPORT+="%0A❌ MySQL: STOPPED" STATUS="fail" fi

# 3. Kiem tra Disk DISK_USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%') if [ "$DISK_USAGE" -gt "$DISK_THRESHOLD" ]; then REPORT+="%0A⚠️ Disk: ${DISK_USAGE}% (vuot nguong ${DISK_THRESHOLD}%)" STATUS="fail" else REPORT+="%0A✅ Disk: ${DISK_USAGE}%" fi

# 4. Kiem tra RAM RAM_TOTAL=$(free | awk 'NR==2 {print $2}') RAM_USED=$(free | awk 'NR==2 {print $3}') RAM_PERCENT=$((RAM_USED * 100 / RAM_TOTAL)) if [ "$RAM_PERCENT" -gt "$RAM_THRESHOLD" ]; then REPORT+="%0A⚠️ RAM: ${RAM_PERCENT}% (vuot nguong ${RAM_THRESHOLD}%)" STATUS="fail" else REPORT+="%0A✅ RAM: ${RAM_PERCENT}%" fi

# 5. Load average LOAD=$(uptime | awk -F'load average:' '{print $2}' | awk -F',' '{print $1}' | tr -d ' ') REPORT+="%0A📊 Load: $LOAD"

# 6. Uptime UP=$(uptime -p) REPORT+="%0A⏱ $UP"

# Ket luan REPORT+="%0A" if [ "$STATUS" = "ok" ]; then REPORT+="%0A🟢 Tat ca binh thuong" else REPORT+="%0A🔴 CO VAN DE CAN XU LY!" fi

# Gui Telegram send_telegram "$REPORT"

# In ra terminal (de debug) echo -e "${REPORT//%0A/\\n}"

Để lấy Bot Token và Chat ID cho Telegram:

  1. Chat với @BotFather trên Telegram, gõ /newbot để tạo bot và lấy token
  2. Chat với @userinfobot để lấy Chat ID của bạn
  3. Thay giá trị BOT_TOKENCHAT_ID trong script

Debug script

Script bị lỗi là chuyện bình thường. Quan trọng là biết cách tìm lỗi nhanh. Bash có sẵn vài công cụ giúp bạn debug.

Chạy với bash -x

Thêm flag -x để Bash in ra từng lệnh trước khi thực thi:

bash -x script.sh

Output sẽ hiện từng dòng lệnh với dấu + phía trước, cùng giá trị thực tế của các biến. Rất hữu ích khi bạn không hiểu tại sao điều kiện if lại đi vào nhánh sai.

set -e và set -u

Hai dòng này nên có ở đầu mọi script nghiêm túc:

#!/bin/bash
set -e  # Dung ngay khi co lenh loi
set -u  # Bao loi khi dung bien chua khai bao
# Hoac viet gon:
set -eu
  • set -e: Script dừng ngay khi bất kỳ lệnh nào trả về lỗi (exit code khác 0). Không có dòng này, script sẽ chạy tiếp dù lệnh trước đó lỗi, và có thể gây hậu quả nghiêm trọng.
  • set -u: Báo lỗi khi bạn sử dụng biến chưa được khai báo. Giúp bắt lỗi typo tên biến ngay lập tức.

Ví dụ không có set -e:

#!/bin/bash
cd /thu-muc-khong-ton-tai    # Lenh nay loi nhung script van chay tiep
rm -rf *                      # XOA HET FILE TRONG THU MUC HIEN TAI!

Với set -e, script sẽ dừng ngay tại dòng cd bị lỗi và không chạy dòng rm. An toàn hơn rất nhiều.

Best practices khi viết shell script

Sau khi đã nắm cú pháp, đây là những thói quen giúp script của bạn ít bug và dễ maintain hơn.

Luôn quote biến

Đây là quy tắc số 1. Luôn đặt biến trong dấu ngoặc kép:

# SAI - se loi neu FILE chua dau cach
if [ -f $FILE ]; then
# DUNG
if [ -f "$FILE" ]; then

Nếu biến chứa khoảng trắng hoặc ký tự đặc biệt mà không quote, Bash sẽ tách nó thành nhiều phần và gây lỗi khó hiểu.

Comment code

Viết comment giải thích tại sao, không phải giải thích cái gì:

# KHONG CAN THIET - ai cung biet lenh nay lam gi
# Tao thu muc backup
mkdir -p /backup
# HUU ICH - giai thich ly do
# Giu lai 7 ngay vi storage chi co 50GB
RETENTION_DAYS=7

Error handling

Kiểm tra kết quả của lệnh quan trọng, đừng giả định mọi thứ luôn thành công:

#!/bin/bash
set -e

# Cach 1: Dung || de xu ly loi cd /var/www/mysite || { echo "Khong the vao thu muc!"; exit 1; }

# Cach 2: Kiem tra exit code mysqldump -u root mydb > backup.sql if [ $? -ne 0 ]; then echo "Dump database that bai!" exit 1 fi

# Cach 3: Trap de don dep khi loi cleanup() { echo "Don dep file tam..." rm -f /tmp/mytemp_* } trap cleanup EXIT

Logging

Script chạy qua cron sẽ không ai nhìn terminal. Ghi log để biết chuyện gì đã xảy ra:

#!/bin/bash
LOGFILE="/var/log/myscript.log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOGFILE" }

log "Bat dau chay script" log "Dang backup database..." # ... cac lenh ... log "Hoan tat"

Dùng tee -a để vừa ghi vào file log, vừa hiện ra terminal khi bạn chạy thủ công.

Kết hợp cron: tự động hóa hoàn chỉnh

Script + cron = tự động hóa hoàn chỉnh. Bạn viết script làm công việc, cron lo việc chạy đúng giờ.

Nếu bạn đã theo dõi từ bài cron jobs trước đó trong series, phần này sẽ rất quen thuộc. Mở crontab:

crontab -e

Thêm các dòng sau:

# Kiem tra disk moi 6 tieng
0 */6 * * * /root/scripts/check_disk.sh >> /var/log/check_disk.log 2>&1

# Backup hang ngay luc 2h sang 0 2 * * * /root/scripts/backup.sh >> /var/log/backup.log 2>&1

# Health check moi 15 phut */15 * * * * /root/scripts/healthcheck.sh >> /var/log/healthcheck.log 2>&1

Vài lưu ý khi kết hợp script với cron:

  • Dùng đường dẫn tuyệt đối cho mọi thứ trong script: lệnh, file, thư mục. Cron không có cùng PATH như terminal.
  • Redirect output vào log bằng >> logfile 2>&1. Không redirect thì output sẽ gửi qua mail (nếu có) hoặc mất hẳn.
  • Test script bằng tay trước khi đưa vào cron. Chạy trực tiếp, xác nhận output đúng, rồi mới thêm vào crontab.
  • Đặt PATH ở đầu script nếu dùng lệnh không nằm trong /usr/bin:
#!/bin/bash
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

Mình thường tạo thư mục /root/scripts/ để gom tất cả script quản trị vào một chỗ. Dễ quản lý, dễ backup, và không bị lẫn với file hệ thống.

Checkpoint: Viết script health check VPS

Đến đây bạn đã biết đủ thứ cần thiết để viết script thật sự. Thử tự viết một script health check theo yêu cầu sau:

  1. Kiểm tra service nginx và mysql có đang chạy không
  2. Kiểm tra disk usage, cảnh báo nếu vượt 80%
  3. Kiểm tra RAM usage
  4. Ghi kết quả vào file log tại /var/log/healthcheck.log
  5. Exit code 0 nếu mọi thứ OK, exit code 1 nếu có vấn đề

Gợi ý: kết hợp những gì đã học ở trên. Dùng function cho từng phần kiểm tra, dùng biến để lưu ngưỡng cảnh báo, dùng exit code phù hợp.

Script mẫu để tham khảo (đừng xem ngay, thử tự viết trước):

#!/bin/bash
set -u

# === CAU HINH === DISK_THRESHOLD=80 RAM_THRESHOLD=90 LOGFILE="/var/log/healthcheck.log" STATUS=0

# === FUNCTIONS === log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOGFILE" }

check_service() { local NAME=$1 if systemctl is-active --quiet "$NAME" 2>/dev/null; then log "[OK] $NAME dang chay" else log "[FAIL] $NAME khong chay!" STATUS=1 fi }

check_disk() { local USAGE USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%') if [ "$USAGE" -gt "$DISK_THRESHOLD" ]; then log "[WARN] Disk: ${USAGE}% (vuot nguong ${DISK_THRESHOLD}%)" STATUS=1 else log "[OK] Disk: ${USAGE}%" fi }

check_ram() { local TOTAL USED PERCENT TOTAL=$(free | awk 'NR==2 {print $2}') USED=$(free | awk 'NR==2 {print $3}') PERCENT=$((USED * 100 / TOTAL)) if [ "$PERCENT" -gt "$RAM_THRESHOLD" ]; then log "[WARN] RAM: ${PERCENT}% (vuot nguong ${RAM_THRESHOLD}%)" STATUS=1 else log "[OK] RAM: ${PERCENT}%" fi }

# === MAIN === log "===== Health Check Start =====" check_service "nginx" check_service "mysql" check_disk check_ram

if [ "$STATUS" -eq 0 ]; then log "Ket qua: Tat ca binh thuong" else log "Ket qua: CO VAN DE CAN XU LY" fi log "===== Health Check End ====="

exit $STATUS

Chạy thử rồi kiểm tra log:

chmod +x healthcheck.sh
sudo ./healthcheck.sh
cat /var/log/healthcheck.log

Khi đã chạy ổn, thêm vào cron để tự động chạy mỗi 15 phút:

*/15 * * * * /root/scripts/healthcheck.sh

Shell script không khó, nhưng cần thực hành. Bắt đầu từ những script nhỏ giải quyết nhu cầu thật của bạn: kiểm tra log, backup file, restart service khi lỗi. Sau đó mới mở rộng dần.

Bài tiếp theo mình sẽ đi vào quản lý user và phân quyền trên Linux, giúp bạn hiểu rõ hệ thống permission và cách thiết lập user an toàn cho VPS.

Chia sẻ:
Bài viết đã được kiểm duyệt bởi AZDIGI Team

Về tác giả

Trần Thắng

Trần Thắng

Chuyên gia tại AZDIGI với nhiều năm kinh nghiệm trong lĩnh vực web hosting và quản trị hệ thống.

Hơn 10 năm phục vụ 80.000+ khách hàng

Bắt đầu dự án web của bạn với AZDIGI