Skip to content

Mở đầu: Vì sao Linux là "vũ khí sống còn" của Backend Dev?

By Nhân Nguyễn on Apr 28, 2026

Mở đầu: Vì sao Linux là “vũ khí sống còn” của Backend Dev?

Nếu bạn là một Java Backend Developer (0-2 năm kinh nghiệm), bạn có thể đã quen phát triển trên Windows với IntelliJ IDEA. Nhưng khi ứng dụng lên production, mọi thứ chạy trên Linux. Và khi production gặp sự cố lúc 2 giờ sáng, không có IDE, không có GUI – chỉ có một terminal SSH vào server. Bạn sẽ không sống sót nếu không biết Linux.


Hãy điểm qua ba câu chuyện thật (được kể trong cộng đồng backend):

Câu chuyện 1: App production đột ngột đóng băng. Junior SSH vào server. Sếp hỏi: “App có chạy không? CPU bao nhiêu? Memory? File log đâu?“. Junior gõ dir, tasklist (lệnh Windows). 30 phút loay hoay. Senior gõ ps aux | grep java, top, tail -f /var/log/app.log – mọi thứ rõ ràng trong 30 giây.

Câu chuyện 2: Connection pool cạn kiệt, app không phản hồi. Junior không biết kiểm tra thế nào. Senior dùng lsof -p <pid> | grep TCP | wc -l phát hiện 1000 connection đang mở – chuẩn đoán ra nguyên nhân ngay tức khắc.

Câu chuyện 3: Disk đầy 100%, app crash. Junior xóa file log /var/log/app.log – nhưng disk vẫn không giải phóng dung lượng. Tại sao? Vì process vẫn còn giữ file handle. Senior dùng lsof | grep deleted để tìm ra file đã xóa nhưng vẫn bị giữ, rồi restart app để giải phóng.

Linux không yêu cầu bạn phải là sysadmin, nhưng bạn phải thuộc lòng 20 lệnh cốt lõi và có một mental model vững chắc để biết cách “đoán” ra lệnh cần dùng khi gặp tình huống mới. Bài viết này chính là tấm bản đồ đó.


1. 5 Tình huống Production Chết Người – Và Cách Senior Xử Lý

Trước khi học lệnh, hãy thấy chúng được dùng để cứu rỗi thế giới thế nào. Dưới đây là 5 kịch bản thường gặp.

1.1. App chậm, CPU 100%

Junior chưa biết bắt đầu từ đâu. Senior làm:

top                              # Xem tiến trình nào ngốn CPU
# PID 1234 java 99% CPU  ← thủ phạm

ps -ef | grep 1234               # Xem command line đầy đủ (JVM options, jar path)

jstack 1234 > thread-dump.txt    # Lấy thread dump
grep -A 20 "RUNNABLE" thread-dump.txt  # Xem thread nào đang chạy, có loop vô hạn?

Phát hiện: một thread đang RUNNABLE và thực thi UserService.search() trong vòng lặp vô tận do lỗi logic.

Giải thích nhanh:

  • top: lệnh xem tài nguyên hệ thống (CPU, memory) theo thời gian thực.
  • jstack: công cụ trong JDK, in ra stack trace của tất cả các thread Java, cực kỳ hữu ích để tìm deadlock hoặc thread chạy không dừng.

1.2. Disk đầy 100%, app crash

df -h                            # Xem dung lượng filesystem
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   50G    0  100% /

