❤️ 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.

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ĩa | Ví dụ |
|---|---|---|
-f | File tồn tại | [ -f /etc/passwd ] |
-d | Thư mục tồn tại | [ -d /var/www ] |
-z | Chuỗi rỗng | [ -z "$VAR" ] |
= | So sánh bằng (chuỗi) | [ "$A" = "$B" ] |
!= | Không bằng (chuỗi) | [ "$A" != "$B" ] |
-gt | Lớn hơn (số) | [ "$A" -gt 10 ] |
-lt | Nhỏ hơn (số) | [ "$A" -lt 10 ] |
-ge | Lớn hơn hoặc bằng | [ "$A" -ge 10 ] |
-le | Nhỏ 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: for và while.
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:
localkhai báo biến chỉ tồn tại trong function đó, không ảnh hưởng ra ngoàireturn 0nghĩa là thành công,return 1nghĩ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:
- Chat với
@BotFathertrên Telegram, gõ/newbotđể tạo bot và lấy token - Chat với
@userinfobotđể lấy Chat ID của bạn - Thay giá trị
BOT_TOKENvàCHAT_IDtrong 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:
- Kiểm tra service nginx và mysql có đang chạy không
- Kiểm tra disk usage, cảnh báo nếu vượt 80%
- Kiểm tra RAM usage
- Ghi kết quả vào file log tại
/var/log/healthcheck.log - 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.
Có thể bạn cần xem thêm
- Backup và phục hồi sự cố cho VPS Linux - Kế hoạch toàn diện
- Tạo Script khởi động lại MySQL khi bị stop trên máy chủ Linux
- Tạo script Monitor tài nguyên VPS và thông báo qua Telegram
- Tạo Bot cảnh báo đăng nhập SSH qua Telegram
- Backup VPS Linux - rsync, tar và chiến lược sao lưu toàn diện
- Logging, Monitoring và IDS trên Linux VPS - auditd, AIDE và Wazuh
Về tác giả
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.