du -sh /var/* | sort -h          # Folder nào trong /var nặng nhất?
20G     /var/log                 # ← Thủ phạm

du -sh /var/log/* | sort -h      # File nào trong /var/log?
15G     /var/log/app.log         # Log không rotate

# Fix an toàn:
truncate -s 0 /var/log/app.log   # Làm rỗng file (giữ nguyên handle file đang mở)
# KHÔNG dùng `rm` vì disk space sẽ không được giải phóng nếu process còn mở file.
# Kiểm tra file đã xóa nhưng còn handle: lsof | grep deleted

Giải thích:

  • df -h: disk free, -h để hiển thị human-readable (KB, MB, GB).
  • du -sh: disk usage, -s summary, -h human-readable, sort -h sắp xếp theo kích thước.
  • truncate -s 0 file: giảm kích thước file về 0 byte, không làm mất file descriptor đang được tiến trình sử dụng. Đây là best practice khi cần dọn log khẩn cấp.
  • lsof (list open files): liệt kê tất cả file descriptor đang mở. Khi file bị xóa (rm) nhưng tiến trình vẫn giữ descriptor, nó sẽ xuất hiện với trạng thái (deleted) và dung lượng chỉ được giải phóng khi descriptor được đóng (thường là khi restart process).

1.3. App không phản hồi (có thể đã chết)

curl -v http://localhost:8080/health
* connect to 127.0.0.1 port 8080: Connection refused   # App không listen hoặc đã chết

ps -ef | grep java              # Xem còn process Java nào không
# nothing – app chết

tail -100 /var/log/app.log       # Lấy 100 dòng log cuối
... OutOfMemoryError: Java heap space   # Nguyên nhân: OOM

ls -la /tmp/*.hprof              # JVM tự tạo heap dump khi crash
-rw-r--r-- 1 app app 4G Apr 25 03:14 /tmp/heap.hprof

Kết luận: OutOfMemoryError. Cần phân tích heap dump (dùng Eclipse MAT) để tìm memory leak, hoặc tăng heap (-Xmx).

Giải thích bổ sung:

  • curl -v: kiểm tra HTTP request, tùy chọn -v bật verbose để thấy cả quá trình bắt tay.
  • tail -100 đọc 100 dòng cuối file log. File log thường rất lớn, ta chỉ cần xem lỗi gần nhất.
  • *.hprof là file heap dump do JVM tạo khi cấu hình -XX:+HeapDumpOnOutOfMemoryError.

1.4. Tìm log lỗi hôm qua

grep "ERROR" /var/log/app.log | grep "2026-04-24"

# Hoặc xem kèm stack trace (10 dòng sau lỗi)
grep -A 10 "ERROR" /var/log/app.log

# Đếm tổng số lỗi
grep -c "ERROR" /var/log/app.log

# Live tail, chỉ theo dõi dòng có ERROR
tail -f /var/log/app.log | grep ERROR

1.5. Service A không gọi được service B (lỗi mạng nội bộ)

ping service-b.internal           # ICMP (có thể bị tường lửa chặn)
nslookup service-b.internal       # Phân giải DNS có trả đúng IP không?
curl -v http://service-b:8080/health   # HTTP có trả lời không?
nc -zv service-b 8080             # Netcat: test TCP port có mở không
ss -tnlp | grep 8080             # Trên service B, kiểm tra nó có listen trên cổng đó không
iptables -L -n -v                # Xem firewall rules (cần root)

Giải thích:

  • nc -zv: -z scan port, -v verbose – rất nhanh để kiểm tra port.
  • ss: công cụ thay thế netstat, hiển thị socket statistics.
  • iptables: quản lý firewall trên Linux.

2. 4 Mental Model Cốt Lõi Để “Hiểu Linux”

Nếu bạn chỉ học thuộc lệnh mà không có mô hình tư duy, bạn sẽ dễ quên và không ứng biến được. Hãy ghi nhớ 4 mô hình này.

Mental Model 1: “Mọi thứ đều là file”

Trong Linux, hầu hết mọi thứ đều được biểu diễn dưới dạng file. Điều này cho phép bạn dùng chung các thao tác như đọc, ghi, tìm kiếm cho mọi tài nguyên:

  • File thường: /home/user/code.java
  • Thư mục: /var/log/ (thực chất cũng là file đặc biệt – directory)
  • Thiết bị: /dev/sda1 (ổ cứng), /dev/null (lỗ đen – vứt rác)
  • Thông tin tiến trình: /proc/1234/status (thông tin process 1234)
  • Socket: /proc/1234/net/tcp (danh sách TCP connection của process)
  • Pipe: kết nối giữa các lệnh (|)

Hệ quả: bạn có thể dùng cat, ls, grep trên chính những “file” này để khám phá hệ thống.

Mental Model 2: Process Tree (Cây tiến trình)

Tất cả tiến trình đều có cha mẹ và con cái. Mọi tiến trình được sinh ra từ init (PID 1). Một session SSH điển hình:

init (PID 1)
├── systemd
├── sshd
│   └── sshd (phiên của bạn)
│       └── bash (shell của bạn)
│           └── java -jar app.jar   ← Process bạn quan tâm
│               ├── Thread (TID 2345)
│               ├── Thread (TID 2346)
│               └── ...

Mỗi process có:

  • PID (Process ID) – số định danh duy nhất
  • PPID (Parent PID)
  • UID (User owner)
  • Working directory
  • Danh sách file descriptor (FD) đang mở
  • Vùng nhớ (heap, stack, mapped files)

Bạn có thể xem tất cả thông tin này qua /proc/<PID>/status hoặc các lệnh như ps.

Mental Model 3: File Permissions (Phân quyền)

Mỗi file/directory đều có quyền cho 3 đối tượng: Owner (chủ sở hữu), Group (nhóm), Others (người dùng khác). Quyền gồm r (read), w (write), x (execute). Khi xem với ls -l:

-rw-r--r--  1 app developers 4096 Apr 25 03:14 app.log
│└┬┘└┬┘└┬┘
│ │  │  └─ others (r--)
│ │  └──── group (r--)
│ └─────── owner (rw-)
└───────── file type (- = file, d = directory, l = symlink)

Giá trị số: r=4, w=2, x=1. Vậy 755 nghĩa là owner: rwx (7), group: r-x (5), others: r-x (5). File production thường để 644 (owner ghi được, còn lại chỉ đọc), config nhạy cảm để 600 (chỉ owner đọc ghi).

Mental Model 4: stdin, stdout, stderr và Pipe

Mỗi process sinh ra có 3 “dòng” (stream) mặc định:

  • stdin (FD 0): nơi nhận dữ liệu đầu vào.
  • stdout (FD 1): nơi xuất dữ liệu bình thường.
  • stderr (FD 2): nơi xuất lỗi.

Các toán tử redirect:

  • > ghi đè stdout vào file (overwrite).
  • >> ghi tiếp stdout vào cuối file (append).
  • 2> redirect stderr.
  • 2>&1 redirect stderr vào cùng vị trí với stdout. Chú ý thứ tự: command 2>&1 > file là sai, vì lúc gặp 2>&1 stdout vẫn đang trỏ vào terminal, nên stderr cũng trỏ vào terminal. Đúng phải là command > file 2>&1 (stdout đã trỏ vào file trước). Shorthand: command &> file.
  • | (pipe) lấy stdout của lệnh trước làm stdin của lệnh sau. Đây chính là sức mạnh “Unix philosophy”: mỗi lệnh làm một việc nhỏ, kết hợp chúng để giải quyết bài toán lớn.

3. Bộ 20+ Lệnh Phải Nằm Lòng (Reflex không cần Google)

Tôi chia thành các nhóm chức năng để bạn dễ nhớ. Các lệnh này đều có mặt trong mọi bản Linux, bạn có thể thực hành ngay trên VPS Ubuntu cá nhân.

3.1. Điều hướng & Thao tác file/directory

LệnhChức năngVí dụ hay dùng
pwdPrint Working Directory – bạn đang ở đâu?pwd
lsList filesls -la (long format + hidden), ls -lh (human size), ls -lt (sort theo thời gian)
cdChange Directorycd ~ (về home), cd - (quay lại folder trước), cd .. (lên một cấp)
mkdirMake Directorymkdir -p a/b/c (tạo cả thư mục cha)
rmRemoverm -rf dir (cảnh báo: xóa mạnh tay, không hỏi)
cpCopycp -r src/ dst/ (copy đệ quy thư mục)
mvMove / Renamemv old new (đổi tên), mv file /path/ (di chuyển)
touchTạo file rỗng hoặc cập nhật timestamptouch newfile.txt
ln -sTạo symbolic link (shortcut)ln -s /opt/app/current /etc/app
catIn toàn bộ file ra màn hình (không nên dùng cho file lớn)cat file.txt
lessXem file theo trang, có thể cuộn (phím q thoát)less huge.log
head / tailXem N dòng đầu / cuốihead -20 file, tail -f file (theo dõi thời gian thực)
wcWord count (thường dùng đếm dòng)wc -l file (số dòng)

3.2. Tìm kiếm & Xử lý text

Đây là phần làm nên “chất senior” – khả năng moi thông tin từ biển log.

  • find: Tìm file theo tên, kích thước, thời gian.
    find /var/log -name "*.log"                     # Theo tên
    find . -mtime -1                                # Sửa đổi trong 1 ngày qua
    find . -size +100M                              # Lớn hơn 100MB
    find . -name "*.java" -exec grep -l "TODO" {} \;  # Tìm file Java chứa "TODO"
  • grep: Tìm pattern trong nội dung file hoặc pipe.
    grep "ERROR" app.log
    grep -i "error" app.log                         # Không phân biệt hoa thường
    grep -r "TODO" src/                             # Đệ quy
    grep -n "ERROR" app.log                         # Kèm số dòng
    grep -A 5 -B 2 "ERROR" app.log                  # 5 dòng sau, 2 dòng trước
    grep -v "DEBUG" app.log                         # Loại trừ dòng chứa DEBUG
    grep -c "ERROR" app.log                         # Đếm số khớp
    grep -E "ERROR|WARN" app.log                    # Regex mở rộng
  • awk: Công cụ xử lý văn bản dạng cột cực mạnh.
    awk '{print $1, $3}' file                       # In cột 1 và 3
    awk -F ',' '{print $1}' file.csv                # Dùng dấu phẩy làm delimiter
    awk '$3 > 100 {print $1}' file                  # In cột 1 nếu cột 3 > 100
  • sed: Stream editor, tìm và thay thế, trích dòng.
    sed 's/foo/bar/g' file                          # Thay foo bằng bar (toàn cục)
    sed -i 's/old/new/g' file                       # Sửa trực tiếp file
    sed -n '10,20p' file                            # In dòng 10-20
  • sort & uniq: Sắp xếp và lọc trùng (input phải được sort trước khi dùng uniq).
    sort file | uniq                                # Bỏ trùng
    sort -u file                                    # Tương đương
    sort -n file                                    # Sắp xếp kiểu số
    sort | uniq -c                                  # Đếm số lần xuất hiện
  • Pipes kết hợp (ví dụ thực tế):
    # Top 10 IP truy cập nhiều nhất
    cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
    
    # Đếm số connection theo trạng thái
    netstat -ant | awk '{print $6}' | sort | uniq -c
    
    # Tìm 5 file lớn nhất trong /var
    du -ah /var | sort -rh | head -5

3.3. Quản lý tiến trình (Process Management)

ps aux                           # Xem mọi tiến trình định dạng dễ đọc
ps -ef                           # Định dạng khác
pgrep -f java                    # Tìm PID của process khớp pattern
top                              # Giám sát tài nguyên thời gian thực (thoát: q)
htop                             # Phiên bản đẹp hơn (cài `apt install htop`)

kill <pid>                       # Gửi SIGTERM (15) – yêu cầu dừng gracefully
kill -9 <pid>                    # Gửi SIGKILL – dừng ngay lập tức, không cleanup
kill -HUP <pid>                  # Gửi SIGHUP – thường dùng reload config (nginx)

nohup command &                  # Chạy lệnh trong nền và không chết khi logout
disown -a                        # Tách job khỏi shell hiện tại

Hiểu sâu về signal:

  • SIGTERM (15): Cho phép tiến trình tự dọn dẹp (đóng file, flush buffer). Java application có thể bắt signal này thông qua Runtime.getRuntime().addShutdownHook() và chạy logic graceful shutdown.
  • SIGKILL (9): Kernel hủy tiến trình ngay, không cho cơ hội kháng cự. Chỉ dùng khi treo cứng hoặc không phản hồi SIGTERM.
  • SIGHUP (1): Ban đầu gửi khi terminal đóng, nay thường được dùng để báo cho tiến trình reload configuration (ví dụ nginx -s reload).

3.4. Giám sát tài nguyên (Resource Monitoring)

free -h                         # Xem tổng bộ nhớ và swap
vmstat 1                        # Thống kê bộ nhớ, CPU, I/O mỗi giây
iostat -x 1                     # Thống kê I/O chi tiết (cần sysstat)
df -h                           # Dung lượng ổ đĩa
du -sh /var/*                   # Dung lượng từng thư mục con của /var
uptime                          # Thời gian hoạt động và load average

Load average là gì? Ba con số trong uptime thể hiện số tiến trình trung bình đang chờ CPU (hoặc I/O) trong 1, 5, 15 phút cuối. Nếu load > số CPU core, hệ thống đang quá tải.

3.5. Mạng (Networking)

ss -tnlp                        # Xem các socket đang listen (t: TCP, n: numeric, l: listen, p: process)
ss -tn state established        # Xem các kết nối TCP đã thiết lập

ping host                       # Gửi ICMP echo request (có thể bị chặn)
curl -v http://host/api         # HTTP client dòng lệnh (verbose)
telnet host port                # Kiểm tra TCP mở (nếu port mở, connection thành công)
nc -zv host port                # Kiểm tra port (netcat) nhanh hơn
dig host.com                    # Tra cứu DNS chi tiết
nslookup host.com               # Tra cứu DNS đơn giản

lsof -i :8080                   # Ai đang chiếm cổng 8080
lsof -p <pid>                   # Liệt kê tất cả file descriptor của process (socket, file)
lsof | grep deleted             # Tìm file đã xóa mà vẫn bị tiến trình giữ

3.6. Phân quyền & Người dùng

chmod 755 file                 # rwxr-xr-x
chmod +x script.sh             # Thêm quyền execute
chown user:group file          # Đổi chủ sở hữu và nhóm
whoami                         # Bạn là ai?
id                             # User ID, Group ID
groups                         # Các nhóm của bạn
sudo command                   # Chạy lệnh với quyền root (nếu được cấp)

3.7. Nén & Giải nén

tar -czf out.tar.gz dir/       # Nén (c: create, z: gzip, f: file)
tar -xzf out.tar.gz            # Giải nén
tar -tzf out.tar.gz            # Xem nội dung
zip -r out.zip dir/
unzip out.zip
gzip file                      # Nén thành file.gz
zgrep "pattern" file.gz        # Grep trực tiếp file gzip

3.8. SSH & SCP

ssh user@host                  # Kết nối SSH
ssh -i ~/.ssh/id_rsa user@host # Dùng key cụ thể
ssh user@host "ls -la"         # Chạy lệnh từ xa
scp file user@host:/path/      # Copy file qua SSH
rsync -avz --progress src/ user@host:/dst/  # Đồng bộ thư mục nhanh hơn scp

3.9. Cron & Systemd (quản lý tác vụ định kỳ và service)

crontab -e                     # Sửa cron jobs của user
# Format: phút giờ ngày tháng thứ lệnh
# */5 * * * * /script.sh       # Mỗi 5 phút
# 0 3 * * * /backup.sh         # 3:00 AM mỗi ngày

systemctl status nginx         # Trạng thái service
systemctl restart nginx        # Khởi động lại
journalctl -u nginx -f         # Xem log service (live)

4. Quy Trình Debug Production Chuyên Nghiệp (Senior Workflow)

Khi có sự cố, đừng làm mò. Hãy đi theo lối mòn có hệ thống từ tổng quan đến chi tiết. Dưới đây là các “kịch bản đổ lỗi” đã được kiểm chứng.

4.1. App chậm không rõ nguyên nhân

  1. Kiểm tra tổng quan: top, free -h, df -h, uptime.
  2. Xác định Java process: JAVA_PID=$(pgrep -f "MyApp.jar").
  3. JVM stats: jstat -gc $JAVA_PID 1s để xem tần suất GC. Nếu Full GC liên tục → nghi ngờ memory leak.
  4. Thread dump: jstack $JAVA_PID > /tmp/td.txt và phân tích bằng grep -A 30 "RUNNABLE", tìm thread tiêu thụ CPU.
  5. Heap dump: jmap -dump:format=b,file=/tmp/heap.hprof $JAVA_PID (file lớn, tải về máy phân tích bằng Eclipse MAT). Chỉ làm khi thực sự cần vì có thể gây treo app tạm thời.
  6. Kiểm tra kết nối: ss -tn state established | wc -l tổng số connection, lsof -p $JAVA_PID | grep TCP | wc -l chỉ riêng Java.
  7. Log gần đây: tail -1000 app.log | grep ERROR hoặc journalctl -u myapp --since "10 min ago".

4.2. Disk đầy 100%

  1. df -h xác định partition đầy.
  2. du -sh /var/* | sort -h tìm thư mục con lớn nhất.
  3. find / -type f -size +1G -exec ls -lh {} \; 2>/dev/null tìm file khổng lồ.
  4. lsof | grep deleted tìm file ảo còn giữ chỗ.
  5. Xử lý: truncate -s 0 file, không rm.

4.3. Service A không gọi được service B

  1. DNS: dig service-b.internal.
  2. TCP: nc -zv service-b 8080.
  3. HTTP: curl -v http://service-b:8080/health.
  4. Bên B: ss -tnlp | grep 8080 xem có listen không? Firewall?
  5. Trace: traceroute service-b.internal (nếu được phép).

4.4. Process bị OOM Killer giết

Hệ thống Linux có cơ chế Out-Of-Memory Killer: khi RAM cạn, nó chọn tiến trình “xấu nhất” (dùng nhiều RAM) để kill. Bạn sẽ không thấy exception bình thường, app tự nhiên biến mất. Kiểm tra kernel log:

dmesg | grep -i "killed process"
# "Out of memory: Killed process 1234 (java)"

Khắc phục: giảm Java heap (-Xmx), tăng swap/RAM, hoặc fix memory leak.

4.5. Theo dõi log nâng cao

tail -f app.log | grep --color=always -E "ERROR|WARN"   # Highlight
tail -f app.log | grep ERROR | tee errors.log            # Vừa xem vừa lưu
multitail app1.log app2.log                              # Nhiều file cùng lúc

5. Shell Scripting Cơ Bản – Đủ Dùng Cho Dev

Tự động hóa việc kiểm tra sức khỏe, backup, restart app là kỹ năng cần có.

Cấu trúc mẫu:

#!/bin/bash
set -euo pipefail   # Thoát nếu lỗi (-e), biến không đặt (-u), pipe fail (-o pipefail)

NAME="MyApp"
echo "Starting $NAME"

if [ "$1" = "start" ]; then
    java -jar app.jar &
    echo "$NAME started with PID $!"
else
    echo "Usage: $0 start"
fi

Script hữu ích: Auto restart Java khi crash

#!/bin/bash
while true; do
    java -jar app.jar
    echo "App crashed at $(date), restarting in 5s..."
    sleep 5
done

Script backup database hàng ngày

#!/bin/bash
DATE=$(date +%Y%m%d)
BACKUP_DIR=/backup
DB_PASS=secret
mysqldump -u root -p$DB_PASS mydb | gzip > "$BACKUP_DIR/db-$DATE.sql.gz"

# Xóa backup cũ hơn 30 ngày
find "$BACKUP_DIR" -name "db-*.sql.gz" -mtime +30 -delete

Best practices:

  • Luôn dùng shebang #!/bin/bash (hoặc #!/usr/bin/env bash cho portable).
  • set -euo pipefail tránh script chạy tiếp khi gặp lỗi.
  • Quote biến "$var" để tránh word splitting.
  • Kiểm tra exit code của lệnh quan trọng bằng if command; then ....

6. Biến Môi Trường & Dotfiles – Tối Ưu Môi Trường Làm Việc

Biến môi trường:

export DB_PASSWORD=topsecret       # Chỉ hiệu lực cho shell hiện tại
echo $DB_PASSWORD
env                                 # Liệt kê tất cả

Để tồn tại vĩnh viễn, thêm vào ~/.bashrc (hoặc ~/.zshrc):

echo 'export JAVA_OPTS="-Xmx2g"' >> ~/.bashrc
source ~/.bashrc

Dotfiles quan trọng:

  • ~/.bashrc: cấu hình shell tương tác (alias, function).
  • ~/.bash_profile hoặc ~/.profile: chạy khi login shell.
  • ~/.ssh/config: cấu hình kết nối SSH (đặt tên, key riêng cho host).
  • ~/.gitconfig: cấu hình Git toàn cục.

Alias nên có:

alias ll='ls -alh'
alias ..='cd ..'
alias gs='git status'
alias gl='git log --oneline --graph --all -20'
alias k='kubectl'
alias mylog='tail -f /var/log/myapp/app.log | grep --color=always -E "ERROR|WARN|"'

7. Docker Basics (Liên quan mật thiết đến Linux)

Container thực chất là một process Linux bị cô lập bởi namespaces và cgroups. Hiểu Linux sẽ giúp bạn làm chủ Docker:

docker ps                         # Container đang chạy
docker logs -f <container>        # Live log
docker exec -it <container> bash  # Chui vào container
docker exec <container> ps aux    # Chạy lệnh bên trong
docker stats                      # Giám sát tài nguyên container
docker system prune -a --volumes  # Dọn dẹp (cẩn thận!)

Khi debug bên trong container, bạn có thể dùng toàn bộ kỹ năng Linux ở trên.


8. Bài Tập Tự Rèn Luyện

Beginner: Thiết lập VPS cá nhân

  1. Mua VPS Ubuntu (DigitalOcean/Vultr $5-6/tháng).
  2. Tạo user mới, setup SSH key, vô hiệu hóa root login.
  3. Cài Java 17, Docker, Git.
  4. Tạo script /opt/myapp/run.sh ghi "Hello $(date)" >> /tmp/output.log.
  5. Cấu hình cron chạy mỗi 5 phút, sau 30 phút kiểm tra file có 6 dòng.

Intermediate: Phân tích file log 100MB

Tải file mẫu Apache/Nginx access.log 100MB (tự tạo bằng generator). Thực hiện hoàn toàn bằng pipe:

  • Top 10 IP request nhiều nhất.
  • Số request mỗi giờ.
  • Top 10 URL được gọi.
  • Số request lỗi (HTTP 5xx) theo IP.
  • Không dùng Python!

Advanced: Debug memory leak trong container

  1. Chạy một Java app (cố tình có memory leak nhỏ) trong Docker.
  2. Dùng top thấy memory tăng dần.
  3. Lấy thread dump (jstack), heap dump (jmap).
  4. Tải về phân tích bằng Eclipse MAT.
  5. Graceful restart: kill -15 <pid> hoặc docker restart.

9. Phỏng Vấn: Họ Hay Hỏi Gì? (Và Trả Lời Sao)

Q1: Phân biệt kill -9kill -15?

kill -15 (SIGTERM) cho phép process tự cleanup (đóng resource, flush log), Java bắt qua shutdown hook. kill -9 (SIGKILL) kernel hủy ngay, không chống cự. Luôn ưu tiên SIGTERM, nếu sau timeout (ví dụ 30s) vẫn không chết mới dùng -9.

Q2: Khác nhau giữa >>>?

> overwrite nội dung file, >> append vào cuối. Nếu file chưa có, cả hai cùng tạo mới. Bẫy: command 2>&1 > file sai thứ tự redirect, stderr vẫn vào terminal. Phải command > file 2>&1.

Q3: Tại sao xóa file log bằng rm mà disk không giảm?

Vì tiến trình vẫn giữ file descriptor mở. rm chỉ xóa liên kết (filename), inode và dữ liệu chỉ giải phóng khi không còn ai tham chiếu. Dùng lsof | grep deleted để phát hiện. Fix: truncate -s 0 hoặc restart process.

Q4: Bạn debug service chậm trong hệ thống microservices (20 service) trên K8s ra sao?

(Đây là câu hỏi kiến trúc – xem phần phụ lục trong tài liệu gốc. Tóm tắt: bắt đầu từ Distributed Tracing (Jaeger/Zipkin), Metrics (Prometheus/Grafana), Centralized Logging (ELK/Loki); xuống K8s: kubectl top, kubectl logs, kubectl exec; vào container: các lệnh Linux như top, jstack, ss; cuối cùng là kiểm tra các dịch vụ phụ thuộc (DB slow query, Redis slowlog)).


10. Checklist Tự Đánh Giá

Đánh dấu [x] khi bạn thực sự thành thạo:

  • 30 lệnh daily: ls, grep, find, ps, top, kill, tail, awk, sed, du, df, lsof, ss, curl, ssh
  • Pipe & redirect: Tự tin viết pipeline 3+ lệnh để extract insight từ log.
  • Process management: Nắm chắc signal, biết khi nào dùng SIGTERM/KILL/HUP.
  • File permissions: Đọc được quyền số, biết đặt chmod 644 cho file, 755 cho executable.
  • 5 Production scenarios: Tự debug: app chậm, disk full, network fail, OOM, log analysis.
  • Shell scripting: Viết được script có vòng lặp, điều kiện, cron tự động.
  • Java debug commands: jstack, jmap, jstat, jcmd.
  • Docker basics: docker logs, docker exec, docker stats.
  • Vim/nano: Thoát được mà không hoảng loạn (:q!). Sửa file config cơ bản.
  • SSH key: Tạo cặp key, đăng nhập không mật khẩu.

Lời kết: Tài nguyên và công cụ nâng cao

Sách:

  • The Linux Command Line (William Shotts) – miễn phí tại linuxcommand.org.
  • Linux for Developers – các bản thực hành cụ thể trên Packt/O’Reilly.

Trang luyện tập:

Công cụ nên cài ngay:

  • htop (top đẹp), ncdu (du tương tác), jq (xử lý JSON), rg (ripgrep – grep siêu nhanh), bat (cat có syntax highlight), fzf (tìm kiếm mờ), tmux (quản lý nhiều terminal).

Khi gặp bế tắc:

  • man <lệnh> – hướng dẫn đầy đủ.
  • tldr <lệnh> – ví dụ thực tế (cài npm install -g tldr).
  • Hỏi ChatGPT/Claude: “How to do X in Linux bash?”

Hãy nhớ: Linux không phải là “học thuộc một lần rồi thôi”, mà là kỹ năng mài giũa hàng ngày. Mỗi lần debug production, bạn lại thêm một mảnh ghép vào bộ não. 20 lệnh, 4 mental model, 5 scenario – đó chính là bộ sinh tồn của bạn. Chúc bạn không bao giờ phải hoảng loạn trước terminal đen lúc 2 giờ sáng!


Phụ lục: Cheatsheet 1 Trang In Ra Dán Tường

=== APP CHẬM ===
top                                # CPU/memory tổng
ps -ef | grep java                 # Tìm Java PID
jstack <pid> > /tmp/td.txt         # Thread dump
jstat -gc <pid> 1s                 # GC live
jmap -dump:format=b,file=heap.hprof <pid>  # Heap dump

=== DISK ĐẦY ===
df -h                              # Filesystem
du -sh /var/* | sort -h            # Folder lớn nhất
find / -type f -size +1G 2>/dev/null
lsof | grep deleted                # File hold by process

=== NETWORK FAIL ===
ping host                          # ICMP
nc -zv host 8080                   # TCP test
curl -v http://host                # HTTP
ss -tnlp | grep 8080               # Service listen?
dig host                           # DNS

=== LOG ANALYSIS ===
tail -f /var/log/app.log | grep ERROR
grep -A 20 "ERROR" app.log         # Stack trace 20 dòng
grep -c "ERROR" app.log            # Count
awk '/ERROR/ {print $1}' app.log | sort | uniq -c

=== PROCESS ===
pgrep -f java                      # Find PID
kill -15 <pid>                     # Graceful
kill -9 <pid>                      # Force

=== K8S DEBUG ===
kubectl get pods
kubectl logs -f <pod>
kubectl exec -it <pod> -- bash
kubectl top pods
kubectl describe pod <pod>

Hãy kết nối

Nếu bạn quan tâm tới việc hợp tác, có câu hỏi về bài viết, hay chỉ đơn giản muốn chuyện trò về backend — cứ ping mình nhé.